目錄
SpringBean實例化流程
Spring的后處理器
Bean工廠后處理器
Bean后處理器
SpringBean實例化流程
Spring容器在進行初始化時,會將xml配置的<bean>的信息封裝成一個BeanDefinition對象,所有的BeanDefinition存儲到一個名為beanDefinitionMap的Map集合中去,Spring框架在對該Map進行遍歷,使用反射創建Bean實例對象,創建好的Bean對象存儲在一個名為sinaletonObiects的Map集合中,當調用getBean方法時則最終從該Map集合中取出Bean實例對象返回
對該方法進行斷點觀察
因此我們可以總結如下流程圖
Spring的后處理器
Spring的后處理器是Spring對外開發的重要擴展點,允許我們介入到Bean的整個實例化流程中來,以達到動態注冊BeanDefinition,動態修改BeanDefinition,以及動態修改Bean的作用。Spring主要有兩種后處理器:
- BeanFactoryPostProcessor:Bean工廠后外理器,在BeanDefinitionMap填充完畢,Bean實例化之前執行。
- BeanPostProcessor:Bean后處理器,一般在Bean實例化之后,填充到單例池singletonObjects之前執行。
Bean工廠后處理器
我們需要實現BeanFactoryPostProcessor接口,然后重寫其方法,該方法中的參數實際上就是Spring容器,我們可以通過該容器獲取BeanDefinition信息,然后對這些信息進行修改,在下面代碼中,我們修改了UserService的全路徑,這會導致在實例化Bean時通過反射拿到的實際上時UserDao類。
public class MyBeanFactoryProcessor implements BeanFactoryPostProcessor {@Override//在Map加載完成后執行public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {//beanFactory無法一次獲取全部的Map信息,只能通過key獲取BeanDefinition beanDefinition = beanFactory.getBeanDefinition("userService");System.out.println("當前的Bean的全路徑"+beanDefinition.getBeanClassName());beanDefinition.setBeanClassName("com.zmt.dao.impl.UserDaoImpl");}
}
配置完成不代表生效,我們需要將該類交給Spring管理,然后由Spring來回調該方法,因此需要修改xml文件
<beans><bean id="userService" class="com.zmt.service.impl.UserServiceImpl"></bean><bean id="userDao" class="com.zmt.dao.impl.UserDaoImpl"></bean><!-- 添加為Bean對象 --><bean class="com.zmt.processor.MyBeanFactoryProcessor"></bean>
</beans>
測試代碼如下
public class ApplicationContextTest {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");Object userService = context.getBean("userService");System.out.println(userService);}
}
運行結果如下
當前的Bean的全路徑com.zmt.service.impl.UserServiceImpl
com.zmt.dao.impl.UserDaoImpl@5649fd9b
如果我們需要向Map中添加BeanDefinition數據,在創建完BeanDefinition之后無法將其注冊到Map中,需要實現該接口的子接口BeanDefinitionRegistryPostProcessor。下面我們創建一個PersonDao接口以及實現類,但是不在xml文件中定義,通過Bean工廠后處理器添加在Map當中,測試能否getBean時獲取到對應類
<beans><bean id="userService" class="com.zmt.service.impl.UserServiceImpl"></bean><bean id="userDao" class="com.zmt.dao.impl.UserDaoImpl"></bean><bean class="com.zmt.processor.MyBeanFactoryRegistryProcessor"></bean>
</beans>
public class MyBeanFactoryRegistryProcessor implements BeanDefinitionRegistryPostProcessor {@Overridepublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {RootBeanDefinition beanDefinition = new RootBeanDefinition();//設置全路徑beanDefinition.setBeanClassName("com.zmt.dao.impl.PersonDaoImpl");//添加到Map中beanDefinitionRegistry.registerBeanDefinition("personDao",beanDefinition);}//該方式是父接口中的方法,我們可以不編寫業務代碼@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {}
}
測試代碼
public class ApplicationContextTest {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");PersonDaoImpl personDao = context.getBean(PersonDaoImpl.class);System.out.println(personDao);}
}
/**
* 運行結果如下
* com.zmt.dao.impl.PersonDaoImpl@64bfbc86
*/
此時,我們可能存在一個疑惑,那就是如果配置了多個Bean工廠后處理器,那么執行順序應該是什么?接下來我們對其進行測試
public class MyBeanFactoryProcessor implements BeanFactoryPostProcessor {@Override//在Map加載完成后執行public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {//beanFactory無法一次獲取全部的Map信息,只能通過key獲取System.out.println("執行MyBeanFactoryProcessor中的postProcessBeanFactory方法");}
}public class MyBeanFactoryRegistryProcessor implements BeanDefinitionRegistryPostProcessor {@Overridepublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {System.out.println("執行MyBeanFactoryRegistryProcessor中的postProcessBeanDefinitionRegistry方法");}@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {System.out.println("執行MyBeanFactoryRegistryProcessor中的postProcessBeanFactory方法");}
}
執行結果如下
執行MyBeanFactoryRegistryProcessor中的postProcessBeanDefinitionRegistry方法
執行MyBeanFactoryRegistryProcessor中的postProcessBeanFactory方法
執行MyBeanFactoryProcessor中的postProcessBeanFactory方法
由此我們可以得出結論,優先執行BeanFactoryProcessor的子類獨有方法,再執行子類中繼承來的父類方法,最后執行BeanFactory中的方法。
因此,我們可以總結Bean工廠后處理器的執行時機在Spring啟動流程中如下
Bean后處理器
Bean被實例化后,到最終緩存到名為singletonObjects單例池之前,中間會經過Bean的初始化過程,例如: 屬性的填充、初始方法init的執行等,其中有一個對外進行擴展的點BeanPostProcessor,我們稱為Bean后處理。跟Bean工廠后處理器相似,它也是一個接口,實現了該接口并被容器管理的BeanPostProcessor,會在流程節點上被Spring自動調用。
Bean后處理器中有兩個方法分別為
要實現Bean后處理器,需要實現BeanPostProcessor接口,重寫需要要實現的方法。接下來編寫Bean后處理器來測試Bean后處理器兩個方法的執行時機
public class MyBeanPostProcessor implements BeanPostProcessor {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.println(beanName+":Bean后處理器初始化前方法");return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {System.out.println(beanName+":Bean后處理器初始化后方法");return bean;}
}
public class UserServiceImpl implements UserService , InitializingBean {public UserServiceImpl() {System.out.println("userService調用無參構造器");}private void init(){System.out.println("userService執行init方法");}//該方法由beanFactory來調用,set注入public void setUserDao(UserDao userDao){System.out.println("由bean工廠調用該set方法");}@Overridepublic void afterPropertiesSet() throws Exception {System.out.println("userService執行屬性注入后方法");}
}
<beans><bean id="userService" class="com.zmt.service.impl.UserServiceImpl" init-method="init"><property name="userDao" ref="userDao"></property></bean><bean id="userDao" class="com.zmt.dao.impl.UserDaoImpl"></bean><bean class="com.zmt.processor.MyBeanFactoryProcessor"></bean><bean class="com.zmt.processor.MyBeanPostProcessor"></bean></beans>
運行結果如下
執行MyBeanFactoryProcessor中的postProcessBeanFactory方法
userService調用無參構造器
userDao:Bean后處理器初始化前方法
userDao:Bean后處理器初始化后方法
由bean工廠調用該set方法
userService:Bean后處理器初始化前方法
userService執行屬性注入后方法
userService執行init方法
userService:Bean后處理器初始化后方法
在Spring中,大量采用Bean后處理器對Bean對象進行加強處理,通常會對bean對象做代理,下面就是一個對userService類對象進行進行加強,在執行方法時統計該方法執行時間
public class UserServiceImpl implements UserService {private String name;public void setName(String name) {this.name = name;}@Overridepublic void print() {try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("方法print開始執行");System.out.println("name:"+name);}
}
public class MyTimeLogBeanPostProcessor implements BeanPostProcessor {/*** 后處理器前初始化方法。作用時間為bean實例化完成之后,可以對bean對象初始化賦值*/@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {if (bean instanceof UserService){((UserSerivce) bean).setName("張三");}return bean;}/*** 后處理器后處理方法。作用時間為bean添加到單例池之前,主要作用是對bean對象做增強*/@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {Object proxyInstance = Proxy.newProxyInstance(bean.getClass().getClassLoader(),bean.getClass().getInterfaces(),(proxy, method, args) -> {System.out.println("記錄時間" + new Date());Object invoke = method.invoke(bean, args);System.out.println("記錄停止" + new Date());return invoke;});return proxyInstance;}
}
執行結果如下
方法print開始執行Fri Dec 08 17:52:46 CST 2023
方法print開始執行
name:張三
方法print執行結束Fri Dec 08 17:52:49 CST 2023
被加強后的bean會被加載到單例池中,并且無論去執行哪些方法都會執行時間日志輸出。
總結Bean處理器執行流程如下