PostConstruct注解原理说明

首先我们知道这个注解的作用是:在类被初始化的时候 会执行被@PostConstruct注解的方法。
那它到底是怎么工作的,什么时候被执行,都有什么限制规则,会引起什么问题呢 ?
本着一切都要验证的原则,开始解剖,但是从哪里开始下刀呢?既然是类的初始化,自然是想到了BeanFactory这个庞大的工厂,那我们就从spring初始化bean的入口开刀吧。

bean被初始化的过程了解过spring运行原理的应该都知道了,这里简单说明一下:

扫描包找到包含注解的bean,放入BeanFactory,等待被初始化,执行初始化生成bean实例放入BeanFactory。

首先我们看BeanFactory,随便找一个实现类AbstractAutowireCapableBeanFactory进去


image.png

我们知道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> T doGetBean(String name, Class<T> requiredType, final Object[] args, boolean typeCheckOnly) throws BeansException {
...
if (mbd.isSingleton()) {
                    sharedInstance = this.getSingleton(beanName, new ObjectFactory<Object>() {
                        public Object getObject() throws BeansException {
                            try {
                                >>>return AbstractBeanFactory.this.createBean(beanName, mbd, args);
                            } catch (BeansException var2) {
                                AbstractBeanFactory.this.destroySingleton(beanName);
                                throw var2;
                            }
                        }
                    });
                    bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
                }
...
}

protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException {
...
  >>>beanInstance = this.doCreateBean(beanName, mbdToUse, args);
...
}

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, Object[] args) throws BeanCreationException {
...
  this.populateBean(beanName, mbd, instanceWrapper);//添加依赖bean(如autowired相关实例)
  if (exposedObject != null) {
    >>>exposedObject = this.initializeBean(beanName, exposedObject, mbd);//初始化当前bean
  }
...
}

protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
    // ...

    Object wrappedBean = bean;
    if (mbd == null || !mbd.isSynthetic()) {
        // 初始化前置处理
        >>>wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
    }

    try {
        // 调用初始化方法
        invokeInitMethods(beanName, wrappedBean, mbd);
    } catch (Throwable ex) {
        // ...
    }
    if (mbd == null || !mbd.isSynthetic()) {
        // 初始化后置处理
        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    }

    return wrappedBean;
}

public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeansException {
        Object result = existingBean;
        Iterator var4 = this.getBeanPostProcessors().iterator();

        do {
            if (!var4.hasNext()) {
                return result;
            }

            BeanPostProcessor beanProcessor = (BeanPostProcessor)var4.next();
            >>>result = beanProcessor.postProcessBeforeInitialization(result, beanName);
        } while(result != null);

        return result;
    }


到这里可以看到,在初始化bean的时候,先循环所有的BeanPostProcessor执行postProcessBeforeInitialization 方法;再调用 bean的初始化;最后执行applyBeanPostProcessorsAfterInitialization方法。而我们的@PostConstruct就是在第一步中的一个 BeanPostProcessor 里处理的。

我们进到BeanPostProcessor里面可以看到它有很多实现。


image.png

而位于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<InitDestroyAnnotationBeanPostProcessor.LifecycleElement> initMethods = new LinkedList();
        LinkedList<InitDestroyAnnotationBeanPostProcessor.LifecycleElement> destroyMethods = new LinkedList();
        Class targetClass = clazz;

        do {
            final LinkedList<InitDestroyAnnotationBeanPostProcessor.LifecycleElement> currInitMethods = new LinkedList();
            final LinkedList<InitDestroyAnnotationBeanPostProcessor.LifecycleElement> 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:
 * <p>
 * <ul>
 * <li>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.</li>
 * <li>The method defined on an interceptor class MUST HAVE one of the
 * following signatures:
 * <p>
 * void &#060;METHOD&#062;(InvocationContext)
 * <p>
 * Object &#060;METHOD&#062;(InvocationContext) throws Exception
 * <p>
 * <i>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.</i>
 * </li>
 * <li>The method defined on a non-interceptor class MUST HAVE the
 * following signature:
 * <p>
 * void &#060;METHOD&#062;()
 * </li>
 * <li>The method on which PostConstruct is applied MAY be public, protected,
 * package private or private.</li>
 * <li>The method MUST NOT be static except for the application client.</li>
 * <li>The method MAY be final.</li>
 * <li>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.</li></ul>
 * @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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343