Spring进阶(Bean的生命周期与Bean的后处理器)

SpringBean生命周期各个阶段:

java 复制代码
@Component
public class LifeCycleBean {
    private static final Logger log = LoggerFactory.getLogger(LifeCycleBean.class);

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

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

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

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

上述的四个阶段,就是Bean从创建到销毁的整个阶段,当然在每个阶段的前后也是可以做一些功能的增强,代码如下:

java 复制代码
@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 {
        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("<<<<<< 实例化之后执行, 这里如果返回 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;
    }
}

此时如果执行主方法,该增强会被读取到,并执行:

DEBUG\] 13:49:17.949 \[main\] com.itheima.a03.MyBeanPostProcessor - \<\<\<\<\<\< 实例化之前执行, 这里返回的对象会替换掉原本的 bean \[DEBUG\] 13:49:17.951 \[main\] com.itheima.a03.LifeCycleBean **- 构造** \[DEBUG\] 13:49:17.953 \[main\] com.itheima.a03.MyBeanPostProcessor - \<\<\<\<\<\< **实例化之后执行, 这里如果返回 false 会跳过依赖注入阶段** \[DEBUG\] 13:49:17.953 \[main\] com.itheima.a03.MyBeanPostProcessor - \<\<\<\<\<\< **依赖注入阶段执行** , 如 @Autowired、@Value、@Resource \[DEBUG\] 13:49:17.955 \[main\] com.itheima.a03.LifeCycleBean -**依赖注入** : C:\\Program Files\\Java\\jdk-17 \[DEBUG\] 13:49:17.957 \[main\] com.itheima.a03.MyBeanPostProcessor - \<\<\<\<\<\<**初始化之前执行**, 这里返回的对象会替换掉原本的 bean, 如 @PostConstruct、@ConfigurationProperties \[DEBUG\] 13:49:17.957 \[main\] com.itheima.a03.LifeCycleBean - **初始化** \[DEBUG\] 13:49:17.957 \[main\] com.itheima.a03.MyBeanPostProcessor - \<\<\<\<\<\< **初始化之后执行**, 这里返回的对象会替换掉原本的 bean, 如代理增强

模板设计模式:

如果在上述四个阶段完成之前与之后做一些增强,此时有什么好的实现方式呢,如果每次增强要改动原有的类,原始类会变得臃肿不好管理,此时需要有解耦的概念。

原始类如下:

java 复制代码
  static class MyBeanFactory {
        public Object getBean() {
            Object bean = new Object();
            System.out.println("构造 " + bean);
            System.out.println("依赖注入 " + bean); // @Autowired, @Resource
            System.out.println("初始化 " + bean);
            return bean;
        }
}

此时如果要在依赖注入之后做一些增强,在原始类上直接调用显然不合理,可以写一个接口作为中间的媒介

java 复制代码
    static interface BeanPostProcessor {
        public void inject(Object bean); // 对依赖注入阶段的扩展
    }

该接口中可以写所有的增强阶段,之后将该所有写好的接口收集起来(add),后期遍历所有的接口,按照方法名对不同阶段进行增强:

java 复制代码
   private List<BeanPostProcessor> processors = new ArrayList<>();

        public void addBeanPostProcessor(BeanPostProcessor processor) {
            processors.add(processor);
        }
java 复制代码
   public Object getBean() {
            Object bean = new Object();
            System.out.println("构造 " + bean);
            System.out.println("依赖注入 " + bean); // @Autowired, @Resource
            for (BeanPostProcessor processor : processors) {
                processor.inject(bean);
            }
            System.out.println("初始化 " + bean);
            return bean;
        }

此时只需要在主方法可以任意添加增强方法即可:

java 复制代码
 public static void main(String[] args) {
        MyBeanFactory beanFactory = new MyBeanFactory();
        beanFactory.addBeanPostProcessor(
            new BeanPostProcessor() {
                @Override
                public void inject(Object bean) {
                    System.out.println("解析 @Autowired");
                }
            }
        );
        beanFactory.addBeanPostProcessor(bean -> System.out.println("解析 @Resource"));
        beanFactory.getBean();
    }

Bean的后处理器的作用:

容器选用:GenericApplicationContext(较为干净的容器,后处理器几乎没有加)

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

Bean1:

java 复制代码
public class Bean1 {
    private static final Logger log = LoggerFactory.getLogger(Bean1.class);

    private Bean2 bean2;

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

    private Bean3 bean3;

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

    private String home;

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

    @PostConstruct
    public void init() {
        log.info("@PostConstruct 生效:{}",home);
    }

    @PreDestroy
    public void destroy() {
        log.info("@PreDestroy 生效:{}",home);
    }

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

Bean2:

java 复制代码
public class Bean2 {
}

Bean3:

java 复制代码
public class Bean3 {
}

常见的后处理器:

java 复制代码
        //解析@Value值的获取
        context.getDefaultListableBeanFactory().setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
        context.registerBean(AutowiredAnnotationBeanPostProcessor.class); // @Autowired @Value
java 复制代码
        context.registerBean(CommonAnnotationBeanPostProcessor.class); // @Resource @PostConstruct @PreDestroy

添加上述解析器之后结果如下:

DEBUG\] 14:40:53.781 \[main\] com.a04.Bean1 - @Resource 生效: com.itheima.a04.Bean3@2f54a33d \[DEBUG\] 14:40:53.796 \[main\] com.a04.Bean1 - @Autowired 生效: com.itheima.a04.Bean2@7e9131d5 \[DEBUG\] 14:40:53.809 \[main\] com.a04.Bean1 - @Value 生效: C:\\Program Files\\Java\\jdk-17 \[DEBUG\] 14:40:53.809 \[main\] com.a04.Bean1 - @PostConstruct 生效 Bean1{bean2=com.a04.Bean2@7e9131d5, bean3=com.a04.Bean3@2f54a33d, home='C:\\Program Files\\Java\\jdk-17'} \[DEBUG\] 14:40:53.820 \[main\] com.a04.Bean1 - @PreDestroy 生效

示例代码如下:

java 复制代码
/*
    java.home=
    java.version=
 */
@ConfigurationProperties(prefix = "java")
public class Bean4 {

    private String home;

    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 + '\'' +
               '}';
    }
}

此时如果直接getBean4,发现home与version的值均为null!

如果加上相应的后置处理器

java 复制代码
        ConfigurationPropertiesBindingPostProcessor.register(context.getDefaultListableBeanFactory());

此时输出正确结果。

Autowired解析器的全流程:

目前要解析Autowired,只需要添加相关的后处理器即可,但是具体的解析流程是什么?

第一步:查找哪些属性、方法加了 @Autowired

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

第二步:获取 Bean1 上加了 @Value @Autowired 的成员变量,方法参数信息

java 复制代码
        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);

第三步:调用 InjectionMetadata 来进行依赖注入, 注入时按类型查找值

java 复制代码
  // 3. 调用 InjectionMetadata 来进行依赖注入, 注入时按类型查找值
        System.out.println(bean1);
        metadata.inject(bean1, "bean1", null);
        System.out.println(bean1);

具体查找过程:

成员变量:

java 复制代码
        Field bean3 = Bean1.class.getDeclaredField("bean3");
        DependencyDescriptor dd1 = new DependencyDescriptor(bean3, false);
        Object o = beanFactory.doResolveDependency(dd1, null, null, null);
        System.out.println(o);

类的参数:

java 复制代码
        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);

值注入:

java 复制代码
        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);
相关推荐
吃好睡好便好7 小时前
在Matlab中绘制峰值图
开发语言·学习·算法·matlab·信息可视化
RingWu7 小时前
高并发三板斧-缓存:命中率、一致性、治理
java·spring·缓存
兩尛7 小时前
std::shared_mutex、std::mutex和std::recursive_mutex是什么锁
开发语言·c++·算法
voyaqi7 小时前
从零设计企业级校验框架:Spring Boot + SPI 实战指南
spring boot·后端·log4j
A-刘晨阳7 小时前
用树莓派搭一个弱网模拟网关,让你的应用在2G、高延迟、丢包环境下跑一遍
开发语言·php
流年如夢7 小时前
类和对象(上)
android·java·开发语言
huipeng9268 小时前
基于SpringCloud的博客系统
java·运维·后端·spring·spring cloud·微服务
kyle~8 小时前
查找---插值查找(二分查找的改进版本)
开发语言·c++
高翔·权衡之境8 小时前
主题7:缓存与队列——速度不匹配的通用解
开发语言·人工智能·物联网·缓存·信息与通信·信号处理