Spring原理学习(二):Bean的生命周期和Bean后处理器

〇、前言

        倘若是为了面试,请背下来下面这段:

        spring的bean的生命周期主要是创建bean的过程,一个bean的生命周期主要是4个步骤:实例化、属性注入、初始化、销毁。但是对于一些复杂的bean的创建,spring会在bean的生命周期中开放很多的接口,可以让你加载bean的时候对bean做一些改变,因此一个普通的bean的生命周期为:

  1. 实现了BeanFactoryPostProcessor接口的bean,在加载其他的bean的时候,也会调用这个bean的 postProcessBeanFactory方法,可以在这个步骤去对bean中的属性去赋值。设置年龄初始化18等等。
  2. 实现了InstantiationAwareBeanPostProcessor接口的bean,会在实例化bean之前调用 postProcessBeforeInstantiation方法
  3. 然后在对bean进行实例化
  4. 对bean进行属性注入
  5. 对bean进行初始化,在初始化中,包含了以下几个步骤:
    1. 实现了BeanFactoryAware接口,会先调用setBeanFactory方法
    2. 实现了BeanNameAware接口,会先调用setBeanName方法
    3. 实现了BeanPostProcessor接口,会先调用postProcessBeforeInitialization方法
    4. 实现了InitializingBean接口,会调用afterPropertiesSet方法
    5. 然后在进行aop后置处理,通过实现BeanPostProcessor接口,在postProcessAfterInitialization方法中进行动态代理
  6. 销毁

        如果不是为了面试,请往下看:

一、Bean的生命周期与Bean后处理器

1.1 Bean的生命周期

        我们先来体验一下bean的生命周期:

@SpringBootApplication
public class A03 {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(A03.class, args);
        //感受销毁阶段
        context.close();
    }
}
@Component
public class LifeCycleBean {
    private static final Logger log = LoggerFactory.getLogger(LifeCycleBean.class);

    public LifeCycleBean() {
        log.debug("构造");
    }

    @Autowired
    public void autowired(@Value("${JAVA_HOME}") String home) {
        log.debug("依赖注入: {}", home);
    }

    @PostConstruct
    public void init() {
        log.debug("初始化");
    }

    @PreDestroy
    public void destroy() {
        log.debug("销毁");
    }
}

1.2 Bean后处理器 

        在前文:Spring原理学习(一):BeanFactory和ApplicationContext的原理和实现 中,我们已经简单了解了Bean后处理器的作用。

        Bean后处理器的作用:为bean生命周期的各个阶段提供一些扩展功能。 

        要实现bean后处理器,需要实现 InstantiationAwareBeanPostProcessor 和 DestructionAwareBeanPostProcessor 这两个接口。

@Component
public class MyBeanPostProcessor implements InstantiationAwareBeanPostProcessor, DestructionAwareBeanPostProcessor {

    private static final Logger log = LoggerFactory.getLogger(MyBeanPostProcessor.class);

    /**
     * 销毁之前执行
     */
    @Override
    public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
        //因为spring中有很多bean,我们只打印自己创建的lifeCycleBean有关的
        if (beanName.equals("lifeCycleBean"))
            log.debug("<<<<<< 销毁之前执行, 如 @PreDestroy");
    }

    /**
     * 实例化之前执行
     */
    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        if (beanName.equals("lifeCycleBean"))
            log.debug("<<<<<< 实例化之前执行, 这里返回的对象会替换掉原本的 bean");
        return null;
    }

    /**
     * 实例化之后执行
     */
    @Override
    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        if (beanName.equals("lifeCycleBean")) {
            log.debug("<<<<<< 实例化之后执行, 这里如果返回true(默认)会执行依赖注入阶段;false 会跳过依赖注入阶段");
//            return false;
        }
        return true;
    }

    /**
     * 依赖注入阶段执行,一般这里扩展功能比较多
     */
    @Override
    public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
        if (beanName.equals("lifeCycleBean"))
            log.debug("<<<<<< 依赖注入阶段执行, 如 @Autowired、@Value、@Resource");
        return pvs;
    }

    /**
     * 初始化之前执行
     */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (beanName.equals("lifeCycleBean"))
            log.debug("<<<<<< 初始化之前执行, 这里返回的对象会替换掉原本的 bean, 如 @PostConstruct、@ConfigurationProperties");
        return bean;
    }

    /**
     * 初始化之后执行
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (beanName.equals("lifeCycleBean"))
            log.debug("<<<<<< 初始化之后执行, 这里返回的对象会替换掉原本的 bean, 如代理增强");
        return bean;
    }
}

 二、常见的Bean后处理器

2.1 bean后处理器的作用 

        先来体验一下bean后处理器的作用:

        启动类: 

public class A04 {
    public static void main(String[] args) {
        // GenericApplicationContext 是一个【干净】的容器,没有一系列spring提供的后处理器
        GenericApplicationContext context = new GenericApplicationContext();

        // 用原始方法注册三个 bean
        context.registerBean("bean1", Bean1.class);
        context.registerBean("bean2", Bean2.class);
        context.registerBean("bean3", Bean3.class);
        
        // 初始化容器
        context.refresh(); // 执行beanFactory后处理器, 添加bean后处理器, 初始化所有单例

        System.out.println(context.getBean(Bean1.class));

        // 销毁容器
        context.close();
}

        Bean1里面使用了各种注解,Bean2和Bean3就是最简单的bean

public class Bean1 {
    private static final Logger log = LoggerFactory.getLogger(Bean1.class);

    private Bean2 bean2;

    @Autowired
    public void setBean2(Bean2 bean2) {
        log.debug("@Autowired 生效: {}", bean2);
        this.bean2 = bean2;
    }

    @Autowired
    private Bean3 bean3;

    @Resource
    public void setBean3(Bean3 bean3) {
        log.debug("@Resource 生效: {}", bean3);
        this.bean3 = bean3;
    }

    private String home;

    @Autowired
    public void setHome(@Value("${JAVA_HOME}") String home) {
        log.debug("@Value 生效: {}", home);
        this.home = home;
    }

    @PostConstruct
    public void init() {
        log.debug("@PostConstruct 生效");
    }

    @PreDestroy
    public void destroy() {
        log.debug("@PreDestroy 生效");
    }

    @Override
    public String toString() {
        return "Bean1{" +
               "bean2=" + bean2 +
               ", bean3=" + bean3 +
               ", home='" + home + '\'' +
               '}';
    }
}

        bean2:

public class Bean2 {
}

        bean3: 

public class Bean3 {
}

        运行:

        发现只打印了一些spring的信息。我们在bean1里写的一些log.dubug信息都没有打印出来,也就是说,我们使用的@Autowired、@Resource等注解都没有被解析。

        解析这些注解的功能实际上是由bean后处理器完成的。 

 2.2 常见的bean后处理器

        AutowiredAnnotationBeanPostProcessor 可以解析@Autowired和@Value注解;

        CommonAnnotationBeanPostProcessor 在依赖注入的时候解析@Resource注解,在初始化完成后解析@PostConstruct,在销毁之前解析@PreDestroy注解

public class A04 {
    public static void main(String[] args) {
        // ⬇️GenericApplicationContext 是一个【干净】的容器
        GenericApplicationContext context = new GenericApplicationContext();

        // ⬇️用原始方法注册三个 bean
        context.registerBean("bean1", Bean1.class);
        context.registerBean("bean2", Bean2.class);
        context.registerBean("bean3", Bean3.class);
        context.registerBean("bean4", Bean4.class);

        //这句可以帮助获取@Value注解里面的值
        context.getDefaultListableBeanFactory().setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
        // 解析@Autowired @Value
        context.registerBean(AutowiredAnnotationBeanPostProcessor.class);
        //解析@Resource @PostConstruct @PreDestroy
        context.registerBean(CommonAnnotationBeanPostProcessor.class);


        // 初始化容器
        context.refresh(); // 执行beanFactory后处理器, 添加bean后处理器, 初始化所有单例

        System.out.println(context.getBean(Bean1.class));

        // 销毁容器
        context.close();

        /*
            学到了什么
                a. @Autowired 等注解的解析属于 bean 生命周期阶段(依赖注入, 初始化)的扩展功能
                b. 这些扩展功能由 bean 后处理器来完成
         */
    }
}

        再引入一个bean4,它带有springboot中的属性绑定注解 @ConfigurationProperties

/*
    ConfigurationProperties是springboot中的注解,有属性绑定功能,也就是可以根据前缀匹配到相应的值
    比如前缀为Java,属性名为home,那么home的值就可以匹配到java.home
 */
@ConfigurationProperties(prefix = "java")
public class Bean4 {

    //java.home的值
    private String home;

    //java.version的值
    private String version;

    public String getHome() {
        return home;
    }

    public void setHome(String home) {
        this.home = home;
    }

    public String getVersion() {
        return version;
    }

    public void setVersion(String version) {
        this.version = version;
    }

    @Override
    public String toString() {
        return "Bean4{" +
               "home='" + home + '\'' +
               ", version='" + version + '\'' +
               '}';
    }
}

        解析@ConfigurationProperties:

ConfigurationPropertiesBindingPostProcessor.register(context.getDefaultListableBeanFactory());

三、AutowiredAnnotationBeanPostProcessor 源码分析 

3.1 AutowiredAnnotationBeanPostProcessor 的具体作用

        我们将AutowiredAnnotationBeanPostProcessor 实例化出来,看看他要调用哪些方法完成依赖注入。 

public class DigInAutowired {
    public static void main(String[] args) throws Throwable {
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        // 为了省事,选择了比较简单的注册方法,省略了创建过程,依赖注入,初始化
        beanFactory.registerSingleton("bean2", new Bean2());
        beanFactory.registerSingleton("bean3", new Bean3());

        //这句能够帮助找到@Value注解里的值
        beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());

        // 1. 查找哪些属性、方法加了 @Autowired, 这称之为 InjectionMetadata
        // 把AutowiredAnnotationBeanPostProcessor实例化出来,看看他要调用哪些方法完成依赖注入
        AutowiredAnnotationBeanPostProcessor processor = new AutowiredAnnotationBeanPostProcessor();
        processor.setBeanFactory(beanFactory);

        //复用前面的bean1,因为是我们自己实例化的,所以肯定没有注入那些依赖
        Bean1 bean1 = new Bean1();
        System.out.println(bean1);
        // 执行依赖注入 @Autowired @Value,再看看结果
        processor.postProcessProperties(null, bean1, "bean1"); 
        System.out.println(bean1);
}

         运行结果:

        可以看到,第一行打印中,bean2、bean3、home都是没有值的;而在最后一行,bean2 和 home有值,说明解析了@Autowired 和 @Value 注解。

3.2 AutowiredAnnotationBeanPostProcessor 原理解析 

        我们看一下 postProcessProperties 方法的源码:

public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
    //找到有哪些数据上有@Autowired注解,将结果封入InjectionMetadata 
    InjectionMetadata metadata = this.findAutowiringMetadata(beanName, bean.getClass(), pvs);

    try {
        //依赖注入
        metadata.inject(bean, beanName, pvs);
        return pvs;
    } catch (BeanCreationException var6) {
        throw var6;
    } catch (Throwable var7) {
        throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", var7);
    }
}

        也就是说 postProcessProperties 总体分为两步:

  1. 先看看有哪些数据上有@Autowired注解
  2. 然后再给这些数据执行依赖注入

        那我们分别看看这两步。

3.2.1 findAutowiringMetadata

        由于该方法是private方法,我们无法直接调用,于是我们采用反射的方式来执行它。 

public class DigInAutowired {
    public static void main(String[] args) throws Throwable {
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        // 为了省事,选择了比较简单的注册方法,省略了创建过程,依赖注入,初始化
        beanFactory.registerSingleton("bean2", new Bean2());
        beanFactory.registerSingleton("bean3", new Bean3());
        beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver()); // @Value
        beanFactory.addEmbeddedValueResolver(new StandardEnvironment()::resolvePlaceholders); // ${} 的解析器

        // 1. 查找哪些属性、方法加了 @Autowired, 这称之为 InjectionMetadata
        AutowiredAnnotationBeanPostProcessor processor = new AutowiredAnnotationBeanPostProcessor();
        processor.setBeanFactory(beanFactory);

        Bean1 bean1 = new Bean1();

        Method findAutowiringMetadata = AutowiredAnnotationBeanPostProcessor.class.getDeclaredMethod("findAutowiringMetadata", String.class, Class.class, PropertyValues.class);
        findAutowiringMetadata.setAccessible(true);
        InjectionMetadata metadata = (InjectionMetadata) findAutowiringMetadata.invoke(processor, "bean1", Bean1.class, null);// 获取 Bean1 上加了 @Value @Autowired 的成员变量,方法参数信息
        System.out.println(metadata);

    }
}

        下断点之后可以看到,metadata里有一个名为injectedElements的ArrayList,injectedElements里装着setBean2 和 setHome,也就是两个有@Autowired的注解

3.2.2  inject

        inject的原理是将findAutowiringMetadata 拿到的成员变量、方法名等信息封装成DependencyDescriptor对象,然后调用beanFactory.doResolveDependency方法。我们模拟一下inject方法。

public class DigInAutowired {
    public static void main(String[] args) throws Throwable {
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        // 为了省事,选择了比较简单的注册方法,省略了创建过程,依赖注入,初始化
        beanFactory.registerSingleton("bean2", new Bean2());
        beanFactory.registerSingleton("bean3", new Bean3());
        beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver()); // @Value
        beanFactory.addEmbeddedValueResolver(new StandardEnvironment()::resolvePlaceholders); // ${} 的解析器

        // 1. 查找哪些属性、方法加了 @Autowired, 这称之为 InjectionMetadata
        AutowiredAnnotationBeanPostProcessor processor = new AutowiredAnnotationBeanPostProcessor();
        processor.setBeanFactory(beanFactory);

        Bean1 bean1 = new Bean1();
        processor.postProcessProperties(null, bean1, "bean1"); 

        // 按类型查找值

        //bean3就相当于已经被findAutowiringMetadata找到的成员变量
        Field bean3 = Bean1.class.getDeclaredField("bean3");
        DependencyDescriptor dd1 = new DependencyDescriptor(bean3, false);
        Object o = beanFactory.doResolveDependency(dd1, null, null, null);
        System.out.println(o);

        //setBean2就相当于已经被findAutowiringMetadata找到的方法
        Method setBean2 = Bean1.class.getDeclaredMethod("setBean2", Bean2.class);
        DependencyDescriptor dd2 =
                new DependencyDescriptor(new MethodParameter(setBean2, 0), true);
        Object o1 = beanFactory.doResolveDependency(dd2, null, null, null);
        System.out.println(o1);

        //setHome就相当于已经被findAutowiringMetadata找到的方法
        Method setHome = Bean1.class.getDeclaredMethod("setHome", String.class);
        DependencyDescriptor dd3 = new DependencyDescriptor(new MethodParameter(setHome, 0), true);
        Object o2 = beanFactory.doResolveDependency(dd3, null, null, null);
        System.out.println(o2);

    }
}

         运行结果成功:

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

如何搭建chatGPT4.0模型-国内如何用chatGPT4.0

国内如何用chatGPT4.0 在国内&#xff0c;目前可以通过以下途径使用 OpenAI 的 ChatGPT 4.0&#xff1a; 自己搭建模型&#xff1a;如果您具备一定的技术能力&#xff0c;可以通过下载预训练模型和相关的开发工具包&#xff0c;自行搭建 ChatGPT 4.0 模型。OpenAI提供了相关的…

旅游心得Traveling Experience

前言 加油 原文 旅游心得常用会话 ❶ Share photos of the trip with friends. 与朋友分享旅游的照片。 ❷ We’ll go to the Great Wall, if you prefer. 你如果愿意的话,我们去长城。 ❸ Would you go to the church or the synagogue or the mosque? 你会去教堂,犹太…

二结(4.11)IO流学习

FIle类只能对文件本身操作&#xff0c;不能读写文件里面存储的数据 文件保存的位置叫路径&#xff0c;而数据传输叫IO流 Java I/O流&#xff08;Input/Output stream&#xff09;在Java应用程序中用于读取和写入数据&#xff0c;可分为基本流和高级流两类 关于什么是输出流、…

CSC中加学者交换项目申报即将开始

3月31日&#xff0c;国家留学基金委&#xff08;CSC&#xff09;发布了2023-2024年度中加学者交换项目遴选通知。根据通知精神&#xff0c;选派规模&#xff1a;100人月&#xff0c;留学及资助期限&#xff1a;4-12个月&#xff0c;网上报名及申请受理时间为2023年4月11日至6月…

SpringCloud学习6(Spring Cloud Alibaba)断路器Sentinel熔断降级

文章目录服务熔断降级Sentinel高并发请求模拟&#xff08;这里我们使用contiperf来进行测试&#xff09;修改tomcat配置最大线程数引入测试依赖编写测试代码服务雪崩服务雪崩的容错方案&#xff08;隔离、超时、限流、熔断、降级&#xff09;隔离机制&#xff1a;超时机制&…

Baumer工业相机堡盟工业相机如何设置网口的IP地址(工业相机连接的网口设置IP地址步骤)

Baumer工业相机堡盟工业相机如何设置网口的IP地址&#xff08;工业相机连接的网口设置IP地址步骤&#xff09;Baumer工业相机Baumer工业相机设置网络端口IP地址匹配设置网络端口IP地址和工业相机IP地址匹配第一次打开CameraExplorer软件确认问题为IP地址不匹配问题打开网络连接…

C++ - 继承 | 菱形继承

之前的文章中我们简要的讲述了C中继承部分的知识&#xff0c;但是还没有完全的讲完&#xff0c;在本文中将会讲到菱形继承的问题。 复杂的菱形继承 单继承&#xff1a;一个子类只有一个直接父类时称这个继承关系为单继承。 多继承&#xff1a;一个子类有两个或以上直接父类时…

最新阿里、腾讯、华为、字节等大厂的薪资和职级对比,看看你差了多少...

互联网大厂新入职员工各职级薪资对应表(技术线)~ 最新阿里、腾讯、华为、字节跳动等大厂的薪资和职级对比 上面的表格不排除有很极端的收入情况&#xff0c;但至少能囊括一部分同职级的收入。这个表是“技术线”新入职员工的职级和薪资情况&#xff0c;非技术线(如产品、运营、…

Android基础四大组件之Activity的启动过程源码解析

前言 Activity是Android中一个很重要的概念&#xff0c;堪称四大组件之首&#xff0c;关于Activity有很多内容&#xff0c;比如生命周期和启动Flags&#xff0c;这二者想要说清楚&#xff0c;恐怕又要写两篇长文&#xff0c;更何况分析它们的源码呢。不过本文的侧重点不是它们…

面试官:你可以用 for of 遍历 Object 吗?

本文以 用 for of遍历 Object 为引 来聊聊 迭代器模式。 什么是迭代器模式 迭代器模式提供一种方法顺序访问一个聚合对象中的各个元素&#xff0c;而又不暴露该对象的内部表示。 ——《设计模式&#xff1a;可复用面向对象软件的基础》 可以说迭代器模式就是为了遍历存在的。提…

HTML5 <body> 标签

HTML <body> 标签 实例 一个简单的 HTML 文档&#xff0c;包含尽可能少的必需的标签&#xff1a; <!DOCTYPE html> <html> <head> <meta charset"utf-8"> <title>文档标题</title> </head><body> 文档内容…

单例设计模式解读

目录 单例设计模式介绍 单例设计模式八种方式 饿汉式&#xff08;静态常量&#xff09; 饿汉式&#xff08;静态代码块&#xff09; 懒汉式(线程不安全) 懒汉式(线程安全&#xff0c;同步方法) 懒汉式(线程安全&#xff0c;同步代码块) 懒汉式(线程安全&#xff0c;同步…

libcurl库的安装及使用说明

目录 一 libcurl库安装 ① 下载网址 ② libcurl库安装步骤 ③ libcurl等第三方库的通用编译方法 二 调用libcurl编程访问百度主页 ① 代码说明 ② 编译说明 ③ 执行说明 三 libcurl的使用说明 ① curl相关函数简介 ② curl_easy_setopt函数部分选项介绍 ③…

【AUTOSAR】【Can通信】CanSM

目录 一、概述 二、限制说明 三、功能说明 3.1 基础需求 3.2 CAN网络的状态机 3.3 错误分类 3.3.1 开发错误 3.3.2 运行错误 3.3.3 拓展生产错误 3.4 ECU在线主动/被动模式 四、API接口 4.1 API定义 4.2 调度接口 一、概述 该文档描述了AUTOSAR基本软件模块CAN状态…

2023年第十四届蓝桥杯Java_大学B组真题

Java_B组试题 A: 阶乘求和试题 B: 幸运数字试题 C: 数组分割试题 D: 矩形总面积试题 E: 蜗牛试题 F: 合并区域试题 G: 买二赠一试题 H: 合并石子试题 I: 最大开支试题 J: 魔法阵【考生须知】 考试开始后&#xff0c;选手首先下载题目&#xff0c;并使用考场现场公布的解压密码解…

编译技术-词法理论

一、文法的种类 1.1 分类定义 Chomsky 文法定义&#xff1a; G(V,Vt,P,Z)G (V, V_t, P, Z)G(V,Vt​,P,Z)VVV&#xff1a;符号集合VtV_tVt​&#xff1a;终结符号集合PPP &#xff1a;有穷规则集合ZZZ&#xff1a;是被符号&#xff0c;不能是终结符 关于不同文法的区别 类型…

【多线程与高并发(锁)】1、锁的概念、分类和状态

1、锁的概念 java当中的锁、是在多线程环境下为保证共享资源健康、线程安全的一种手段。 线程操作某个共享资源之前&#xff0c;先对资源加一层锁&#xff0c;保证操作期间没有其他线程访问资源&#xff0c;当操作完成后&#xff0c;再释放锁。 2、锁的分类 Java中的锁按照…

ChatGPT对我们的影响-ChatGPT能给我们带来什么

ChatGPT日常应用 ChatGPT是一种应用广泛的自然语言处理算法&#xff0c;其可以应用于多种日常场景。以下是一些ChatGPT的日常应用&#xff1a; 聊天机器人&#xff1a;ChatGPT可用于构建聊天机器人&#xff0c;通过与用户进行自然语言交互来提供个性化的服务和支持。 新闻稿和…

MyBatis零基础入门实践

MyBatis一、MyBatis简介1、MyBatis历史2、MyBatis特性3、MyBatis下载4、和其它持久化层技术对比二、搭建MyBatis1、开发环境2、创建maven工程3、创建MyBatis的核心配置文件4、创建mapper接口5、创建MyBatis的映射文件6、通过junit测试功能7、加入log4j日志功能三、核心配置文件…

代码随想录算法训练营第五十天 | 123. 买卖股票的最佳时机 III、188. 买卖股票的最佳时机 IV

123. 买卖股票的最佳时机 III 关键在于至多买卖两次&#xff0c;这意味着可以买卖一次&#xff0c;可以买卖两次&#xff0c;也可以不买卖。 动规五部曲&#xff1a; 1、确定dp数组以及下标的含义 一天一共就有五个状态&#xff0c; 没有股票第一次持有股票第一次不持有股…