如果没有用Spring框架,程序员创建对象的方式有以下5种方式:
- 使用new关键字
- 使用Class的newInstance()方法
- 使用Constructor的newInstance()方法
- 使用clone()方法
- 使用反序列化
虽然这些方式都能实现创建一个对象,但在当今的应用开发中对于频繁使用到的类,比如dao层对象,如果频繁的创建对象,无形中会加剧内存的产生,对系统性能产生影响,而且也是不利于管理的。为了解决上述提到的痛点,Spring就应运而生,并且业界在进行企业级开发的时候,都会把它引入到自己的项目中。我们知道Spring在创建对象的时候,有两种方式,一种是singleton(单例),一种是prototype(原型)。默认是singleton的。那么Spring是如何创建对象的呢?我们下面就来聊聊Spring对象创建流程。
Spring创建对象流程
Spring创建对象的流程在AbstractApplicationContext的refresh方法中。
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
// Prepare this context for refreshing.
// 1.刷新前的预处理。
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
// 2.这里会判断能否刷新,并且返回一个BeanFactory, 刷新不代表完成情况,主要是先执行Bean的销毁,然后重新生成一个BeanFactory,再在接下来的步骤中重新去扫描等等
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
// 3.准备BeanFactory
// 3.1设置BeanFactory的类加载器、SpringEL表达式解析器、类型转化注册器
// 3.2添加三个BeanPostProcessor,注意是具体的BeanPostProcessor实例对象
// 3.3记录ignoreDependencyInterface
// 3.4记录ResolvableDependency
// 3.5添加三个单例Bean
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
// 4.子类来设置一下BeanFactory
postProcessBeanFactory(beanFactory);
StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
// Invoke factory processors registered as beans in the context.
// 5.BeanFactory准备好了之后,执行BeanFactoryPostProcessor,开始对BeanFactory进行处理
// 默认情况下:
// 此时beanFactory的beanDefinitionMap中有6个BeanDefinition,5个基础BeanDefinition+AppConfig的BeanDefinition
// 而这6个中只有一个BeanFactoryPostProcessor:ConfigurationClassPostProcessor
// 这里会执行ConfigurationClassPostProcessor进行@Component的扫描,扫描得到BeanDefinition,并注册到beanFactory中
// 注意:扫描的过程中可能又会扫描出其他的BeanFactoryPostProcessor,那么这些BeanFactoryPostProcessor也得在这一步执行
invokeBeanFactoryPostProcessors(beanFactory); // scanner.scan()
// Register bean processors that intercept bean creation.
// 6.将扫描到的BeanPostProcessors实例化并排序,并添加到BeanFactory的beanPostProcessors属性中去
registerBeanPostProcessors(beanFactory);
beanPostProcess.end();
// Initialize message source for this context.
// 7.设置ApplicationContext的MessageSource,要么是用户设置的,要么是DelegatingMessageSource
initMessageSource();
// Initialize event multicaster for this context.
// 8.设置ApplicationContext的applicationEventMulticaster,要么是用户设置的,要么是SimpleApplicationEventMulticaster
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
// 9.给子类的模板方法
onRefresh();
// Check for listener beans and register them.
// 10.把定义的ApplicationListener的Bean对象,设置到ApplicationContext中去,并执行在此之前所发布的事件
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
// 11.初始化所有剩下的单实例bean
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
// 12.完成BeanFactory的初始化创建工作:IOC就创建完成。
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
contextRefresh.end();
}
}
}
Spring创建bean的详细流程
Spring最重要的功能就是帮助程序员创建对象(也就是IOC),而启动Spring就是为创建Bean对象做准备,所以我们先明白Spring到底是怎么去创建Bean的,也就是弄明白Bean的生命周期。
Bean的生命周期就是指:在Spring中,一个Bean是如何生成的,如何销毁的。
首先来看看Bean生命周期流程图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kVXNGFeb-1690619325407)(/Volumes/My Passport/个人文档/课程/Bean的生命周期流程.jpg)]
bean创建的这个过程大致可以分为五步:
-
扫描用户定义的配置信息
-
实例化bean;
-
bean属性填充;
-
初始化bean;
-
后置操作;
那我们就基于这五大步来看看Spring是如何创建bean的。
1.扫描用户定义的配置信息
当我们用以下代码创建一个Spring容器时,都做了什么呢?
new AnnotationConfigApplicationContext(AppConfig.class);
在AnnotationConfigApplicationContext的构造方法中会调用this()构造方法。在这个构造方法中会实例化一个ClassPathBeanDefinitionScanner对象。这个扫描器对象的scan()方法,就是用来扫描用户在xml中配置的信息或者在配置类上标注的注解。scan()方法里的核心方法是doScan(basePackages)。doScan(basePackages)方法中的核心逻辑:
- findCandidateComponents(basePackage),该方法用来获得用户配置的basePackage中所有的BeanDefinition的Set集合。进入findCandidateComponents(basePackage)方法后,基本都会走else分支,即调用scanCandidateComponents(basePackage)方法。下面来说一下scanCandidateComponents(basePackage)方法的主要逻辑。首先拼接一个路径名,例如:classpath*:com/ray/**/*.class,从路径名也可以看出来,Spring扫描的是.class文件。之后读取这些.class文件,并封装成为Resource,即File对象,之后存入Resource数组。之后程序遍历Resource数组,判断当前Resource对象是不是可读,如果可读,则根据Resource对象构建一个MetadataReader,即当前遍历的Resource对象的元数据读取器。MetadataReader的作用就是可以读取类上配置的注解,判断是不是一个抽象的类,类实现的接口,父类是谁等功能,底层使用的是ASM技术。接着用isCandidateComponent(metadataReader)方法,作用是判断给定的metadataReader是不是被排除的,还是包含的。Spring在启动的时候会自动在includeFilters添加一个Component类进去。之后在isConditionMatch(metadataReader)判断类上面是否标注了@Conditional注解,如果类上没有标注@Conditional注解,则不能被跳过,则isConditionMatch(MetadataReader metadataReader) 返回true,isCandidateComponent(MetadataReader metadataReader)也返回true。之后构造一个ScannedGenericBeanDefinition对象,其中最主要的步骤,是把beanClassName设置到了AbstractBeanDefinition中beanClass属性中。为什么beanClass的类型是Object呢?因为在扫描时仅仅是赋值类的名字,还没有真正的加载类。当要实例化一个Bean的时候,才把具体的Class对象赋值给beanClass属性。接着判断Bean是独立的,并且是一个具体的类(不是接口或者不是抽象类)。或者Bean是独立的,并且是抽象的,并且有@Lookup注解,才能是一个候选的Bean。如果判断都是true,则将构造的ScannedGenericBeanDefinition对象加入到candidates集合中,最后方法返回candidates集合。
- 遍历candidates的BeanDefinition集合,解析出ScopeMetadata、beanName,接下来解析@Lazy、@Primary、@DependsOn、@Role、@Description注解。
- checkCandidate(beanName, candidate),检查Spring容器中是否已经存在该beanName。如果返回true,则将BeanDefinition和beanName封装为BeanDefinitionHolder。将beanName和beanDefinition关系注册到DefaultListableBeanFactory中的beanDefinitionMap中。
【扩展资料】
UserService里面有一个属性User,设置User的scope为prototype,那么创建多个UserService实例,其中的User属性创建了几个对象呢?其实只创建了一个对象。即时User的scope为prototype,但是UserService是单例的,所以里面的User属性也只被赋值了一次。
2.实例化bean
扫描用户配置的Bean信息结束之后,就进入到实例化非懒加载的Bean这一步了。
Spring框架会调用DefaultListableBeanFactory类的getBean方法去实例化Bean。
在调用如下代码创建Spring容器时
new ClassPathXmlApplicationContext("spring.xml")
根据方法的调用栈,会调用到AbstractApplicationContext类的refresh()方法,在refresh()方法中调用finishBeanFactoryInitialization(beanFactory)方法,来实例化所有非懒加载的单例bean。真正实例化bean的方法,包含在接口ConfigurableListableBeanFactory的preInstantiateSingletons()方法中,框架调用的实现类是DefaultListableBeanFactory类中的preInstantiateSingletons()方法。下面我们分析一下这个方法的主要逻辑。
- 首先根据this.beanDefinitionNames集合初始化一个beanNames集合,接下来遍历beanNames集合。
- 根据遍历的beanName调用getMergedLocalBeanDefinition(beanName)方法,获得合并的BeanDefinition。
- 判断这个BeanDefinition对象如果不是抽象的,并且是单例模式,并且不是延迟初始化的,那么接下来判断是否是工厂bean,如果是则从容器中获得工厂bean对象。
- 调用getBean(beanName)方法,创建单例对象。
- 遍历beanNames集合,调用getSingleton(beanName)方法,从单例池中获得对象,如果对象实现了SmartInitializingSingleton接口,调用afterSingletonsInstantiated()方法。
容器会先去尝试getBean–>doGetBean–>getSingleton等操作在这些操作都拿不到对象以后才会开始着手创建对象。
需要说的是getSingleton会尝试从三级缓存中依次去获取Bean,当所有缓存都获取不到时就可以确认当前bean没有被创建,然后就可以启动创建的相关动作。
(1)利用BeanDefinition检查是否有依赖的bean(配置了@DependOn注解)如有,需要先加载依赖bean。
(2)利用BeanDefinition检查是否单例bean,是走单例bean的创建流程,不是再判断是否是原型bean,是走原型bean创建,否则都是另一套路径创建。
(3)开始实例化,调用getSingleton,此时传入的是对象工厂(ObjectFactory)的实现类,因为对象工厂是函数式接口,这里传入的其实就是createBean的lamda表达式。
(4)将当前bean加入到正在创建bean的一个set。
(5)调用对象工厂的getObject方法,因为我们再上面已经传入了对象工厂(通过lamda表达式传入)这里相当于调用刚刚的lamda表达式,调用里面的createBean方法。
(6)createBean去调了doCreateBean又调了createBeanInstance,在这里底层通过反射技术获取构造参数将对象创建了出来,此时的对象只是通过空参构造创建出来的对象,他并没有任何的属性。
(7)调用addSingletonFactory将实例化完成的bean加入到三级缓存,到这里实例化就算是结束了。
3、bean属性填充
属性填充其实就为自身属性进行赋值的过程,根据我们的DI注解这里会先从三个缓存中获取bean,若是获取不到,则会尝试进行bean的创建,若是走到了bean的创建,则会重新走一边bean创建的整个流程,这里是递归逻辑。
(1)populateBean该方法是填充属性的入口,传入beanName和BeanDefinition。
(2)从BeanDefinition中获取属性注入相关信息然后判断是名称注入还是类型注入。
(3)调用getSingleton从容器中获取所需对象,若是获取不到则会重走对象创建的整个流程,拿到完整对象后将其给到当前bean的属性,到这里属性填充就结束了。
4、初始化bean
属性填充完毕后并没有立即结束这个过程,还有一些其他的操作需要spring进行处理,比如aware接口的处理,postprocessor接口的处理,初始化的处理等操作其实这里主要就是处理这三个动作的。
(1)判断有无实现aware接口,如有则去执行他的实现类的实现方法,在spring初始化时会对他们进行是否实现的判断。
(2)获取容器中所有postprocessor接口,然后开始执行他的前置方法。
(3)判断有无实现初始化接口InitializingBean如有则去执行初始化方法afterPropertiesSet。
(4)执行postprocessor的后置方法,通过前置和后置方法我们可以实现自定义的一些逻辑,不过需要注意的是这些前置和后置方法会作用到所有bean。
5、后置操作
这里的后置操作,主要是完成一些清扫工作和适配工作,比如删除二级、三级缓存中无用的bean引用等,下面是具体操作。
(1)将bean从创建中的集合中删除。
(2)将bean加入到单例池中将其从二级三级缓存中删除。
(3)对对象进行一些适配操作,到这里完成了初始化的所有操作,后面就是一步步返回调用的地方了。
看了这五步,不知道是不是对bean的创建过程有了清晰的认识。
三、bean的生命周期
bean的生命周期其实就是从创建到销毁,上面创建已经说完了,其实只差销毁这一步了。bean销毁发生在容器关闭时对单例bean进行清除操作。在Spring中我们通常有三种方式定义bean销毁时的逻辑。
1、通过PreDestroy注解修饰方法
Bean销毁时会检查有无该注解修饰的方法,如有,会对该注解修饰的方法进行执行
2、通过指定destroy-method方法
在使用xml对bean进行注入时,我们可以指定init-method方法,也可以指定destroy-method方法,同样的使用Bean注解时也是支持这两个属性的,Spring容器关闭时会寻找当前bean有无指定destroy-method,如有则会进行执行。
3、实现DisposableBean接口
实现该接口重写他的destroy方法,同样的Spring容器关闭时也会检查有无实现该接口,如有实现也会执行这里的销毁方法。
4、下面是对于三种销毁方式的测试代码
第一段是自定义启动Spring容器,给容器注册钩子,这样当我们关闭Spring容器时会自动调用我们的销毁方法。
public class AppStartClass { public static void main(String[] args) { AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(yuCloseSpring.class); annotationConfigApplicationContext.start(); annotationConfigApplicationContext.registerShutdownHook(); }
这一段是测试代码了,分别使用三种方式写了销毁方法
public class MyDisposableBean implements DisposableBean{ @Override public void destroy() throws Exception { System.out.println("执行DisposableBean的销毁方法"); } public void test(){ System.out.println("执行destroy-method销毁方法"); } @PreDestroy public void testPreDestroy(){ System.out.println("执行PreDestroy注解修饰的销毁方法"); } } @Configuration class yuCloseSpring{ @Bean(destroyMethod = "test") public MyDisposableBean getMyDisposableBean(){ return new MyDisposableBean(); } }
下面是启动main方法后的执行截图,可以清晰的看到三种销毁方法都是正常执行的,且他们执行顺序是固定的。
即:···PreDestroy–>DisposableBean–>destroy-method···。
到这里其实bean整个生命周期就算是彻底结束了。
四、总结
这篇主要总结Spring中bean的创建过程,主要分为加载bean信息–>实例化bean–>属性填充–>初始化阶段–>后置处理等步骤,且每个步骤Spring做的事情都很多,这块源码还是很值得我们都去看一看的。
而Spring中Bean的声明周期其实就是创建到使用到销毁,使用应该没啥需要说的,销毁在第三部分也正常介绍了三种销毁的方式。希望这一篇可以对路过的你有所帮助。