对于Spring bean的初始化可以归纳为三种方式:
- 被
PostConstruct注解标注的方法 - 实现
InitializingBean接口的afterPropertiesSet()方法 - 自定义初始化方法
- XML配置:
<bean init-method = "init" /> - Java注解:
@Bean(initMethod = "init") - Java API:
AbstractBeanDefinition#setInitMethodName(String)
- XML配置:
PostConstruct
PostConstruct是jsr规范中的一员,而不是spring框架的概念
虽然在javax中强调一个类中被PostConstruct注解标注的方法只能有一个,但在spring中可以写多个,不过为了移植性,推荐还是只写一个这样的方法。
- 示例代码:
@Component
public class BeanInitTestService {
@PostConstruct
public void init() {
System.out.println("PostConstruct注解的bean初始化");
}
}
- 源码浅析
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());
try {
metadata.invokeInitMethods(bean, beanName);
}
catch (InvocationTargetException ex) {
throw new BeanCreationException(beanName, "Invocation of init method failed", ex.getTargetException());
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Failed to invoke init method", ex);
}
return bean;
}
public void invokeInitMethods(Object target, String beanName) throws Throwable {
Collection<LifecycleElement> checkedInitMethods = this.checkedInitMethods;
Collection<LifecycleElement> initMethodsToIterate =
(checkedInitMethods != null ? checkedInitMethods : this.initMethods);
if (!initMethodsToIterate.isEmpty()) {
for (LifecycleElement element : initMethodsToIterate) {
if (logger.isTraceEnabled()) {
logger.trace("Invoking init method on bean '" + beanName + "': " + element.getMethod());
}
element.invoke(target);
}
}
}
PostConstruct注解标注的方法被调用的入口, 在类InitDestroyAnnotationBeanPostProcessor的postProcessBeforeInitialization()方法 中,postProcessBeforeInitialization() 内部调用 invokeInitMethods() 方法。
tips:PostConstruct是通过反射调用的
InitializingBean
Spring提供的一个接口,凡是实现InitializingBean接口的bean,在初始化的时候会执行afterPropertiesSet方法
示例代码:
@Component
public class BeanInitTestService implements InitializingBean {
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("bean初始化");
}
}
源码分析:
afterPropertiesSet方法的调用入口是负责加载 spring bean 的AbstractAutowireCapableBeanFactory,源码如下:
protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
throws Throwable {
boolean isInitializingBean = (bean instanceof InitializingBean);
if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
if (logger.isDebugEnabled()) {
logger.debug("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
}
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();
}
}
InitializingBean接口是直接调用afterPropertiesSet方法,而不是通过反射调用的
自定义初始化方法
- xml配置
暂时不贴示例代码了
bean注解
@Configuration
class TestConfig() {
@Bean(initMethod = "initMethod")
public Phone initPhone() {
return new Phone();
}
}
class Phone {
public void initMethod() {
System.out.println("initMethod....");
}
}
AbstractBeanDefinition#setInitMethodName(String)
@Component
public class CustomBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
beanFactory.getBeanDefinition("phone").setInitMethodName("initMethod");
}
}
@Component
public class Phone {
public void initMethod() {
System.out.println("initMethod....");
}
}
执行顺序
- spring bean的初始化执行顺序:
构造方法>@Autowired>PostConstruct>InitializingBean>initMethod InitializingBean的afterPropertiesSet()通过接口实现方式调用(效率上高一点),@PostConstruct和initMethod都是通过反射机制调用- 在初始化的过程中,执行完一个Bean的构造方法后会执行该Bean的@PostConstruct方法(如果有),然后再执行下一个Bean的构造方法和@PostConstruct。
- 如果@PostConstruct方法内的逻辑处理时间较长,就会增加SpringBoot应用初始化Bean的时间,进而增加应用启动的时间