likes
comments
collection
share

Spring底层原理分析-六(Bean可实现的接口)

作者站长头像
站长
· 阅读数 15

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第2天,点击查看活动详情

Bean实现的接口

  在Spring中,Bean本身可以实现众多的接口,来对Bean本身做一定的补充功能,本篇就先对于常见的Aware类型接口、初始化、销毁接口做进一步分析。

Aware

  Aware是一个接口系列,可以注入一些与容器相关的信息,常见的有以下几种:

  • BeanNameAware:注入Bean的名字;
  • BeanFactoryAware:注入BeanFactory容器;
  • ApplicationContextAware:注入ApplicationContext容器;
  • EmbededValueResolverAware:注入${}解析器;

Aware使用举例

public class MyBean implements BeanNameAware,ApplicationContextAware {
    @Override
    public void setBeanName(String name) {
        System.out.println("MyBean_BeanName:"+name);
    }
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.out.println("MyBean_ApplicationContext:"+applicationContext);
    }
}

  使用方式就如上面例子一样,需要什么功能就在Bean的类中实现相应的方法,如BeanNameAware会有一个setBeanName的方法,当容器注入该Bean,并初始化的时候,就会调用该方法,这时我们就可以将name存储在类中以便后续使用。setApplicationContext方法也同理。

InitializingBean和DesposableBean

@Override
public void afterPropertiesSet() throws Exception {
    System.out.println("初始化执行");
}
@Override
public void destroy() throws Exception {
    System.out.println("销毁执行");
}

  这两个接口是用来执行bean初始化方法和销毁方法的,比较简单,不做过多赘述。

接口本质作用

  如果留意之前的章节,会发现,上面的功能似曾相识,好像用@Autowired注解就可以解决,以及初始化方法可以用@PostConstruct来处理,那为什么还要多写很多代码来实现这些接口呢?那是因为我们之前也提到了,每次使用一种注解的时候,都需要添加相应的后处理器。如果是一个简单的容器,比如前面提到的GenericApplicationContext容器,该容器本身没有添加任何后处理器,也就没有这些扩展的功能。而Aware和InitializingBean都是属于Spring内置的功能,不用添加任何的扩展就可以进行识别。

注解失效时机

  @Autowired和@PostConstruct在有些时候是会失效的,这也就反映了接口实现的作用。那么什么时候会失效呢?我们来测试一下。

@Configuration
public class Config01 {
    @Autowired
    public void setApplicationContext(ApplicationContext applicationContext){
        System.out.println("获取到容器:"+applicationContext);
    }
    @PostConstruct
    public void init(){
        System.out.println("执行初始化方法");
    }
}
public static void main(String[] args) throws IOException {
    GenericApplicationContext context = new GenericApplicationContext();
    context.registerBean("config01", Config01.class);
    context.registerBean(ConfigurationClassPostProcessor.class);
    context.registerBean(AutowiredAnnotationBeanPostProcessor.class);
    context.registerBean(CommonAnnotationBeanPostProcessor.class);
    context.refresh();
    for (String name : context.getBeanDefinitionNames()) {
        System.out.println(name);
    }
    context.close();
}

  这里使用注解的方式写一个例子,这里我们也加入了用来解析@Autowired和@PostConstruct的后处理器,这里是可以正确解析到两种注解的。

@Bean
public BeanFactoryPostProcessor processor(){
    return beanFactory -> {
        System.out.println("执行processor");
    };
}

  在Config01中添加一段代码,查看打印结果。

执行processor

  只有这一句执行了,前面的两句都不见了,也就是上面所说的注解失效了,这里我们先了解一下context.refresh();这一句初始化都执行了些什么逻辑。 Spring底层原理分析-六(Bean可实现的接口)   这是一个一般的Bean创建过程的流程图,我们发现BeanFactoryPostProcessor是最先执行的。那么我们上面的代码在Bean里面添加了BeanFactoryPostProcessor会发生什么?看下面的图。 Spring底层原理分析-六(Bean可实现的接口)   回看上面的代码,来分析为何会发生这样一个顺序的转变,之前章节讲过@Bean是使用的工厂方法来创建的Bean,想要调用该方法,前提是要先创建方法所在类的实例对象。这样的话,就把创建和初始化放在了前面,直接跳过了BeanPostProcesser的注册,而失效的两个注解也正好是后处理器来处理的逻辑。没有了后处理器的参与,对象本身只能执行Aware和InitializingBean的方法。等到最后注册后处理器的时候,对象也已经创建完毕,自然也不会执行相应的注解了。   当然日常开发中一般不会这么写,想要解决也比较容易,换成接口实现方式即可。当然也可以在工厂方法上添加static关键字,这样方法的执行就不会依赖于对象的创建。由于接口比注解更加的可靠性质,Spring框架内部也是常用接口的方式来实现。

初始化及销毁执行顺序

  我们通过之前所描述的内容可知,Bean的初始化方法有三种定义的方式,分别是:@Bean注解的initMethod参数、实现InitializingBean接口、@PostConstruct注解。销毁的方法也有三种定义方式,分别是:@Bean注解的destroyMethod参数、实现DesposableBean接口、@PreDestroy注解。   执行顺序初始化和销毁是一致的。先注解,再接口,后参数定义。Aware接口是在三种初始化方法之前执行。 Spring底层原理分析-六(Bean可实现的接口)