Spring 框架中 @PostConstruct 注解详解

简介: 在Spring项目经常遇到@PostConstruct注解,首先介绍一下它的用途: 被注解的方法,在对象加载完依赖注入后执行。此注解是在Java EE5规范中加入的,在Servlet生命周期中有一定作用,它通常都是一些初始化的操作,但初始化可能依赖于注入的其他组件,所以要等依赖全部加载完再执行。

image.png

在Spring项目经常遇到@PostConstruct注解,首先介绍一下它的用途: 被注解的方法,在对象加载完依赖注入后执行。


此注解是在Java EE5规范中加入的,在Servlet生命周期中有一定作用,它通常都是一些初始化的操作,但初始化可能依赖于注入的其他组件,所以要等依赖全部加载完再执行。


与之对应的还有@PreDestroy,在对象消亡之前执行,原理差不多,这里不做过多介绍。


那么首先看下源码注释


image.png


PostConstruct介绍


总体概括如上,注意其中几个点


1. 要在依赖加载后,对象使用前执行,而且只执行一次,原因在上面已经说了。


2. 所有支持依赖注入的类都要支持此方法。


首先,我们可以看到这个注解是在javax.annotation包下的,也就是java拓展包定义的注解,并不是spring定义的,但至于为什么不在java包下,是因为java语言的元老们认为这个东西并不是java核心需要的工具,因此就放到扩展包里(javax中的x就是extension的意思),而spring是支持依赖注入的,因此spring必须要自己来实现@PostConstruct的功能。


3. 文档中说一个类只能有一个方法加此注解,但实际测试中,我在一个类中多个方法加了此注解,并没有报错,而且都执行了,我用的是 Spring Boot 框架。


再往下看,这个注解有一些使用条件,挑一些重点的说一下


image.png


PostConstruct注释规则


1. 除了拦截器这个特殊情况以外,其他情况都不允许有参数,否则spring框架会报IllegalStateException;而且返回值要是void,但实际也可以有返回值,至少不会报错,只会忽略


2. 方法随便你用什么权限来修饰,public、protected、private都可以,反正功能是由反射来实现


3. 方法不可以是static的,但可以是final的


所以,综上所述,在spring项目中,在一个bean的初始化过程中,方法执行先后顺序为


Constructor > @Autowired > @PostConstruct


先执行完构造方法,再注入依赖,最后执行初始化操作,所以这个注解就避免了一些需要在构造方法里使用依赖组件的尴尬。


以上是对@PostConstruct的简单介绍,下面会从spring源码分析其具体实现原理。Spring 最常用的 7 大类注解这篇推荐看下。


PostConstruct实现原理


spring遵守了JSR-250标准,实现了javax.annotation包里面的各种注解功能,首先我们在GitHub下载spring-framework源码,我下的是5.0.x分支代码,导入到idea中,下面就开始动手分析。


首先代码中搜索"import javax.annotation.PostConstruct",庆幸的是只有CommonAnnotationBeanPostProcessor这一个类有引用PostConstruct类,看名字八九不离十就是它了,它是在org.springframework.context.annotation包下,大致介绍如下:


image.png


CommonAnnotationBeanPostProcessor


看来没什么营养,只是一些简单介绍说明了我们在什么版本,基于什么标准,实现了这几个注解,那么看代码。


image.png


CommonAnnotationBeanPostProcessor构造方法


看来只有CommonAnnotationBeanPostProcessor的构造方法使用了这个注解,声明了这个BeanPostProcessor要支持PostConstruct初始化注解。


关注微信公众号:Java技术栈,在后台回复:Spring,可以获取我整理的 N 篇最新 Spring 教程,都是干货。


跟进去setInitAnnotationType这个方法,是父类InitDestroyAnnotationBeanPostProcessor中的方法,只是简单的将PostConstruct.class赋值给成员变量initAnnotationType。


那么谁去使用了这个变量,再次意外的发现,只有buildLifecycleMetadata一个方法使用了这个变量。


image.png


buildLifecycleMetadata方法


这个方法做的事情也很简单,输入一个类,检查它或者它的祖先类是否有初始化方法以及销毁方法,如果有,把这些信息封装成一个LifecycleMetadata类,里面大概信息就是类名、初始化和销毁方法列表,方便bean注册或消亡的时候去调用。


偶然看到LifecycleMetadata中初始化方法列表是List ,LifecycleElement类里面的构造方法有限制方法不能有参数,否则报错IllegalStateException,和前文测试结果对应上了。


image.png


LifecycleElement构造方法


这是题外话了,接着看buildLifecycleMetadata方法中while循环里,不断遍历父类,找PostConstruct注解,每找完一个父类,往initMethods中累加,最后注册到与这个bean相应的initMethods中。


前文说了 “我在一个类中多个方法加了此注解,并没有报错,而且都执行了”,看过上述代码后就知道了,spring根本没有按照javax的要求做限制,可能认为没必要吧。那么多个PostConstruct注解或父类也有此注解,他们是什么顺序执行的呢?


1. 首先父类的初始化方法是先于子类的先执行,但注意不要被子类方法重写,那父类初始化方法就不会执行了,因为中间有一步是用LinkedHashSet存了method的名字。


2. 同一类内,多个PostConstruct注解方法不是按声明顺序执行的,看了一下代码逻辑,虽然存储方法的集合都是有序集合,看起来应该可以顺序执行,但实际上是以一种非常诡异的顺序来执行。


为了看一下spring的初始化过程,在application.properties中设置trace=true,在控制台看debug日志后发现,跟存储方法的集合没声明关系,最开始反射取方法的时候顺序就打乱了。


罪魁祸首就是ReflectionUtils.doWithLocalMethods 这个方法啦!看了一下JDK的API,发现它强调了Class类不能保证getDeclaredMethods()的顺序,因为JVM有权在编译时,自行决定类成员的顺序。


好了,所以现在知道了buildLifecycleMetadata这个方法,就是将bean生命周期的元数据组装一下返回,在类中也只有下面一个方法调用了


image.png


findLifecycleMetadata方法


它把bean的LifecycleMetadata放到一个ConcurrentHashMap保存。然后再往上找,就是AbstractAutowireCapableBeanFactory对bean的初始化和消亡操作了。


在注册完之后就会invoke方法,这是另外一个话题了,此处不再过多介绍,所以本文到此为止。


综上,通过源码来学习还是很高效的嘛,主要是学习大神们的代码精髓。


相关文章
|
1天前
|
前端开发 Java 数据库连接
Spring框架初识
Spring 是一个分层的轻量级开源框架,核心功能包括控制反转(IOC)和面向切面编程(AOP)。主要模块有核心容器、Spring 上下文、AOP、DAO、ORM、Web 模块和 MVC 框架。它通过 IOC 将配置与代码分离,简化开发;AOP 提供了声明性事务管理等增强功能。
33 21
Spring框架初识
|
6天前
|
监控 Java Spring
SpringBoot:SpringBoot通过注解监测Controller接口
本文详细介绍了如何通过Spring Boot注解监测Controller接口,包括自定义注解、AOP切面的创建和使用以及具体的示例代码。通过这种方式,可以方便地在Controller方法执行前后添加日志记录、性能监控和异常处理逻辑,而无需修改方法本身的代码。这种方法不仅提高了代码的可维护性,还增强了系统的监控能力。希望本文能帮助您更好地理解和应用Spring Boot中的注解监测技术。
34 16
|
8天前
|
XML Java 开发者
通过springboot框架创建对象(一)
在Spring Boot中,对象创建依赖于Spring框架的核心特性——控制反转(IoC)和依赖注入(DI)。IoC将对象的创建和管理交由Spring应用上下文负责,开发者只需定义依赖关系。DI通过构造函数、setter方法或字段注入实现依赖对象的传递。Spring Boot的自动配置机制基于类路径和配置文件,自动为应用程序配置Spring容器,简化开发过程。Bean的生命周期包括定义扫描、实例化、依赖注入、初始化和销毁回调,均由Spring容器管理。这些特性提高了开发效率并简化了代码维护。
|
16天前
|
XML Java 测试技术
Spring IOC—基于注解配置和管理Bean 万字详解(通俗易懂)
Spring 第三节 IOC——基于注解配置和管理Bean 万字详解!
104 26
|
19天前
|
缓存 Java 数据库
SpringBoot缓存注解使用
Spring Boot 提供了一套方便的缓存注解,用于简化缓存管理。通过 `@Cacheable`、`@CachePut`、`@CacheEvict` 和 `@Caching` 等注解,开发者可以轻松地实现方法级别的缓存操作,从而提升应用的性能和响应速度。合理使用这些注解可以大大减少数据库的访问频率,优化系统性能。
169 89
|
19天前
|
SQL Java 数据库连接
对Spring、SpringMVC、MyBatis框架的介绍与解释
Spring 框架提供了全面的基础设施支持,Spring MVC 专注于 Web 层的开发,而 MyBatis 则是一个高效的持久层框架。这三个框架结合使用,可以显著提升 Java 企业级应用的开发效率和质量。通过理解它们的核心特性和使用方法,开发者可以更好地构建和维护复杂的应用程序。
109 29
|
1月前
|
开发框架 运维 监控
Spring Boot中的日志框架选择
在Spring Boot开发中,日志管理至关重要。常见的日志框架有Logback、Log4j2、Java Util Logging和Slf4j。选择合适的日志框架需考虑性能、灵活性、社区支持及集成配置。本文以Logback为例,演示了如何记录不同级别的日志消息,并强调合理配置日志框架对提升系统可靠性和开发效率的重要性。
|
2月前
|
Java Spring 容器
【SpringFramework】Spring IoC-基于注解的实现
本文主要记录基于Spring注解实现IoC容器和DI相关知识。
62 21
|
2月前
|
Java 开发者 Spring
理解和解决Spring框架中的事务自调用问题
事务自调用问题是由于 Spring AOP 代理机制引起的,当方法在同一个类内部自调用时,事务注解将失效。通过使用代理对象调用、将事务逻辑分离到不同类中或使用 AspectJ 模式,可以有效解决这一问题。理解和解决这一问题,对于保证 Spring 应用中的事务管理正确性至关重要。掌握这些技巧,可以提高开发效率和代码的健壮性。
128 13
|
2月前
|
Java Spring
【Spring】方法注解@Bean,配置类扫描路径
@Bean方法注解,如何在同一个类下面定义多个Bean对象,配置扫描路径
183 73