服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - Java教程 - Spring Bean的生命周期解读

Spring Bean的生命周期解读

2023-10-23 16:50山河亦问安 Java教程

Spring 只负责创建,当容器创建了 Bean 的实例后,Bean 的实例就交给客户端代码管理,Spring 容器将不再跟踪其生命周期。每次客户端请求 prototype 作用域的 Bean 时,Spring 容器都会创建一个新的实例,并且不会管那些被配置成 prototyp

1. Spring IOC容器

1.1 Spring IOC 容器的设计

Spring IOC 容器的设计主要是基于BeanFactoryApplicationContext两个接口,其中ApplicationContext是BeanFactory的子接口之一,换句话说BeanFactory是Spring IOC容器所定义的最顶层接口,而ApplicationContext是其高级接口之一,并且对于BeanFactory功能做了许多有用的扩展,所以在绝大部分的工作场景中,都会使用ApplicationContext作为Spring IOC 容器,如下图所示:

Spring Bean的生命周期解读

 首先我们定义一个User实体类:

public class User {
    String username;
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    @Override
    public String toString() {
        return "User{" +
                "username='" + username + '\'' +
                '}';
    }
}
1.1.1 BeanFactory

BeanFactory的使用注册Bean对象以及获取Bean对象代码如下:

DefaultListableBeanFactory beanFactory=new DefaultListableBeanFactory();
        RootBeanDefinition rootBeanDefinition=new RootBeanDefinition(User.class);
        beanFactory.registerBeanDefinition("user",rootBeanDefinition);
        System.out.println(beanFactory.getBean("user",User.class));
1.1.2 ApplicationContext

ApplicationContext的使用注册Bean对象以及获取Bean对象代码如下:

 AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        applicationContext.register(User.class);
        applicationContext.refresh();
        System.out.println(applicationContext.getBean("user", User.class));

1.2 Spring Bean的生命周期

Spring 容器可以管理 singleton 作用域 Bean 的生命周期,在此作用域下,Spring 能够精确地知道该 Bean 何时被创建,何时初始化完成,以及何时被销毁。

而对于 prototype 作用域的 Bean,Spring 只负责创建,当容器创建了 Bean 的实例后,Bean 的实例就交给客户端代码管理,Spring 容器将不再跟踪其生命周期。每次客户端请求 prototype 作用域的 Bean 时,Spring 容器都会创建一个新的实例,并且不会管那些被配置成 prototype 作用域的 Bean 的生命周期。

生命周期主要是为了了解Spring IOC容器初始化和销毁Bean的过程,通过对它的学习就可以知道如何在初始化和销毁的时候加入自定义的方法,以满足特定的需求。如下图:

Spring Bean的生命周期解读

 

从上图可以看到,Spring IoC容器对Bean 的管理还是比较复杂的,Spring loC容器在执行了初始化和依赖注入后,会执行一定的步骤来完成初始化,通过这些步骤我们就能自定义初始化,而在Spring IoC 容器正常关闭的时候,它也会执行一定的步骤来关闭容器,释放资源。除需要了解整个生命周期的步骤外,还要知道这些生命周期的接口是针对什么而言的,首先介绍生命周期的步骤。

1. 如果 Bean 实现了接口 BeanNameAware的setBeanName方法,那么它就会调用这个方法。

2. 如果 Bean 实现了接口 BeanFactoryAware 的 setBeanFactory方法,那么它就会调用这个方法。

3. 如果 Bean实现了接口 ApplicationContextAware 的 setApplicationContext方法,且

Spring loC容器也必须是一个ApplicationContext 接口的实现类,那么才会调用这个方法,否则是不调用的。

4. 如果 Bean 实现了接口 BeanPostProcessor 的 postProcessBeforeInitialization方法,那么它就会调用这个方法。

5 .如果 Bean实现了接口BeanFactoryPostProcessor的afterPropertiesSet方法,那么它就会调用这个方法。

6. 如果 Bean自定义了初始化方法,它就会调用已定义的初始化方法。

7. 如果Bean 实现了接口 BeanPostProcessor 的postProcessAfterInitialization方法,完成了这些调用,这个时候Bean 就完成了初始化,那么 Bean就生存在Spring loC的容器中了,使用者就可以从中获取 Bean的服务。

8. 当服务器正常关闭,或者遇到其他关闭 Spring loC 容器的事件,它就会调用对应的方完成Bean 的销毁,其步骤如下:

        如果Bean实现了接口 DisposableBean 的 destroy方法,那么就会调用它。

        如果定义了自定义的销毁方法,那么就会调用它。

1.2.1 BeanDefinition

Spring容器启动的过程中,会将Bean解析成Spring内部的BeanDefinition结构。不管是是通过xml配置文件的标签,还是通过注解配置的@Bean,还是@Compontent标注的类,还是扫描得到的类,它最终都会被解析成一个BeanDefinition对象,最后我们的Bean工厂就会根据这份Bean的定义信息,对bean进行实例化、初始化等等操作。

你可以把BeanDefinition丢给Bean工厂,然后Bean工厂就会根据这个信息帮你生产一个Bean实例,拿去使用。

BeanDefinition里面里面包含了bean定义的各种信息,如:bean对应的class、scope、lazy信息、dependOn信息、autowireCandidate(是否是候选对象)、primary(是否是主要的候选者)等信息。

Spring Bean的生命周期解读

 RootBeanDefinition类:表示根bean定义信息,通常bean中没有父bean的就使用这种表示。

ChildBeanDefinition类:表示子bean定义信息,如果需要指定父bean的,可以使用ChildBeanDefinition来定义子bean的配置信息,里面有parentName属性,用来指定父bean的名称。

GenericBeanDefinition类:通用的bean定义信息,既可以表示没有父bean的bean配置信息,也可以表示有父bean的子bean配置信息,这个类里面也有parentName属性,用来指定父bean的名称。

ConfigurationClassBeanDefinition类:表示通过配置类中@Bean方法定义bean信息

可以通过配置类中使用@Bean来标注一些方法,通过这些方法来定义bean,这些方法配置的bean信息最后会转换为ConfigurationClassBeanDefinition类型的对象。

AnnotatedBeanDefinition接口:表示通过注解的方式定义的bean信息。

BeanDefinitionBuilder:构建BeanDefinition的工具类

1.2.2 InstantiationAwareBeanPostProcessor和BeanPostProcessor

 

InstantiationAwareBeanPostProcessor实际上继承了BeanPostProcessor接口。InstantiationAwareBeanPostProcessor作用于实例化阶段的前后,BeanPostProcessor作用于初始化阶段的前后。如下图:

Spring Bean的生命周期解读

 BeanPostProcessor是一个接口,还有很多子接口,这些接口中提供了很多方法,spring在bean生命周期的不同阶段,会调用BeanPostProcessor中的一些方法,来对生命周期进行扩展,bean生命周期中的所有扩展点都是依靠这个集合中的BeanPostProcessor来实现的。该接口提供了两个函数:postProcessBeforeInitialzation( Object bean, String beanName ) 当前正在初始化的bean对象会被传递进来,我们就可以对这个bean作任何处理。 这个函数会先于InitialzationBean执行,因此称为前置处理。 所有Aware接口的注入就是在这一步完成的。postProcessAfterInitialzation( Object bean, String beanName ) 当前正在初始化的bean对象会被传递进来,我们就可以对这个bean作任何处理。 这个函数会在InitialzationBean完成后执行,因此称为后置处理。

 

Spring Aware是Spring定义的回调接口。何为回调?就是客户程序C调用服务程序S中的某个函数A,然后S又在某个时候反过来调用C中的某个函数B,对于C来说,这个B便叫做回调函数。

1.2.3 测试生命周期

我们自定义一个User实体类,要求Spring容器使用我们自定义的@MyAutowired注解标注的构造方法进行构造Bean对象,然后我们观察在Bean周期的日志打印,更好的理解Bean周期过程。

自定义@MyAutowired注解

@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target(ElementType.CONSTRUCTOR)
public @interface MyAutowired {
}

定义User实体类

public class User implements InitializingBean, DisposableBean {
    String username;
    //Spring为了降低对客户代码的侵入性,给bean的配置提供了init-method属性,
    // 该属性指定了在这一阶段需要执行的函数名。Spring便会在初始化阶段执行我们设置的函数。
    // init-method本质上仍然使用了InitializingBean接口。
    public void init(){
        System.out.println(this.getClass().getSimpleName()+" 执行自定义初始化方法");
    }

    public User() {
    }

    @MyAutowired
    public User(String username) {
        this.username = username;
    }


    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    @Override
    public String toString() {
        return "User{" +
                "username='" + username + '\'' +
                '}';
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("调用DisposableBean接口的destroy方法");
    }
    //afterPropertiesSet()这一阶段也可以在bean正式构造完成前增加我们自定义的逻辑,
    // 但它与前置处理不同,由于该函数并不会把当前bean对象传进来,因此在这一步没办法处理对象本身,
    // 只能增加一些额外的逻辑。 若要使用它,我们需要让bean实现该接口,把要增加的逻辑写在该函数中。
    // 然后Spring会在前置处理完成后检测当前bean是否实现了该接口,并执行afterPropertiesSet函数
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("调用afterPropertiesSet方法");
    }
}

测试代码

public class MyTest {
    public static void main(String[] args) {
        DefaultListableBeanFactory beanFactory=new DefaultListableBeanFactory();
        //InstantiationAwareBeanPostProcessor接口在Bean对象实例化前的方法
        beanFactory.addBeanPostProcessor(new InstantiationAwareBeanPostProcessor() {
            public Object postProcessBeforeInstantiation(Class beanClass, String beanName) throws BeansException {
                if(beanName.equals("user"))
                System.out.println(beanName+" Bean对象在实例化之前操作**");
                return null;
            }
        });
        //InstantiationAwareBeanPostProcessor接口在Bean对象实例化后的方法
        beanFactory.addBeanPostProcessor(new InstantiationAwareBeanPostProcessor() {
            public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
                if(beanName.equals("user"))
                System.out.println(beanName+" Bean对象在实例化之后操作**");
                return false;
            }
        });
        //使用自己自定义含有@MyAutowired注解的构造方法,实例化Bean对象
        beanFactory.addBeanPostProcessor(new SmartInstantiationAwareBeanPostProcessor() {
            public Constructor[] determineCandidateConstructors(Class beanClass, String beanName) throws BeansException {
                if(beanName.equals("user"))
                System.out.println("实例化Bean对象");
                Constructor[] declaredConstructors = beanClass.getDeclaredConstructors();
                List> collect = Arrays.stream(declaredConstructors).filter(i -> i.isAnnotationPresent(MyAutowired.class)).collect(Collectors.toList());
                Constructor[] constructors = collect.toArray(new Constructor[collect.size()]);
                return constructors.length>0?constructors:null;

            }
        });
        //BeanPostProcessor接口在Bean对象初始化之前的方法调用
        beanFactory.addBeanPostProcessor(new BeanPostProcessor() {
            @Override
            public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
                if(beanName.equals("user"))
                    System.out.println(beanName+" Bean对象在初始化之前操作**");
                return null;
            }
        });
       //BeanPostProcessor接口在Bean对象初始化之后的方法调用
        beanFactory.addBeanPostProcessor(new BeanPostProcessor() {
            @Override
            public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
                if(beanName.equals("user"))
                    System.out.println(beanName+" Bean对象在初始化之后操作**");
                return null;
            }
        });

        RootBeanDefinition rootBeanDefinition= (RootBeanDefinition) BeanDefinitionBuilder.rootBeanDefinition(User.class).setInitMethodName("init").getBeanDefinition();
        beanFactory.registerBeanDefinition("user",rootBeanDefinition);
        beanFactory.registerBeanDefinition("username", BeanDefinitionBuilder.genericBeanDefinition(String.class)
                .addConstructorArgValue("admin").getBeanDefinition());
        System.out.println(beanFactory.getBean("user",User.class));
        beanFactory.destroySingletons();


    }
}

测试截图

Spring Bean的生命周期解读

至此这篇文章到此结束。

到此这篇关于Spring Bean的生命周期解读的文章就介绍到这了,更多相关内容请搜索服务器之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持服务器之家!

原文地址:https://blog.csdn.net/qq_43649937/article/details/131476837

延伸 · 阅读

精彩推荐
  • Java教程快速理解spring中的各种注解

    快速理解spring中的各种注解

    这篇文章主要介绍了快速理解spring中的各种注解,具有一定借鉴价值,需要的朋友可以了解下。...

    沧海一滴9522021-03-02
  • Java教程Springboot+MDC+traceId日志中打印唯一traceId

    Springboot+MDC+traceId日志中打印唯一traceId

    本文主要介绍了Springboot+MDC+traceId日志中打印唯一traceId,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    W3C_010110002022-02-21
  • Java教程Java中HashMap集合的常用方法详解

    Java中HashMap集合的常用方法详解

    本篇文章给大家带来的内容是关于Java中HashMap集合的常用方法详解,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。下面我们就来学...

    xiaostudy11432022-03-11
  • Java教程Spring Boot无缝集成MongoDB

    Spring Boot无缝集成MongoDB

    这篇文章主要介绍了Spring Boot无缝集成MongoDB的相关知识,本文涉及到MongoDB的概念和nosql的应用场景,需要的朋友可以参考下...

    Java之家3692020-09-18
  • Java教程java数据结构之希尔排序

    java数据结构之希尔排序

    这篇文章主要为大家详细介绍了java数据结构之希尔排序的相关代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    阿木侠11552021-02-02
  • Java教程mybatis-plus 版本不兼容问题的解决

    mybatis-plus 版本不兼容问题的解决

    这篇文章主要介绍了mybatis-plus 版本不兼容问题的解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们...

    space-max10732020-09-26
  • Java教程spring boot使用thymeleaf为模板的基本步骤介绍

    spring boot使用thymeleaf为模板的基本步骤介绍

    Spring Boot项目的默认模板引擎是Thymeleaf,这没什么好说的,个人觉得也非常好,下面这篇文章主要给大家介绍了关于spring boot使用thymeleaf为模板的相关资料,...

    梦想修补师7342021-03-19
  • Java教程JavaMail入门教程之发送邮件(3)

    JavaMail入门教程之发送邮件(3)

    这篇文章主要为大家详细介绍了JavaMail入门教程之发送邮件的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下 ...

    汪先森4752020-07-07