Spring--初始化bean:InitializingBean、init-method

1、Spring为bean提供了两种初始化bean的方式,实现InitializingBean接口或者通过在XML配置文件中添加init-method的方式,这两种方式可以同时使用。

2、实现InitializingBean接口是直接调用afterPropertiesSet方法,比通过反射调用init-method指定的方法效率要高一点,但是init-method方式消除了对spring的依赖。

3、如果调用afterPropertiesSet方法时出错,则不调用init-method指定的方法。

InitializingBean

  InitializingBean是Spring提供的拓展性接口,InitializingBean接口为bean提供了属性初始化后的处理方法,它只有一个afterPropertiesSet方法,凡是继承该接口的类,在bean的属性初始化后都会执行该方法。

作用

我们直接通过查看源码注释可知 --  InitializingBean源码注释翻译

示例

自定义MyInitializingBean实现InitializingBean接口

1 @Component
2 public class MyInitializingBean implements InitializingBean {
3 
4     @Override
5     public void afterPropertiesSet() throws Exception {
6         System.out.println("implements InitializingBean......");
7     }
8 
9 }
View Code

运行结果如下:

 这说明在 spring 初始化 bean 的时候,如果 bean 实现了 InitializingBean 接口,会自动调用 afterPropertiesSet 方法。

问题:如果bean实现了InitializingBean接口,并且同时配置了init-method,会怎么样?

配置init-method

我们可以通过XML文件的方式或者更简洁的@Bean注解的方式去配置init-methodf方法。

一. 通过XML文件方式配置init-method方法

项目的resource下新建xml文件

在配置文件中加入 init-method="testInit"。 

通过配置类加载 xml 配置文件

/**
 * 通过配置类加载 xml 配置文件
 * @author JustJavaIt
 * @date 2022/9/10 15:20
 */
@ImportResource(locations = "classpath:bean.xml")
@Configuration
public class WebMvcConfig {
}
View Code

 然后在MyInitializingBean中添加初始化方法testInit()

View Code

注意注释掉@Compoment,否者会报bean已经被定义了

二.  通过@Bean的方式配置init-method方法

 1 @Component
 2 public class MyInitializingBean implements InitializingBean {
 3 
 4     @Override
 5     public void afterPropertiesSet() throws Exception {
 6         System.out.println("implements InitializingBean......");
 7     }
 8 
 9     public void testInit(){
10         System.out.println("init-method......");
11     }
12 
13     /**
14      * 相当于在xml配置文件中加入
15      * <bean id="myInitializingBean" class="类路径" init-method="testInit"></bean>
16      * @return
17      */
18     @Bean(initMethod = "testInit")
19     public MyInitializingBean test() {
20         return new MyInitializingBean();
21     }
22 }
View Code

最终运行结果

   由结果可看出,在 spring 初始化 bean 的时候,如果该 bean 是实现了 InitializingBean 接口,并且同时在配置文件中指定了 init-method,系统则是先调用 afterPropertiesSet 方法,然后再调用 init-method 中指定的方法

 invokeInitMethods源码分析

  通过查看Spring加载bean的源码类AbstractAutowiredCapableBeanFactory可以看出其中的奥妙,AbstractAutowiredCapableBeanFactory类中的invokeInitMethods说的非常清楚,如下:

 1 protected void invokeInitMethods(String beanName, final Object bean, RootBeanDefinition mbd) throws Throwable {
 2     //判断该bean是否实现了实现了InitializingBean接口,如果实现了InitializingBean接口,则只掉调用bean的afterPropertiesSet方法
 3     boolean isInitializingBean = (bean instanceof InitializingBean);
 4     if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
 5         if (logger.isDebugEnabled()) {
 6             logger.debug("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
 7         }
 8          
 9         if (System.getSecurityManager() != null) {
10             try {
11                 AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
12                     public Object run() throws Exception {
13                         //直接调用afterPropertiesSet
14                         ((InitializingBean) bean).afterPropertiesSet();
15                         return null;
16                     }
17                 },getAccessControlContext());
18             } catch (PrivilegedActionException pae) {
19                 throw pae.getException();
20             }
21         }                
22         else {
23             //直接调用afterPropertiesSet
24             ((InitializingBean) bean).afterPropertiesSet();
25         }
26     }
27     if (mbd != null) {
28         String initMethodName = mbd.getInitMethodName();
29         //判断是否指定了init-method方法,如果指定了init-method方法,则再调用制定的init-method
30         if (initMethodName != null && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
31                 !mbd.isExternallyManagedInitMethod(initMethodName)) {
32             //进一步查看该方法的源码,可以发现init-method方法中指定的方法是通过反射实现
33             invokeCustomInitMethod(beanName, bean, mbd);
34         }
35     }
36 }

InitializingBean扩展

 1 /**
 2  * 构造方法、注解postConstruct,实现InitializingBean方法afterPropertiesSet,bean初始化init方法执行顺序。
 3  * @author JustJavaIt
 4  * @date 2022/9/10 15:030
 5  */
 6 @Component
 7 public class MyInitializingBean implements InitializingBean {
 8     public MyInitializingBean() {
 9         System.out.println("我是MyInitializingBean构造方法执行...");
10     }
11 
12     public void testInit(){
13         System.out.println("我是init方法执行...");
14     }
15 
16     @Bean(initMethod = "testInit")
17     public MyInitializingBean test() {
18         return new MyInitializingBean();
19     }
20     
21     @Override
22     public void afterPropertiesSet() throws Exception {
23         System.out.println("我是afterPropertiesSet方法执行...");
24     }
25 
26     @PostConstruct
27     public void postConstruct() {
28         System.out.println("我是postConstruct方法执行...");
29     }
30 
31 }
View Code

 运行结果如下:

 我们可以看出执行顺序优先级:构造方法 > postConstruct >afterPropertiesSet > init方法

 

posted @ 2022-09-10 16:16  JustJavaIt  阅读(2552)  评论(0编辑  收藏  举报