07-spring Spring扩展接口总结

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/my_momo_csdn/article/details/90986148

Spring扩展接口

一、扩展接口分类

  • Spring中的扩展接口非常多,给开发者留下了足够多的扩展点。在Spring中最核心的是IOC容器,而IOC容器中核心的是Bean,Bean就是Java对象,因此扩展点围绕的就是对这个Java对象(Bean)的生命周期的一些关键点做一些扩展。生命周期包括实例化,初始化,销毁等,针对每个阶段Spring几乎都有对应的扩展点,了解这些可以更好的帮助我们理解整个Spring容器的运行和对Bean的扩展和管理。按照扩展点的分类,我梳理了Spring中有下面这些扩展点。

1.1 Aware系列

  • BeanNameAware
  • ApplicationContextAware
  • BeanFactoryAware

1.2 BeanPostProcessor系列

  • BeanPostProcessor
  • BeanFactoryPostProcessor
  • InstantiationAwareBeanPostProcessor
  • MergedBeanDefinitionPostProcessor

1.3 初始化和销毁

  • InitialingBean和@PostConstruct,initMethod
  • DisposableBean和@PreDestroy,destroyMethod

1.4 其他

  • FactoryBean

二、详解

2.1 Aware系列解析

2.1.1 BeanNameAware

  • Aware系列的接口作用很相似,都是给一个bean赋予一种能力,或者说给bean附一个值进去,比如BeanNameAware接口就是将beanName传给bean,后面的ApplicationContextAware接口,就是将ApplicationContext传给bean,BeanFactoryAware接口就是将BeanFactory传给bean,几乎是类似的,如果我们的bean在业务功能上需要这些对象,就可以通过相对应的Aware接口来实现字段的注入。
public class Man implements DisposableBean, InitializingBean, ApplicationContextAware, BeanNameAware, BeanFactoryAware, EnvironmentAware, ApplicationEventPublisherAware {

    private String name;
    private int age;
    private String beanName;


    public Man(String name, int age) {
        System.out.println("Man 带参构造方法执行...");
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Man{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }


    @Override
    public void destroy() throws Exception {
        System.out.println("Man 继承自DisposableBean接口的destroy 方法执行...");
    }


    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("Man 继承自InitializingBean接口的afterPropertiesSet 方法执行...");
    }

    public void init() {
        System.out.println("Man 类通过@Bean的init-method指定的init方法执行...");
    }

    public void destroySelf() {
        System.out.println("Man 类通过@Bean的destroyMethod指定的destroySelf方法执行...");
    }

    @PostConstruct
    public void postConstruct() {
        System.out.println("Man 类通过@PostConstruct注解指定的PostConstruct方法执行........");
    }

    @PreDestroy
    public void preDestroy() {
        System.out.println("Man 类通过@PreDestroy注解指定的PreDestroy方法执行......");
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.out.println("Man 继承自ApplicationContextAware接口的setApplicationContext方法执行......" + applicationContext.getId());
    }

    @Override
    public void setBeanName(String name) {
        this.beanName = name;
        System.out.println("Man 继承自BeanNameAware接口的setBeanName方法执行......" + name);
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        System.out.println("Man 继承自BeanFactoryAware接口的setBeanFactory方法执行......" + beanFactory.containsBean(beanName));
    }

    @Override
    public void setEnvironment(Environment environment) {
        System.out.println("Man 继承自EnvironmentAware接口的setEnvironment方法执行......" + environment.toString());
    }

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        System.out.println("Man 继承自ApplicationEventPublisherAware接口的setApplicationEventPublisher方法执行......"
                + applicationEventPublisher.toString());
    }
}

配置类:
@Configuration
@ComponentScan("com.intellif.ch1")
@PropertySource(value = "classpath:/application.properties")
public class MainConfig1 {

    @Bean(value = "man", initMethod = "init", destroyMethod = "destroySelf")
    public Man man() {
        return new Man("Parker", 33);
    }

}

测试:

public class Test1 {

    @Test
    public void test01() {
        ApplicationContext app = new AnnotationConfigApplicationContext(MainConfig1.class);
        ((AnnotationConfigApplicationContext) app).close();
    }
}

打印:
MyInstantiationAwareBeanPostProcessor后置处理器的postProcessBeforeInstantiation方法执行...man
Man 带参构造方法执行...
MyMergedBeanDefinitionPostProcessor后置处理器的postProcessMergedBeanDefinition方法执行...man
MyInstantiationAwareBeanPostProcessor后置处理器的postProcessAfterInstantiation方法执行...man
MyInstantiationAwareBeanPostProcessor后置处理器的的PropertyDescriptor方法执行...man
Man 继承自BeanNameAware接口的setBeanName方法执行......man
Man 继承自BeanFactoryAware接口的setBeanFactory方法执行......true
Man 继承自EnvironmentAware接口的setEnvironment方法执行......StandardEnvironment {activeProfiles=[], defaultProfiles=[default], propertySources=[MapPropertySource {name='systemProperties'}, SystemEnvironmentPropertySource {name='systemEnvironment'}, ResourcePropertySource {name='class path resource [application.properties]'}]}
Man 继承自ApplicationEventPublisherAware接口的setApplicationEventPublisher方法执行......org.springframework.context.annotation.AnnotationConfigApplicationContext@2471cca7: startup date [Tue May 07 13:03:12 CST 2019]; root of context hierarchy
Man 继承自ApplicationContextAware接口的setApplicationContext方法执行......org.springframework.context.annotation.AnnotationConfigApplicationContext@2471cca7
MyBeanPostProcessor后置处理器的postProcessBeforeInitialization方法执行,the bean name is :  man
Man 类通过@PostConstruct注解指定的PostConstruct方法执行........
Man 继承自InitializingBean接口的afterPropertiesSet 方法执行...
Man 类通过@Bean的init-method指定的init方法执行...
MyBeanPostProcessor后置处理器的postProcessAfterInitialization方法执行,the bean name is : man
Man 类通过@PreDestroy注解指定的PreDestroy方法执行......
Man 继承自DisposableBean接口的destroy 方法执行...
Man 类通过@Bean的destroyMethod指定的destroySelf方法执行...

  • 我们看到6-10行,就是Aware接口的调用打印。Aware接口方法会在BeanPostProcessor的前置Before方法调用之前调用,具体可以参照后面的图片日志。

2.1.2 ApplicationContextAware

  • 同上,不再赘述

2.1.3 BeanFactoryAware

  • 同上,不再赘述

2.1.4 EnvironmentAware

  • 同上,不再赘述

2.1.5 ApplicationEventPublisherAware

  • 同上,不再赘述

2.2 BeanPostProcessor系列解析

2.2.1 BeanPostProcessor

  • 方法会在bean的初始化前后被调用

2.2.2 InstantiationAwareBeanPostProcessor

  • 方法会在bean的实例化前后被调用(注意实例化和初始化的区别,先实例化,再初始化)

2.2.3 MergedBeanDefinitionPostProcessor

  • 方法会在bean的实例化之后调用,但是在InstantiationAwareBeanPostProcessor的后置After方法之前调用

2.2.4 BeanFactoryPostProcessor

  • 这个方法放在"其他"里面分析

2.3 初始化和销毁

2.3.1 初始化

  • @PostConstruct:JSR规范注解,JDK的注解,不是Spring的注解,注解的方法会在初始化阶段调用,也在这里一并讲解
  • InitialingBean:Spring提供的扩展接口,会在初始化阶段调用,在属性设置完毕后做一些自定义操作
  • initMethod:@Bean注解的属性方法,会在初始化阶段调用,在属性设置完毕后做一些自定义操作

2.3.2 销毁(关闭容器前执行)

  • @PreDestroy:JSR规范注解,JDK的注解,不是Spring的注解,注解的方法会在销毁阶段调用,也在这里一并讲解
  • DisposableBean:Spring提供的扩展接口,会在Bean销毁阶段调用
  • destroyMethod:@Bean注解的属性方法,会在销毁阶段调用

2.3.3 调用时机

  • 上面的6个方法,具体调用时机,参照后面的日志打印,或者时序图,比较清晰。

2.4 其他

2.4.1 FactoryBean

  • FactoryBean在Spring中是非常有用的一个接口,传统的Spring容器加载一个Bean的整个过程,都是由Spring控制的,换句话说,开发者除了设置Bean相关属性之外,没有太多的自主权的。FactoryBean改变了这一点,开发者可以个性化地定制自己想要实例化出来的Bean,就是基于FactoryBean接口实现的。
@Component
public class MyFactoryBean implements FactoryBean<Man> {
    @Override
    public Man getObject() throws Exception {
        if (new Random().nextBoolean()) {
            return new Man("Kobe", 24);
        }
        return new Man("James", 23);
    }

    @Override
    public Class<?> getObjectType() {
        return Man.class;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }
}

public class Man  {

    private String name;
    private int age;
    
    public Man(String name, int age) {
        System.out.println("Man 带参构造方法执行...");
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Man{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    //get and set methods
}

@Configuration
@ComponentScan("com.intellif.ch1")
@PropertySource(value = "classpath:/application.properties")
public class MainConfig1 {

}

public class Test1 {

    @Test
    public void test01() {
        ApplicationContext app = new AnnotationConfigApplicationContext(MainConfig1.class);
        System.out.println(app.getBean("myFactoryBean").toString());
        ((AnnotationConfigApplicationContext) app).close();
    }
}

这里会随机打印:Man{name='James', age=23}或者Man{name='Kobe', age=24},当然这里只是简单演示,我们可以
在构造bean的时候写更多的复杂逻辑来满足我们的需求,比如根据配置生成不同的bean。

2.4.2 BeanFactoryPostProcessor

  • Spring允许在Bean创建之前,读取Bean的元属性,并根据自己的需求对元属性进行改变,比如将Bean的scope从singleton改变为prototype,PropertyPlaceholderConfigurer就是最典型的应用,其作用是替换xml文件中的占位符,替换为properties文件中相应的key对应的value值。
  • 经验证BeanFactoryPostProcessor的postProcessBeanFactory方法执行时机比InstantiationAwareBeanPostProcessor的前置方法还要早,也就是说这个方法的执行时机早于前面的时序图的全部流程,是在最前面的,且只会执行一次,那么它的作用是什么呢,先看下示例代码如下:
@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        System.out.println("MyBeanFactoryPostProcessor的postProcessBeanFactory方法执行");
        BeanDefinition beanDefinition = beanFactory.getBeanDefinition("bear");
        beanDefinition.setLazyInit(true);
    }
}
  • 这里的ConfigurableListableBeanFactory方法参数,这个变量非常丰富,如下如图所示,因为这个阶段很早,我们就可以对bean的定义信息做修改,下面我来修改bean的加载时机(这里面其实可以修改很多东西,这里只做示例),
    修改的代码在上面,我自定义一个Bear类,我在这里讲其设置为懒加载,我们知道在默认情况下,我没有给Bean添加Lazy注解的话是立即加载的,也就是在容器初始化就会创建bean,再看我下面的测试代码:
    image
public class Test1 {

    @Test
    public void test01() {
        ApplicationContext app = new AnnotationConfigApplicationContext(MainConfig1.class);

        //1.打印IOC容器中所有的 实例对象名称
        String[] names = app.getBeanDefinitionNames();
        System.out.println("下面打印IOC容器中已经存在的Bean.. ");
        for (String name : names) {
            System.out.println(name);
        }
        System.out.println("下面打印IOC容器中已经存在的Bean完毕.. ");
        System.out.println(app.getBean("bear").toString());
        ((AnnotationConfigApplicationContext) app).close();
    }
}
  • 按照我们的预期,在postProcessBeanFactory方法没有设置为懒加载之前,Bear的构造方法应该在遍历bean的名字之前执行,日志如下,是符合我们的预期的:
MyBeanFactoryPostProcessor的postProcessBeanFactory方法执行
Bear 带参构造方法执行...
下面打印IOC容器中已经存在的Bean.. 
mainConfig1
myBeanFactoryPostProcessor
bear
下面打印IOC容器中已经存在的Bean完毕.. 
Bear{name='myBear'}
  • 我们设置为懒加载之后,日志如下,我们看到,我们在没有getBean(“bear”)之前,bean的构造方法没有被执行,说明是懒加载的,等遍历bean的name完毕之后执行app.getBean(“bear”)才执行了构造方法,因此我们验证了在BeanFactoryPostProcessor中修改bean的加载方式是有效的,当然这里面还可以修改其他的比如scop作用域等。
MyBeanFactoryPostProcessor的postProcessBeanFactory方法执行,
下面打印IOC容器中已经存在的Bean.. 
mainConfig1
myBeanFactoryPostProcessor
bear
下面打印IOC容器中已经存在的Bean完毕.. 
Bear 带参构造方法执行...
Bear{name='myBear'}

三、图示

  • 下面是上述接口的调用时序
    image

  • 下面是上面代码的日志记录,和上面的时序是一样的
    image

四、总结

  • Bean的生命周期在刚开始看还是比较复杂的,前后接口子接口什么的非常多,我们按照上面的流程图,将Bean的生命周期先大致分为几个阶段,然后关注每个阶段有哪些扩展点。
  • 实例化:InstantiationAwareBeanPostProcessor 在该阶段前后拦截,初始化之后还有
    MergedBeanDefinitionPostProcessor接口,这个接口是实现@Autowire和@Value的关键,可以查看参考文档:04-spring AutowiredAnnotationBeanPostProcessor接口
  • 初始化:BeanPostProcess 在该阶段前后拦截,初始化阶段过程中介绍了3种拦截方法
  • 销毁阶段:主要是容器关闭的时候调用,也介绍了3种拦截方法
  • 能力接口:就是Aware系列接口,会在初始化之前调用,也就是BeanPostProcess的Before方法之前
  • 另外文中提到的@PostConstructor和PreDestroy并不是Spring中的内容,也一并带出了。

五、参考

猜你喜欢

转载自blog.csdn.net/my_momo_csdn/article/details/90986148