java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > InitializingBean接口

Spring中的InitializingBean接口源码解析

作者:securitit

这篇文章主要介绍了Spring中的InitializingBean接口源码解析,InitializingBean接口为Bean初始化提供了一种方式,实现InitializingBean接口的Bean,在BeanFactory设置其所有属性后会调用其afterPropertiesSet()方法,需要的朋友可以参考下

简介

InitializingBean

InitializingBean接口为Bean初始化提供了一种方式。

实现InitializingBean接口的Bean,在BeanFactory设置其所有属性后会调用其afterPropertiesSet()方法。可以在afterPropertiesSet()方法中执行自定义初始化、属性检查或强制校验等,若不满足要求可以抛出异常以中断Spring的加载流程。

InitializingBean应用时有几点需要注意:

① Bean必须实现InitializingBean接口。

② Bean的afterPropertiesSet不能使用@PostConstruct注释。

init-method

init-method定义初始化方法为Bean初始化提供了另一种方式。

Bean声明时配置init-method属性,用于指定初始化方法。与InitializingBean方式类似,在BeanFactory设置其所有属性后会调用其init-method指定的方法。可以在init-method方法中执行自定义初始化、属性检查或强制校验等,若不满足要求可以抛出异常以中断Spring的加载流程。

init-method应用时有几个限制需要注意:

① init-method指定属性不能为空。

② Bean不可以实现InitializingBean接口或Bean的init-method方法名不可以为afterPropertiesSet。

③ Bean的init-method方法不能使用@PostConstruct注释。

演示示例

InitializingBean和init-method可以作用于同一个Bean,但是需要满足上面所罗列的注意事项,下面来使用一个简单示例看一下。

1) 建Bean,实现InitializingBean接口,并额外填加一个方法用于init-method配置。

package com.arhorchin.securitit.initbean;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;

/**
 * @author Securitit.
 * @note Bean初始化测试.
 */
public class InitTestBean implements InitializingBean {

    /**
     * logger.
     */
    private Logger logger = LoggerFactory.getLogger(InitTestBean.class);
    
    @Override
    public void afterPropertiesSet() throws Exception {
        logger.info("调用InitializingBean的afterPropertiesSet方法.");
    }
    
    public void initMethod() throws Exception {
        logger.info("调用init-method的initMethod方法.");
    }

}

2) 在Spring的配置文件中增加Bean声明,并指定init-method属性。

<bean class="com.arhorchin.securitit.initbean.InitTestBean" init-method="initMethod"></bean>

3) 运行程序查看效果,可以看到如下的输出。

2020-12-09 10:58:29 INFO [c.a.s.i.InitTestBean] 调用InitializingBean的afterPropertiesSet方法.
2020-12-09 10:58:29 INFO [c.a.s.i.InitTestBean] 调用init-method的initMethod方法.

从结果可以看到,InitializingBean的afterPropertiesSet先于Bean的init-method指定的方法调用。

源码解析

InitializingBean和init-method源码基本集中在org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory类中。

1)initializeBean(...)方法

AbstractAutowireCapableBeanFactory的initializeBean(...)方法

initializeBean(...)方法中针对Bean进行了几个操作:

① 若Bean实现了Aware接口,则触发方法调用。包括:BeanNameAware、BeanClassLoaderAware和BeanFactoryAware。

② 调用注册的BeanPostProcessor的postProcessBeforeInitialization(...)方法。

③ 调用初始化方法,包括InitializingBean的afterPropertiesSet()方法和Bean的init-method指定的方法。

④ 调用注册的BeanPostProcessor的postProcessAfterInitialization(...)方法。

/**
 * 初始化给定的Bean实例,应用工厂回调、init方法和Bean后处理程序.
 * 对于传统定义的Bean,从createBean调用,对于现有的Bean实例从initializeBean调用.
 * @param beanName 工厂中的Bean名称(用于调试).
 * @param bean 需要初始化新的Bean实例.
 * @param mbd 创建Bean时使用的Bean定义(如果给定现有的Bean实例,也可以是null).
 * @return 初始化的Bean实例(可能被包装).
 */
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
    // 若Bean实现了Aware接口,则触发方法调用.
    if (System.getSecurityManager() != null) {
        AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
            invokeAwareMethods(beanName, bean);
            return null;
        }, getAccessControlContext());
    }
    else {
        invokeAwareMethods(beanName, bean);
    }

    Object wrappedBean = bean;
    // 在Bean初始化前处理BeanPostProcessor.
    if (mbd == null || !mbd.isSynthetic()) {
        wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
    }
    // 进行Bean初始化,包括如下两种方式:
    // 1.调用InitializingBean.afterPropertiesSet()方法.
    // 2.调用Bean配置的init-method方法.
    try {
        invokeInitMethods(beanName, wrappedBean, mbd);
    }
    catch (Throwable ex) {
        throw new BeanCreationException(
            (mbd != null ? mbd.getResourceDescription() : null),
            beanName, "Invocation of init method failed", ex);
    }
    // 在Bean初始化后处理BeanPostProcessor.
    if (mbd == null || !mbd.isSynthetic()) {
        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    }

    return wrappedBean;
}

2)实现Aware接口

若Bean实现了Aware接口,则触发方法调用。包括:BeanNameAware、BeanClassLoaderAware和BeanFactoryAware

/**
 * 若Bean实现了Aware接口,则触发方法调用.
 */
private void invokeAwareMethods(final String beanName, final Object bean) {
    if (bean instanceof Aware) {
        // 调用BeanNameAware.setBeanName(...)方法.
        if (bean instanceof BeanNameAware) {
            ((BeanNameAware) bean).setBeanName(beanName);
        }
        // 调用BeanClassLoaderAware.setBeanClassLoader(...)方法.
        if (bean instanceof BeanClassLoaderAware) {
            ClassLoader bcl = getBeanClassLoader();
            if (bcl != null) {
                ((BeanClassLoaderAware) bean).setBeanClassLoader(bcl);
            }
        }
        // 调用BeanFactoryAware.setBeanFactory(...).
        if (bean instanceof BeanFactoryAware) {
            ((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
        }
    }
}

3)InitializingBean的afterPropertiesSet方法调用

invokeInitMethods主要用于InitializingBean的afterPropertiesSet方法调用,从方法源码中也可以看到,是先调用InitializingBean的afterPropertiesSet方法,然后再调用Bean的init-method指定的方法,查看代码的注释,可以看到相关的内容,不做过多解析。

/**
 * 所有属性设置完成后可选的Bean初始化.
 * Bean实现了InitializingBean接口或定义了init-method方法,则会进行回调处理.
 * @param beanName 工厂中的Bean名称(用于调试).
 * @param bean 需要初始化新的Bean实例.
 * @param mbd 创建Bean时使用的合并Bean定义(如果给定现有的Bean实例,也可以是null).
 */
protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
    throws Throwable {
    // Bean是否实现了InitializingBean接口.
    boolean isInitializingBean = (bean instanceof InitializingBean);
    // 调用InitializingBean.afterPropertiesSet()方法.需要满足条件:
    // 1.Bean实现了InitializingBean接口.
    // 2.Bean为空或afterPropertiesSet方法未被@PostConstruct注释.
    if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
        if (logger.isDebugEnabled()) {
            logger.debug("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
        }
        // 调用InitializingBean.afterPropertiesSet()方法.
        if (System.getSecurityManager() != null) {
            try {
                AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
                    ((InitializingBean) bean).afterPropertiesSet();
                    return null;
                }, getAccessControlContext());
            }
            catch (PrivilegedActionException pae) {
                throw pae.getException();
            }
        }
        else {
            ((InitializingBean) bean).afterPropertiesSet();
        }
    }
    // 调用配置init-method方法处理.
    if (mbd != null && bean.getClass() != NullBean.class) {
        String initMethodName = mbd.getInitMethodName();
        // 1.init-method配置不能为空.
        // 2.Bean不能实现InitializingBean或init-method方法不是afterPropertiesSet.
        // 3.init-method方法未被@PostConstruct注释.
        if (StringUtils.hasLength(initMethodName) &&
            !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
            !mbd.isExternallyManagedInitMethod(initMethodName)) {
            invokeCustomInitMethod(beanName, bean, mbd);
        }
    }
}

4)invokeCustomInitMethod

invokeCustomInitMethod主要用于调用init-method指定的方法,调用方式仅是通过反射来调用,查看代码的注释,可以看到相关的内容,不做过多解析。

/**
 * 在给定的Bean上调用指定的自定义init方法.由invokeInitMethods调用.
 * 可以在子类中重写,以便使用参数自定义解析init方法.
 */
protected void invokeCustomInitMethod(String beanName, final Object bean, RootBeanDefinition mbd)
    throws Throwable {
    // 取得Method实例.
    String initMethodName = mbd.getInitMethodName();
    Assert.state(initMethodName != null, "No init method set");
    final Method initMethod = (mbd.isNonPublicAccessAllowed() ?
                               BeanUtils.findMethod(bean.getClass(), initMethodName) :
                               ClassUtils.getMethodIfAvailable(bean.getClass(), initMethodName));
    if (initMethod == null) {
        if (mbd.isEnforceInitMethod()) {
            throw new BeanDefinitionValidationException("Couldn't find an init method named '" +
                                                        initMethodName + "' on bean with name '" + beanName + "'");
        }
        else {
            if (logger.isDebugEnabled()) {
                logger.debug("No default init method named '" + initMethodName +
                             "' found on bean with name '" + beanName + "'");
            }
            // Ignore non-existent default lifecycle methods.
            return;
        }
    }
    if (logger.isDebugEnabled()) {
        logger.debug("Invoking init method  '" + initMethodName + "' on bean with name '" + beanName + "'");
    }
    // 调用Bean的init-method配置的方法.
    if (System.getSecurityManager() != null) {
        AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
            ReflectionUtils.makeAccessible(initMethod);
            return null;
        });
        try {
            AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () ->
                                          initMethod.invoke(bean), getAccessControlContext());
        }
        catch (PrivilegedActionException pae) {
            InvocationTargetException ex = (InvocationTargetException) pae.getException();
            throw ex.getTargetException();
        }
    }
    else {
        try {
            ReflectionUtils.makeAccessible(initMethod);
            initMethod.invoke(bean);
        }
        catch (InvocationTargetException ex) {
            throw ex.getTargetException();
        }
    }
}

总结

InitializingBean是一个很神奇的接口,Spring框架中对InitializingBean的应用很是频繁,init-method同样如此,一定要了解两者之间的调用顺序,才能在更细粒度控制Bean的初始化过程。

源码解析基于spring-framework-5.0.5.RELEASE版本源码。

若文中存在错误和不足,欢迎指正!

到此这篇关于Spring中的InitializingBean接口源码解析的文章就介绍到这了,更多相关InitializingBean接口 内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文