Spring后處理器詳細(xì)介紹
一、概述
Spring的后處理器是Spring對外開發(fā)的重要擴(kuò)展點、允許我們介入到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文件中進(jìn)行配置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
//強(qiáng)轉(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文件中進(jìn)行配置
<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@5a8e6209ps:方法的執(zhí)行順序
1.MyBeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry方法
2.MyBeanDefinitionRegistryPostProcessor的postProcessBeanFactory方法
3.MyBeanFactoryPostProcessor的postProcessBeanFactory方法
③:使用Spring的BeanFactoryPostProcessor擴(kuò)展點完成自定義注解掃描
要求如下:
- 自定義@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進(jìn)行注冊
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í)行等, 其中有一個對外進(jìn)行擴(kuò)展的點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方法進(jìn)行執(zhí)行時間日志增強(qiáng)
要求如下:
- Bean的方法執(zhí)行之前控制臺打印當(dāng)前時間;
- Bean的方法執(zhí)行之后控制臺打印當(dāng)前時間。
分析:
- 對方法進(jìn)行增強(qiáng)主要就是代理設(shè)計模式和包裝設(shè)計模式;
- 由于Bean方法不確定, 所以使用動態(tài)代理在運行期間執(zhí)行增強(qiáng)操作;
- 在Bean實例創(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)行動態(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進(jìn)行增強(qiáng),返回proxy對象,進(jìn)而存儲到單例池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)文章
hibernate 配置數(shù)據(jù)庫方言的實現(xiàn)方法
這篇文章主要介紹了hibernate 配置數(shù)據(jù)庫方言的實現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-05-05
Java應(yīng)用服務(wù)器之tomcat部署的詳細(xì)教程
這篇文章主要介紹了Java應(yīng)用服務(wù)器之tomcat部署,本文通過實例圖文相結(jié)合給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-07-07
spring mvc+localResizeIMG實現(xiàn)HTML5端圖片壓縮上傳
這篇文章主要為大家詳細(xì)介紹了使用spring mvc+localResizeIMG實現(xiàn)HTML5端圖片壓縮上傳,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-04-04
Java多線程 Producer and Consumer設(shè)計模式
這篇文章主要介紹了Java多線程 Producer and Consumer設(shè)計模式,producer是生產(chǎn)者的意思:指生產(chǎn)數(shù)據(jù)的線程,consumer是消費者的意思,指的是使用數(shù)據(jù)的線程,下文圍繞Producer及Consumer展開話題,需要的朋友可以參考一下2021-10-10
mybatis同一張表多次連接查詢相同列賦值問題小結(jié)
這篇文章主要介紹了mybatis同一張表多次連接查詢相同列賦值問題,非常不錯,具有參考借鑒價值,需要的的朋友參考下2017-01-01

