首先我们知道这个注解的作用是:在类被初始化的时候 会执行被@PostConstruct注解的方法。
那它到底是怎么工作的,什么时候被执行,都有什么限制规则,会引起什么问题呢 ?
本着一切都要验证的原则,开始解剖,但是从哪里开始下刀呢?既然是类的初始化,自然是想到了BeanFactory这个庞大的工厂,那我们就从spring初始化bean的入口开刀吧。
bean被初始化的过程了解过spring运行原理的应该都知道了,这里简单说明一下:
扫描包找到包含注解的bean,放入BeanFactory,等待被初始化,执行初始化生成bean实例放入BeanFactory。
首先我们看BeanFactory,随便找一个实现类AbstractAutowireCapableBeanFactory进去
我们知道bean被实例化有两种方式,一种是注入的时候就实例化,一种是第一次用的时候才实例化(懒加载)。先看autowireBean方法
public void autowireBean(Object existingBean) {
RootBeanDefinition bd = new RootBeanDefinition(ClassUtils.getUserClass(existingBean));
bd.setScope("prototype");
bd.allowCaching = ClassUtils.isCacheSafe(bd.getBeanClass(), this.getBeanClassLoader());
BeanWrapper bw = new BeanWrapperImpl(existingBean);
this.initBeanWrapper(bw);
>>>this.populateBean(bd.getBeanClass().getName(), bd, bw);>>>
}
protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) {
...
if (mbd.getResolvedAutowireMode() == 1 || mbd.getResolvedAutowireMode() == 2) {
MutablePropertyValues newPvs = new MutablePropertyValues((PropertyValues)pvs);
if (mbd.getResolvedAutowireMode() == 1) {
>>>this.autowireByName(beanName, mbd, bw, newPvs);
}
if (mbd.getResolvedAutowireMode() == 2) {
>>>this.autowireByType(beanName, mbd, bw, newPvs);
}
pvs = newPvs;
}
...
}
protected void autowireByName(String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {
...
if (this.containsBean(propertyName)) {
>>>Object bean = this.getBean(propertyName);
pvs.add(propertyName, bean);
this.registerDependentBean(propertyName, beanName);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Added autowiring by name from bean name '" + beanName + "' via property '" + propertyName + "' to bean named '" + propertyName + "'");
}
}
...
}
public Object getBean(String name) throws BeansException {
>>>return this.doGetBean(name, (Class)null, (Object[])null, false);
}
protected T doGetBean(String name, Class requiredType, final Object[] args, boolean typeCheckOnly) throws BeansException {
...
if (mbd.isSingleton()) {
sharedInstance = this.getSingleton(beanName, new ObjectFactory
到这里可以看到,在初始化bean的时候,先循环所有的BeanPostProcessor执行postProcessBeforeInitialization 方法;再调用 bean的初始化;最后执行applyBeanPostProcessorsAfterInitialization方法。而我们的@PostConstruct就是在第一步中的一个 BeanPostProcessor 里处理的。
我们进到BeanPostProcessor里面可以看到它有很多实现。
而位于spring-beans包下的org.springframework.beans.factory.annotaion.InitDestroyAnnotationBeanPostProcessor成功的引起了我们的怀疑,那就先进去看看吧。
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
//先找到所有待执行方法
>>>>InitDestroyAnnotationBeanPostProcessor.LifecycleMetadata metadata = this.findLifecycleMetadata(bean.getClass());
try {
//通过反射调用相关方法执行
metadata.invokeInitMethods(bean, beanName);
return bean;
} catch (InvocationTargetException var5) {
throw new BeanCreationException(beanName, "Invocation of init method failed", var5.getTargetException());
} catch (Throwable var6) {
throw new BeanCreationException(beanName, "Failed to invoke init method", var6);
}
}
private InitDestroyAnnotationBeanPostProcessor.LifecycleMetadata findLifecycleMetadata(Class> clazz) {
if (this.lifecycleMetadataCache == null) {
>>>> return this.buildLifecycleMetadata(clazz);
} else {
InitDestroyAnnotationBeanPostProcessor.LifecycleMetadata metadata = (InitDestroyAnnotationBeanPostProcessor.LifecycleMetadata)this.lifecycleMetadataCache.get(clazz);
if (metadata == null) {
synchronized(this.lifecycleMetadataCache) {
metadata = (InitDestroyAnnotationBeanPostProcessor.LifecycleMetadata)this.lifecycleMetadataCache.get(clazz);
if (metadata == null) {
metadata = this.buildLifecycleMetadata(clazz);
this.lifecycleMetadataCache.put(clazz, metadata);
}
return metadata;
}
} else {
return metadata;
}
}
}
private InitDestroyAnnotationBeanPostProcessor.LifecycleMetadata buildLifecycleMetadata(final Class> clazz) {
final boolean debug = this.logger.isDebugEnabled();
LinkedList initMethods = new LinkedList();
LinkedList destroyMethods = new LinkedList();
Class targetClass = clazz;
do {
final LinkedList currInitMethods = new LinkedList();
final LinkedList currDestroyMethods = new LinkedList();
//反射获取所有方法,基于initAnnotationType和 destroyAnnotationType 分别处理。
ReflectionUtils.doWithLocalMethods(targetClass, new MethodCallback() {
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
if (InitDestroyAnnotationBeanPostProcessor.this.initAnnotationType != null && method.getAnnotation(InitDestroyAnnotationBeanPostProcessor.this.initAnnotationType) != null) {
InitDestroyAnnotationBeanPostProcessor.LifecycleElement element = new InitDestroyAnnotationBeanPostProcessor.LifecycleElement(method);
currInitMethods.add(element);
if (debug) {
InitDestroyAnnotationBeanPostProcessor.this.logger.debug("Found init method on class [" + clazz.getName() + "]: " + method);
}
}
if (InitDestroyAnnotationBeanPostProcessor.this.destroyAnnotationType != null && method.getAnnotation(InitDestroyAnnotationBeanPostProcessor.this.destroyAnnotationType) != null) {
currDestroyMethods.add(new InitDestroyAnnotationBeanPostProcessor.LifecycleElement(method));
if (debug) {
InitDestroyAnnotationBeanPostProcessor.this.logger.debug("Found destroy method on class [" + clazz.getName() + "]: " + method);
}
}
}
});
//找到所有的初始化方法 放在initMethods中
>>>>initMethods.addAll(0, currInitMethods);
destroyMethods.addAll(currDestroyMethods);
targetClass = targetClass.getSuperclass();
} while(targetClass != null && targetClass != Object.class);
return new InitDestroyAnnotationBeanPostProcessor.LifecycleMetadata(clazz, initMethods, destroyMethods);
}
那么 initAnnotationType 都有哪些呢
public CommonAnnotationBeanPostProcessor() {
setOrder(Ordered.LOWEST_PRECEDENCE - 3);
setInitAnnotationType(PostConstruct.class);
setDestroyAnnotationType(PreDestroy.class);
ignoreResourceType("javax.xml.ws.WebServiceContext");
}
CommonAnnotationBeanPostProcessor 是 继承自InitDestroyAnnotationBeanPostProcessor的,到这里,我们可以知道。跟我们前面的猜测一样,解析过程是通过反射来获取@PostConstruct注解的方法,并放到initMethods集合里面去。然后再通过反射调用这个集合里面的所有方法,完成初始化行为的。
最后再通过PostConstruct注解类源码看看它的使用注意事项:
package javax.annotation;
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.*;
/**
* The PostConstruct annotation is used on a method that needs to be executed
* after dependency injection is done to perform any initialization. This
* method MUST be invoked before the class is put into service. This
* annotation MUST be supported on all classes that support dependency
* injection. The method annotated with PostConstruct MUST be invoked even
* if the class does not request any resources to be injected. Only one
* method can be annotated with this annotation. The method on which the
* PostConstruct annotation is applied MUST fulfill all of the following
* criteria:
*
*
* - The method MUST NOT have any parameters except in the case of
* interceptors in which case it takes an InvocationContext object as
* defined by the Interceptors specification.
* - The method defined on an interceptor class MUST HAVE one of the
* following signatures:
*
* void <METHOD>(InvocationContext)
*
* Object <METHOD>(InvocationContext) throws Exception
*
* Note: A PostConstruct interceptor method must not throw application
* exceptions, but it may be declared to throw checked exceptions including
* the java.lang.Exception if the same interceptor method interposes on
* business or timeout methods in addition to lifecycle events. If a
* PostConstruct interceptor method returns a value, it is ignored by
* the container.
*
* - The method defined on a non-interceptor class MUST HAVE the
* following signature:
*
* void <METHOD>()
*
* - The method on which PostConstruct is applied MAY be public, protected,
* package private or private.
* - The method MUST NOT be static except for the application client.
* - The method MAY be final.
* - If the method throws an unchecked exception the class MUST NOT be put into
* service except in the case of EJBs where the EJB can handle exceptions and
* even recover from them.
* @since Common Annotations 1.0
* @see javax.annotation.PreDestroy
* @see javax.annotation.Resource
*/
@Documented
@Retention (RUNTIME)
@Target(METHOD)
public @interface PostConstruct {
}
至此,针对@PostConstruct总结如下:
1.作用在类的方法上,在依赖注入完成之后初始化方法,这个方法是在类被放入服务之前被调用,调用顺序是 构造函数->依赖注入->PostConstruct
2.所有支持依赖注入的类都要支持这个方法。
3.只有一个方法可以加这个注解,虽然PostConstruct官方的类上有这个描述,但真实运行效果上看的话,可以给多个方法加,而且都可以被执行。
4.被PostContruct修饰的方法要满足以下几点:
-方法不能有任何参数,除非是 拦截器中的 InvocationContext对象。
-方法不能是静态的,除非是启动类。
-方法是final的, 不能被重写。
-如果方法对应的类是容器中的类,不要抛出UncheckedException