@PostConstruct注解原理解析

@PostConstruct注解使用简介
在了解一个东西的原理之前,我们得初步的懂得如何使用它。所以,本文先从@PostConstruct注解如何简单的使用开始。

简单起见,我们准备一个springboot项目快速启动。项目目录结构如下:

下面我们在cn.lay.postconstruct目录下创建一个类,并添加一个@PostConstruct的方法,如

最后,我们执行PostConstructApplication的main方法,启动项目。在控制台里,我们会看到

到这里,我们可以知道@PostConstruct注解的用途了。当一个class被注解为一个Bean,那么class上被@PostConstruct注解的方法将会在程序启动的时候执行。

知道了如何使用@PostConstruct以后,我们会产生疑问。为什么@PostConstruct注解的方法会在程序启动的时候执行呢?后续的内容将为你解开疑惑。

回顾spring中一个Bean的创建过程
在关注@PostConstruct原理之前,我们不得不先回顾一下spring中一个Bean是如何被创建的,这将有助于我们理清脉络。

配置Bean通常采用xml配置或者@Component、@Service、@Controller等注解配置,这是我们很熟悉的。这意味着Bean的创建过程第一步是配置Bean

配置Bean -->
无论是xml配置,还是注解配置,都会执行解析处理,处理后的结果会变成BeanDefinition这样的对象,存储在Bean容器里面。我们可以把BeanDefinition理解为Bean的元数据。

所以,第二步就是将配置解析成Bean的元数据

配置Bean --> 解析为Bean的元数据 -->
到这里,还只是Bean元数据,并不是我们最熟悉的Bean。所以,第三步就会根据Bean的元数据来创建Bean了。

这里注意了,触发某个Bean的创建,就是从Bean容器中第一次获取Bean的时候,也就是BeanFactory的getBean()方法。而不是解析了Bean元数据后就马上创建为Bean。

配置Bean --> 解析为Bean的元数据 --> 根据Bean的元数据生成Bean
这样,我们就大体明白了一个Bean的创建过程。生成的Bean将会存放在Bean容器当中,或者我们称呼其为Bean工厂。

@PostConstruct原理
前面,我们了解了Bean的创建过程。而@PostConstruct方法将在最后生成Bean的时候被调用。getBean方法是一个Bean生成的入口,为此,我们找到BeanFactory的getBean方法。

BeanFactory是一个抽象接口,它抽象了一个Bean工厂或者Bean容器。BeanDefinition和Bean实例都存放在BeanFactory中。

那么,我们根据继承关系,向下找到AbstractAutowireCapableBeanFactory这个抽象类,该类间接实现了BeanFactory,并跟进其中一个getBean方法

再跟进doGetBean,doGetBean方法很长,我们做一些删减。关注一下核心内容

复制代码
复制代码
protected T doGetBean(
final String name,
@Nullable final Class requiredType,
@Nullable final Object[] args,
boolean typeCheckOnly
) throws BeansException {

final String beanName = transformedBeanName(name);

Object bean;

Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
    // ...
} else {
    try {
        // ...

        // 创建Bean实例
        if (mbd.isSingleton()) {
            sharedInstance = getSingleton(beanName, () -> {
                try {
                    return createBean(beanName, mbd, args);
                } catch (BeansException ex) {
                    // ...
                }
            });
            bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
        } else if (mbd.isPrototype()) {
            // ...
        } else {
            // ...
        }
    } catch (BeansException ex) {
        cleanupAfterBeanCreationFailure(beanName);
        throw ex;
    }
}
// ...

return (T) bean;

}
复制代码
复制代码
这里以创建单例Bean为例,我们注意到createBean方法将会创建一个Bean实例,所以createBean方法包含了创建一个Bean的核心逻辑。

再跟进createBean方法

复制代码
复制代码
protected Object createBean(
String beanName,
RootBeanDefinition mbd,
@Nullable Object[] args)
throws BeanCreationException {

// ...

try {
    Object beanInstance = doCreateBean(beanName, mbdToUse, args);
    // ...
    return beanInstance;
} catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
    // ...
} catch (Throwable ex) {
    // ...
}

}
复制代码
复制代码

createBean委托给了doCreateBean处理

复制代码
复制代码
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {

BeanWrapper instanceWrapper = null;
// ...

// 创建Bean的实例对象
if (instanceWrapper == null) {
    instanceWrapper = createBeanInstance(beanName, mbd, args);
}

// ...

// 初始化一个Bean
Object exposedObject = bean;
try {
    // 处理Bean的注入
    populateBean(beanName, mbd, instanceWrapper);
    // 处理Bean的初始化操作
    exposedObject = initializeBean(beanName, exposedObject, mbd);
} catch (Throwable ex) {
    // ...
}

// ...

return exposedObject;

}
复制代码
复制代码
到这里,我们可以知道。BeanFactory的getBean()方法将会去创建Bean,在doCreateBean方法的创建逻辑中主要包含了三个核心逻辑:

1)创建一个Bean的实例对象,createBeanInstance方法执行

2)处理Bean之间的依赖注入,比如@Autowired注解注入的Bean。所以,populateBean方法将会先去处理注入的Bean,因此对于相互注入的Bean来说不用担心Bean的生成先后顺序问题。

3)Bean实例生成,相互注入以后。还需要对Bean进行一些初始化操作。比如我们@PostConstruct注解注释的方法,将再初始化的时候被解析并调用。当然还有一些Aware接口,@Schedule注解啥的也会做相应的处理。

我们继续跟进初始化过程,进入initializeBean方法

复制代码
复制代码
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;

}
复制代码
复制代码
这里主要包含了初始化的前置、后置处理,以后初始化方法的调用。@PostConstruct注解将在applyBeanPostProcessorsBeforeInitialization这个前置处理

我们跟进applyBeanPostProcessorsBeforeInitialization前置方法

复制代码
复制代码
@Override
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
throws BeansException {

Object result = existingBean;
// 遍历所有的后置处理器
for (BeanPostProcessor processor : getBeanPostProcessors()) {
    // 调用初始化前置方法
    Object current = processor.postProcessBeforeInitialization(result, beanName);
    if (current == null) {
        return result;
    }
    result = current;
}
return result;

}
复制代码
复制代码
我们注意到,这里遍历了在spring启动过程中被注册的BeanPostProcessor接口,并调用其前置方法。

BeanPostProcessor接口被称作Bean的后置处理器接口,也就是说当一个Bean生成以后,会针对生成的Bean做一些处理。比如我们注解了@PostConstruct注解的Bean将会被其中一个BeanPostProcessor处理。或者一些@Schedule之类注解的Bean也会被处理,等一些所谓的后置处理操作。

到这里呢,我们意识到,原来@PostConstruct注解是会被一个专门的BeanPostProcessor接口的具体实现类来处理的。

我们找到该实现类:InitDestroyAnnotationBeanPostProcessor,根据名字我们就大体可以知道这个后置处理器是用于处理Bean的初始化方法注解和Bean的销毁方法注解的。这里,我们只关注@PostConstruct初始化注解相关的

我们跟进InitDestroyAnnotationBeanPostProcessor的postProcessBeanInitialization方法

复制代码
复制代码
@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;
}
复制代码
复制代码
findLifecycleMetadata方法将会解析元数据,所以@PostConstruct注解的初始化方法也会在这里被找到。

invokeInitMethods方法将会触发上一步被找到的方法。

其实,到这里我们大体都可以猜测这两个方法的逻辑了。就是通过反射将Method给找出来,然后再通过反射去调用这些method方法。

跟进findLifecycleMetadata方法看看初始化方法的查找过程吧

复制代码
复制代码
private LifecycleMetadata findLifecycleMetadata(Class<?> clazz) {
// …

LifecycleMetadata metadata = this.lifecycleMetadataCache.get(clazz);
if (metadata == null) {
    synchronized (this.lifecycleMetadataCache) {
        metadata = this.lifecycleMetadataCache.get(clazz);
        if (metadata == null) {
            // 构建元数据
            metadata = buildLifecycleMetadata(clazz);
            this.lifecycleMetadataCache.put(clazz, metadata);
        }
        return metadata;
    }
}
return metadata;

}
复制代码
复制代码
这里做了一个双重校验来控制缓存,我们更关心的是buildLifecycleMetadata这个构建方法

跟进方法,简单起见这里删除了destroy相关的部分

复制代码
复制代码
private LifecycleMetadata buildLifecycleMetadata(final Class<?> clazz) {
List initMethods = new ArrayList<>();
Class<?> targetClass = clazz;

do {
    final List<LifecycleElement> currInitMethods = new ArrayList<>();// 变量类中的方法Method对象
    ReflectionUtils.doWithLocalMethods(targetClass, method -> {
        // 判断是否被@PostConstruct注解注释
        if (this.initAnnotationType != null && method.isAnnotationPresent(this.initAnnotationType)) {
            LifecycleElement element = new LifecycleElement(method);
            currInitMethods.add(element);
        }
        // ...
    });

    // 添加到集合中,后续调用
    initMethods.addAll(0, currInitMethods);
    // ...
    targetClass = targetClass.getSuperclass();
} while (targetClass != null && targetClass != Object.class);

return new LifecycleMetadata(clazz, initMethods, destroyMethods);

}
复制代码
复制代码
可以看到,doWithLocalMethods这个工具方法将会从class中获取方法的反射对象。而后判断该方法是否被被initAnnotationType指定的注释注解。最后,添加到initMethods集合当中供后续反射调用。

这里还向父类进行了递归处理,直到Object类为止。

我们看看initAnnotationType是一个什么注解

复制代码
复制代码
public CommonAnnotationBeanPostProcessor() {
setOrder(Ordered.LOWEST_PRECEDENCE - 3);
setInitAnnotationType(PostConstruct.class);
setDestroyAnnotationType(PreDestroy.class);
ignoreResourceType(“javax.xml.ws.WebServiceContext”);
}
复制代码
复制代码
可以看到,@PostConstruct注解被设置为了initAnnotationType的值。值得注意的是,这是在CommonAnnotationBeanPostProcessor这个后置处理器的构造方法中执行的。

而CommonAnnotationBeanPostProcessor和InitDestroyAnnotationBeanPostProcessor的关系是继承关系,前者继承了后者。我们可以断点看看getBeanPostProcessors方法

到这里,我们可以知道。跟我们前面的猜测一样,解析过程是通过反射来获取@PostConstruct注解的方法,并放到一个List集合里面去。

下面,我们再简单看看这些Method被调用的过程吧。

回到InitDestroyAnnotationBeanPostProcessor的postProcessBeforeInitialization方法

复制代码
复制代码
@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;
}
复制代码
复制代码
这次我们关注invokeInitMethods方法

复制代码
复制代码
public void invokeInitMethods(Object target, String beanName) throws Throwable {
Collection checkedInitMethods = this.checkedInitMethods;
Collection 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);
}
}
}
复制代码
复制代码
跟进invoke,单纯地invoke了method,是我们很熟悉的反射调用

public void invoke(Object target) throws Throwable {
ReflectionUtils.makeAccessible(this.method);
this.method.invoke(target, (Object[]) null);
}

回到顶部
总结
至此,本文就结束了。做一个简单的总结,本文内容包含三块:1)如何使用@PostConstruct;2)Bean创建过程简介;3)@PostConstruct的原理分析。

我们提出了一个问题:为什么@PostConstruct注解的方法会在启动的时候执行呢?

到这里大家应该能够知道答案了,spring的Bean在创建的时候会进行初始化,而初始化过程会解析出@PostConstruct注解的方法,并反射调用该方法。从而,在启动的时候该方法被执行了。

还有一个小点要注意,spring中的Bean默认是不会lazy-init的,所以在启动过程就会调用getBean方法。如果不希望该Bean在启动过程就调用,那么将lazy-init设置为true,它就会在程序第一次使用的时候进行初始化。

把每一件简单的事情做好,就是不简单;把每一件平凡的事情做好,就是不平凡!相信自己,创造奇迹~~
https://bbs.mountblade.com.cn/thread-2065561-1-1.html
https://bbs.mountblade.com.cn/thread-2065562-1-1.html
https://bbs.mountblade.com.cn/thread-2065563-1-1.html
https://bbs.mountblade.com.cn/thread-2065564-1-1.html
https://bbs.mountblade.com.cn/thread-2065565-1-1.html
https://bbs.mountblade.com.cn/thread-2065566-1-1.html
https://bbs.mountblade.com.cn/thread-2065567-1-1.html
https://mvp.leju.com/article/6676019357896235273.html
https://bbs.mountblade.com.cn/thread-2065572-1-1.html
https://bbs.mountblade.com.cn/thread-2065573-1-1.html
https://bbs.mountblade.com.cn/thread-2065574-1-1.html
https://bbs.mountblade.com.cn/thread-2065575-1-1.html
https://bbs.mountblade.com.cn/thread-2065576-1-1.html
https://bbs.mountblade.com.cn/thread-2065577-1-1.html
https://bbs.mountblade.com.cn/thread-2065578-1-1.html
https://bbs.mountblade.com.cn/thread-2065579-1-1.html
https://blog.csdn.net/beifang01/article/details/106648362
https://blog.csdn.net/beifang01/article/details/106648409
https://blog.csdn.net/sinat_36220051/article/details/106648442
https://blog.csdn.net/sinat_36220051/article/details/106648475
https://blog.csdn.net/daiyun132/article/details/106648511
https://blog.csdn.net/daiyun132/article/details/106648546
https://blog.csdn.net/BHdream/article/details/106648630
https://blog.csdn.net/BHdream/article/details/106648646
https://www.wenjuan.com/z/rA7F3qD
https://www.wenjuan.com/d/rA7F3qD
https://www.wenjuan.com/s/rA7F3qD
https://www.wenjuan.com/t/rA7F3qD
https://www.wenjuan.com/z/AN7JNvm
https://www.wenjuan.com/d/AN7JNvm
https://www.wenjuan.com/s/AN7JNvm
https://www.wenjuan.com/t/AN7JNvm
https://www.wenjuan.com/z/ve6FVv8
https://www.wenjuan.com/d/ve6FVv8
https://www.wenjuan.com/s/ve6FVv8
https://www.wenjuan.com/t/ve6FVv8
https://www.wenjuan.com/z/AjEVjud
https://www.wenjuan.com/d/AjEVjud
https://www.wenjuan.com/s/AjEVjud
https://www.wenjuan.com/t/AjEVjud
https://www.wenjuan.com/z/VVziqy2
https://www.wenjuan.com/d/VVziqy2
https://www.wenjuan.com/s/VVziqy2
https://www.wenjuan.com/t/VVziqy2
https://www.wenjuan.com/z/7nUB3i5
https://www.wenjuan.com/d/7nUB3i5
https://www.wenjuan.com/s/7nUB3i5
https://www.wenjuan.com/t/7nUB3i5
https://www.wenjuan.com/z/yYVjqa7
https://www.wenjuan.com/d/yYVjqa7
https://www.wenjuan.com/s/yYVjqa7
https://www.wenjuan.com/t/yYVjqa7
https://www.wenjuan.com/z/ZJNVnaB
https://www.wenjuan.com/d/ZJNVnaB
https://www.wenjuan.com/s/ZJNVnaB
https://www.wenjuan.com/t/ZJNVnaB
https://www.wenjuan.com/z/RvMrea1
https://www.wenjuan.com/d/RvMrea1
https://www.wenjuan.com/s/RvMrea1
https://www.wenjuan.com/t/RvMrea1
https://www.wenjuan.com/z/MfeqEz
https://www.wenjuan.com/d/MfeqEz
https://www.wenjuan.com/s/MfeqEz
https://www.wenjuan.com/t/MfeqEz
https://www.wenjuan.com/z/ramyi2J
https://www.wenjuan.com/d/ramyi2J
https://www.wenjuan.com/s/ramyi2J
https://www.wenjuan.com/t/ramyi2J
https://www.wenjuan.com/z/i6Nzeq
https://www.wenjuan.com/d/i6Nzeq
https://www.wenjuan.com/s/i6Nzeq
https://www.wenjuan.com/t/i6Nzeq
https://www.wenjuan.com/z/MVBzy2q
https://www.wenjuan.com/d/MVBzy2q
https://www.wenjuan.com/s/MVBzy2q
https://www.wenjuan.com/t/MVBzy2q
https://www.wenjuan.com/z/7BJvAzn
https://www.wenjuan.com/d/7BJvAzn
https://www.wenjuan.com/s/7BJvAzn
https://www.wenjuan.com/t/7BJvAzn
https://www.wenjuan.com/z/YZJVJfe
https://www.wenjuan.com/d/YZJVJfe
https://www.wenjuan.com/s/YZJVJfe
https://www.wenjuan.com/t/YZJVJfe
https://www.wenjuan.com/z/byQNVzv
https://www.wenjuan.com/d/byQNVzv
https://www.wenjuan.com/s/byQNVzv
https://www.wenjuan.com/t/byQNVzv
https://www.wenjuan.com/z/uEJ3Yb
https://www.wenjuan.com/d/uEJ3Yb
https://www.wenjuan.com/s/uEJ3Yb
https://www.wenjuan.com/t/uEJ3Yb
https://www.wenjuan.com/z/n2Inqye
https://www.wenjuan.com/d/n2Inqye
https://www.wenjuan.com/s/n2Inqye
https://www.wenjuan.com/t/n2Inqye
https://www.wenjuan.com/z/FFR36fw
https://www.wenjuan.com/d/FFR36fw
https://www.wenjuan.com/s/FFR36fw
https://www.wenjuan.com/t/FFR36fw
https://www.wenjuan.com/z/IBBVNjb
https://www.wenjuan.com/d/IBBVNjb
https://www.wenjuan.com/s/IBBVNjb
https://www.wenjuan.com/t/IBBVNjb
https://www.wenjuan.com/z/vqInIzc
https://www.wenjuan.com/d/vqInIzc
https://www.wenjuan.com/s/vqInIzc
https://www.wenjuan.com/t/vqInIzc
https://www.wenjuan.com/z/QnmUru
https://www.wenjuan.com/d/QnmUru
https://www.wenjuan.com/s/QnmUru
https://www.wenjuan.com/t/QnmUru
https://www.wenjuan.com/z/YFNzYjj
https://www.wenjuan.com/d/YFNzYjj
https://www.wenjuan.com/s/YFNzYjj
https://www.wenjuan.com/t/YFNzYjj
https://www.wenjuan.com/z/zaY3IvI
https://www.wenjuan.com/d/zaY3IvI
https://www.wenjuan.com/s/zaY3IvI
https://www.wenjuan.com/t/zaY3IvI
https://www.wenjuan.com/z/MZF3miV
https://www.wenjuan.com/d/MZF3miV
https://www.wenjuan.com/s/MZF3miV
https://www.wenjuan.com/t/MZF3miV
https://www.wenjuan.com/z/qiQzIfc
https://www.wenjuan.com/d/qiQzIfc
https://www.wenjuan.com/s/qiQzIfc
https://www.wenjuan.com/t/qiQzIfc
https://www.wenjuan.com/z/FNziamcQ
https://www.wenjuan.com/d/FNziamcQ
https://www.wenjuan.com/s/FNziamcQ
https://www.wenjuan.com/t/FNziamcQ
https://www.wenjuan.com/z/RJjaiaR
https://www.wenjuan.com/d/RJjaiaR
https://www.wenjuan.com/s/RJjaiaR
https://www.wenjuan.com/t/RJjaiaR
https://www.wenjuan.com/z/6fimQbi
https://www.wenjuan.com/d/6fimQbi
https://www.wenjuan.com/s/6fimQbi
https://www.wenjuan.com/t/6fimQbi
https://www.wenjuan.com/z/QNra63g
https://www.wenjuan.com/d/QNra63g
https://www.wenjuan.com/s/QNra63g
https://www.wenjuan.com/t/QNra63g
https://www.wenjuan.com/z/Mn2iieg
https://www.wenjuan.com/d/Mn2iieg
https://www.wenjuan.com/s/Mn2iieg
https://www.wenjuan.com/t/Mn2iieg
https://www.wenjuan.com/z/Jjaq6n4
https://www.wenjuan.com/d/Jjaq6n4
https://www.wenjuan.com/s/Jjaq6n4
https://www.wenjuan.com/t/Jjaq6n4
https://www.wenjuan.com/z/j6vE7bN
https://www.wenjuan.com/d/j6vE7bN
https://www.wenjuan.com/s/j6vE7bN
https://www.wenjuan.com/t/j6vE7bN
https://www.wenjuan.com/z/FnERZb4
https://www.wenjuan.com/d/FnERZb4
https://www.wenjuan.com/s/FnERZb4
https://www.wenjuan.com/t/FnERZb4
https://www.wenjuan.com/z/NbiQraa
https://www.wenjuan.com/d/NbiQraa
https://www.wenjuan.com/s/NbiQraa
https://www.wenjuan.com/t/NbiQraa
https://www.wenjuan.com/z/qmia2yH
https://www.wenjuan.com/d/qmia2yH
https://www.wenjuan.com/s/qmia2yH
https://www.wenjuan.com/t/qmia2yH
https://www.wenjuan.com/z/Rr6BNjI
https://www.wenjuan.com/d/Rr6BNjI
https://www.wenjuan.com/s/Rr6BNjI
https://www.wenjuan.com/t/Rr6BNjI

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值