spring框架中@PostConstruct的实现原理
在spring项⽬经常遇到@PostConstruct注解,⾸先介绍⼀下它的⽤途: 被注解的⽅法,在对象加载完依赖注⼊后执⾏。
此注解是在Java EE5规范中加⼊的,在Servlet⽣命周期中有⼀定作⽤,它通常都是⼀些初始化的操作,但初始化可能依赖于注⼊的其他组件,所以要等依赖全部加载完再执⾏。与之对应的还有@PreDestroy,在对象消亡之前执⾏,原理差不多,这⾥不做过多介绍。
那么⾸先看下源码注释
PostConstruct注释介绍
总体概括如上,注意其中⼏个点
1. 要在依赖加载后,对象使⽤前执⾏,⽽且只执⾏⼀次,原因在上⾯已经说了。
2. 所有⽀持依赖注⼊的类都要⽀持此⽅法。⾸先,我们可以看到这个注解是在javax.annotation包下的,也就是java拓展包定义的注解,并不是spring定义的,但⾄于为什么不在java包下,是因为java语⾔的元⽼们认为这个东西并不是java核⼼需要的⼯具,因此就放到扩展包⾥(javax中的x就是extension的意思),⽽spring是⽀持依赖注⼊的,因此spring必须要⾃⼰来实现@PostConstruct的功能。
3. ⽂档中说⼀个类只能有⼀个⽅法加此注解,但实际测试中,我在⼀个类中多个⽅法加了此注解,并没有报错,⽽且都执⾏了,我⽤的是springboot框架。
再往下看,这个注解有⼀些使⽤条件,挑⼀些重点的说⼀下
PostConstruct注释规则
1. 除了这个特殊情况以外,其他情况都不允许有参数,否则spring框架会报IllegalStateException;⽽且返回值要是void,但实际也可以有返回值,⾄少不会报错,只会忽略
2. ⽅法随便你⽤什么权限来修饰,public、protected、private都可以,反正功能是由反射来实现
3. ⽅法不可以是static的,但可以是final的
所以,综上所述,在spring项⽬中,在⼀个bean的初始化过程中,⽅法执⾏先后顺序为
Constructor > @Autowired > @PostConstruct
先执⾏完构造⽅法,再注⼊依赖,最后执⾏初始化操作,所以这个注解就避免了⼀些需要在构造⽅法⾥使⽤依赖组件的尴尬。
计算机中spring是什么意思==========以上是对@PostConstruct的简单介绍,下⾯会从spring源码分析其具体实现原理==========
spring遵守了JSR-250标准,实现了javax.annotation包⾥⾯的各种注解功能,⾸先我们在GitHub下载spring-framework源码,我下的是5.0.x 分⽀代码,导⼊到idea中,下⾯就开始动⼿分析。
⾸先代码中搜索"import javax.annotation.PostConstruct",庆幸的是只有CommonAnnotationBeanPostProcessor这⼀个类有引⽤PostConstruct类,看名字⼋九不离⼗就是它了,它是在t.annotation包下,⼤致介绍如下CommonAnnotationBeanPostProcessor注释
看来没什么营养,只是⼀些简单介绍说明了我们在什么版本,基于什么标准,实现了这⼏个注解,那么看代码。
CommonAnnotationBeanPostProcessor构造⽅法
看来只有CommonAnnotationBeanPostProcessor的构造⽅法使⽤了这个注解,声明了这个BeanPostProcessor要⽀持PostConstruct初始化注解,跟进去setInitAnnotationType这个⽅法,是⽗类InitDestroyAnnotationBeanPostProcessor中的⽅法,只是简单的将PostConstruct.class赋值给成员变量i
nitAnnotationType,那么谁去使⽤了这个变量,再次意外的发现,只有buildLifecycleMetadata⼀个⽅法使⽤了这个变量。
buildLifecycleMetadata⽅法
这个⽅法做的事情也很简单,输⼊⼀个类,检查它或者它的祖先类是否有初始化⽅法以及销毁⽅法,如果有,把这些信息封装成⼀个LifecycleMetadata类,⾥⾯⼤概信息就是类名、初始化和销毁⽅法列表,⽅便bean注册或消亡的时候去调⽤。
偶然看到LifecycleMetadata中初始化⽅法列表是List<LifecycleElement>,LifecycleElement类⾥⾯的构造⽅法有限制⽅法不能有参数,否则报
错IllegalStateException,和前⽂测试结果对应上了。
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⽣命周期的元数据组装⼀下返回,在类中也只有下⾯⼀个⽅法调⽤了
findLifecycleMetadata⽅法
它把bean的LifecycleMetadata放到⼀个ConcurrentHashMap保存。【说实话第⼀次看到对ConcurrentH
ashMap这么加锁的,改⽇写⼀篇⽂章解析⼀下java中锁的应⽤以及ConcurrentHashMap吧】然后再往上,就是AbstractAutowireCapableBeanFactory对bean的初始化和消亡操作了,在注册完之后就会invoke⽅法,这是另外⼀个话题了,此处不再过多介绍,所以本⽂到此为⽌。
综上,通过源码来学习还是很⾼效的嘛,主要是学习⼤神们的代码精髓。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。