简介
控制初始化的方法
我们可以在bean初始化阶段指定方法,主要有以下几种:
- @PostContruct
- 实现InitializingBean接口(afterPropertiesSet方法)
- @Bean(initMethod = "xxx")
- 实现BeanPostProcessor接口(postProcessBeforeInitialization方法)
初始化方法总体流程
AnnotationConfigApplicationContext(创建容器)
->refresh
->finishBeanFactoryInitialization
->preInstantiateSingletons
->getBean
->doGetBean
->createBean
->doCreateBean
-> this.populateBean(beanName, mbd, instanceWrapper);(类属性赋值)
this.initializeBean(beanName, exposedObject, mbd);(BeanPostProcessor postProcessBeforeInstantiation和postProcessAfterInstantiation执行)
示例
package com.example.config; import org.springframework.beans.BeansException; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import javax.annotation.PostConstruct; @Configuration public class TestInitConfig { @Bean(name = "myUser", initMethod = "initUser") Object userBean() { return new User(); } public static class User implements InitializingBean, BeanPostProcessor { static { System.out.println("static"); } User(){ System.out.println("construct"); } public void initUser() { System.out.println("initMethod"); } @Override public void afterPropertiesSet() throws Exception { System.out.println("InitializingBean"); } @PostConstruct public void userPostConstruct() { System.out.println("PostConstruct"); } @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("BeanPostProcessor==> " + "beanName:" + beanName); return bean; } } }
执行结果:
static construct PostConstruct InitializingBean initMethod
@PostContruct
说明
指定调用完构造函数之后执行的方法
原理
InitializingBean接口
说明
指定初始化时调用的方法,可以针对某个具体的bean进行配置。
该方法在Bean的所有属性初始化完成后调用,但是当Spring 配置bean为延迟初始化(懒加载(lazy-init))(比如有@Lazy)时,类对象如果不被使用,则不会实例化该类对象,导致InitializingBean子类的afterPropertiesSet()的方法不能执行。若需要Spring容器启动时就初始化并调用afterPropertiesSet()方法,可在类上通过注解@Lazy(value=false) (org.springframework.context.annotation.Lazy)配置为非延迟加载即可。
原理
AbstractAutowireCapableBeanFactory完成一个Bean初始化方法的调用工作。
AbstractAutowireCapableBeanFactory类中的invokeInitMethods
/** * Initialize the given bean instance, applying factory callbacks * as well as init methods and bean post processors. * <p>Called from {@link #createBean} for traditionally defined beans, * and from {@link #initializeBean} for existing bean instances. * @param beanName the bean name in the factory (for debugging purposes) * @param bean the new bean instance we may need to initialize * @param mbd the bean definition that the bean was created with * (can also be {@code null}, if given an existing bean instance) * @return the initialized bean instance (potentially wrapped) * @see BeanNameAware * @see BeanClassLoaderAware * @see BeanFactoryAware * @see #applyBeanPostProcessorsBeforeInitialization * @see #invokeInitMethods * @see #applyBeanPostProcessorsAfterInitialization */ protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) { if (System.getSecurityManager() != null) { AccessController.doPrivileged(new PrivilegedAction<Object>() { @Override public Object run() { invokeAwareMethods(beanName, bean); return null; } }, getAccessControlContext()); } else { // invokeAwareMethods方法为bean设置bean名称、类加载器、bean工厂 invokeAwareMethods(beanName, bean); } Object wrappedBean = bean; if (mbd == null || !mbd.isSynthetic()) { //通过责任链进行初始化前的处理 wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); } try { //调用初始化方法,afterPropertiesSet和初始化方法就在这个方法里 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()) { //通过责任链进行初始化后的处理 wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); } return wrappedBean; }
追踪invokeInitMethods(其在AbstractAutowireCapableBeanFactory里)
protected void invokeInitMethods(String beanName, final Object bean, RootBeanDefinition mbd) throws Throwable { //判断该bean是否实现了实现了InitializingBean接口,如果实现了InitializingBean接口,则只掉调用bean的afterPropertiesSet方法 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(new PrivilegedExceptionAction<Object>() { public Object run() throws Exception { //直接调用afterPropertiesSet ((InitializingBean) bean).afterPropertiesSet(); return null; } },getAccessControlContext()); } catch (PrivilegedActionException pae) { throw pae.getException(); } } else { //直接调用afterPropertiesSet ((InitializingBean) bean).afterPropertiesSet(); } } if (mbd != null) { String initMethodName = mbd.getInitMethodName(); //判断是否指定了init-method方法,如果指定了init-method方法,则再调用制定的init-method if (initMethodName != null && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) && !mbd.isExternallyManagedInitMethod(initMethodName)) { //进一步查看该方法的源码,可以发现init-method方法中指定的方法是通过反射实现 invokeCustomInitMethod(beanName, bean, mbd); } } }
通过分析上面的源代码我们可以看到,init-method是通过反射执行的,而afterPropertiesSet是直接执行的。所以 afterPropertiesSet的执行效率比init-method要高。
如果一个bean被定义为非单例的,那么afterPropertiesSet和init-method在bean的每一个实例被创建时都会执行。单例 bean的afterPropertiesSet和init-method只在bean第一次被实例时调用一次。大多数情况下 afterPropertiesSet和init-method都应用在单例的bean上。
initMethod
说明
指定初始化时调用的方法,可以针对某个具体的bean进行配置。
Spring要求init-method是一个无参数的方法,如果init-method指定的方法中有参数,那么Spring将会抛出java.lang.NoSuchMethodException。init-method指定的方法可以是public、protected以及private的,并且方法也可以是final的。
原理
在上边“InitializingBean接口”部分已经说过。
BeanPostProcessor接口
其他网址