Spring创建对象的过程

如果没有用Spring框架,程序员创建对象的方式有以下5种方式:

  1. 使用new关键字
  2. 使用Class的newInstance()方法
  3. 使用Constructor的newInstance()方法
  4. 使用clone()方法
  5. 使用反序列化

虽然这些方式都能实现创建一个对象,但在当今的应用开发中对于频繁使用到的类,比如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创建的这个过程大致可以分为五步:

  1. 扫描用户定义的配置信息

  2. 实例化bean;

  3. bean属性填充;

  4. 初始化bean;

  5. 后置操作;

    那我们就基于这五大步来看看Spring是如何创建bean的。

    1.扫描用户定义的配置信息

    当我们用以下代码创建一个Spring容器时,都做了什么呢?

    new AnnotationConfigApplicationContext(AppConfig.class);
    

    在AnnotationConfigApplicationContext的构造方法中会调用this()构造方法。在这个构造方法中会实例化一个ClassPathBeanDefinitionScanner对象。这个扫描器对象的scan()方法,就是用来扫描用户在xml中配置的信息或者在配置类上标注的注解。scan()方法里的核心方法是doScan(basePackages)。doScan(basePackages)方法中的核心逻辑:

    1. 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集合。
    2. 遍历candidates的BeanDefinition集合,解析出ScopeMetadata、beanName,接下来解析@Lazy、@Primary、@DependsOn、@Role、@Description注解。
    3. 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的声明周期其实就是创建到使用到销毁,使用应该没啥需要说的,销毁在第三部分也正常介绍了三种销毁的方式。希望这一篇可以对路过的你有所帮助。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值