Springboot扩展点之InitializingBean

Springboot扩展点系列实现方式、工作原理集合:

Springboot扩展点之ApplicationContextInitializer

Springboot扩展点之BeanFactoryPostProcessor

Springboot扩展点之BeanDefinitionRegistryPostProcessor

Springboot扩展点之BeanPostProcessor

Springboot扩展点之InstantiationAwareBeanPostProcessor

Springboot扩展点之SmartInstantiationAwareBeanPostProcessor

Springboot扩展点之ApplicationContextAwareProcessor

Springboot扩展点之@PostConstruct

Springboot扩展点之InitializingBean

Springboot扩展点之SmartInitializingSingleton

Springboot扩展点之CommandLineRunner和ApplicationRunner

Springboot扩展点之FactoryBean

Springboot扩展点之DisposableBean

Springboot扩展点系列之终结篇:Bean的生命周期

前言

InitializingBean这个扩展点,其实在Springboot扩展点之BeanPostProcessor中已经简单有所涉及,而这篇文章的将重点分析其功能特性、实现方式和工作原理。

功能特性

1、Spring中提供了InitializingBean接口,帮助用户实现一些自定义的初始化操作;

在bean实例化、属性注入后的提供了一个扩展方法afterPropertiesSet();

2、其实现方式很简单,需要bean实现InitializingBean接口并且重写afterPropertiesSet(),且bean要注册到Spring容器中,那么bean在实例化、属性注入后,重写的afterPropertiesSet()就会触发执行;

3、与InitializingBean#afterPropertiesSet()类似效果的是init-method,但是需要注意的是InitializingBean#afterPropertiesSet()执行时机要略早于init-method;

4、InitializingBean#afterPropertiesSet()的调用方式是在bean初始化过程中真接调用bean的afterPropertiesSet();

5、bean自定义属性init-method是通过java反射的方式进行调用 ;

6、InitializingBean#afterPropertiesSet()与init-method的执行时机是在BeanPostProcessor#postProcessBeforeInitialization()和BeanPostProcessor#postProcessAfterInitialization()之间;

注:关于BeanPostProcessor接口的相关功能特性、实现方式及工作原理可移步Springboot扩展点之BeanPostProcessor

Springboot扩展点之InitializingBean_第1张图片

实现方式

1、定义Dog类,注入Food类型的属性,定义一个myInitMethod,且实现InitializingBean,用于观察Dog类的实例化时机、属性注入时机、afterPropertiesSet()和init-method定义方法的执行时机

@Slf4j
public class Dog implements InitializingBean {
    private String name = "wang cai";
    private Food food;
    public Dog() {
        log.info("----Dog的无参构造方法被执行");
    }
    @Autowired
    public void setFood(Food food) {
        this.food = food;
        log.info("----dog的food属性被注入");
    }
    @Override
    public void afterPropertiesSet() throws Exception {
        log.info("----com.fanfu.entity.Dog.afterPropertiesSet触发执行");
    }
    public void myInitMethod() {
        log.info("----com.fanfu.entity.Dog.myInitMethod触发执行");
    }
}

2、定义Food类,用于注入到Dog类中;

public class Food {
    private String name = "大骨头";
    public Food() {
        log.info("----Food的无参数构造方法被执行");
    }
}

3、把定义好的Dog、Food类,使用Configuriation配置类的方式注入到Spring容器中,并定义Dog类中的init-method方法为Dog#myInitMethod();

@Configuration
public class SpringConfig {
    @Bean(initMethod = "myInitMethod")
    public Dog dog(){
        Dog dog = new Dog();
        return dog;
    }
    @Bean
    public Food food(){
        Food food = new Food();
        return food;
    }
}

4、定义MyBeanPostProcessor类,并实现BeanPostProcessor接口,重写postProcessBeforeInitialization()和postProcessAfterInitialization(),用于观察InitializingBean#afterPropertiesSet()、init-method、postProcessBeforeInitialization()和postProcessAfterInitialization()的执行时机;

@Component
@Slf4j
public class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (beanName.equals("dog")) {
            log.info("----postProcessBeforeInitialization---" + beanName);
            //如果特定的bean实例化完成后,还未执行InitializingBean.afterPropertiesSet()方法之前,有一些其他操作,可以在这里实现
        }
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (beanName.equals("dog")) {
            log.info("----postProcessAfterInitialization---" + beanName);
            //如果特定的bean实例化完成,InitializingBean.afterPropertiesSet()方法执行后,有一些其他操作,可以在这里实现
        }
        return bean;
    }
}

5、单元测试

  @Test
    public void test4(){
        log.info("----单元测试执行开始");
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("com.fanfu");
        log.info("----单元测试执行完毕");
    }

验证结果:

Springboot扩展点之InitializingBean_第2张图片

工作原理

从实现方式示例的执行结果来看,也验证了其功能特性,那么InitializingBean#afterPropertiesSet方法和init-method是什么时候开始调用?是用什么方式来调用的呢?下面从源码的角度来研究一下:

在Dog类的afterPropertiesSet()方法打上断点,让程序执行到断点处,然后观察其方法执行栈(方法执行栈按顺序来看是从下往上看的哦):

1、AbstractApplicationContext#refresh是Spring容器启动的关键一步,属于最外层的入口了;

2、AbstractApplicationContext#finishBeanFactoryInitialization,是Spring容器启动最后一大步了,前面关于容器必需的组件基本上已经创建好了,这里主要是把需要Spring管理的单例bean实例化注册到Spring容器里;

3、DefaultListableBeanFactory#preInstantiateSingletons,从名字上看更可以看出来是要实例化单例类型的bean了;

4、AbstractBeanFactory#getBean(java.lang.String)-->AbstractBeanFactory#doGetBean

-->DefaultSingletonBeanRegistry#getSingleton(String,ObjectFactory),

要调用getSingleton(String,ObjectFactory)时,这里要注意一下ObjectFactory是一个函数式接口,使用了java8的lambda表达式的写法,在进入到getSingleton()方法内,执行到ObjectFactory#getObject()方法时,才会触发createBean()方法执行。(这一段看不太懂的小伙伴要稍微注意一下,顺便可以学习一下lambda表达式是怎么使用的)

if (mbd.isSingleton()) {
    //java8的lambda表达式
   sharedInstance = getSingleton(beanName, () -> {
      try {
         return createBean(beanName, mbd, args);
      }
      catch (BeansException ex) {
         destroySingleton(beanName);
         throw ex;
      }
   });
   bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
@FunctionalInterface
public interface ObjectFactory {
   T getObject() throws BeansException;
}

5、AbstractAutowireCapableBeanFactory#createBeanString,RootBeanDefinition, Object[])

-->AbstractAutowireCapableBeanFactory#doCreateBean,这时完成了Bean的实例化,属性还未注入;

Springboot扩展点之InitializingBean_第3张图片

6、AbstractAutowireCapableBeanFactory#createBeanString,RootBeanDefinition, Object[])

-->AbstractAutowireCapableBeanFactory#populateBean,完成属性的注入;

Springboot扩展点之InitializingBean_第4张图片

7、顺着AbstractAutowireCapableBeanFactory#populateBean往下执行到initializeBean(),(如果看过之前分享的Springboot扩展点之BeanPostProcessor,那么对initializeBean()就比较熟了),BeanPostProcessor的postProcessBeforeInitialization()和postProcessAfterInitialization()就是在这个方法中执行的;

protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
   if (System.getSecurityManager() != null) {
      AccessController.doPrivileged((PrivilegedAction) () -> {
         invokeAwareMethods(beanName, bean);
         return null;
      }, getAccessControlContext());
   }else {
      invokeAwareMethods(beanName, bean);
   }
   Object wrappedBean = bean;
   if (mbd == null || !mbd.isSynthetic()) {
       //执行BeanPostProcessor接口实现类的postProcessBeforeInitialization方法
      wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
   }
   try {
       //如果bean实现了InitializingBean或者自定义了initMethod,
       //会在这里执行InitializingBean#afterPropertiesSet和initMethod方法
      invokeInitMethods(beanName, wrappedBean, mbd);
   }
   catch (Throwable ex) {
      throw new BeanCreationException(
            (mbd != null ? mbd.getResourceDescription() : null),
            beanName, "Invocation of init method failed", ex);
   }
   if (mbd == null || !mbd.isSynthetic()) {
       //执行BeanPostProcessor接口实现类的postProcessAfterInitialization方法
      wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
   }
   return wrappedBean;
} 
   

8、从最外层以剥洋葱的方式一层一层分析到这里,AbstractAutowireCapableBeanFactory#initializeBean()--->invokeInitMethods()才正式走入高潮部分,InitializingBean#afterPropertiesSet和initMethod方法的执行时机就在这里;

protected void invokeInitMethods(String beanName, Object bean, @Nullable RootBeanDefinition mbd)
      throws Throwable {
   //判断bean是否实现了InitializingBean
   boolean isInitializingBean = (bean instanceof InitializingBean);
   if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
      if (logger.isTraceEnabled()) {
         logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
      }
      if (System.getSecurityManager() != null) {
         try {
            AccessController.doPrivileged((PrivilegedExceptionAction) () -> {
               ((InitializingBean) bean).afterPropertiesSet();
               return null;
            }, getAccessControlContext());
         }
         catch (PrivilegedActionException pae) {
            throw pae.getException();
         }
      }
      else {
          //如果实现了InitializingBean,真接调用afterPropertiesSet(),简单粗暴;
         ((InitializingBean) bean).afterPropertiesSet();
      }
   }

   if (mbd != null && bean.getClass() != NullBean.class) {
       //尝试获取一下bean自定义的init-method方法
      String initMethodName = mbd.getInitMethodName();
      if (StringUtils.hasLength(initMethodName) &&
            !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
            !mbd.isExternallyManagedInitMethod(initMethodName)) {
         //如果自定义了init-method方法,在这里开始执行;
         invokeCustomInitMethod(beanName, bean, mbd);
      }
   }
} 
   

9、进入到invokeCustomInitMethod()内,使用java反射来执行自定义的init-method方法;

Springboot扩展点之InitializingBean_第5张图片

到这里InitializingBean#afterPropertiesSet方法和init-method是什么时候开始调用,是用什么方式来调用的呢,已经分析清楚了,同时还了解到InitializingBean#afterPropertiesSet方法和init-method有类似的效果,执行时机也比较接近,但是是两个完全不同的东西,且InitializingBean#afterPropertiesSet方法的执行时机要稍早于init-method。

应用场景

这个扩展点其实是比较有用的一个扩展点,可以用于修改默认设置的属性、添加补充额外的属性值,或者针对关键属性做校验。而Spring内部也有比较经典的实现,有些经常用到,但是可能没有觉察到,如Spring MVC中的RequestMappingHandlerMapping,完成了URL与controller的映射工作。

Springboot扩展点之InitializingBean_第6张图片
@Override
    @SuppressWarnings("deprecation")
    public void afterPropertiesSet() {
        this.config = new RequestMappingInfo.BuilderConfiguration();
        this.config.setUrlPathHelper(getUrlPathHelper());
        this.config.setPathMatcher(getPathMatcher());
        this.config.setSuffixPatternMatch(useSuffixPatternMatch());
        this.config.setTrailingSlashMatch(useTrailingSlashMatch());
        this.config.setRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch());
        this.config.setContentNegotiationManager(getContentNegotiationManager());
        super.afterPropertiesSet();
    }

Springboot扩展点之ApplicationContextInitializer

Springboot扩展点之BeanDefinitionRegistryPostProcessor

Springboot扩展点之BeanFactoryPostProcessor

Springboot扩展点之BeanPostProcessor

Springboot扩展点之InstantiationAwareBeanPostProcessor

Springboot扩展点之SmartInstantiationAwareBeanPostProcessor

Springboot扩展点之ApplicationContextAwareProcessor

Springboot扩展点之@PostConstruct

Springboot扩展点之InitializingBean

你可能感兴趣的:(#,Springboot原理,Springboot,spring,boot,java,Initializing,init-method,Spring源码分析)