Spring后處理器詳細(xì)介紹
一、概述
Spring的后處理器是Spring對(duì)外開發(fā)的重要擴(kuò)展點(diǎn)、允許我們介入到Bean的整個(gè)實(shí)例化流程中來, 以達(dá)到動(dòng)態(tài)注冊(cè) BeanDefinition, 動(dòng)態(tài)修改BeanDefinition, 以及動(dòng)態(tài)修改Bean的作用。Spring主要有兩種后處理器:
- BeanFactoryPostProcessor:Bean工廠后處理器, 在BeanDefinitionMap填充完畢, Bean實(shí)例化之前執(zhí)行;
- BeanPostProcessor:Bean后處理器, 一般在Bean實(shí)例化之后, 填充到單例池singletonObjects之前執(zhí)行。
Bean工廠后處理器-BeanFactoryPostProcessor BeanFactoryPostProcessor是一個(gè)接口規(guī)范, 實(shí)現(xiàn)了該接口的類只要交由Spring容器管理的話, 那么Spring就會(huì)回調(diào)該接口的方法, 用于對(duì)BeanDefinition注冊(cè)和修改的功能。
BeanFactoryPostProcessor定義如下:
public interface BeanFactoryPostProcessor{
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) ;
}二、案例演示
①:注冊(cè)BeanDefinition
1.編寫MyBeanFactoryPostProcessor類,實(shí)現(xiàn)BeanFactoryPostProcessor接口,并實(shí)現(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文件中進(jìn)行配置bean
//動(dòng)態(tài)注冊(cè)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
//強(qiáng)轉(zhuǎn)成DefaultListableBeanFactory
DefaultListableBeanFactory defaultListableBeanFactory= (DefaultListableBeanFactory) beanFactory;
defaultListableBeanFactory.registerBeanDefinition("PersonDao",beanDefinition);
}
}2.測(cè)試
//創(chuàng)建ApplicationContext,加載配置文件,實(shí)例化容器
ClassPathXmlApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
PeresonDao bean = applicationContext.getBean(PeresonDao.class);
System.out.println(bean);//com.tangyuan.dao.impl.PeresonDaoImpl@6ddf90b0②:注冊(cè)BeanDefinition
Spring提供了一個(gè)BeanFactoryPostProcessor的子接BeanDefinitionRegistryPostProcessor專門用于注冊(cè)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類,實(shí)現(xiàn)BeanDefinitionRegistryPostProcessor接口,并實(shí)現(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)中注冊(cè)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文件中進(jìn)行配置
<bean class="com.tangyuan.processor.MyBeanDefinitionRegistryPostProcessor"></bean>
3.測(cè)試
//創(chuàng)建ApplicationContext,加載配置文件,實(shí)例化容器
ClassPathXmlApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
PeresonDao bean = applicationContext.getBean(PeresonDao.class);
System.out.println(bean);//com.tangyuan.dao.impl.PeresonDaoImpl@5a8e6209ps:方法的執(zhí)行順序
1.MyBeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry方法
2.MyBeanDefinitionRegistryPostProcessor的postProcessBeanFactory方法
3.MyBeanFactoryPostProcessor的postProcessBeanFactory方法
③:使用Spring的BeanFactoryPostProcessor擴(kuò)展點(diǎn)完成自定義注解掃描
要求如下:
- 自定義@MyComponent注解, 使用在類上;
- 使用資料中提供好的包掃描器工具BaseClassScanUtils完成指定包的類掃描;
- 自定義BeanFactoryPostProcessor完成注解@MyComponent的解析, 解析后最終被Spring管理。
1.創(chuàng)建一個(gè)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)建容器存儲(chǔ)使用了指定注解的Bean字節(jié)碼對(duì)象
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)){
//獲得注解對(duì)象
MyComponent annotation = clazz.getAnnotation(MyComponent.class);
//獲得屬value屬性值
String beanName = annotation.value();
//判斷是否為""
if(beanName!=null&&!beanName.equals("")){
//存儲(chǔ)到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類,并實(shí)現(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進(jìn)行注冊(cè)
map.forEach((beanName,clazz)->{
//獲取BeanClassName
String name = clazz.getName();//com.tangyuan.beans.OtherBean
//創(chuàng)建BeanDefinition
BeanDefinition beanDefinition=new RootBeanDefinition();
beanDefinition.setBeanClassName(name);
//注冊(cè)
beanDefinitionRegistry.registerBeanDefinition(name,beanDefinition);
});
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
}
}4.在xml文件編寫MyComponentBeanFactoryPostProcessor類的bean
<bean class="com.tangyuan.processor.MyComponentBeanFactoryPostProcessor"></bean>
5.測(cè)試
OtherBean bean1 = applicationContext.getBean(OtherBean.class); System.out.println(bean1);//com.tangyuan.beans.OtherBean@1877ab81
6.編寫一個(gè)BookBean類,并實(shí)現(xiàn)@MyComponent注解
package com.tangyuan.beans;
import com.tangyuan.anno.MyComponent;
import org.springframework.stereotype.Component;
@MyComponent("BookBean")
public class BookBean {
}7.再次測(cè)試
//創(chuàng)建ApplicationContext,加載配置文件,實(shí)例化容器
ClassPathXmlApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
BookBean bean2 = applicationContext.getBean(BookBean.class);
System.out.println(bean2);//com.tangyuan.beans.BookBean@1877ab81三、Bean的后處理器BeanPostProcessor
Bean被實(shí)例化后, 到最終緩存到名為singletonObjects單例池之前, 中間會(huì)經(jīng)過Bean的初始化過程, 例如:屬性的 填充、初始方法init的執(zhí)行等, 其中有一個(gè)對(duì)外進(jìn)行擴(kuò)展的點(diǎn)BeanPostProcessor,我們稱為Bean后處理。跟上面的 Bean工廠后處理器相似, 它也是一個(gè)接口, 實(shí)現(xiàn)了該接口并被容器管理的BeanPostProcessor, 會(huì)在流程節(jié)點(diǎn)上被 Spring自動(dòng)調(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;
}
}案例:對(duì)Bean方法進(jìn)行執(zhí)行時(shí)間日志增強(qiáng)
要求如下:
- Bean的方法執(zhí)行之前控制臺(tái)打印當(dāng)前時(shí)間;
- Bean的方法執(zhí)行之后控制臺(tái)打印當(dāng)前時(shí)間。
分析:
- 對(duì)方法進(jìn)行增強(qiáng)主要就是代理設(shè)計(jì)模式和包裝設(shè)計(jì)模式;
- 由于Bean方法不確定, 所以使用動(dòng)態(tài)代理在運(yùn)行期間執(zhí)行增強(qiáng)操作;
- 在Bean實(shí)例創(chuàng)建完畢后, 進(jìn)入到單例池之前, 使用Proxy代替真是的目標(biāo)Bean
思路:編寫B(tài)eanPostProcessor, 增強(qiáng)邏輯編寫在after方法中
public Object post Process After Initialization(Object bean,String beanName) throws BeansException{
//Bean進(jìn)行動(dòng)態(tài)代理,返回的是Proxy代理對(duì)象
Object proxy Bean=Proxy.newProxyInstance(bean.getClass() .getClassLoader() ,
bean.getClass() .getInterfaces() ,
(Object proxy, Method method,Object[] args) ->{
long start=System.currentTimeMillis() ;
system.out.println("開始時(shí)間:"+new Date(start) ) ;
//執(zhí)行目標(biāo)方法BIN
Object result=method.invoke(bean,args) ;
long end=System.currentTimeMillis() ;
System.out.println("結(jié)束時(shí)間:"+new Date(end) ) ;
return result;
});
//返回代理對(duì)象
return proxy Bean;
}實(shí)現(xiàn)代碼如下:
1.編寫一個(gè)TimeLogBeanPostProcessor類,接口BeanPostProcessor實(shí)現(xiàn),方法也實(shí)現(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 {
//使用動(dòng)態(tài)代理對(duì)目標(biāo)Bean進(jìn)行增強(qiáng),返回proxy對(duì)象,進(jìn)而存儲(chǔ)到單例池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.輸出開始時(shí)間
System.out.println("方法:"+method.getName()+"-開始時(shí)間:"+new Date());
//2.執(zhí)行目標(biāo)方法
Object invoke = method.invoke(bean, args);
//3.輸出結(jié)束時(shí)間
System.out.println("方法:"+method.getName()+"-結(jié)束時(shí)間:"+new Date());
return invoke;
}
}
);
return beanProxy;
}
}2.在xml文件上完成對(duì)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實(shí)例化");
}
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.測(cè)試
//創(chuàng)建ApplicationContext,加載配置文件,實(shí)例化容器
ClassPathXmlApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao dao = (UserDao) applicationContext.getBean("userDao");
dao.show();
方法:show-開始時(shí)間:Tue Nov 29 10:21:31 GMT+08:00 2022
show.....
方法:show-結(jié)束時(shí)間:Tue Nov 29 10:21:31 GMT+08:00 2022
四、Spring ioc整體流程總結(jié)

到此這篇關(guān)于Spring后處理器詳細(xì)介紹的文章就介紹到這了,更多相關(guān)Spring后處理器內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
使用RequestBodyAdvice實(shí)現(xiàn)對(duì)Http請(qǐng)求非法字符過濾
這篇文章主要介紹了使用RequestBodyAdvice實(shí)現(xiàn)對(duì)Http請(qǐng)求非法字符過濾的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-06-06
詳解hibernate雙向多對(duì)多關(guān)聯(lián)映射XML與注解版
本篇文章主要介紹了詳解hibernate雙向多對(duì)多關(guān)聯(lián)映射XML與注解版,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-05-05
一文帶你深入了解Java中延時(shí)任務(wù)的實(shí)現(xiàn)
延時(shí)任務(wù)相信大家都不陌生,在現(xiàn)實(shí)的業(yè)務(wù)中應(yīng)用場(chǎng)景可以說是比比皆是。這篇文章主要為大家介紹幾種實(shí)現(xiàn)延時(shí)任務(wù)的辦法,感興趣的可以了解一下2022-11-11
Java多線程之CAS算法實(shí)現(xiàn)線程安全
這篇文章主要介紹了java中如何通過CAS算法實(shí)現(xiàn)線程安全,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,下面小編和大家一起來學(xué)習(xí)一下吧2019-05-05
Java利用Geotools實(shí)現(xiàn)不同坐標(biāo)系之間坐標(biāo)轉(zhuǎn)換
GeoTools 是一個(gè)開源的 Java GIS 工具包,可利用它來開發(fā)符合標(biāo)準(zhǔn)的地理信息系統(tǒng)。本文將利用工具包Geotools實(shí)現(xiàn)不同坐標(biāo)系之間坐標(biāo)轉(zhuǎn)換,感興趣的可以了解一下2022-08-08
Struts2學(xué)習(xí)手冊(cè)之文件上傳基礎(chǔ)教程
Struts2提供的文件上傳下載機(jī)制十分簡(jiǎn)便,使得我們寫很少的代碼,下面這篇文章主要給大家介紹了關(guān)于Struts2學(xué)習(xí)手冊(cè)之文件上傳的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2018-05-05
SpringBoot整合EasyExcel進(jìn)行大數(shù)據(jù)處理的方法詳解
EasyExcel是一個(gè)基于Java的簡(jiǎn)單、省內(nèi)存的讀寫Excel的開源項(xiàng)目。在盡可能節(jié)約內(nèi)存的情況下支持讀寫百M(fèi)的Excel。本文將在SpringBoot中整合EasyExcel進(jìn)行大數(shù)據(jù)處理,感興趣的可以了解一下2022-05-05

