【探索Spring底层】3.Bean生命周期与Bean后处理器

【探索Spring底层】Bean生命周期与Bean后处理器

1. Bean生命周期

Spring管理的Bean,生命周期的由以下阶段:

  1. 创建,可以根据Bean的构造方法或者工厂方法来创建Bean实例对象
  2. 依赖注入,对Bean内部使用@Autowired,@Value 或其它一些手段进行依赖注入的成员变量,为其填充值、创建关系
  3. 初始化,调用对象各种初始化方法
  4. 销毁,在容器关闭的时候,会销毁所有的单例对象(即调用它们的销毁方法)
    • prototype 对象也能够销毁,不过需要容器这边主动调用

这里就演示一下Bean的生命周期

准备一个Bean,交给Spring管理

@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("销毁");
    }
}

这里面由两个注解需要解释一下,分别是@PostConstruct 和 @PreDestroy

  • @PostConstruct, 该注解是用来修饰一个非静态的void( )方法,被@PostConstruct修饰的方法会在服务器加载Servlet的时候运行,并且只会被调用一次
  • @PreDestroy, 该注解也是是用来修饰一个非静态的void( )方法,被@PreDestroy修饰的方法会在服务器卸载Servlet的时候运行,并且只会被调用一次

除了这两个注释,里面的@Autowired的位置相信应该觉得奇怪吧,一般来说@Autowired的注解都是方法某个变量上面,为的是注入某个变量,但是这里却是放置在方法上。

@Autowired
public void autowire(UserService userService) {
    log.debug("依赖注入: {}", home);
}

这方法的方法参数是UserService,然后方法中有@Autowired注解,所以Spring就在在Bean工厂中寻找有没有UserService这个Bean,找到之后就会注入进去。

其实这是一种写法,就比如说,这种注入只能是自己定义的对象,比如String这种,就不能像上面这样写,因为Bean工厂中并没有String的Bean,所以上面添加了@Value注解,通过@Value注解就可以找到环境变量JDK的位置

接下来准备一个SpringBoot启动类测试一下

@SpringBootApplication
public class A03 {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(A03.class, args);
        context.close();
    }
}

在这里插入图片描述

在这里就可以清晰看到Bean的生命周期


1.1 Bean后处理器之Bean生命周期扩展

这里准备了一个Bean后处理器类,它实现了InstantiationAwareBeanPostProcessor和DestructionAwareBeanPostProcessor两个接口

  • postProcessBeforeDestruction,这个方法是在销毁之前执行
  • postProcessBeforeInstantiation,这个方法是在Bean实例化之前执行,若返回null则保持原有对象不变,不为nukk则会替换原来的Bean
  • postProcessAfterInstantiation,此方法在Bean实例化之后执行,如果返回值是true继续后续依赖注入阶段,否则跳过依赖注入阶段
  • postProcessProperties,这个方法是在依赖注入阶段执行,比如@Autowired、@Value、@Resource
  • postProcessBeforeInitialization,和postProcessBeforeInstantiation方法差不多,返回的Bean会替换掉原来的Bean,解析@PostConstruct、@ConfigurationProperties等注解
  • postProcessAfterInitialization,这个方法在初始化之后执行,如果返回Bean则会替换对象,这里做的是一些代理增强
@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");
        }
    }

    /**
     * bean实例化之前,返回null则是保持原有对象不变,不为null则会替换掉原来的bean
     * @param beanName
     * @return
     * @throws BeansException
     */

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

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

    /**
     * 依赖注入阶段
     * @param pvs
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    @Override
    public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
        if (beanName.equals("lifeCycleBean"))
            log.debug("<<<<<< 依赖注入阶段执行, 如 @Autowired、@Value、@Resource");
        return pvs;
    }

    /**
     * 初始化之前执行,返回的对象会被替换
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    @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生命周期的各个时期都会有增强,是靠Bean后处理器实现的


2. 常见的Bean后处理器

学习Bean后处理器之前先准备一个干净的容器,然后注册几个Bean

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

        // ⬇️用原始方法注册三个 bean
        //不指定Bean的配置范围默认为单例
        context.registerBean("bean1", Bean1.class);
        context.registerBean("bean2", Bean2.class);
        context.registerBean("bean3", Bean3.class);
        

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


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

    }
}

这个容器之所以说是干净的,是因为这里面没有添加有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 + '\'' +
               '}';
    }
}
public class Bean2 {
}
public class Bean3 {
}

这里根据Bean1的注解来讲些相关注解是由哪些Bean处理器实现的

现在运行一下程序发现,什么东东也没有

在这里插入图片描述

证明Bean1的相关注解并没有被解析


2.1 AutowiredAnnotationBeanPostProcessor

//拿到BeanFactory
//AutowireCandidateResolver默认解析器不能解析字符串
context.getDefaultListableBeanFactory().setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());

//解析@Autowired @Value注解
context.registerBean(AutowiredAnnotationBeanPostProcessor.class); // @Autowired @Value

添加AutowiredAnnotationBeanPostProcessor,使得可以解析@Autowired @Value注解

但是AutowireCandidateResolver默认解析器不能解析字符串,所以还需要拿到Bean工厂设置自动装配候选者,设置为ContextAnnotationAutowireCandidateResolver的对象

这时候运行程序发现@Autowired @Value都生效了

在这里插入图片描述

但是@Resource @PostConstruct @PreDestroy这些注解并没有生效


2.2 CommonAnnotationBeanPostProcessor

接着添加CommonAnnotationBeanPostProcessor Bean后处理器就可以解析 @Resource @PostConstruct @PreDestroy这些注解

context.registerBean(CommonAnnotationBeanPostProcessor.class); 

在这里插入图片描述

这里可以发现,这里是先解析 @Resource,再解析@Autowired,这是因为有排序,前面的【探索Spring底层】2.容器的实现中的1.1小节有提到过


2.3 ConfigurationPropertiesBindingPostProcessor

这个Bean后处理器是用来处理@ConfigurationProperties注解的,@ConfigurationProperties是SpringBoot的一个注解

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

当Bean被实例化时,@ConfigurationProperties会将对应前缀的后面的属性与Bean对象的属性匹配。符合条件则进行赋值。

比如上面的前缀是java,然后它就会找java.home和java.version这些属性

给容器添加Bean后处理器,就可以解析@ConfigurationProperties注解了

当然前提是要先将Bean4添加到容器中

context.registerBean("bean4", Bean4.class);
ConfigurationPropertiesBindingPostProcessor.register(context.getDefaultListableBeanFactory());

在初始化容器之后获取到Bean4并打印出来

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

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

在这里插入图片描述


3. 分析@Autowired Bean后处理器(AutowiredAnnotationBeanPostProcessor 运行分析)

先初始化,创建beanFactory和添加Bean

DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerSingleton("bean2", new Bean2()); // 创建过程,依赖注入,初始化
beanFactory.registerSingleton("bean3", new Bean3());
beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver()); // @Value
beanFactory.addEmbeddedValueResolver(new StandardEnvironment()::resolvePlaceholders); // ${} 的解析器

这里使用了beanFactory的registerSingleton方法来注册Bean,较为方便,但是因为这里new了一个对象,所以创建过程、依赖注入、初始化这些操作都不会再进行一遍了


3.1 使用InjectionMetadata模拟@Autowired 依赖注入

之前都是将这个后处理器添加到beanFactory中,由beanFactory间接调用,为了更加清除AutowiredAnnotationBeanPostProcessor 的流程,可以把它单拿出来

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

给这个@Autowired Bean后处理器设置BeanFctory,这是因为@Autowired需要根据成员变量的类型或者方法参数类型找到要注入的对象,但是这些Bean都注册再BeanFactory中,所以需要需要给这个Bean后处理器添加BeanFactory,也就是间接使用Bean工厂

 Bean1 bean1 = new Bean1();
System.out.println(bean1);
//第一个参数是指定对象的值,也就是这个bean1第一个参数什么值、第二个参数什么值,这里设置为null,是因为这些成员变量参数不需要我们自己设置,而是需要在bean工厂中寻找
processor.postProcessProperties(null, bean1, "bean1"); // 执行依赖注入 @Autowired @Value
System.out.println(bean1);

可以发现一开始Bean1没有值,但是执行依赖注入之后,Bean1的成员变量就有值了

在这里插入图片描述

通过查看postProcessProperties的源码

@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
   InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
   try {
      metadata.inject(bean, beanName, pvs);
   }
   catch (BeanCreationException ex) {
      throw ex;
   }
   catch (Throwable ex) {
      throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
   }
   return pvs;
}

可以发现,第一步是执行findAutowiringMetadata方法,这个方法的作用是寻找有没有加@Autowired 的成员变量和方法参数,找到之后将信息封装在InjectionMetadata对象中

然后执行metadata.inject(bean, beanName, pvs);,通过反射对成员变量或者方法参数赋值

接下来就深究一下这个findAutowiringMetadata方法

查看源码之后发现这是一个私有方法

private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
   // Fall back to class name as cache key, for backwards compatibility with custom callers.
   String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
   // Quick check on the concurrent map first, with minimal locking.
   InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
   if (InjectionMetadata.needsRefresh(metadata, clazz)) {
      synchronized (this.injectionMetadataCache) {
         metadata = this.injectionMetadataCache.get(cacheKey);
         if (InjectionMetadata.needsRefresh(metadata, clazz)) {
            if (metadata != null) {
               metadata.clear(pvs);
            }
            metadata = buildAutowiringMetadata(clazz);
            this.injectionMetadataCache.put(cacheKey, metadata);
         }
      }
   }
   return metadata;
}

但是我们可以通过反射去获取这个方法

首先通过反射获取findAutowiringMetadata方法,String.class, Class.class, PropertyValues.class这些为findAutowiringMetadata的参数

Method findAutowiringMetadata = AutowiredAnnotationBeanPostProcessor.class.getDeclaredMethod("findAutowiringMetadata", String.class, Class.class, PropertyValues.class);

因为这个findAutowiringMetadata方法是私有的,通过setAccessible方法,设置为true,就可以屏蔽Java语言的访问检查,使得对象的私有属性以及方法也能被调用

findAutowiringMetadata.setAccessible(true);

然后用反射来执行这个findAutowiringMetadata方法,要传的参数为findAutowiringMetadata方法中要传的参数

// 获取 Bean1 上加了 @Value @Autowired 的成员变量,方法参数信息
InjectionMetadata metadata = (InjectionMetadata) findAutowiringMetadata.invoke(processor, "bean1", Bean1.class, null);

由于返回的InjectionMetadata对象应该没有重写toString方法,所以可以使用DEBUG形式查到值

在这里插入图片描述

可以看到里面有一个集合,集合里面有三个元素,这是因为Bean里面有两个方法和一个成员变量添加了@Autowired注解,所以找到的是三个

在这里插入图片描述

然后调用inject注入bean1

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

也就是源码中的这一步

在这里插入图片描述

运行程序看可以发现Bean1注入成功

在这里插入图片描述


3.2 使用DependencyDescriptor模拟@Autowired基于类型查找实现注入

首先利用反射获取这个成员变量

Field bean3 = Bean1.class.getDeclaredField("bean3");

然后Spring会在InjectionMetadata内部将它封装成一个DependencyDescriptor对象

第一个参数为成员变量,第二个参数true代表必选,到时候在BeanFactory找不到这个Bean的话就会注入;false的话到时候找不到也不会报错

DependencyDescriptor dd1 = new DependencyDescriptor(bean3, false);

接着InjectionMetadata也就是上面的metadata就会调用beanFactory的doResolveDependency方法

根据成员变量找需要注入的数据,也就是根据封装好的DependencyDescriptor对象找

第一个参数为DependencyDescriptor

第二个参数为bean的名字,可以不指定

Object o = beanFactory.doResolveDependency(dd1, null, null, null);

那为什么给了DependencyDescriptor对象就可以找到要注入谁呢?

这是因为@Autowired是根据类型来找,成员变量有了,就可以知道这个成员变量的类型,进而在容器中找到符合类型的Bean

同样的方法也类似,以方法参数为标准

new MethodParameter(setBean2, 0)是指setBean2方法的第一个参数

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);

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);

在这里插入图片描述


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

起名方面没有灵感

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值