作用

  1.注解@PostConstruct可以添加在类的方法上面,如果这个类被IOC容器托管,那么在对Bean进行初始化前的时候会调用被这个注解修饰的方法

被定义在哪里?

  1.被定义在了CommonAnnotationBeanPostProcessor类,这个类是InitDestroyAnnotationBeanPostProcessor类的子类,也实现了InstantiationAwareBeanPostProcessor接口(BeanDefinition的后置处理接口)。代码展示:

public CommonAnnotationBeanPostProcessor() {
setOrder(Ordered.LOWEST_PRECEDENCE - 3);
setInitAnnotationType(PostConstruct.class);
setDestroyAnnotationType(PreDestroy.class);
ignoreResourceType("javax.xml.ws.WebServiceContext");
}

  2.故在这个CommonAnnotationBeanPostProcessor类实例化的时候注解就会被定义下来。

    

在何处被扫描?

  1.在BeanDefinition的后置处理时调用 postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName)进行扫描

  2.在初始化前中调用处理器InitDestroyAnnotationBeanPostProcessor的postProcessBeforeInitialization方法进行扫描

  汇总:

    两处扫描的本质都是调用了 LifecycleMetadata findLifecycleMetadata(Class<?> clazz)方法(位于InitDestroyAnnotationBeanPostProcessor类里面),

扫描方法分析

  1.findLifecycleMetadata方法分析:

    说明:

      1)判断缓存有没有构建,没有构建则调用构建Metadata对象的方法

      2)缓存构建了,就去缓存里面寻找,没找到就调用构建Metadata对象的方法,把拿回来的对象存入缓存中

    代码展示:

private LifecycleMetadata findLifecycleMetadata(Class<?> clazz) {
if (this.lifecycleMetadataCache == null) {
return buildLifecycleMetadata(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;
}

  2.buildLifecycleMetadata方法分析:

    说明:

      1)主要应用类反射机制的概念,doWithLocalMethods通过类获取所有方法,然后利用反射机制构建调用对象

      2)LifecycleMetadata对象便是包含了该类的所有的初始化方法和销毁方法

    代码展示:

private LifecycleMetadata buildLifecycleMetadata(final Class<?> clazz) {
List<LifecycleElement> initMethods = new ArrayList<>();
List<LifecycleElement> destroyMethods = new ArrayList<>();
Class<?> targetClass = clazz; do {
final List<LifecycleElement> currInitMethods = new ArrayList<>();
final List<LifecycleElement> currDestroyMethods = new ArrayList<>(); //doWithLocalMethods,深入源码其实可知是通过类对象取出所有的方法,逐一进行调用lambda表达式的方法
ReflectionUtils.doWithLocalMethods(targetClass, method -> {
//判断初始化方法
if (this.initAnnotationType != null && method.isAnnotationPresent(this.initAnnotationType)) {
LifecycleElement element = new LifecycleElement(method);
currInitMethods.add(element);
}
//判断销毁方法
if (this.destroyAnnotationType != null && method.isAnnotationPresent(this.destroyAnnotationType)) {
currDestroyMethods.add(new LifecycleElement(method));
}
}); //根据继承关系故会有父类要比子类先构造,子类要比父类先销毁
//所以这里采用头插法
initMethods.addAll(0, currInitMethods);
//这里会往末尾存放
destroyMethods.addAll(currDestroyMethods);
//寻找父类
targetClass = targetClass.getSuperclass();
}
while (targetClass != null && targetClass != Object.class); return new LifecycleMetadata(clazz, initMethods, destroyMethods);
}

在何处被调用?(过程分析)

  1.既然是在初始化前的处理器中调用,而且源于InitDestroyAnnotationBeanPostProcessor这个处理器会在初始化前这个步骤中执行@PostConstruct的方法

@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;
}

  

  2.基于反射机制调用方法对象来调用类对象的方法:

public void invokeInitMethods(Object target, String beanName) throws Throwable {
Collection<LifecycleElement> checkedInitMethods = this.checkedInitMethods;
Collection<LifecycleElement> initMethodsToIterate = (checkedInitMethods != null ? checkedInitMethods : this.initMethods);
if (!initMethodsToIterate.isEmpty()) {
for (LifecycleElement element : initMethodsToIterate) {
element.invoke(target);
}
}
}

    

postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName)

注解@PostConstruct分析的更多相关文章

  1. 【JPA】注解@PostConstruct、@PreDestroy

    从Java EE5规范开始,Servlet增加了两个影响Servlet生命周期的注解@PostConstruct和@PreConstruct.这两个注解被用来修饰一个非静态的void()方法,而且这个 ...

  2. uboot-的start.S详细注解及分析

    原文地址:uboot-的start.S详细注解及分析 作者:zhouyg11 大多数bootloader都分为stage1和stage2两部分,u-boot也不例外.依赖于CPU体系结构的代码(如设备 ...

  3. Spring注解@PostConstruct与@PreDestroy

    关于在spring  容器初始化 bean 和销毁前所做的操作定义方式有三种: 第一种:通过@PostConstruct 和 @PreDestroy 方法 实现初始化和销毁bean之前进行的操作 第二 ...

  4. servlet注解@PostConstruct与@PreDestroy

    从Java EE 5规范开始,Servlet中增加了两个影响Servlet生命周期的注解(Annotion):@PostConstruct和@PreDestroy.这两个注解被用来修饰一个非静态的vo ...

  5. 注解@PostConstruct与@PreDestroy讲解及实例

    从Java EE 5规范开始,Servlet中增加了两个影响Servlet生命周期的注解(Annotion):@PostConstruct和@PreDestroy.这两个注解被用来修饰一个非静态的vo ...

  6. 注解@PostConstruct与@PreDestroy 特性说明

    简介 Java EE5 引入了@PostConstruct和@PreDestroy这两个作用于Servlet生命周期的注解,实现Bean初始化之前和销毁之前的自定义操作.此文主要说明@PostCons ...

  7. Spring 注解详细分析解释有实例

    概述 注释配置相对于 XML 配置具有很多的优势: 它可以充分利用 Java 的反射机制获取类结构信息,这些信息可以有效减少配置的工作.如使用 JPA 注释配置 ORM 映射时,我们就不需要指定 PO ...

  8. springboot - Constructor、@Autowired、@PostConstruct分析

    1.Constructor 构造方法 2.@Autowired 依赖注入 3.@PostConstruct 在依赖注入完成后被自动调用 4. 三者的顺序: 从依赖注入的字面意思就可以知道,要将对象p注 ...

  9. 注解@PostConstruct与@PreDestroy详解及实例

    Java EE5 引入了@PostConstruct和@PreDestroy这两个作用于Servlet生命周期的注解,实现Bean初始化之前和销毁之前的自定义操作.此文主要说明@PostConstru ...

随机推荐

  1. 初识Java GUI

    1. 使用Java  Swing 显示的窗口如下 在原有代码基础上添加代码实现对窗口大小 标题等信息

  2. Tapdata 实时数据融合平台解决方案(四):技术选型

    作者介绍:TJ,唐建法,Tapdata 钛铂数据CTO,MongoDB中文社区主席,原MongoDB大中华区首席架构师,极客时间MongoDB视频课程讲师. 常见搭建数据中台的技术产品 数据中台包括: ...

  3. 如何准备论文线上Presentation视频录制教程(Summary of Video Recording)

    0:前言 由于国外的疫情严重,目前大多数学术会议都是线上举办,因此往往需要制作presentation的视频录制.由于各种软件横飞,有的需要会员并且不熟悉操作,特别浪费时间.因此,我将这次的操作和遇到 ...

  4. AspectJ和AOP细节

    AspectJ1. 简介 AspectJ:Java社区里最完整最流行的AOP框架.(在Spring中AOP是一种思想,而AspectJ是一种AOP的更明确具体实现) 在Spring2.0以上版本中,可 ...

  5. vue this.getOptions is not a function

    错误提示截图: 问题原因:是由于sass-loader引用的版本过低导致 解决方法:在package.json中增加以下配置后 "sass-loader": "^10&q ...

  6. 5-15 Virtual 虚拟机

    虚拟机基本使用 Virtualbox安装流程 RockyLinux VirtualBox清华大学个版本下载路径 https://mirrors.tuna.tsinghua.edu.cn/virtual ...

  7. 面试官:你确定 Redis 是单线程的进程吗?

    作者:小林coding 计算机八股文网站:https://xiaolincoding.com 大家好,我是小林. 这次主要分享 Redis 线程模型篇的面试题. Redis 是单线程吗? Redis ...

  8. 使用uart串口接收模块接收信号,控制led灯闪烁

    功能描述: 使用遵循uart协议的接收模块接收控制信号,用来控制led的闪烁. 设计输入: 1.uart输入信号 2.时钟信号 3.复位信号 4.led信号 设计思路: 总体上:前面已经写了串口接收模 ...

  9. P2183 [国家集训队]【一本通提高组合数学】礼物

    [国家集训队]礼物 题目背景 一年一度的圣诞节快要来到了.每年的圣诞节小 E 都会收到许多礼物,当然他也会送出许多礼物.不同的人物在小 E 心目中的重要性不同,在小 E 心中分量越重的人,收到的礼物会 ...

  10. 【Java中的线程】java.lang.Thread 类分析

    进程和线程 联想一下现实生活中的例子--烧开水,烧开水时是不是不需要在旁边守着,交给热水机完成,烧开水这段时间可以去干一点其他的事情,例如将衣服丢到洗衣机中洗衣服.这样开水烧完,衣服洗的也差不多了.这 ...