一、Spring注入示例
这里以注解的形式来进行注入示例的演示。
(1)通过@ComponentScan扫描com.ywl.leetcode下面所有的类。
@ComponentScan("com.ywl.leetcode")
public class AppConfig {
}
(2)申明一个类并加上@Component注解,无参构造函数中打印日志记录。
@Component
public class TestService {
public TestService() {
System.out.println("testService创建结束");
}
}
(3)启动spring容器,观察运行结果。
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
TestService testService = context.getBean(TestService.class);
}
(4)运行结果
运行完毕,TestService这个bean创建完毕。
二、疑问点:TestService这个bean是在spring容器启动时被创建出来的还是在context.getBean时候被创建出来的?
验证方式:注释getBean代码。仍打印创建结束日志,说明是在spring容易启动时被创建出来的。
代码如下:
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
// TestService testService = context.getBean(TestService.class);
}
//运行结果testService创建结束
三、阅读spring源码来验证bean初始化的过程
(1)普通类的java初始化过程。
java文件经过虚拟机编译,编译成.class字节码文件,当运行main方法时,会启动JVM虚拟机,通过虚拟机从磁盘上将.class字节码文件存储到方法区或者元数据空间,当对象被new出来时,会在堆上分配一块内存用来存储这个对象。
(2)bean的初始化过程。
开始的初始化过程与普通类的初始化过程一样,会由jvm分配一块内存空间给这个对象。当spring容器开始加载时,首先会解析AppConfig.class。
发现AppConfig中指定了扫描路径,需要找到扫描路径中需要被spring容器加载的类,即加了@Component、@Service、@Controller等注解的类,对这些类进行解析。
解析后,这些类会被构建成一个spring中的BeanDefinition类,来存储类中的所有基本信息,比如beanClassName(类的type)、parentName(类的父类名字)、scope(类的作用域)、lazyInt(是否懒加载)等。
BeanDefinition详细存储信息可以查看org.springframework.beans.factory.config.BeanDefinition。
构建成BeanDefinition后会把他放到一个map中,key为beanName,value为BeanDefinition。
最后对这些类进行遍历,会在spring加载时对单例类并且不是懒加载的类进行bean的初始化,初始化完毕后,会放入到一个单例池的map中,即singletonMap。
而对于那些不是单例或者是懒加载的类,就会在调用getBean方法时被初始化出来。
· 查看TestService的beanDefinition
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
BeanDefinition beanDefinition = context.getBeanDefinition("testService");
System.out.println(beanDefinition);
}
beanDefinition里面存储的信息:
// Generic bean: class [com.ywl.leetcode.spring.ready.TestService];
// scope=singleton; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0;
// autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null;
// initMethodName=null; destroyMethodName=null;
· java对象和spring bean的区别
上述初始化过程中可以看到java对象和bean其实有很大的区别。总而言之spring bean是一个java对象,而java对象不一定是一个bean。
因为我读完spring源码后,个人认为bean与java对象最大的区别在于,java对象就是一个普通的对象,而bean是一个具有spring生命周期的一个对象。
· 调用AnnotationConfigApplicationContext
public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
this();
register(annotatedClasses);
refresh();
}
开启断点进行调试,发现refresh方法执行完毕,发现构造函数中的日志被打印,说明初始化的核心方法在refresh中。
· refresh()
synchronized (this.startupShutdownMonitor) { //... //这里主要是扫描指定路径下加了主键的类,并放入到beanDefinitionMap中
invokeBeanFactoryPostProcessors(beanFactory);
//...
在执行invokeBeanFactoryPostProcessors之前的beanDefinitionMap没有我们需要的类。
执行完后,出现了testService。
debug执行完finishBeanFactoryInitialization后发现,打印了构造函数中的日志。说明bean初始化的核心方法在该方法中。
// ...
finishBeanFactoryInitialization(beanFactory);
// ...
· finishBeanFactoryInitialization()
核心代码在beanFactory.preInstantiateSingletons中,该方法的作用为加载非懒加载的单例bean。
//...
beanFactory.preInstantiateSingletons();
//...
· preInstantiateSingletons()
@Override
public void preInstantiateSingletons() throws BeansException {
//... //获取需要加载的所有beanName,进行遍历 List<String> beanNames = new ArrayList<String>(this.beanDefinitionNames);
for (String beanName : beanNames) { //根据beanDefinitionMap获取beanDefinition RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
//不是抽象类 并且 是单例 并且 不是懒加载 才可以在这时候被初始化 if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) { //判断是否是factoryBean if (isFactoryBean(beanName)) {
final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
boolean isEagerInit;
if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
@Override
public Boolean run() {
return ((SmartFactoryBean<?>) factory).isEagerInit();
}
}, getAccessControlContext());
}
else {
isEagerInit = (factory instanceof SmartFactoryBean &&
((SmartFactoryBean<?>) factory).isEagerInit());
}
if (isEagerInit) {
getBean(beanName);
}
} else { //testService会走进这里来初始化bean getBean(beanName);
}
}
}
//...
}
上述的方法其实很简单,遍历所有需要初始化的bean,就是遍历存储beanName的list,并根据beanName作为key去查询beanDefinitionMap中的beanDefinition,校验对应的类,只有不是抽象类、是单例、不是懒加载的类才可以在spring容器初始化时被初始化。
bean初始化的方法在getBean(beanName)中。
四、核心方法doGetBean()
protected <T> T doGetBean(
final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
throws BeansException { //校验beanName合法性
final String beanName = transformedBeanName(name);
Object bean;
//查询缓存池中是否存在该bean //当前处于spring加载时,所以testService肯定为null
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
if (logger.isDebugEnabled()) { //校验是否bean在初始化中 if (isSingletonCurrentlyInCreation(beanName)) {
logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
"' that is not fully initialized yet - a consequence of a circular reference");
}
else {
logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
}
}
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
else {
//bean是否在初始化中,循环依赖会涉及到
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
//...
try {
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);
// 查询该bean是否存在depend on依赖的其他bean
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) { // 遍历依赖的bean,对bean进行查找与初始化 for (String dep : dependsOn) {
if (isDependent(beanName, dep)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}
registerDependentBean(dep, beanName);
try {
getBean(dep);
}
catch (NoSuchBeanDefinitionException ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
}
}
}
//bean是否为单例,testService为单例,所以会走进这个分支
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
try { //创建bean
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
destroySingleton(beanName);
throw ex;
}
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
//bean作用域为原型的会进这个分支初始化bean
else if (mbd.isPrototype()) {
Object prototypeInstance = null;
try {
beforePrototypeCreation(beanName);
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
} //...}
doGetBean()中存放许多核心的方法,比如getSingleton、createBean等,doGetBean大致的流程为上述源码展示的那样:
(1)会去缓存池中查找该bean是否被加载过,如果被加载过,返回。由于此时spring加载时,会加载testService,因此testService该bean不存在缓存池中。
(2)进行单例作用域与原型作用域bean的创建,由于testService为单例因此,我们只需要关注单例的创建即可。
除此之外我们需要关注几个核心的方法:
· getSingleton
protected Object getSingleton(String beanName, boolean allowEarlyReference) {Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
大致的意思为:
(1)先从singletonObjects一级缓存中查询是否有bean,如果有则返回。
(2)判断是否bean处于正在初始化中,这个条件主要是为了循环依赖使用,循环依赖情况下,可能存在bean正在创建中的情况。这种情况今天的初始化过程先不涉及。
(3)去三级缓存earlySingletonObjects中查询是否有bean,如果有则返回。
(4)去二级缓存singletonFactories中查询是否存在bean的工厂,如果存在,获取该bean工厂对应的bean,放到三级缓存中。返回。
上述代码中展示了spring容器总共有三级缓存,来保证获取bean的性能。
· createBean
@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException {
//...
// 从beanDefinition中取出该bean的class类型
Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
mbdToUse = new RootBeanDefinition(mbd);
mbdToUse.setBeanClass(resolvedClass);
}
//...
//debug到此处运行完毕后发现,运行了构造函数的日志打印
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
if (logger.isDebugEnabled()) {
logger.debug("Finished creating instance of bean '" + beanName + "'");
}
return beanInstance;
}
· doCreateBean
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
throws BeanCreationException {
//...
if (instanceWrapper == null) {//实例化对象instanceWrapper = createBeanInstance(beanName, mbd, args);
}
//...
}
调用完createBeanInstance后对象被new出来,并执行了构造函数中的日志打印。createBeanInstance中做的事情就是推断出合适的构造函数,通过反射来构造对象。
此时的instanceWrapper只是一个普通的对象,但是并不是一个bean。
修改代码如下图所示:
@Component
public class TestService {
@Autowired
private UserService userService;
public TestService() {
System.out.println("testService创建结束");
}
}
通过@Autowired自动注入userSerivce,debug到createBeanInstance执行完毕后发现此时的userSerivce并没有被注入。只是对象被构造完成,执行完了构造函数。
此时的testSerivce还不是bean,也可以使用指定testService的初始化方法来观察,有没有执行bean的初始化方法。
运行到createBeanInstance后,没有执行bean的初始化方法。
继续执行代码:
//判断是否允许循环依赖//条件一:单例//条件二:私有属性默认为true,不过可以修改,私有属性在beanFactory中//条件三:是否处于正在创建队列中。在调用createBean方法之前,已经放在了创建队列中。boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isDebugEnabled()) {
logger.debug("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
} //将bean生成的bean工厂放到二级缓存中 addSingletonFactory(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException { //这里主要判断是否存在aop,在后面的spring aop中会讲到,这里不做阐述
return getEarlyBeanReference(beanName, mbd, bean);
}
});
}
//...
继续执行代码,发现在populateBean之后完成了userService的注入:
//...bject exposedObject = bean;
try {
populateBean(beanName, mbd, instanceWrapper);
if (exposedObject != null) {
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
}//...
· populateBean
populateBean主要会解析注入时使用的注解比如@Autowired或@Resource或者Xml文件注入来进行依赖注入。最后都会调用beanFactory.getBean对需要依赖注入的bean进行初始化。
· 回到doGetBean继续执行getSingleton方法
源码如下:
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "'beanName' must not be null");
synchronized (this.singletonObjects) { //先从一级缓存中获取,是否存在bean Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
//... //这里会将该bean放到集合中,表示正在创建该bean beforeSingletonCreation(beanName) //... try {//该singletonFactory表示,调用createBean创建出来的beanFactory //根据bean工厂获取工厂中的bean
singletonObject = singletonFactory.getObject() newSingleton = true;
}
//...
finally {
if (recordSuppressedExceptions) {
this.suppressedExceptions = null;
} //从正在创建bean的集合中将该bean移除,意味着bean马上就要创建成功了 afterSingletonCreation(beanName);
}
if (newSingleton) { //将bean放入到一级缓存中 addSingleton(beanName, singletonObject);
}
}
return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
}
五、普通bean的初始化过程-总结
以上就是普通spring bean的初始化过程,跟着debug阅读bean的初始化过程其实并不复杂,期间有比较多的参数以及为什么需要用到三级缓存,在接下来的循环依赖和aop中都会涉及到,先可以放下这些细节,学习下在没有循环依赖情况下bean的初始化过程。
用画图来总结bean的过程其实是这样的: