@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> T doGetBean(
final String name,
@Nullable final Class<T> 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;resource和autowired注解的区别
}
到这⾥,我们可以知道。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 = Class());
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 = (clazz);
if (metadata == null) {
synchronized (this.lifecycleMetadataCache) {
metadata = (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<LifecycleElement> 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 = Superclass();
} 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("l.ws.WebServiceContext");
}
可以看到,@PostConstruct注解被设置为了initAnnotationType的值。值得注意的是,这是在CommonAnnotationBeanPostProcessor这个后置处理器的构造⽅法中执⾏的。
⽽CommonAnnotationBeanPostProcessor和InitDestroyAnnotationBeanPostProcessor的关系是继承关系,前者继承了后者。我们可以断点看看getBeanPostProcessors⽅法
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论