前言
Spring 很復(fù)雜因篇,很多東西都很難完全深入了解泞辐,這里寫下Bean 的初始化的分析笔横,主要是為了解SpringBoot 與Shiro 整合時(shí),引起的事務(wù)失敗問題咐吼,網(wǎng)上很多說是初始化順序引起沒有生成事務(wù)代理類而導(dǎo)致失敗吹缔。為了知其然知其所以然的原則,決定了解了解厢塘。
以下分析基于Spring 5.1.9.RELEASE、shiro1.4.0
一肌幽、Bean 初始化過程
1. Bean初始化入口:ApplicationContext
Application 繼承結(jié)構(gòu)圖
2. Bean 的生命周期圖示
生命周期相關(guān)步驟解析:
注意: Spring 只幫我們管理單例模式 Bean 的完整生命周期,對于 prototype 的 bean 喂急,Spring 在創(chuàng)建好交給使用者之后則不會再管理后續(xù)的生命周期。
- 通過構(gòu)造函數(shù)實(shí)例化Bean
- 依賴注入推薦使用setter 方法(主要注入方法:setter注入 煮岁、 constructor構(gòu)造器注入)
- 當(dāng)依賴注入完成讥蔽,以下接口方法將會回調(diào):BeanNameAware.setBeanName(String name);該方法是在Bean Factory 創(chuàng)建bean 時(shí)画机,設(shè)置Bean的名稱
- 然后調(diào)用: BeanClassLoaderAware.setBeanClassLoader(), 設(shè)置類加載器加載Bean 實(shí)例
- 然后調(diào)用: BeanFactoryAware.setBeanFactory(BeanFactory beanFactory) 步氏,為當(dāng)前的Bean 設(shè)置Bean Factory
- 容器調(diào)用: BeanPostProcessor.postProcessBeforeInitialization, 回調(diào)該方法荚醒,可將原始bean包裝成任意成對像返回
- 然后如果方法上有注解@PostConstruct 則會被調(diào)用(注意:這里需要開啟組件掃描芋类,以便對注解進(jìn)行處理)
- 在注解@PostConstruct 方法調(diào)用后,回調(diào)接口方法InitializingBean.afterPropertiesSet()
- 調(diào)用在xml 配置中bean 標(biāo)簽屬性init-method 定義的方法
- 然后調(diào)用 BeanPostProcessor.postProcessAfterInitialization()界阁, 調(diào)用該方法侯繁,用于包裝原始bean
- 到目前為止,bean已經(jīng)初始化贮竟,可以使用了
- 當(dāng)ApplicationContext 銷毀鉤子registerShutdownHook() 調(diào)用(調(diào)用后不是會立即銷毀), 有注解@PreDestroy 等銷毀方法會被調(diào)用
- 然后接著調(diào)用Bean 實(shí)現(xiàn)接口 DisposableBean.destroy() 方法
- 然后調(diào)用在xml 配置中bean 標(biāo)簽屬性 destroy-method 的方法
- 垃圾回收器會自動(dòng)調(diào)用Object 類的finalize() 方法銷毀對象
初始化回調(diào)的方法
- 接口InitializingBean 的afterPropertiesSet() 方法咕别,Spring 不推薦使用
- 注解@PostConstruct的方法
- xml配置中bean標(biāo)簽屬性 init-method 指定的方法 或 JavaConfig 注解@Bean 中屬性initMethod 指定的方法
- 如果以上上三種方法都使用了,則按以下順序執(zhí)行
- 先調(diào)用注解@PostConstruct的方法
- 再調(diào)用 接口InitializingBean 的afterPropertiesSet() 方法
- 然后才調(diào)用 xml 配置的 init-method 指定方法 或 Java配置中的initMethod 指定的方法
銷毀回調(diào)的方法
- 接口DisposableBean的 destroy()惰拱, Spring 不推薦使用
- 推薦使用 注解@PreDestroy標(biāo)識的方法 或 在xml 配置 bean 標(biāo)簽屬性destroy-method 定義的方法(Java 配置中,注解@Bean 屬性destroyMethod 定義的方法)
- 如果以上方法都配置偿短,則按以下順序執(zhí)行
- 注解@PreDestroy 標(biāo)識的方法先執(zhí)行
- 然后執(zhí)行在xml 配置 bean 標(biāo)簽屬性destroy-method 定義的方法(Java 配置中欣孤,注解@Bean 屬性destroyMethod 定義的方法)
- 最后執(zhí)行接口DisposableBean的 destroy()
二翔冀、例子
1. 創(chuàng)建Maven 項(xiàng)目
添加Spring 依賴
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
2. 創(chuàng)建Student 類导街,并實(shí)現(xiàn)相關(guān)接口
public class Student implements InitializingBean, DisposableBean,
BeanFactoryAware, BeanNameAware {
private String name;
private Integer age;
public Student() {
System.out.println("初始化構(gòu)造函數(shù)");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@PostConstruct
public void springPostConstruct() {
System.out.println("---@PostConstruct--- 執(zhí)行");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("---InitializingBean.afterPropertiesSet---");
}
public void myInitMethod() {
System.out.println("---init-method---");
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public void setBeanName(String name) {
System.out.println("---BeanNameAware.setBeanName---");
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println("---BeanFactoryAware.setBeanFactory---");
}
@PreDestroy
public void springPreDestroy(){
System.out.println("-----@PreDestroy-----");
}
@Override
public void destroy() throws Exception {
System.out.println("-----DisposableBean.destroy()------");
}
public void myDestroyMethod(){
System.out.println("---destroy-method---");
}
}
3. 創(chuàng)建BeanPostProcessor 自定義實(shí)現(xiàn)類
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
System.out.println("BeanPostProcessor.postProcessAfterInitialization");
return bean;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
System.out.println("BeanPostProcessor.postProcessBeforeInitialization");
return bean;
}
}
4. 使用xml配置Bean
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util
https://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!--組件掃描-->
<context:component-scan base-package="cn.hdj"/>
<!--配置bean, 加載的順序由上到下,先定義的Bean先初始化-->
<bean id="student" class="cn.hdj.entity.Student" init-method="myInitMethod" destroy-method="myDestroyMethod"/>
<bean id="myBeanPostProcessor" class="cn.hdj.MyBeanPostProcessor"/>
</beans>
5. 實(shí)例化上下文
public static void main(String[] args) {
AbstractApplicationContext context = new ClassPathXmlApplicationContext("classpath:application-Context.xml");
Student bean = context.getBean(Student.class);
System.out.println(bean);
context.registerShutdownHook();
}
6. 啟動(dòng)項(xiàng)目款票,輸出結(jié)果
初始化構(gòu)造函數(shù)
---BeanNameAware.setBeanName---
---BeanFactoryAware.setBeanFactory---
BeanPostProcessor.postProcessBeforeInitialization
---@PostConstruct--- 執(zhí)行
---InitializingBean.afterPropertiesSet---
---init-method---
BeanPostProcessor.postProcessAfterInitialization
Student{name='null', age=null}
-----@PreDestroy-----
-----DisposableBean.destroy()------
---destroy-method---
以上就是Bean 初始化的過程,如果說我們想要對Bean 初始化過程中艾少,對Bean 做一些處理,那么接下來我們需要了解一下缚够,Spring 為我們提供的擴(kuò)展點(diǎn)接口幔妨。
三谍椅、Aware 接口
*Aware
接口可以用于在初始化 bean 時(shí)獲得 Spring 中的一些對象,如獲取Spring 上下文
等雏吭。
以下為Spring 提供的*Aware接口
Aware 接口 | 實(shí)現(xiàn)的方法 | 作用(在Bean 構(gòu)造函數(shù)初始后回調(diào)锁施,但在初始化回調(diào)方法前調(diào)用) |
---|---|---|
ApplicationContextAware | void setApplicationContext(ApplicationContext applicationContext) throws BeansException; | Interface to be implemented by any object that wishes to be notified of the ApplicationContext that it runs in. |
ApplicationEventPublisherAware | void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher); | Set the ApplicationEventPublisher that this object runs in. |
BeanClassLoaderAware | void setBeanClassLoader (ClassLoader classLoader); | Callback that supplies the bean class loader to a bean instance. |
BeanFactoryAware | void setBeanFactory (BeanFactory beanFactory) throws BeansException; | Callback that supplies the owning factory to a bean instance. |
BeanNameAware | void setBeanName(String name); | Set the name of the bean in the bean factory that created this bean. |
BootstrapContextAware | void setBootstrapContext (BootstrapContext bootstrapContext); | Set the BootstrapContext that this object runs in. |
LoadTimeWeaverAware | void setLoadTimeWeaver (LoadTimeWeaver loadTimeWeaver); | Set the LoadTimeWeaver of this object’s containing ApplicationContext. |
MessageSourceAware | void setMessageSource (MessageSource messageSource); | Set the MessageSource that this object runs in. |
NotificationPublisherAware | void setNotificationPublisher(NotificationPublisher notificationPublisher); | Set the NotificationPublisher instance for the current managed resource instance. |
PortletConfigAware | void setPortletConfig (PortletConfig portletConfig); | Set the PortletConfig this object runs in. |
PortletContextAware | void setPortletContext (PortletContext portletContext); | Set the PortletContext that this object runs in. |
ResourceLoaderAware | void setResourceLoader (ResourceLoader resourceLoader); | Set the ResourceLoader that this object runs in. |
ServletConfigAware | void setServletConfig (ServletConfig servletConfig); | Set the ServletConfig that this object runs in. |
ServletContextAware | void setServletContext (ServletContext servletContext); | Set the ServletContext that this object runs in. |
四悉抵、增強(qiáng)處理器
種類
- BeanPostProcessor
- BeanFactoryPostProcessor (不在Bean 生命周期內(nèi))
增強(qiáng)處理器處理過程
增強(qiáng)處理器的特點(diǎn)
- BeanFactoryPostProcessor是加載Bean定義后執(zhí)行
- BeanPostProcessor 則是在Bean 初始化和依賴注入后執(zhí)行
- 如果你在一個(gè)容器內(nèi)定義了增強(qiáng)處理器,它就只作用于這個(gè)容器內(nèi)的bean姥饰,即使其它容器與該容器有相同的層級關(guān)系
- 想要控制多個(gè)處理器的處理順序,可以實(shí)現(xiàn)Order 接口來控制媳否,數(shù)值越小,優(yōu)先級越大
BeanFactoryPostProcessor的接口定義
public interface BeanFactoryPostProcessor {
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory);
}
BeanPostProcessor 的接口定義
public interface BeanPostProcessor {
//前置處理
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
//后置處理
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
BeanPostProcessor 的注冊過程荆秦?
//用于實(shí)例化和注冊BeanPostProcessor
protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this);
}
public static void registerBeanPostProcessors(
ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {
String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);
// Register BeanPostProcessorChecker that logs an info message when
// a bean is created during BeanPostProcessor instantiation, i.e. when
// a bean is not eligible for getting processed by all BeanPostProcessors.
int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;
beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));
// 區(qū)分不同優(yōu)先級的BeanPostProcessor
//優(yōu)先級分別有:PriorityOrdered,Ordered和其他的
List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
List<BeanPostProcessor> internalPostProcessors = new ArrayList<>();
List<String> orderedPostProcessorNames = new ArrayList<>();
List<String> nonOrderedPostProcessorNames = new ArrayList<>();
for (String ppName : postProcessorNames) {
if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
//實(shí)現(xiàn)了接口PriorityOrdered的BeanPostProcessor
//會優(yōu)先實(shí)例化
BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
priorityOrderedPostProcessors.add(pp);
if (pp instanceof MergedBeanDefinitionPostProcessor) {
internalPostProcessors.add(pp);
}
}
else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
orderedPostProcessorNames.add(ppName);
}
else {
nonOrderedPostProcessorNames.add(ppName);
}
}
// 首先對實(shí)現(xiàn)了接口 PriorityOrdered.的BeanPostProcessor進(jìn)行排序
//然后注冊
sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);
// 接著對實(shí)現(xiàn)了接口 Ordered.的BeanPostProcessor進(jìn)行排序
//然后注冊
List<BeanPostProcessor> orderedPostProcessors = new ArrayList<>();
for (String ppName : orderedPostProcessorNames) {
BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
orderedPostProcessors.add(pp);
if (pp instanceof MergedBeanDefinitionPostProcessor) {
internalPostProcessors.add(pp);
}
}
sortPostProcessors(orderedPostProcessors, beanFactory);
registerBeanPostProcessors(beanFactory, orderedPostProcessors);
// 再接著對普通的BeanPostProcessor進(jìn)行排序
//然后注冊
List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<>();
for (String ppName : nonOrderedPostProcessorNames) {
BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
nonOrderedPostProcessors.add(pp);
if (pp instanceof MergedBeanDefinitionPostProcessor) {
internalPostProcessors.add(pp);
}
}
registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors);
//最后對內(nèi)部定義的BeanPostProcessor進(jìn)行排序掺逼,然后注冊
sortPostProcessors(internalPostProcessors, beanFactory);
registerBeanPostProcessors(beanFactory, internalPostProcessors);
//重新注冊用于將內(nèi)部bean檢測為ApplicationListener的后處理器,
//將其移到處理器鏈的末尾(for picking up proxies etc)吕喘。
beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));
}
BeanPostProcessor按優(yōu)先級分為PriorityOrdered赘那,Ordered和其他的氯质,對他們分別進(jìn)行以下操作募舟。
- 先beanFactory.getBean進(jìn)行實(shí)例化闻察,
- 再使用sortPostProcessors() 進(jìn)行排序
- 最后registerBeanPostProcessors()進(jìn)行注冊拱礁。
所以辕漂,優(yōu)先級高Bpp的先被實(shí)例化呢灶,而優(yōu)先級低的Bpp在實(shí)例化時(shí)可以應(yīng)用優(yōu)先級高的(不過同級或優(yōu)先級低的不行钉嘹,因?yàn)檫€沒初始化)
那問題來了,BeanPostProcessor依賴的Bean, 導(dǎo)致其提前初始化跋涣,有什么后果缨睡?<br />根據(jù)BeanPostProcessor 的注冊過程陈辱,我們可以知道如果Bean 被優(yōu)先級高的Bpp依賴而導(dǎo)致提前初始化奖年,那么這個(gè)Bean 就不會在被其他優(yōu)先級低的Bpp處理(Bpp還沒初始化)性置; 例如典型的例子就是 auto-proxy拾并,因?yàn)?lt;br />AutowiredAnnotationBeanPostProcessor(用于處理依賴注入的Bpp)的優(yōu)先級是PriorityOrdered, 而AnnotationAwareAspectJAutoProxyCreator(用于 auto-proxy)的優(yōu)先級是Ordered嗅义, 如果Bean 在AnnotationAwareAspectJAutoProxyCreator注冊前實(shí)例化,那么這個(gè)Bean依賴注入時(shí)之碗,就不會以代理類(JDK代理或CGlib代理)的形式注入
解決的方法: 1. 對依賴的Bean 添加@Lazy(懶加載注解),2. 使用注解@DependsOn
五季希、Spring 整合Shiro 引起事務(wù)失敗問題解答
1.問題原因
- 在DbShiroRealm 中注入U(xiǎn)serService時(shí),未加懶加載注解@Lazy 式塌,打印的Bean 實(shí)例化順序
- Shiro 的配置
@Configuration
public class ShiroConfig {
/**
* 驗(yàn)證過濾器
*
* @return
*/
@Bean
public JwtAuthFilter jwtAuthFilter() {
JwtAuthFilter authFilter = new JwtAuthFilter();
return authFilter;
}
/**
* 不加入Servlet Filter中
*
* @return
*/
@Bean
public FilterRegistrationBean filterRegistrationBean() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setFilter(jwtAuthFilter());
filterRegistrationBean.setEnabled(false);
return filterRegistrationBean;
}
@Bean
public Realm dbShiroRealm() {
DbShiroRealm myShiroRealm = new DbShiroRealm();
myShiroRealm.setCredentialsMatcher(new HashedCredentialsMatcher(Sha256Hash.ALGORITHM_NAME));
return myShiroRealm;
}
@Bean
public Realm jwtShiroRealm() {
JwtShiroRealm myShiroRealm = new JwtShiroRealm();
myShiroRealm.setCredentialsMatcher(new JwtCredentialsMatcher());
return myShiroRealm;
}
/**
* 關(guān)閉session
*
* @return
*/
@Bean
public SessionStorageEvaluator sessionStorageEvaluator() {
DefaultWebSessionStorageEvaluator sessionStorageEvaluator = new DefaultWebSessionStorageEvaluator();
sessionStorageEvaluator.setSessionStorageEnabled(false);
return sessionStorageEvaluator;
}
/**
* 驗(yàn)證器
*
* @return
*/
@Bean
public Authenticator authenticator() {
MyModularRealmAuthenticator authenticator = new MyModularRealmAuthenticator();
return authenticator;
}
/**
* 設(shè)置過濾器鏈
*/
@Bean("shiroFilter")
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
//設(shè)置登陸的url
factoryBean.setLoginUrl("/api/admin/user/signIn");
factoryBean.setSecurityManager(securityManager);
//設(shè)置過濾器
Map<String, Filter> filters = factoryBean.getFilters();
filters.put("authToken", jwtAuthFilter());
factoryBean.setFilters(filters);
// 自定義url規(guī)則使用LinkedHashMap有序Map
Map<String, String> filterMap = new LinkedHashMap<>();
// 兩個(gè)url規(guī)則都可以匹配同一個(gè)url,只執(zhí)行第一個(gè)
filterMap.put("/api/admin/user/signIn", "anon");
filterMap.put("/api/admin/**", "authToken");
factoryBean.setFilterChainDefinitionMap(filterMap);
return factoryBean;
}
}
- 說明
- ShiroFilterFactoryBean 是FactoryBean偏窝,且實(shí)現(xiàn)了BeanPostProcessor接口
- 在實(shí)例化時(shí),依賴securityManager
- 而SecurityManager 在Shiro自動(dòng)化配置中祭往,又依賴于Realm 類的
- 所以實(shí)例化的順序?yàn)椋篠hiroFilterFactoryBean > securityManager > dbShiroRealm > userServiceImpl
- 此時(shí)代理事務(wù)配置 ProxyTransactionManagementConfiguration還沒實(shí)例化
2.解決方法
- 采用懶加載的方式
@Lazy
@Autowired
private UserService userService;
- 使用注解@DependsOn
說明:
既然我們再創(chuàng)建dbShiroRealm時(shí)伦意,需要依賴注入userServiceImpl而且希望注入的是事務(wù)代理增強(qiáng)類硼补,但是事務(wù)代理增強(qiáng)的配置在 ProxyTransactionManagementConfiguration類中,那么我們在實(shí)例化dbShiroRealm時(shí)已骇,先讓配置類實(shí)例化离钝,這樣就可以享受到事務(wù)代理了疾捍。
//@DependsOn中的Bean 名稱可以在ProxyTransactionManagementConfiguration中找
@Bean
@DependsOn("org.springframework.transaction.config.internalTransactionAdvisor")
public Realm dbShiroRealm() {
DbShiroRealm myShiroRealm = new DbShiroRealm();
myShiroRealm.setCredentialsMatcher(new HashedCredentialsMatcher(Sha256Hash.ALGORITHM_NAME));
return myShiroRealm;
}
六 奈辰、參考
- https://zhuanlan.zhihu.com/p/30112785 (bean 初始化順序)
- https://www.concretepage.com/spring/spring-bean-life-cycle-tutorial
- https://juejin.im/post/5ab30714f265da237b21fbcc
- https://blog.csdn.net/weixin_43364172/article/details/84630770
- https://www.iteye.com/blog/jinnianshilongnian-1894973
- https://blog.csdn.net/finalcola/article/details/81197584
- https://blog.csdn.net/f641385712/article/details/89737791