Spring中的ImportBeanDefinitionRegistrar接口詳解
接口功能介紹
描述:ImportBeanDefinitionRegistrar接口是也是spring的擴(kuò)展點(diǎn)之一,它可以支持我們自己寫(xiě)的代碼封裝成BeanDefinition對(duì)象,注冊(cè)到Spring容器中,功能類似于注解@Service @Component。
很多三方框架集成Spring的時(shí)候,都會(huì)通過(guò)該接口,實(shí)現(xiàn)掃描指定的類,然后注冊(cè)到spring容器中,比如Mybatis中的Mapper接口,springCloud中的FeignClient接口,都是通過(guò)該接口實(shí)現(xiàn)的自定義注冊(cè)邏輯。
1、ImportBeanDefinitionRegistrar接口實(shí)現(xiàn)類,只能通過(guò)@Import注解的方式來(lái)注入,通常把@Import修飾在啟動(dòng)類或配置類。
2、使用@Import,如果括號(hào)中的類是ImportBeanDefinitionRegistrar的實(shí)現(xiàn)類,啟動(dòng)時(shí)會(huì)觸發(fā)ImportBeanDefinitionRegistrar接口的方法,將其中要注冊(cè)的類注冊(cè)成bean。
3、實(shí)現(xiàn)該接口的類擁有注冊(cè)bean的能力。
//接口所有抽象方法,合并看就一個(gè)注冊(cè)BeanDefinition的方法
public interface ImportBeanDefinitionRegistrar {
//把自定義的類封裝成BeanDefinition對(duì)象,注冊(cè)到Spring里面去
default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {
registerBeanDefinitions(importingClassMetadata, registry);
}
//我們平時(shí)重寫(xiě)這個(gè)就可以了
default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
}
}使用案例
1、案例1
自定義業(yè)務(wù)類UserServiceTest,通過(guò)ImportBeanDefinitionRegistrar將注冊(cè)Spring容器中。在通過(guò)spring容器獲取Bean=UserServiceTest
//業(yè)務(wù)類
public class UserServiceTest {
/**
* 獲取用戶名稱
* @return 用戶名稱
*/
public String getUserName(){
return "測(cè)試";
}
}ImportBeanDefinitionRegistrar實(shí)現(xiàn)類
//注意這里不能加注解,要通過(guò)Import導(dǎo)入進(jìn)去。
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
//業(yè)務(wù)類轉(zhuǎn)成bd,注冊(cè)到spring容器中注入
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//1、通過(guò)Bd工具類生成bd對(duì)象,只是這個(gè)Db對(duì)象比較純潔沒(méi)有綁定任何類
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition();
GenericBeanDefinition beanDefinition = (GenericBeanDefinition) beanDefinitionBuilder.getBeanDefinition();
//2、設(shè)置bd綁定的類型
beanDefinition.setBeanClass(UserServiceTest.class);
//3、注冊(cè)到spring容器中
registry.registerBeanDefinition("userServiceTest",beanDefinition);
}
}
@Configuration
@Import(MyImportBeanDefinitionRegistrar.class)
public class AppConfigClassTest {
//在配置類導(dǎo)入ImportBeanDefinitionRegistrar實(shí)現(xiàn)類
}//測(cè)試
public static void main(String[] args) {
AnnotationConfigApplicationContext AnnotationConfigApplicationContext =
new AnnotationConfigApplicationContext(AppConfigClassTest.class);
UserServiceTest userServiceTest = AnnotationConfigApplicationContext.getBean("userServiceTest", UserServiceTest.class);
String userName = userServiceTest.getUserName();
System.out.println(userName);
}如果只是把業(yè)務(wù)類注冊(cè)到Spring容器中我們通過(guò)其他注解就可以了,那么ImportBeanDefinitionRegistrar有沒(méi)有更高級(jí)的玩法。
2、案例2
public interface UserServiceTestInterface {
public void list();
}//因?yàn)槭墙涌冢莝pring的容器里面是不允許注入接口的,只能是接口的實(shí)現(xiàn)類。如果我們寫(xiě)個(gè)去實(shí)現(xiàn)接口,那就沒(méi)有什么意義了,沒(méi)必要搞得那么復(fù)雜,這次我們通過(guò)代理類來(lái)完成對(duì)接口的實(shí)現(xiàn)。
public class MyInvocationHandler implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理類邏輯代碼");
return null;
}
}//注意這里不能加注解,要通過(guò)Import導(dǎo)入進(jìn)去。
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//通過(guò)工具類生成一個(gè)bd,只是這個(gè)Db對(duì)象比較純潔沒(méi)有綁定任何類
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition();
GenericBeanDefinition beanDefinition = (GenericBeanDefinition) beanDefinitionBuilder.getBeanDefinition();
//設(shè)置Bean的類型MyInvocationHandler,類型是實(shí)現(xiàn)類的類型,不是接口類型。因?yàn)樵趯?shí)例化的時(shí)候,調(diào)用的是設(shè)置類型所以對(duì)應(yīng)的構(gòu)造方法。
beanDefinition.setBeanClass(MyInvocationHandler.class);
//注冊(cè)到Spring容器中進(jìn)去
registry.registerBeanDefinition("userServiceTest",beanDefinition);
}
}
//通過(guò)配置類,導(dǎo)入ImportBeanDefinitionRegistrar的實(shí)現(xiàn)類
@Configuration
@Import(MyImportBeanDefinitionRegistrar.class)
public class AppConfigClassTest {
}測(cè)試
public static void main(String[] args) {
AnnotationConfigApplicationContext AnnotationConfigApplicationContext = new AnnotationConfigApplicationContext(AppConfigClassTest.class);
//通過(guò)name獲取Bean,注意此時(shí)的bean類型不是UserServiceTestInterface類型,而是MyInvocationHandler
Object userServiceTestInterface = AnnotationConfigApplicationContext.getBean("userServiceTest");
if(userServiceTestInterface instanceof MyInvocationHandler){
//代碼實(shí)際會(huì)走到這里,把object轉(zhuǎn)成帶代理類。
MyInvocationHandler u = (MyInvocationHandler) userServiceTestInterface;
//生成接口UserServiceTestInterface的代理對(duì)象
UserServiceTestInterface o = (UserServiceTestInterface) Proxy.newProxyInstance(MainTest.class.getClassLoader(), new Class[]{UserServiceTestInterface.class}, u);
}
}3、案例3
定義一個(gè)業(yè)務(wù)接口,通過(guò)FactoryBean+InvocationHandler來(lái)生成該接口的代理類,無(wú)需手動(dòng)寫(xiě)業(yè)務(wù)接口的實(shí)現(xiàn)類,很多底層框架就是這樣實(shí)現(xiàn)的。
- InvocationHandler:主要是通過(guò)Invoke方法來(lái)攔截業(yè)務(wù)接口的方法
- FactoryBean:主要是用來(lái)將生成的代理類。
- ImportBeanDefinitionRegistrar:在這里的作用就是幫忙我們把自定義的FactoryBean注冊(cè)到Spring中
//業(yè)務(wù)接口
public interface UserServiceTestInterface {
public void list();
}自定義FactoryBean這樣我們控制Bean的創(chuàng)建的過(guò)程,實(shí)現(xiàn)InvocationHandler用來(lái)攔截業(yè)務(wù)接口的方法。
//創(chuàng)建代理類,代理UserServiceTestInterface接口,UserServiceTestInterface接口方法在執(zhí)行前后都會(huì)被invoke方法攔截
//FactoryBean可以生成某一個(gè)類型Bean實(shí)例,它最大的一個(gè)作用是:可以讓我們自定義Bean的創(chuàng)建過(guò)程
public class MyFactoryBean implements FactoryBean, InvocationHandler {
//為了使這個(gè)類更好地?cái)U(kuò)展。創(chuàng)建更多的接口,我們定義一個(gè)參數(shù),讓他們通過(guò)參數(shù)傳遞進(jìn)來(lái)。
private Class classs;
//添加一個(gè)有參的構(gòu)造方法。
public MyFactoryBean(Class classs){
this.classs = classs;
}
//攔截Class的所有方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("diaoyonlejiekou123");
return null;
}
//返回bean的對(duì)象。spring會(huì)自動(dòng)把它add到容器里面去。
@Override
public Object getObject() throws Exception {
Class[] clazzs = new Class[]{classs};//目標(biāo)類集合。
//通過(guò)proxy來(lái)得到代理對(duì)象。本來(lái)最有一個(gè)參數(shù)需要穿代理類對(duì)象,但因?yàn)楸绢悓?shí)現(xiàn)了InvocationHandler,所以只需傳this
Object proxy = Proxy.newProxyInstance(this.getClass().getClassLoader(), clazzs, this);
return proxy;//返回的這個(gè)對(duì)象,會(huì)把加到spring的容器中。
}
//返回要添加到容器里bean的類型
@Override
public Class<?> getObjectType() {
return this.classs;
}
}自定義ImportBeanDefinitionRegistrar實(shí)現(xiàn)類,把我們自定義的FactoryBean注冊(cè)到Spring中。
//注意這里不能加注解,要通過(guò)Import導(dǎo)入進(jìn)去。
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//通過(guò)工具類生成一個(gè)bd,只是這個(gè)Db對(duì)象比較純潔沒(méi)有綁定任何類
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition();
//為什么要轉(zhuǎn)成GenericBeanDefinition這種類型。因?yàn)镚enericBeanDefinition有更多修改bd屬性的方法。后面我會(huì)介紹為什么要修改屬性。
GenericBeanDefinition beanDefinition = (GenericBeanDefinition) beanDefinitionBuilder.getBeanDefinition();
//這里很重要。getConstructorArgumentValues是為了獲取該bd的所有構(gòu)造方法,因?yàn)槲覀冎貙?xiě)了有參構(gòu)造方法,所有我們需要帶參數(shù)過(guò)去
//不然spring沒(méi)法幫我們實(shí)例化,addGenericArgumentValue是添加參數(shù),該代碼會(huì)執(zhí)行兩步
//第一步是匹配對(duì)應(yīng)的構(gòu)造方法,第二步是實(shí)例化。
beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(UserServiceTestInterface.class.getName());
//因?yàn)榇韺?duì)象類型的,實(shí)例化的時(shí)候走的是代理類的構(gòu)造方法
beanDefinition.setBeanClass(MyFactoryBean.class);
//注冊(cè)bd
registry.registerBeanDefinition("userServiceTest",beanDefinition);
}
}測(cè)試
public static void main(String[] args) {
AnnotationConfigApplicationContext AnnotationConfigApplicationContext = new AnnotationConfigApplicationContext(AppConfigClassTest.class);
//通過(guò)name獲取Bean
Object userServiceTestInterface = AnnotationConfigApplicationContext.getBean("userServiceTest");
//針對(duì)這種場(chǎng)景Bean的類型是,通過(guò)FactoryBean的getObjectType方法返回的。
UserServiceTestInterface u = (UserServiceTestInterface) userServiceTestInterface;
u.list();
}?到此這篇關(guān)于Spring中的ImportBeanDefinitionRegistrar接口詳解的文章就介紹到這了,更多相關(guān)ImportBeanDefinitionRegistrar接口內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java?EasyExcel利用填充模版動(dòng)態(tài)生成多個(gè)sheet頁(yè)
這篇文章主要為大家詳細(xì)介紹了Java?EasyExcel如何利用填充模版動(dòng)態(tài)生成多個(gè)sheet頁(yè),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-12-12
關(guān)于cron表達(dá)式每天整點(diǎn)執(zhí)行一次的問(wèn)題
這篇文章主要介紹了關(guān)于cron表達(dá)式每天整點(diǎn)執(zhí)行一次的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-12-12
SpringBoot集成極光推送完整實(shí)現(xiàn)代碼
本文主要介紹了SpringBoot集成極光推送完整實(shí)現(xiàn)代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-12-12
springboot使用Logback把日志輸出到控制臺(tái)或輸出到文件
這篇文章給大家介紹springboot項(xiàng)目使用日志工具Logback把日志不僅輸出到控制臺(tái),也可以輸出到文件的操作方法,本文通過(guò)實(shí)例圖文相結(jié)合給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2020-10-10
通過(guò)Java實(shí)現(xiàn)設(shè)置Word文檔頁(yè)邊距的方法詳解
頁(yè)邊距是指頁(yè)面的邊線到文字的距離。通??稍陧?yè)邊距內(nèi)部的可打印區(qū)域中插入文字和圖形等。今天這篇文章將為您展示如何通過(guò)編程方式,設(shè)置Word?文檔頁(yè)邊距,感興趣的可以了解一下2023-02-02
深入Synchronized和java.util.concurrent.locks.Lock的區(qū)別詳解
本篇文章是對(duì)Synchronized和java.util.concurrent.locks.Lock的區(qū)別進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-06-06
解決IDEA克隆代碼后在右下角沒(méi)有g(shù)it分支的問(wèn)題
這篇文章主要介紹了解決IDEA克隆代碼后在右下角沒(méi)有g(shù)it分支的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-02-02
java中關(guān)于移位運(yùn)算符的demo與總結(jié)(推薦)
下面小編就為大家?guī)?lái)一篇java中關(guān)于移位運(yùn)算符的demo與總結(jié)(推薦)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-05-05
SpringBoot HATEOAS用法簡(jiǎn)介(入門(mén))
這篇文章主要介紹了SpringBoot HATEOAS用法簡(jiǎn)介(入門(mén)),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-10-10

