Spring后處理器詳細(xì)介紹
一、概述
Spring的后處理器是Spring對外開發(fā)的重要擴展點、允許我們介入到Bean的整個實例化流程中來, 以達(dá)到動態(tài)注冊 BeanDefinition, 動態(tài)修改BeanDefinition, 以及動態(tài)修改Bean的作用。Spring主要有兩種后處理器:
- BeanFactoryPostProcessor:Bean工廠后處理器, 在BeanDefinitionMap填充完畢, Bean實例化之前執(zhí)行;
- BeanPostProcessor:Bean后處理器, 一般在Bean實例化之后, 填充到單例池singletonObjects之前執(zhí)行。
Bean工廠后處理器-BeanFactoryPostProcessor BeanFactoryPostProcessor是一個接口規(guī)范, 實現(xiàn)了該接口的類只要交由Spring容器管理的話, 那么Spring就會回調(diào)該接口的方法, 用于對BeanDefinition注冊和修改的功能。
BeanFactoryPostProcessor定義如下:
public interface BeanFactoryPostProcessor{ void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) ; }
二、案例演示
①:注冊BeanDefinition
1.編寫MyBeanFactoryPostProcessor類,實現(xiàn)BeanFactoryPostProcessor接口,并實現(xiàn)接口方法
package com.tangyuan.processor; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.RootBeanDefinition; public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { System.out.println("beanDefinitionMap填充完畢后回調(diào)該方法"); //不在主xml文件中進行配置bean //動態(tài)注冊Beandifinition BeanDefinition beanDefinition=new RootBeanDefinition(); beanDefinition.setBeanClassName("com.tangyuan.dao.impl.PeresonDaoImpl"); System.out.println(beanFactory); //org.springframework.beans.factory.support.DefaultListableBeanFactory@737996a0: defining beans //強轉(zhuǎn)成DefaultListableBeanFactory DefaultListableBeanFactory defaultListableBeanFactory= (DefaultListableBeanFactory) beanFactory; defaultListableBeanFactory.registerBeanDefinition("PersonDao",beanDefinition); } }
2.測試
//創(chuàng)建ApplicationContext,加載配置文件,實例化容器 ClassPathXmlApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml"); PeresonDao bean = applicationContext.getBean(PeresonDao.class); System.out.println(bean);//com.tangyuan.dao.impl.PeresonDaoImpl@6ddf90b0
②:注冊BeanDefinition
Spring提供了一個BeanFactoryPostProcessor的子接BeanDefinitionRegistryPostProcessor專門用于注冊BeanDefinition操作
public class MyBeanFactoryPostProcessor2 implements BeanDefinitionRegistryPostProcessor{ @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException{} @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException{ BeanDefinition beanDefinition=new RootBeanDefinition() ; beanDefinition.setBeanClassName("com.tangyuan.dao.UserDaoImp12"); beanDefinitionRegistry.registerBeanDefinition("userDao2",beanDefinition) ; } }
案例演示:
1.編寫MyBeanDefinitionRegistryPostProcessor類,實現(xiàn)BeanDefinitionRegistryPostProcessor接口,并實現(xiàn)接口方法
package com.tangyuan.processor; import com.alibaba.druid.support.spring.stat.SpringStatUtils; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; import org.springframework.beans.factory.support.RootBeanDefinition; public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor { @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException { System.out.println("MyBeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry方法"); //向容器當(dāng)中注冊BeanDefinition BeanDefinition beanDefinition=new RootBeanDefinition(); beanDefinition.setBeanClassName("com.tangyuan.dao.impl.PeresonDaoImpl"); beanDefinitionRegistry.registerBeanDefinition("PersonDao",beanDefinition); } @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException { System.out.println("MyBeanDefinitionRegistryPostProcessor的postProcessBeanFactory方法"); } }
2.將MyBeanDefinitionRegistryPostProcessor類在xml文件中進行配置
<bean class="com.tangyuan.processor.MyBeanDefinitionRegistryPostProcessor"></bean>
3.測試
//創(chuàng)建ApplicationContext,加載配置文件,實例化容器 ClassPathXmlApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml"); PeresonDao bean = applicationContext.getBean(PeresonDao.class); System.out.println(bean);//com.tangyuan.dao.impl.PeresonDaoImpl@5a8e6209
ps:方法的執(zhí)行順序
1.MyBeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry方法
2.MyBeanDefinitionRegistryPostProcessor的postProcessBeanFactory方法
3.MyBeanFactoryPostProcessor的postProcessBeanFactory方法
③:使用Spring的BeanFactoryPostProcessor擴展點完成自定義注解掃描
要求如下:
- 自定義@MyComponent注解, 使用在類上;
- 使用資料中提供好的包掃描器工具BaseClassScanUtils完成指定包的類掃描;
- 自定義BeanFactoryPostProcessor完成注解@MyComponent的解析, 解析后最終被Spring管理。
1.創(chuàng)建一個OtherBean類,并定義@MyComponent注解
package com.tangyuan.beans; import com.tangyuan.anno.MyComponent; @MyComponent("OtherBean") public class OtherBean { }
package com.tangyuan.anno; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface MyComponent { String value(); }
2.BaseClassScanUtils 幫助類編寫
package com.tangyuan.utils; import com.tangyuan.anno.MyComponent; import org.springframework.core.io.Resource; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.core.io.support.ResourcePatternResolver; import org.springframework.core.type.classreading.CachingMetadataReaderFactory; import org.springframework.core.type.classreading.MetadataReader; import org.springframework.core.type.classreading.MetadataReaderFactory; import org.springframework.util.ClassUtils; import java.lang.annotation.Annotation; import java.util.HashMap; import java.util.List; import java.util.Map; public class BaseClassScanUtils { //設(shè)置資源規(guī)則 private static final String RESOURCE_PATTERN = "/**/*.class"; public static Map<String, Class> scanMyComponentAnnotation(String basePackage) { //創(chuàng)建容器存儲使用了指定注解的Bean字節(jié)碼對象 Map<String, Class> annotationClassMap = new HashMap<String, Class>(); //spring工具類,可以獲取指定路徑下的全部類 ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver(); try { String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + ClassUtils.convertClassNameToResourcePath(basePackage) + RESOURCE_PATTERN; Resource[] resources = resourcePatternResolver.getResources(pattern); //MetadataReader 的工廠類 MetadataReaderFactory refractory = new CachingMetadataReaderFactory(resourcePatternResolver); for (Resource resource : resources) { //用于讀取類信息 MetadataReader reader = refractory.getMetadataReader(resource); //掃描到的class String classname = reader.getClassMetadata().getClassName(); Class<?> clazz = Class.forName(classname); //判斷是否屬于指定的注解類型 if(clazz.isAnnotationPresent(MyComponent.class)){ //獲得注解對象 MyComponent annotation = clazz.getAnnotation(MyComponent.class); //獲得屬value屬性值 String beanName = annotation.value(); //判斷是否為"" if(beanName!=null&&!beanName.equals("")){ //存儲到Map中去 annotationClassMap.put(beanName,clazz); continue; } //如果沒有為"",那就把當(dāng)前類的類名作為beanName annotationClassMap.put(clazz.getSimpleName(),clazz); } } } catch (Exception exception) { } return annotationClassMap; } public static void main(String[] args) { Map<String, Class> stringClassMap = scanMyComponentAnnotation("com.tangyuan"); System.out.println(stringClassMap); } }
3.編寫MyComponentBeanFactoryPostProcessor類,并實現(xiàn)BeanDefinitionRegistryPostProcessor接口
package com.tangyuan.processor; import com.tangyuan.utils.BaseClassScanUtils; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; import org.springframework.beans.factory.support.RootBeanDefinition; import java.util.Map; public class MyComponentBeanFactoryPostProcessor implements BeanDefinitionRegistryPostProcessor { @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException { //通過掃描工具去掃描指定包及其包下的所有類,收集使用@MyComponent注解的類 Map<String, Class> map = BaseClassScanUtils.scanMyComponentAnnotation( "com.tangyuan" ); //循環(huán)遍歷,組裝BeanDefinition進行注冊 map.forEach((beanName,clazz)->{ //獲取BeanClassName String name = clazz.getName();//com.tangyuan.beans.OtherBean //創(chuàng)建BeanDefinition BeanDefinition beanDefinition=new RootBeanDefinition(); beanDefinition.setBeanClassName(name); //注冊 beanDefinitionRegistry.registerBeanDefinition(name,beanDefinition); }); } @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException { } }
4.在xml文件編寫MyComponentBeanFactoryPostProcessor類的bean
<bean class="com.tangyuan.processor.MyComponentBeanFactoryPostProcessor"></bean>
5.測試
OtherBean bean1 = applicationContext.getBean(OtherBean.class); System.out.println(bean1);//com.tangyuan.beans.OtherBean@1877ab81
6.編寫一個BookBean類,并實現(xiàn)@MyComponent注解
package com.tangyuan.beans; import com.tangyuan.anno.MyComponent; import org.springframework.stereotype.Component; @MyComponent("BookBean") public class BookBean { }
7.再次測試
//創(chuàng)建ApplicationContext,加載配置文件,實例化容器 ClassPathXmlApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml"); BookBean bean2 = applicationContext.getBean(BookBean.class); System.out.println(bean2);//com.tangyuan.beans.BookBean@1877ab81
三、Bean的后處理器BeanPostProcessor
Bean被實例化后, 到最終緩存到名為singletonObjects單例池之前, 中間會經(jīng)過Bean的初始化過程, 例如:屬性的 填充、初始方法init的執(zhí)行等, 其中有一個對外進行擴展的點BeanPostProcessor,我們稱為Bean后處理。跟上面的 Bean工廠后處理器相似, 它也是一個接口, 實現(xiàn)了該接口并被容器管理的BeanPostProcessor, 會在流程節(jié)點上被 Spring自動調(diào)用。
BeanPostProcessor的接口定義如下:
public interface BeanPostProcessor{ @Nullable //在屬性注入完畢, init初始化方法執(zhí)行之前被回調(diào) default Object postProcesBeforeInitialization(Object bean, String bean Name)throws BeansException{ return bean; } //在初始化方法執(zhí)行之后, 被添加到單例池singletonObjects之前被回調(diào) @Nullable default Object postProcessAfterInitialization(Object bean, String bean Name) throws BeansException{ return bean; } }
案例:對Bean方法進行執(zhí)行時間日志增強
要求如下:
- Bean的方法執(zhí)行之前控制臺打印當(dāng)前時間;
- Bean的方法執(zhí)行之后控制臺打印當(dāng)前時間。
分析:
- 對方法進行增強主要就是代理設(shè)計模式和包裝設(shè)計模式;
- 由于Bean方法不確定, 所以使用動態(tài)代理在運行期間執(zhí)行增強操作;
- 在Bean實例創(chuàng)建完畢后, 進入到單例池之前, 使用Proxy代替真是的目標(biāo)Bean
思路:編寫B(tài)eanPostProcessor, 增強邏輯編寫在after方法中
public Object post Process After Initialization(Object bean,String beanName) throws BeansException{ //Bean進行動態(tài)代理,返回的是Proxy代理對象 Object proxy Bean=Proxy.newProxyInstance(bean.getClass() .getClassLoader() , bean.getClass() .getInterfaces() , (Object proxy, Method method,Object[] args) ->{ long start=System.currentTimeMillis() ; system.out.println("開始時間:"+new Date(start) ) ; //執(zhí)行目標(biāo)方法BIN Object result=method.invoke(bean,args) ; long end=System.currentTimeMillis() ; System.out.println("結(jié)束時間:"+new Date(end) ) ; return result; }); //返回代理對象 return proxy Bean; }
實現(xiàn)代碼如下:
1.編寫一個TimeLogBeanPostProcessor類,接口BeanPostProcessor實現(xiàn),方法也實現(xiàn)
package com.tangyuan.processor; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.Date; public class TimeLogBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return null; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { //使用動態(tài)代理對目標(biāo)Bean進行增強,返回proxy對象,進而存儲到單例池singletonObjects中 Object beanProxy= Proxy.newProxyInstance( bean.getClass().getClassLoader(), bean.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //1.輸出開始時間 System.out.println("方法:"+method.getName()+"-開始時間:"+new Date()); //2.執(zhí)行目標(biāo)方法 Object invoke = method.invoke(bean, args); //3.輸出結(jié)束時間 System.out.println("方法:"+method.getName()+"-結(jié)束時間:"+new Date()); return invoke; } } ); return beanProxy; } }
2.在xml文件上完成對TimeLogBeanPostProcessor類的配置
<bean class="com.tangyuan.processor.TimeLogBeanPostProcessor"></bean>
3.編寫方法
package com.tangyuan.dao; public interface UserDao { void show(); }
package com.tangyuan.dao.impl; import com.tangyuan.dao.UserDao; import org.springframework.beans.factory.InitializingBean; public class UserDaoImpl implements UserDao , InitializingBean { public UserDaoImpl() { System.out.println("UserDao實例化"); } public void init(){ System.out.println("init初始化方法執(zhí)行"); } @Override public void afterPropertiesSet() throws Exception { System.out.println("屬性設(shè)置之后執(zhí)行~~~~"); } @Override public void show() {//ctrl+Alt+t try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("show....."); } }
4.測試
//創(chuàng)建ApplicationContext,加載配置文件,實例化容器
ClassPathXmlApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao dao = (UserDao) applicationContext.getBean("userDao");
dao.show();
方法:show-開始時間:Tue Nov 29 10:21:31 GMT+08:00 2022
show.....
方法:show-結(jié)束時間:Tue Nov 29 10:21:31 GMT+08:00 2022
四、Spring ioc整體流程總結(jié)
到此這篇關(guān)于Spring后處理器詳細(xì)介紹的文章就介紹到這了,更多相關(guān)Spring后處理器內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
使用RequestBodyAdvice實現(xiàn)對Http請求非法字符過濾
這篇文章主要介紹了使用RequestBodyAdvice實現(xiàn)對Http請求非法字符過濾的操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-06-06詳解hibernate雙向多對多關(guān)聯(lián)映射XML與注解版
本篇文章主要介紹了詳解hibernate雙向多對多關(guān)聯(lián)映射XML與注解版,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-05-05一文帶你深入了解Java中延時任務(wù)的實現(xiàn)
延時任務(wù)相信大家都不陌生,在現(xiàn)實的業(yè)務(wù)中應(yīng)用場景可以說是比比皆是。這篇文章主要為大家介紹幾種實現(xiàn)延時任務(wù)的辦法,感興趣的可以了解一下2022-11-11Java利用Geotools實現(xiàn)不同坐標(biāo)系之間坐標(biāo)轉(zhuǎn)換
GeoTools 是一個開源的 Java GIS 工具包,可利用它來開發(fā)符合標(biāo)準(zhǔn)的地理信息系統(tǒng)。本文將利用工具包Geotools實現(xiàn)不同坐標(biāo)系之間坐標(biāo)轉(zhuǎn)換,感興趣的可以了解一下2022-08-08Struts2學(xué)習(xí)手冊之文件上傳基礎(chǔ)教程
Struts2提供的文件上傳下載機制十分簡便,使得我們寫很少的代碼,下面這篇文章主要給大家介紹了關(guān)于Struts2學(xué)習(xí)手冊之文件上傳的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2018-05-05SpringBoot整合EasyExcel進行大數(shù)據(jù)處理的方法詳解
EasyExcel是一個基于Java的簡單、省內(nèi)存的讀寫Excel的開源項目。在盡可能節(jié)約內(nèi)存的情況下支持讀寫百M的Excel。本文將在SpringBoot中整合EasyExcel進行大數(shù)據(jù)處理,感興趣的可以了解一下2022-05-05