敞开成长之旅!这是我参与「日新计划 12 月更文挑战」的第4天
Spring Bean生命周期,从入门到进阶:
【Spring Bean生命周期】小白也能看懂的入门篇
【Spring Bean生命周期】高手如何解读源码(一) – 资源的扫描和注册
【Spring Bean生命周期】高手如何解读源码(二) – Bean的生命周期

  Spring之所以具备如此强壮的生态,扩展能力强是很重要的一方面,这得益于它本身供给了十分多的扩展点。本节咱们针对Spring生命周期中涉及到的扩展点来看一看,究竟它们是何方神圣?

1 扩展点流程

  Spring生命周期的主要扩展点如图:

【Spring Bean生命周期】聊透扩展点的流程及应用

2 扩展点解析及运用场景

ApplicationContextInitializer接口

  用于在履行refresh()之前初始化ConfigurationApplicationContext的回调接口。
一般用于需求对运用程序上下文进行编程初始化的web运用程序中。例如,注册特点源或依据上下文环境激活装备文件。 比方:Spring boot就写了几个ApplicationContextInitializer的完结类,常见作用:

  • ConfigurationWarningsApplicationContextInitializer:关于一般装备错误在日志中作出正告
  • ContextIdApplicationContextInitializer:设置ApplicationContext#getId()所获取的ID值,默许取spring.application.name特点值,没有装备时默许为 application
  • DelegatingApplicationContextInitializer:运用环境特点context.initializer.classes指定的初始化器(initializers)进行初始化工作,假如没有指定则什么都不做
    ……
    完结方式如下
public class CustomApplicationContextInitializer implements ApplicationContextInitializer {
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        // 打印当时办法类名和办法名,调试用
        CountUtil.println(getClass(),Thread.currentThread().getStackTrace()[2].getMethodName());
    }
}

在装备文件中设置:

context.initializer.classes=com.zyq.demo.expand.CustomApplicationContextInitializer

BeanDefinitionRegistryPostProcessor

  这个接口在读取项目中的beanDefinition之后履行,供给一个补充的扩展点。运用场景:能够在这儿动态注册自己的beanDefinition,能够加载classpath之外的bean。

  比方咱们在常常运用的mybatis的mapper和咱们的feignClient,往往只需求界说接口而不需求写完结,那么这些完结的生成及注入便是在此处完结的。

@Component
public class CustomBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        // 能够在此处自界说扫描mapper,并生成BeanDefinition信息,经过registry注册到容器中
        RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Test001.class);
        registry.registerBeanDefinition("test001",rootBeanDefinition);
        CountUtil.println(getClass(),Thread.currentThread().getStackTrace()[2].getMethodName());
    }
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        CountUtil.println(getClass(),Thread.currentThread().getStackTrace()[2].getMethodName());
    }
}

BeanFactoryPostProcessor

  这个接口是beanFactory的扩展接口,调用时机在spring在读取beanDefinition信息之后,实例化bean之前。在这个时机,用户能够经过完结这个扩展接口来自行处理一些东西,比方修改现已注册的beanDefinition的元信息。依据注释可知,此时只能承上(即修改元信息),坚决不允许启下(即触发实例化)

  实践工作中,自界说BeanFactoryPostProcessor的情况确实少,比方对灵敏信息的解密处理,在数据库的衔接装备中,用户名和暗码都是明文装备的,这就存在走漏风险,还有redis的衔接装备、shiro的加密算法、rabbitmq的衔接装备等等,凡是涉及到灵敏信息的,都需求进行加密处理,信息安全十分重要。

  装备的时分以密文装备,在真实用到之前在spring容器中进行解密,然后用解密后的信息进行真实的操作。

@Component
public class CustomBeanFactoryAware implements BeanFactoryPostProcessor {
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        MutablePropertySources propSources = environment.getPropertySources();
        StreamSupport.stream(propSources.spliterator(), false)
                .filter(ps -> ps instanceof OriginTrackedMapPropertySource)
                .collect(Collectors.toList())
                .forEach(ps -> convertPropertySource((PropertySource<LinkedHashMap>) ps));
        LOGGER.info("灵敏信息解密完结.....");
    }
}

InstantiationAwareBeanPostProcessor

  InstantiationAwareBeanPostProcessor接口承继BeanPostProcessor接口,它内部供给了3个办法,再加上BeanPostProcessor接口内部的2个办法,所以完结这个接口需求完结5个办法。能够称作是Spring生命周期的大宝剑!Spring的核心功能之一AOP便是根据它来完结的。

@Component
public class CustomInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {
    //在实例化之前被调用
    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        if(beanClass == TargetClass.class){
            //运用cglib完结动态代理AOP
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(beanClass);
            enhancer.setCallback(new TargetClassProxy());
            TargetClass proxyClass = (TargetClass)enhancer.create();
            return proxyClass;
        }
        return null;
    }
    }

BeanNameAware

  这个类也是Aware扩展的一种,触发点在bean的初始化之前,也便是postProcessBeforeInitialization之前,这个类的触发点办法只有一个:setBeanName。运用场景:用户能够扩展这个点,在初始化bean之前拿到spring容器中注册的的beanName,来自行修改这个beanName的值。整个约定了在编写服务代码时,就能够知道客户端调用时的bean名称,可在此处打标签,用于一些特殊bean的捕获。

public class CustomBeanNameAware implements BeanNameAware {
    @Override
    public void setBeanName(String name) {
        System.out.println("在这儿修改Bean名称");
    }
}

@PostConstruct

  @PostConstruct这个并不算一个扩展点,其实便是一个标示。从Java EE5规范开始,Servlet中添加的注解,被@PostConstruct修饰的办法会在服务器加载Servlet的时分运行,而且只会被服务器履行一次。其作用是在bean的初始化阶段,假如对一个办法标示了@PostConstruct,会先调用这个办法。这儿重点是要重视下这个标准的触发点,这个触发点是在postProcessBeforeInitialization之后,InitializingBean.afterPropertiesSet之前。

  运用场景:假如想在生成目标时分完结某些初始化操作,而偏偏这些初始化操作又依靠于依靠注入,那么就无法在构造函数中完结。为此,能够运用@PostConstruct注解一个办法来完结初始化,@PostConstruct注解的办法将会在依靠注入完结后被主动调用。

  spring自带的@schedule,没有开关,项目发动总会发动一个线程;做项目的时分就运用Java的timer,这个设置开关即可自在的控制,封闭的时分,不会发动线程;Java的timer也需求找到一个发动类,能够放到main函数里边发动,这样的话,代码的耦合性太高了,而运用PostConstruct是很干净的。
@PostConstruct
    public void init() {
        // 发动守时任务线程
    }

InitializingBean

  InitializingBean的作用是Bean注入到Spring容器且初始化后,履行特定事务化的操作。Spring允许容器中的Bean,在Bean初始化完结后或者Bean毁掉前,履行特定事务化的操作,常用的完结方式有以下三种:

  • 经过完结InitializingBean/DisposableBean接口来处理初始化后/毁掉前的操作;
  • 经过标签的init-method/destroy-method特点处理初始化后/毁掉前的操作;
  • 在指定办法上加上@PostConstruct或@PreDestroy注解来处理初始化后/毁掉前的操作。

在spring初始化bean的时分,假如该bean是完结了InitializingBean接口,而且一起在装备文件中指定了init-method,体系则是先调用afterPropertiesSet办法,然后在调用init-method中指定的办法

public class CustomInitializingBean implements InitializingBean {
    @Override
    public void afterPropertiesSet() throws Exception {
        if (StringUtils.isEmpty(path)) {
            log.error("PLEASE SET THE RULE'S PATH: (spring.drools.path = XXX).");
        }
    }
}

BeanPostProcessor

  在Bean目标在实例化和依靠注入结束后,在显示调用初始化办法的前后添加咱们自己的逻辑。留意是Bean实例化结束后及依靠注入完结后触发的。

public interface BeanPostProcessor {
   /**
    * Apply this {@code BeanPostProcessor} to the given new bean instance <i>before</i> any bean
    * initialization callbacks (like InitializingBean's {@code afterPropertiesSet}
    * or a custom init-method). The bean will already be populated with property values.
    * The returned bean instance may be a wrapper around the original.
    */
   @Nullable
   default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
      return bean;
   }
   /**
    * Apply this {@code BeanPostProcessor} to the given new bean instance <i>after</i> any bean
    * initialization callbacks (like InitializingBean's {@code afterPropertiesSet}
    * or a custom init-method). The bean will already be populated with property values.
    * The returned bean instance may be a wrapper around the original.
    */
   @Nullable
   default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
      return bean;
   }
}

从代码能够看出来,回来的时经过处理后的Bean,这种就很适合代理类的场景,假如需求完结类似AOP的逻辑,能够在此处理。

好了,上面便是涉及到Spring生命周期的一些主要扩展点,主要是协助咱们集合Spring生命周期来理解Spring的整体逻辑。