PostConstruct注解原理说明
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了PostConstruct注解原理说明相关的知识,希望对你有一定的参考价值。
参考技术A 首先我们知道这个注解的作用是:在类被初始化的时候 会执行被@PostConstruct注解的方法。那它到底是怎么工作的,什么时候被执行,都有什么限制规则,会引起什么问题呢 ?
本着一切都要验证的原则,开始解剖,但是从哪里开始下刀呢?既然是类的初始化,自然是想到了BeanFactory这个庞大的工厂,那我们就从spring初始化bean的入口开刀吧。
bean被初始化的过程了解过spring运行原理的应该都知道了,这里简单说明一下:
首先我们看BeanFactory,随便找一个实现类AbstractAutowireCapableBeanFactory进去
我们知道bean被实例化有两种方式,一种是注入的时候就实例化,一种是第一次用的时候才实例化(懒加载)。先看autowireBean方法
到这里可以看到,在初始化bean的时候,先循环所有的BeanPostProcessor执行postProcessBeforeInitialization 方法;再调用 bean的初始化;最后执行applyBeanPostProcessorsAfterInitialization方法。而我们的@PostConstruct就是在第一步中的一个 BeanPostProcessor 里处理的。
我们进到BeanPostProcessor里面可以看到它有很多实现。
而位于spring-beans包下的org.springframework.beans.factory.annotaion.InitDestroyAnnotationBeanPostProcessor成功的引起了我们的怀疑,那就先进去看看吧。
那么 initAnnotationType 都有哪些呢
CommonAnnotationBeanPostProcessor 是 继承自InitDestroyAnnotationBeanPostProcessor的,到这里,我们可以知道。跟我们前面的猜测一样,解析过程是通过反射来获取@PostConstruct注解的方法,并放到initMethods集合里面去。然后再通过反射调用这个集合里面的所有方法,完成初始化行为的。
最后再通过PostConstruct注解类源码看看它的使用注意事项:
至此,针对@PostConstruct总结如下:
1.作用在类的方法上,在依赖注入完成之后初始化方法,这个方法是在类被放入服务之前被调用,调用顺序是 构造函数->依赖注入->PostConstruct
2.所有支持依赖注入的类都要支持这个方法。
3.只有一个方法可以加这个注解,虽然PostConstruct官方的类上有这个描述,但真实运行效果上看的话,可以给多个方法加,而且都可以被执行。
4.被PostContruct修饰的方法要满足以下几点:
-方法不能有任何参数,除非是 拦截器中的 InvocationContext对象。
-方法不能是静态的,除非是启动类。
-方法是final的, 不能被重写。
-如果方法对应的类是容器中的类,不要抛出UncheckedException
简单介绍@PostConstruct@PreDestroy@DependsOn@Order等注解的作用及嵌套使用时优先级问题
简单介绍@PostConstruct、@PreDestroy、@DependsOn、@Order等注解的作用及嵌套使用时优先级问题
1、@PostConstruct和@PreDestroy注解
说明:@PostConstruct注解好多人以为是Spring提供的,其实是Java自己的注解。
从JavaEE5规范开始,Servlet中增加了两个影响Servlet生命周期的注解,@PostConstruct
和@PreDestroy
这两个注解被用来修饰一个非静态的void()方法
,并且被修饰的方法不能抛出异常。
被@PostContruct注解的方法:
-
会在服务器加载Servlet的时候运行,并且只会被服务器执行一次,具体运行时期是在构造函数执行之后,init()初始化方法之前。
-
通常我们会在Spring框架中使用@postConstruct注解,该注解的方法在整个Bean初始化中的顺序:
Constructor(构造方法)
->@Autowired(依赖注入)
->@PostConstruct(注释的方法)
-
该注解的用法:
@PostConstruct public void method()
-
或者:
public @PostConstruct void method()
被@PreDestroy修饰的方法:
- 会在服务器卸载Servlet的时候运行,并且只会被服务器调用一次,类似于Servlet的destroy()方法,会在destroy()方法之后运行,在Servlet被彻底卸载之前。
执行大致流程图:
被注解的Servlet生命周期:
-
需要注意的是,注解会多多少少地影响到服务器的启动速度。服务器在启动时候会遍历Web 应用的
WEB-INF/classes
下的所有class文件与WEB-INF/lib
下的所有jar文件,以检查哪些类使用了注解。 -
如果应用程序中没有 使用任何注解,可以在Web.xml中设置
metadata-complete
属性为true。 -
支持@PostConstruct和@PreDestroy注解的服务器需要支持Servlet2.5规范,Tomcat5.x仅支持Servlet2.4规范。
@PostContruct注解的方法,Spring中如何发现?
其实在Bean依赖注入也会经常用到,比如所有这样一个场景:
要将对象B注入到对象A,那么首先就必须得生成对象A和对象B,才能执行注入。
- 如果一个类A中有个成员变量b被@Autowried注解,那么@Autowired注入是发生在A的构造方法执行完之后的。
- 如果想在生成对象时完成某些初始化操作,而偏偏这些初始化操作又依赖于依赖注入,那么就无法在构造函数中实现。为此,可以使用@PostConstruct注解一个方法来完成初始化,@PostConstruct注解的方法将会在依赖注入完成后被自动调用。
实例演示:
A类:
@Component
public class A
@Autowired
private B b;
public A()
System.out.println("执行A的构造函数,此时b还未被注入,b=" + b);
@PostConstruct
public void init()
System.out.println("@PostConstruct注解此方法,会在对象b注入完成之后自动调用此方法,b=" + b);
B类:
@Component
public class B
public B()
System.out.println("执行B的构造函数!");
直接运行Spring容器,查看结果:
执行A的构造函数,此时b还未被注入,b=null
执行B的构造函数!
@PostConstruct注解此方法,会在对象b注入完成之后自动调用此方法,b=cn.wbs.test.B@265adfad
2、@DependsOn注解
该注解的作用:
- 具有依赖关系。
- 假如在Test02类上加上
@DependsOn(value = "test01")
,那么就说明Test02在加载时,要依赖于Test01类,Spring IOC 容器会优先加载Test01,然后再加载Test02类。
参考文章:https://blog.csdn.net/weixin_43591980/article/details/121547379?spm=1001.2014.3001.5501
实例演示:假设现在有2个类Test01、Test02,需要交给Spring IOC容器托管:
- 静态变量的属性值需要通过Spring容器赋值,值(hello和world)定义在application.properties中。
- 注意:@Value注解不可以给静态变量注入属性值 (否则获取的注入结果为null)。
- 所以需要再setter方法上标注注入值,setter方法也是不可以加static关键字的
@Component
public class Test01
public static String HELLO;
public static String WORLD;
@Value("$spring.test.hello")
public void setHELLO(String HELLO)
Test01.HELLO = HELLO;
@Value("$spring.test.world")
public void setWORLD(String WORLD)
Test01.WORLD = WORLD;
@Component
public class Test02
@PostConstruct
public void init()
public Test02()
业务需求:我需要在Test02的无参构造方法加载时,控制台打印Test01类中的HELLO静态变量值,然后在 init()方法执行时,控制台打印Test01类中的WORLD静态变量值。
最初的简单想法如下:
@Component
public class Test02
@PostConstruct
public void init()
System.out.println("2:" + Test01.HELLO);
public Test02()
System.out.println("1:" + Test01.WORLD);
此时运行Spring容器,查看控制台结果:
1:null
2:null
为什么会出现这种情况呢?原因就是因为我们在Test02类中调用Test01类中的静态变量时,Test01类即使是和Test02类同时注入Spring容器中,微观上可以认为是同步的,所以在Test02类去调用Test01类中的静态变量时,HELLO
和WORLD
的值还没有被@Value
注解加载注入,所以会打印null值。
解决办法:就是使用@DependOn(value = “test01”)注解!
@Component
@DependsOn(value = "test01")
public class Test02
@PostConstruct
public void init()
System.out.println("2:" + Test01.HELLO);
public Test02()
System.out.println("1:" + Test01.WORLD);
再次运行Spring容器,查看控制台结果:
#这样值就有了,意思是运行Test02类时,知道Test02中调用了Test01类的资源了,所以先去加载Test01类
1:WORLD
2:HELLO
3、@Order注解
@Order
注解的作用是定义Spring IOC容器中Bean的执行顺序的优先级。
实例演示:
@Component
@Order(1)
public class Test01
System.out.println("执行Test01类");
@Component
@Order(2)
public class Test02
System.out.println("执行Test02类");
运行spring容器,查看控制台结果:
执行Test01类
执行Test02类
如上述代码所示,通过@Order注解定义优先级,2个Bean对象从IOC容器中的加载顺序为:Test01、Test02,实际上上述演示的业务需求使用@Order
注解也可以实现。
以上是关于PostConstruct注解原理说明的主要内容,如果未能解决你的问题,请参考以下文章
@PostConstruct和@PreConstruct注解
@PostConstruct与@PreConstruct注解
@PostConstruct方法的使用以及原理,@Component+@PostConstruct方法将一个方法完成初始化操作