【Java框架】Spring框架(四)——Spring中的Bean的创建与生命周期

news2024/11/26 12:48:59

目录

  • SpringBean的创建
    • 步骤
      • 后置处理器(PostProcessor)
        • BeanFactoryPostProcessor
        • BeanPostProcessor
          • InstantiationAwareBeanPostProcessor
          • postProcessBeforeInstantiation
          • postProcessAfterInstantiation
          • postProcessProperties
        • SmartInstantiationAwareBeanPostProcessor
          • determineConstructorsFromBeanPostProcessors
          • getEarlyBeanReference()
        • MergedBeanDefinitionPostProcessor
        • DestructionAwareBeanPostProcessor
        • ApplicationContextAwareProcessor
        • 注册时机
    • 小结
      • Spring的Bean创建过程
        • 总体流程
        • 容器预热阶段
          • 1.BeanDefination:bean对象配置信息的存储形式
          • 2.BeanDefinationReader:将xml等文件配置信息读取为BeanDefination
          • 3. BeanFactoryPostRegistry:BeanDefination的存储仓库
          • 4.BeanFactoryPostProcessor:对BeanDefination进行扩展处理
          • 小结
    • 引用
  • SpringBean的生命周期
    • 实例化
      • 实例化时机
      • BeanWrapper:Bean对象的封装外壳
    • 属性赋值
    • 初始化
      • 检测对象是否实现aware接口
      • BeanPostProcessor前置处理
      • 自定义初始化逻辑
        • xml形式:
        • 注解形式:
      • BeanPostProcessor后置处理
      • 自定义销毁逻辑
    • 使用
    • 销毁
    • SpringBean的生命周期流程图

SpringBean的创建

  • Spring提供了xml、注解、JavaConfig多种方式来配置bean,不论何种方式,Spring最终都会将bean封装成BeanDefinition对象,Spring创建bean的依据也是通过BeanDefinition来完成的。
  • 当我们调用getBean()方法获取bean实例时,不管是单例bean还是原型bean,首次调用时容器内都不存在可用的bean实例,这时就不得不去创建bean了

步骤

简单来说,完全可以对标一个人的创建

  1. 获取Bean定义
    扫描工程内所有被标记的Bean,获取其类型,名称,属性,构造方法等信息,存在一个Map里
  2. 生成实例
    这一步也很简单,遍历上述Map,利用Bean定义里的无参构造方法创建对象,和new 对象同理
  3. 属性装填
    刚创建的对象所有属性都是默认值,需要我们给它装填上需要的内容
  4. 初始化
    如果这个Bean实现了InitializingBean接口,则会调用你写在afterPropertiesSet方法里的内容。

以上四步是Spring创建Bean的核心步骤,在2-生成实例 3-属性装填 4-初始化的前后都预留了处理点,Spring自己或用户都可以通过编写 Bean后置处理器(BeanPostProcessor) 来实现自己的目的,这些处理器会在对应的处理点被执行,从而完成对Bean的修改

后置处理器(PostProcessor)

Spring中的后置处理器分为两大类:

  • 一类是针对Bean工厂的BeanFactoryPostProcessor
  • 一类是针对Bean的BeanPostProcessor

以上两者都是接口,Spring已经给定了一些实现类,用户也可以自己写一些实现类来实现全局的Bean相关的操作;顾名思义,BeanFactoryPostProcessor针对Bean工厂(它还有个子接口BeanDefinitionRegistryPostProcessor),调整Bean工厂的属性、影响Bean定义,注意此时还没有Bean进行实例化。BeanPostProcessor则更直接的作用于Bean实例生成过程中的修改。

BeanFactoryPostProcessor
  • 很多时候,我们需要添加一些自定义的Bean,或者出于项目需要,改动一些Spring原生Bean属性时就用的上了。

  • 比如我们常用的MyBatis组件,我们会在mapper层的接口上写@Mapper注解或者配置MapperScan扫描mapper层,最后就会在Spring中生成对应的Bean对象。

  • 然而这里有一个问题:1.@Mapper注解不是Spring规定的Bean注解,怎么被扫描进容器的?自然是依托于BeanFactory后置处理器。 MyBatis中写有工厂后置处理器的实现
    在这里插入图片描述
    这个处理器起了扫描的作用,找到了被我们标记的接口,并指定一个Bean定义,并把Bean的类型设置为MapperFactoryBean.class,即工厂类,然后把它添加到Bean定义注册器中。而在我们需要实例化这个Bean的时候,mybatis又会从这个工厂对象中使用getObject()为我们取出一个Bean实例,这个Bean实例是使用我们写的Mapper接口产生的代理,而后再把这个代理放入Spring容器
    在这里插入图片描述

BeanPostProcessor

而Bean后置处理器则更加常见,种类也更丰富,他们的详细作用和工作时机都可以在下图中看到
在这里插入图片描述在这里插入图片描述
导图链接:https://www.processon.com/view/link/6458dc476bb8cc38e2a0e950
继承图如下:
在这里插入图片描述
在图里可以发现,有两个子类继承了他的祖辈们的财产,分别是AutowiredAnnotationBeanPostProcessor和CommonAnnotationPostProcessos ,下面讲的那几个接口,很多时候都是靠他们两个实现类去实现的。

InstantiationAwareBeanPostProcessor

在这里插入图片描述
InstantiationAwareBeanPostProcessor有三个方法,而默认实现类是AbstractAutoProxyCreator

  1. postProcessBeforeInstantiation() 在bean实例化前调用,也是第一次扩展后置处理器的调用时机。

2.postProcessAfterInstantiation()在bean实例化后、属性赋值前 调用,第五次扩展后置处理器的调用时机。

  1. postProcessPropertyValues() @Autowired在这里进行DI,将获取的属性封装在 PropertyValues 的实例对象 pvs 中,在属性赋值阶段,第六次扩展后置处理器的调用时机
@Data
@ToString
public class Father {
    private int id;
    private String name;
    private int age;
}

@Data
@ToString
public class Son extends Father{
    private String money;
}
    <bean id="father" class="cn.smbms.pojo.Father">
        <property name="id" value="1"/>
        <property name="name" value="老周"/>
        <property name="age" value="32"/>
    </bean>

    <!-- 普通beanDefinition GenericBeanDefinition -->
    <!-- 合并后GenericBeanDefinition 变成 RootBeanDefinition并且覆盖 父类属性 -->
    <!-- primary=true 优先级最高,增加一个money属性 -->
    <bean id="son" class="cn.smbms.pojo.Son" parent="father" primary="true">
        <property name="money" value="10000000"/>
    </bean>
    @Test
    public void test3(){
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        beanFactory.addBeanPostProcessor( new MyInstantiationAwareBeanPostProcessor() );
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
        String location = "spring-config.xml";
        Resource resource = new ClassPathResource(location);
        //字符编码格式
        EncodedResource encodedResource = new EncodedResource(resource,"UTF-8");
        beanDefinitionReader.loadBeanDefinitions( encodedResource );
        Son son = beanFactory.getBean("son",Son.class);
        System.out.println(son);
		Father father = beanFactory.getBean( "father",Father.class );
		System.out.println(father);

    }

    class MyInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {
        /**
         * 实例化bean之前,相当于new这个bean之前
         * 当调用postProcessBeforeInstantiation返回对象时,就可以直接返回对象了,就不会走到AbstractAutowireCapableBeanFactory的doCreateBean方法
         */
        @Override
        public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
            System.out.println("MyInstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation,beanName="+beanName);
            return null;
        }

        /**
         * 实例化bean之后,相当于new这个bean之后
         * 如果返回值是false,那么就不进行下面的依赖注入流程了
         */
        @Override
        public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
            System.out.println("MyInstantiationAwareBeanPostProcessor.postProcessAfterInstantiation");
            return true;
        }

        /**
         * bean已经实例化完成,在属性注入时阶段触发,
         * Instantiation(实例化)
         */
        @Override
        public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
            System.out.println("MyInstantiationAwareBeanPostProcessor.postProcessProperties");
            return null;
        }

        /**
         * 初始化bean之前,相当于把bean注入spring上下文之前
         * Initialization(初始化)
         */
        @Override
        public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
            System.out.println("MyInstantiationAwareBeanPostProcessor.postProcessBeforeInitialization");
            return bean;
        }

        /**
         * 初始化bean之后,相当于把bean注入spring上下文之后
         */
        @Override
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
            System.out.println("MyInstantiationAwareBeanPostProcessor.postProcessAfterInitialization");
            return bean;
        }
    }
[DEBUG] 2024-04-17 11:07:50,306 org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loaded 2 bean definitions from class path resource [spring-config.xml]
[DEBUG] 2024-04-17 11:07:50,309 org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'son'
MyInstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation,beanName=son
MyInstantiationAwareBeanPostProcessor.postProcessAfterInstantiation
MyInstantiationAwareBeanPostProcessor.postProcessProperties
MyInstantiationAwareBeanPostProcessor.postProcessBeforeInitialization
MyInstantiationAwareBeanPostProcessor.postProcessAfterInitialization
Son(money=10000000)
[DEBUG] 2024-04-17 11:07:50,362 org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'father'
MyInstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation,beanName=father
MyInstantiationAwareBeanPostProcessor.postProcessAfterInstantiation
MyInstantiationAwareBeanPostProcessor.postProcessProperties
MyInstantiationAwareBeanPostProcessor.postProcessBeforeInitialization
MyInstantiationAwareBeanPostProcessor.postProcessAfterInitialization
Father(id=1, name=老周, age=32)
postProcessBeforeInstantiation

这个方法因为是在对象实例化前调用,如果返回的是具体的对象,而不是null的话,这个返回值就会代替原来该生成的目标对象的实例,相当于偷梁换日了。如果该方法的返回值代替原本该生成的目标对象,后续只有postProcessAfterInitialization方法会调用,其它方法不再调用;否则按照正常的流程走
在这里插入图片描述
改一下代码

        @Override
        public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
            if(ObjectUtils.nullSafeEquals("son",beanName) && Son.class.equals(beanClass)){
                //把配置完成的 superUser bean覆盖
                return  new Father();
            }
            return null;//这里表示什么都不变化
        }

在这里插入图片描述
执行完核心方法applyBeanPostProcessorsBeforeInstantiation后,正常返回的bean是null,可是因为我们前面让bean有返回值了,所以下面源码在继续debug的时候就能看到它返回一个father对象,替换了原先的son对象,成功“偷梁换日”

继续进入applyBeanPostProcessorsBeforeInstantiation()方法,这里就是循环实现了InstantiationAwareBeanPostProcessor的接口,其实就只有一个接口,那就是我们自定义的
在这里插入图片描述在这里插入图片描述

然后继续进入postProcessBeforeInstantiation方法里
在这里插入图片描述
在这里插入图片描述

因为bean生成了代理对象,所以会继续调用applyBeanPostProcessorsAfterInitialization,如果有实现了postProcessAfterInstantiation()这个方法,就会被直接执行。如果没有生成代理对象,这个方法将会在后面实例化对象后,则第五次扩展后置处理器,还会被调用到

postProcessAfterInstantiation

在这里插入图片描述

postProcessProperties

在这里插入图片描述
改代码,重走流程

        @Override
        public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
            /*if(ObjectUtils.nullSafeEquals("son",beanName) && Son.class.equals(beanClass)){
                //把配置完成的 superUser bean覆盖
                return  new Father();
            }*/
            return null;//这里表示什么都不变化
        }

现在默认beanPostProcessorsBeforeInitialization()方法返回是空值(否则走不到postProcessProperties这一步)。再看一下这里
在这里插入图片描述
进入populateBean()方法,从这里可以看出是在属性赋值阶段执行这个后置处理器的
在这里插入图片描述在这里插入图片描述

AutowiredAnnotationBeanPostProcessor 中的 postProcessProperties 方法
在这里插入图片描述

SmartInstantiationAwareBeanPostProcessor

在这里插入图片描述在这里插入图片描述

  1. determineCandidateConstructors() 选举出合适的构造函数对象(如AutowiredAnnotationBeanPostProcessor会把加了@Autowired注解的构造方法找出来)

  2. getEarlyBeanReference() 解决循环依赖,提前暴露一个工厂,工厂是三级缓存(OK,解决循环依赖的东西出来了)

determineConstructorsFromBeanPostProcessors

在这里插入图片描述
createBeanInstance()核心逻辑是寻找合适的构造器,并实例化对象
在这里插入图片描述在这里插入图片描述
一般我们不需要自定义接口去寻找他的构造器,他有实现类AutowiredAnnotationBeanPostProcessor,@AutoWired注解在构造器上时才会触发这个后置处理器的使用,这里就不继续讲他的实现类了。

getEarlyBeanReference()

提前曝光了一个工厂对象,是一个三级缓存来的(singletonFactories),用于创建beanName所对应的Bean对象。这个bean是前面实例化后,还未到属性赋值阶段的

简单说一下spring三级缓存:

  • Map<String, Object> singletonObjects (一级缓存)
    • key:beanName,value:经历了spring完整生命周期的bean对象。
  • Map<String, Object> earlySingletonObjects(二级缓存)
    • key:beanName,value:不完整的单例对象,也就是还没有初始化完毕。
  • Map<String, ObjectFactory<?>> singletonFactories(三级缓存)
    • key:beanName,value:单例工厂,用于创建beanName所对应的Bean对象。

在AbstractAutowireCapableBeanFactory的doCreateBean()方法中,在对bean实例化以后填充属性之前(一般称为早期对象,还没被属性赋值),会将其放入第三级缓存。
在这里插入图片描述
addSingletonFactory:把我们的早期对象包装成一个singletonFactory,该对象提供了一个getObject方法,该方法内部调用的就是getEarlyBeanReference()方法

addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

看下addSingletonFactory(),他就是把工厂对象给存了起来

进入getEarlyBeanReference()方法中:
三级缓存通过函数接口回调方式(即不会立即缓存单例对象,而是等待调用时再返回)
1.延迟获取,提高性能,因为不是所有的Bean都存在循环依赖,所以当真正调用时再缓存该单例到二级缓存
2.提高扩展性
3.aop的考虑,如果以来的Bean是代理类,那么将代理创建的方法作为回调方法
在这里插入图片描述

AbstractAutoProxyCreator是SmartInstantiationAwareBeanPostProcessor的实现类之一,其重写了getEarlyBeanReference方法:
在这里插入图片描述在这里插入图片描述

  1. advisedBeans表示已经判断过了的bean,false表示此bean不需要进行Aop
  2. 当前正在创建的Bean不用进行Aop,如切面Bean
  3. 判断当前bean是否存在匹配的advice,如果存在则要生成一个代理对象
  4. advisedBeans记录了某个Bean已经进行过Aop了
  5. this.proxyTypes.put():添加到缓存
MergedBeanDefinitionPostProcessor

在这里插入图片描述
这个后置处理器只有CommonAnntationBeanPostProcessor和AutowiredAnnotationBeanPostProcessor两个实现类是有内容的,其他的都是空实现。他们两个的功能很类似,就是把实例化后的bean对象,解析它里面的@Resource 、@Autowired、@Value属性和方法,封装成InjectionMetadata类型,并放进缓存中,等属性赋值阶段时再从缓存中拿出数据来赋值
在这里插入图片描述在这里插入图片描述在这里插入图片描述
CommonAnntationBeanPostProcessor也类似是这样,只不过是获取@Resource注解的属性/方法而已

前面提到,封装成InjectionMetadata类型,并放进缓存中,等属性赋值阶段时再从缓存中拿出数据来赋值
在这里插入图片描述

这里获取的缓存就是前面set进去的

DestructionAwareBeanPostProcessor

在这里插入图片描述

  1. postProcessBeforeDestruction(Object bean, String beanName) Bean销毁之前的回调方法,
    其典型实现有ApplicationListenerDetector用于处理ApplicationListener的添加和销毁、InitDestroyAnnotationBeanPostProcessor用于处理@PostConstruct和@PreDestroy、ScheduledAnnotationBeanPostProcessor用于处理@Scheduled注解。

  2. requiresDestruction()判断是否需要进行销毁,一般情况下都是需要的

实现类

  1. ApplicationListenerDetector 处理ApplicationListener的添加和销毁

  2. InitDestroyAnnotationBeanPostProcessor 处理@PostConstruct和@PreDestroy

  3. ScheduledAnnotationBeanPostProcessor 处理@Scheduled注解

ApplicationContextAwareProcessor

在这里插入图片描述

注册时机

PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this)

  1. 该方法分别对实现了PriorityOrdered、Ordered和无顺序接口进行注册,而且MergedBeanDefinitionPostProcessor 接口还多添加了一次。

  2. 在代码的最后还添加了一个ApplicationListenerDetector()方法,用于获取容器中的ApplicationListener进行注册。

  3. 添加方法使用BeanFactory#addBeanPostProcessor(BeanPostProcessor beanPostProcessor)。此时容器中已经保存的是BeanPostProcessor的实例,因为已经调用BeanFactory#getBean方法。

  4. 关于MergedBeanDefinitionPostProcessor接口,虽然通过internalPostProcessors重新添加了一次,但是并不会重复添加,因为addBeanPostProcessor方法是先remove,再add,这些BeanPostProcessor就会添加到最后。

小结

在这里插入图片描述

Spring的Bean创建过程

总体流程

spring将内部管理的诸多对象称为一个个bean,而这些bean的创建流程大致分为两个大阶段:

  1. spring 容器预热阶段
  2. bean实际创建阶段
容器预热阶段
  • 在对象进行创建之前,spring容器需要了解所创建的对象的信息,才能在后续阶段根据了解的信息创建bean对象。这些信息即是实际工作中我们为对象所写的配置信息,它们一般以xml文件、properties文件和注解的形式存在于我们的项目之中

  • 因此,在容器预热阶段,spring将会读取配置文件,并将bean的必要信息存储在自己容器内部,用于后续对象创建。

1.BeanDefination:bean对象配置信息的存储形式
  • 那么,spring内部是用什么样的形式来存储bean的配置信息的呢?我们知道,在java中,万物皆对象,spring选择用java对象的形式存储上述的bean配置信息,而这个对象名字就是BeanDefination。
  • BeanDefination中存储了包括属性、构造方法参数、依赖的 Bean 名称及是否单例、延迟加载等信息
2.BeanDefinationReader:将xml等文件配置信息读取为BeanDefination
  • xml等形式的配置信息转化为BeanDefination自然不是眨个眼就能直接完成转化的,需要通过某种工具对其进行读取并且转化。这里的工具即是我们此处提出的BeanDefinationReader。
  • BeanDefinationReader是一个接口,其不同的实现类对不同形式的配置信息进行读取并封装为BeanDefination
    在这里插入图片描述
    XmlBeanDefinationReader可以读取xml文件类型的配置信息并存储为BeanDefination。其他Reader以此类推
3. BeanFactoryPostRegistry:BeanDefination的存储仓库
  • 当BeanDefinationReader将配置文件读取并存储到BeanDefination中后,Spring需要通过bean的id寻找到对应的BeanDefination从而获取其配置信息。这种通过Bean定义的id找到对象的BeanDefination的对应关系或者说映射关系又是如何保存的呢?这就引出了BeanDefinationRegistry了。
  • Spring通过BeanDefinationReader将配置元信息加载到内存生成相应的BeanDefination之后,就将其注册到BeanDefinationRegistry中,BeanDefinationRegistry就是一个存放BeanDefination的仓库,它也是一种键值对的形式,通过特定的Bean定义的id,映射到相应的BeanDefination。
4.BeanFactoryPostProcessor:对BeanDefination进行扩展处理
  • BeanFactoryPostProcessor是容器启动阶段Spring提供的一个扩展点,主要负责对注册到BeanDefinationRegistry中的一个个的BeanDefination进行一定程度上的修改与替换。
  • 例如我们的配置元信息中有些可能会修改的配置信息散落到各处,不够灵活,修改相应配置的时候比较麻烦,这时我们可以使用占位符的方式来配置。例如配置Jdbc的DataSource连接的时候可以这样配置
<bean id="dataSource"  
    class="org.apache.commons.dbcp.BasicDataSource"  
    destroy-method="close">  
    <property name="maxIdle" value="${jdbc.maxIdle}"></property>  
    <property name="maxActive" value="${jdbc.maxActive}"></property>  
    <property name="maxWait" value="${jdbc.maxWait}"></property>  
    <property name="minIdle" value="${jdbc.minIdle}"></property>  
  
    <property name="driverClassName"  
        value="${jdbc.driverClassName}">  
    </property>  
    <property name="url" value="${jdbc.url}"></property>  
  
    <property name="username" value="${jdbc.username}"></property>  
    <property name="password" value="${jdbc.password}"></property>  
</bean>

BeanFactoryPostProcessor就会对注册到BeanDefinationRegistry中的BeanDefination做最后的修改,替换$占位符为配置文件中的真实的数据。

小结

上述4点即是容器预热阶段的几个部分,下面我们使用图片的形式回顾上述流程:
在这里插入图片描述

引用

在这里插入图片描述

SpringBean的生命周期

简单来说:实例化 -> 属性赋值 -> 初始化 -> 使用 -> 销毁

实例化

目的:Spring将转化BeanDefinition中BeanDefinition为实例Bean(放在包装类BeanWrapper中)。

实例化时机

  • 容器启动阶段与Bean实例化阶段存在多少时间差,Spring把这个决定权交给了程序员自己进行决定。Bean创建时间有两种策略:懒加载和非懒加载。

    • 懒加载(isLazyInit):直到我们伸手向Spring要依赖对象实例之前,Bean都是以BeanDefinationRegistry中的一个个的BeanDefination的形式存在,也就是Spring只有在我们需要依赖对象的时候才开启相应对象的实例化阶段。
    • 非懒加载:容器启动阶段完成之后,将立即启动Bean实例化阶段,通过隐式的调用所有依赖对象的getBean方法来实例化所有配置的Bean并保存起来。
  • 实例化的三种方式
    Spring中Bean的实例化本质其实就是JVM中java实例对象的加载-连接-初始化过程。

    • 使用类构造器实例化(无参构造函数)
      直接通过Spring工厂返回类的实例对象。
    • 使用静态工厂方法实例化(简单工厂模式)
      Spring工厂调用自定义工厂的静态方法返回类的实例对象。
    • 使用实例工厂方法实例化(工厂方法模式)
      Spring工厂调用工厂的普通方法(非静态方法)返回类的实例对象。

BeanWrapper:Bean对象的封装外壳

  • Spring中的Bean并不是以一个个的本来模样存在的,由于Spring IOC容器中要管理多种类型的对象,因此为了统一对不同类型对象的访问,Spring给所有创建的Bean实例穿上了一层外套,这个外套就是BeanWrapper。
  • BeanWrapper实际上是对反射相关API的简单封装,使得上层使用反射完成相关的业务逻辑大大的简化,我们要获取某个对象的属性,调用某个对象的方法,现在不需要在写繁杂的反射API了以及处理一堆麻烦的异常,直接通过BeanWrapper就可以完成相关操作,非常方便。

属性赋值

目的:上一步创建出来的对象还是个空白对象,需要为其设置属性以及依赖对象。

  • 对于基本类型的属性:如果配置元信息中有配置,那么将直接使用配置元信息中的设置值赋值即可,即使基本类型的属性没有设置值,那么得益于JVM对象实例化过程,属性依然可以被赋予默认的初始化零值。
  • 对于引用类型的属性:Spring会将所有已经创建好的对象放入一个Map结构中,此时Spring会检查所依赖的对象是否已经被纳入容器的管理范围之内,也就是Map中是否已经有对应对象的实例了。如果有,那么直接注入,如果没有,那么Spring会暂时放下该对象的实例化过程,转而先去实例化依赖对象,再回过头来完成该对象的实例化过程。

初始化

目的:在交付bean之前做一些处理

检测对象是否实现aware接口

  • aware接口为Bean对象提供了解Spring容器本身的能力,aware系列接口增强了Spring bean的功能,但是也会造成对Spring框架的绑定,增大了与Spring框架的耦合度。(Aware是“意识到的,察觉到的”的意思,实现了Aware系列接口表明:可以意识到、可以察觉到)
  • aware接口特点:
    • 都以“Aware”结尾
    • 都是Aware接口的子接口,即都继承了Aware接口
    • 接口内均定义了一个set方法
  • 使用方式:一个Bean对象想要获得spring容器某个部分的引用作为自己成员变量进行使用,就需要实现上述某个接口,并声明相关的成员变量来接收。

Spring IOC容器大体可以分为两种:

  • BeanFactory:提供IOC思想所设想所有的功能,同时也融入AOP等相关功能模块,可以说BeanFactory是Spring提供的一个基本的IOC容器。
  • ApplicationContext:构建于BeanFactory之上,同时提供了诸如容器内的时间发布、统一的资源加载策略、国际化的支持等功能,是Spring提供的更为高级的IOC容器。
  • 对于BeanFactory来说,这一步的实现是先检查相关的Aware接口,然后去Spring的对象池(也就是容器,也就是那个Map结构)中去查找相关的实例(例如对于ApplicationContextAware接口,就去找ApplicationContext实例),也就是说我们必须要在配置文件中或者使用注解的方式,将相关实例注册容器中,BeanFactory才可以为我们自动注入。
  • 对于ApplicationContext来说,由于其本身继承了一系列的相关接口,所以当检测到Aware相关接口,需要相关依赖对象的时候,ApplicationContext完全可以将自身注入到其中。

BeanPostProcessor前置处理

目的:BeanPostProcessor前置处理就是在要生产的Bean实例放到容器之前,允许我们程序员对Bean实例进行一定程度的修改,替换等操作。

  • ApplicationContext对于Aware接口的检查与自动注入就是通过BeanPostProcessor实现的,在这一步Spring将检查Bean中是否实现了相关的Aware接口,如果是的话,那么就将其自身注入Bean中即可。
  • Spring中AOP就是在这一步实现的偷梁换柱,产生对于原生对象的代理对象,然后将对源对象上的方法调用,转而使用代理对象的相同方法调用实现的。

自定义初始化逻辑

  • 初始化有两种方式,实现InitializingBean接口或者配置init-method参数。
  • 在两者同时存在时,实现InitializingBean接口的afterpropertiesSet()方法执行顺序在配置init-method参数的方法前。
    • 实现InitializingBean接口:InitializingBean是Spring提供的拓展性接口,InitializingBean接口为bean提供了属性赋值后初始化的处理方法,它只有一个afterPropertiesSet方法,凡是继承该接口的类,在bean的属性初始化后都会执行该方法。
    • 配置init-method参数:通过init-method参数指定某个方法为初始化方法,然后此方法就会在bean初始化的时候执行。
xml形式:
<bean id="user" class="cn.smbms.pojo.User" init-method="userInit"></bean>
注解形式:
@Configuration
public class BeanConfig {
    @Bean(initMethod = "userInit", destroyMethod = "userDestroy")
    public User create(){
        User user = new User();
        return user;
    }
}

BeanPostProcessor后置处理

与前置处理类似,这里是在Bean自定义逻辑也执行完成之后,Spring又留给我们的最后一个扩展点。我们可以在这里在做一些我们想要的扩展。

自定义销毁逻辑

销毁有两种方式,实现DisposableBean接口或者配置init-method参数。

  1. 实现DisposableBean接口:类似初始化实现InitializingBean接口。
  2. 配置destroy-method参数:类似初始化配置init-method参数。

使用

这个时候可以对bean对象进行正常使用。

销毁

Spring的Bean在为我们服务完之后,马上就要消亡了(通常是在容器关闭的时候),别忘了我们的自定义销毁逻辑,这时候Spring将以回调的方式调用我们自定义的销毁逻辑

SpringBean的生命周期流程图

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1606882.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Python_PySide6学习笔记(三十六)】基于QGroupBox和QScrollArea实现带有滚动条的QGroupBox(分组框)

基于QGroupBox和QScrollArea实现带有滚动条的QGroupBox分组框 基于QGroupBox和QScrollArea实现带有滚动条的QGroupBox(分组框)前言正文1、创建QGroupBox并创建其内部布局2、创建QScrollArea并将QGroupBox设置为其内容3、将QScrollArea添加到主窗口的中心部件的布局中4、完整代码…

HTML的超链接

前言&#xff1a; 如图&#xff0c;我们在浏览网页时经常可以看到这样的字体&#xff08;点击便跳转到了别的地方了&#xff09;&#xff0c;今日就和各位一起学习一下超链接的相关知识。 相关知识1&#xff1a; 超链接的标签为&#xff1a;a ~使用格式为&#xff1a; <a h…

智慧城市可视化,山海鲸引领未来

随着信息技术的迅猛发展&#xff0c;智慧城市已成为城市发展的必然趋势。在这一背景下&#xff0c;山海鲸智慧城市可视化解决方案应运而生&#xff0c;以其独特的可视化技术和智能化管理功能&#xff0c;为城市管理者提供了全新的决策支持手段。 山海鲸智慧城市可视化解决方案…

docker的安装以及docker中nginx配置

机器 test3 192.168.23.103 1机器初始化配置 1.1关闭防火墙&#xff0c;清空防火墙规则 systemctl stop firewalld iptables -F setenforce 01.2部署时间同步 yum install ntp ntpdate -y1.3安装基础软件包 yum install -y wget net-tools nfs-utils lrzsz gcc gcc-c make…

如何访问内网?

在互联网万维网上&#xff0c;我们可以轻松访问各种网站和资源。但是&#xff0c;有时我们需要访问局域网内的资源&#xff0c;例如公司内部的文件共享、打印机等。本文将介绍几种方法&#xff0c;帮助您实现访问内网的需求。 内网穿透技术 内网穿透技术是一种通过互联网将局域…

人工智能论文GPT-3(1):2020.5 Language Models are Few-Shot Learners;摘要;引言;scaling-law

摘要 近期的工作表明&#xff0c;在大量文本语料库上进行预训练&#xff0c;然后针对特定任务进行微调&#xff0c;可以在许多NLP任务和基准测试中取得实质性进展。虽然这种方法在架构上通常是与任务无关的&#xff0c;但仍然需要包含数千或数万示例的针对特定任务的微调数据集…

STM32G431RBT6移植FreeRTOS

引言&#xff1a; 本文专门为参加了蓝桥杯嵌入式赛道的同学准备&#xff0c; 大家可能会有这样一个问题&#xff0c; 比完赛之后&#xff0c; 对于像继续使用STM32G431RBT6学习FreeRTOS的&#xff0c; 发现网上的教程使用的板子基本上都是F1和F4的&#xff0c; 其实呢&#xff…

多系统源代码暴露在互联网,超四百万公民个人信息存在泄露的风险

#哈尔滨等保# 一 安全事件阐述 截至2024年3月&#xff0c;绿盟科技创新研究院监测到上万个互联网中暴露的DevOps资产存在未授权访问情况。其中&#xff0c;源代码仓库成为未授权访问的“重灾区”。这些允许未授权访问的源代码仓库暴露了境内多家机构的重要系统源代码&#x…

html公众号页面实现点击按钮跳转到导航

实现效果&#xff1a; 点击导航自动跳转到&#xff1a; html页面代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>跳转导航</title><meta name"keywords" conten…

【AR开发示例】实现AR管线巡检

写在前面的话 这是一篇旧文档&#xff0c;代码仓库见 https://gitee.com/tanyunxiu/AR-pipe 本文档是基于超图移动端SDK的AR模块开发的示例&#xff0c;仅供参考&#xff0c;SDK在持续迭代中&#xff0c;相关描述可能有变化。 示例介绍 这是一个使用AR查看墙内管线的基础示…

uniapp 如何区分目前运行环境(app、web、mp-weixin)

platform 区分 iOS、Android uniplatform 区分 app、web、mp-weixin ....

fatal error LNK1120: xxxx unresolved externals

场景&#xff1a; A项目Library&#xff0c;编译环境VS2008DDKWarizd, 编译平台WIN7XP; B项目驱动, 编译环境VS2008&#xff0c;编译平台Rlease32位&#xff0c;DDK版本相同,都是7.0 在B项目集成A项目的32位LIB库时&#xff0c;出现此错误&#xff0c;看错误很常见的找不到符号…

Linux【实战】—— LAMP环境搭建 部署网站

目录 一、介绍 1.1什么是LAMP&#xff1f; 1.2LAMP的作用 二、部署静态网站 2.1 虚拟主机&#xff1a;一台服务器上部署多个网站 2.1.1 安装Apache服务 2.1.2 防火墙配置 2.1.3 准备网站目录 2.1.4 创建网站的配置文件 2.1.5 检查配置文件是否正确 2.1.6 Linux客户端…

润石科技(RUNIC)汽车电子应用方案和物料选型

一、润石科技&#xff08;RUNIC&#xff09;简介 江苏润石科技有限公司是一家专注于高性能、高品质模拟/混合信号集成电路研发和销售的高科技半导体设计公司。公司主要产品线分为两类&#xff1a;信号链和电源管理&#xff0c;其中信号链包含运算放大器、比较器、模拟开关、数…

Mac 部署 GPT-2 预训练模型 gpt2-chinese-cluecorpussmall

文章目录 下载 GPT-2 模型快速开始 GPT-2 下载 GPT-2 模型 https://huggingface.co/uer/gpt2-chinese-cluecorpussmall git clone https://huggingface.co/uer/gpt2-chinese-cluecorpussmall # 或单独下载 LFS GIT_LFS_SKIP_SMUDGE1 git clone https://huggingface.co/uer/gpt…

基于Spring Boot+Vue的校园网上店铺,开启便捷购物新模式

作者介绍&#xff1a;✌️大厂全栈码农|毕设实战开发&#xff0c;专注于大学生项目实战开发、讲解和毕业答疑辅导。 &#x1f345;获取源码联系方式请查看文末&#x1f345; 推荐订阅精彩专栏 &#x1f447;&#x1f3fb; 避免错过下次更新 Springboot项目精选实战案例 更多项目…

递归 python

↵一、简单理解 解决问题的一种方法&#xff0c;它将问题不断的分成更小的子问题&#xff0c;直到子问题可以用普通的方法解决。通常情况下&#xff0c;递归会使用一个不停调用自己的函数。 【注】&#xff1a;每一次递归调用都是在解决一个更小的问题&#xff0c;如此进行下…

Leetcode - 128双周赛

目录 一&#xff0c;3110. 字符串的分数 二&#xff0c;3111. 覆盖所有点的最少矩形数目 三&#xff0c;3112. 访问消失节点的最少时间​编辑 写法一&#xff1a;朴素 Dijkstra&#xff08;适用于稠密图&#xff0c;即边比较多的图&#xff09; 写法二&#xff1a;堆优化 …

⑤【Shiro】SpringBoot整合Shiro,实现登录认证

个人简介&#xff1a;Java领域新星创作者&#xff1b;阿里云技术博主、星级博主、专家博主&#xff1b;正在Java学习的路上摸爬滚打&#xff0c;记录学习的过程~ 个人主页&#xff1a;.29.的博客 学习社区&#xff1a;进去逛一逛~ ⑤【Shiro】SpringBoot整合Shiro&#xff0c;实…

作为Boss,还在写代码。what?赶紧改掉这个坏毛病

有些创业中的老板&#xff0c;经常或者偶尔也要写代码&#xff0c;我听了很震惊呀&#xff0c;这叫创业吗&#xff1f;这不是给员工打工吗&#xff1f;其他重要的事情谁来干&#xff0c;这个毛病一定要改。 一、比起写代码&#xff0c;你还有更重要的事情要做 作为BOSS和创业…