一、Spring注入示例
这里以注解的形式来进行注入示例的演示。
(1)通过@ComponentScan扫描com.ywl.leetcode下面所有的类。
@ComponentScan("com.ywl.leetcode")public class AppConfig {
}
(2)申明一个类并加上@Component注解,无参构造函数中打印日志记录。
@Componentpublic 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 beanNames = new ArrayList(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() {@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() {@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作用域为原型的会进这个分支初始化beanelse 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
@Overrideprotected 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。
修改代码如下图所示:
@Componentpublic 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() {@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的过程其实是这样的: