Spring中的ImportBeanDefinitionRegistrar接口詳解
接口功能介紹
描述:ImportBeanDefinitionRegistrar接口是也是spring的擴展點之一,它可以支持我們自己寫的代碼封裝成BeanDefinition對象,注冊到Spring容器中,功能類似于注解@Service @Component。
很多三方框架集成Spring的時候,都會通過該接口,實現(xiàn)掃描指定的類,然后注冊到spring容器中,比如Mybatis中的Mapper接口,springCloud中的FeignClient接口,都是通過該接口實現(xiàn)的自定義注冊邏輯。
1、ImportBeanDefinitionRegistrar接口實現(xiàn)類,只能通過@Import注解的方式來注入,通常把@Import修飾在啟動類或配置類。
2、使用@Import,如果括號中的類是ImportBeanDefinitionRegistrar的實現(xiàn)類,啟動時會觸發(fā)ImportBeanDefinitionRegistrar接口的方法,將其中要注冊的類注冊成bean。
3、實現(xiàn)該接口的類擁有注冊bean的能力。
//接口所有抽象方法,合并看就一個注冊BeanDefinition的方法 public interface ImportBeanDefinitionRegistrar { //把自定義的類封裝成BeanDefinition對象,注冊到Spring里面去 default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) { registerBeanDefinitions(importingClassMetadata, registry); } //我們平時重寫這個就可以了 default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { } }
使用案例
1、案例1
自定義業(yè)務類UserServiceTest,通過ImportBeanDefinitionRegistrar將注冊Spring容器中。在通過spring容器獲取Bean=UserServiceTest
//業(yè)務類 public class UserServiceTest { /** * 獲取用戶名稱 * @return 用戶名稱 */ public String getUserName(){ return "測試"; } }
ImportBeanDefinitionRegistrar實現(xiàn)類
//注意這里不能加注解,要通過Import導入進去。 public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { //業(yè)務類轉成bd,注冊到spring容器中注入 @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { //1、通過Bd工具類生成bd對象,只是這個Db對象比較純潔沒有綁定任何類 BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(); GenericBeanDefinition beanDefinition = (GenericBeanDefinition) beanDefinitionBuilder.getBeanDefinition(); //2、設置bd綁定的類型 beanDefinition.setBeanClass(UserServiceTest.class); //3、注冊到spring容器中 registry.registerBeanDefinition("userServiceTest",beanDefinition); } } @Configuration @Import(MyImportBeanDefinitionRegistrar.class) public class AppConfigClassTest { //在配置類導入ImportBeanDefinitionRegistrar實現(xiàn)類 }
//測試 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è)務類注冊到Spring容器中我們通過其他注解就可以了,那么ImportBeanDefinitionRegistrar有沒有更高級的玩法。
2、案例2
public interface UserServiceTestInterface { public void list(); }
//因為是接口,但是spring的容器里面是不允許注入接口的,只能是接口的實現(xiàn)類。如果我們寫個去實現(xiàn)接口,那就沒有什么意義了,沒必要搞得那么復雜,這次我們通過代理類來完成對接口的實現(xiàn)。 public class MyInvocationHandler implements InvocationHandler { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("代理類邏輯代碼"); return null; } }
//注意這里不能加注解,要通過Import導入進去。 public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { //通過工具類生成一個bd,只是這個Db對象比較純潔沒有綁定任何類 BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(); GenericBeanDefinition beanDefinition = (GenericBeanDefinition) beanDefinitionBuilder.getBeanDefinition(); //設置Bean的類型MyInvocationHandler,類型是實現(xiàn)類的類型,不是接口類型。因為在實例化的時候,調用的是設置類型所以對應的構造方法。 beanDefinition.setBeanClass(MyInvocationHandler.class); //注冊到Spring容器中進去 registry.registerBeanDefinition("userServiceTest",beanDefinition); } } //通過配置類,導入ImportBeanDefinitionRegistrar的實現(xiàn)類 @Configuration @Import(MyImportBeanDefinitionRegistrar.class) public class AppConfigClassTest { }
測試
public static void main(String[] args) { AnnotationConfigApplicationContext AnnotationConfigApplicationContext = new AnnotationConfigApplicationContext(AppConfigClassTest.class); //通過name獲取Bean,注意此時的bean類型不是UserServiceTestInterface類型,而是MyInvocationHandler Object userServiceTestInterface = AnnotationConfigApplicationContext.getBean("userServiceTest"); if(userServiceTestInterface instanceof MyInvocationHandler){ //代碼實際會走到這里,把object轉成帶代理類。 MyInvocationHandler u = (MyInvocationHandler) userServiceTestInterface; //生成接口UserServiceTestInterface的代理對象 UserServiceTestInterface o = (UserServiceTestInterface) Proxy.newProxyInstance(MainTest.class.getClassLoader(), new Class[]{UserServiceTestInterface.class}, u); } }
3、案例3
定義一個業(yè)務接口,通過FactoryBean+InvocationHandler來生成該接口的代理類,無需手動寫業(yè)務接口的實現(xiàn)類,很多底層框架就是這樣實現(xiàn)的。
- InvocationHandler:主要是通過Invoke方法來攔截業(yè)務接口的方法
- FactoryBean:主要是用來將生成的代理類。
- ImportBeanDefinitionRegistrar:在這里的作用就是幫忙我們把自定義的FactoryBean注冊到Spring中
//業(yè)務接口 public interface UserServiceTestInterface { public void list(); }
自定義FactoryBean這樣我們控制Bean的創(chuàng)建的過程,實現(xiàn)InvocationHandler用來攔截業(yè)務接口的方法。
//創(chuàng)建代理類,代理UserServiceTestInterface接口,UserServiceTestInterface接口方法在執(zhí)行前后都會被invoke方法攔截 //FactoryBean可以生成某一個類型Bean實例,它最大的一個作用是:可以讓我們自定義Bean的創(chuàng)建過程 public class MyFactoryBean implements FactoryBean, InvocationHandler { //為了使這個類更好地擴展。創(chuàng)建更多的接口,我們定義一個參數(shù),讓他們通過參數(shù)傳遞進來。 private Class classs; //添加一個有參的構造方法。 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的對象。spring會自動把它add到容器里面去。 @Override public Object getObject() throws Exception { Class[] clazzs = new Class[]{classs};//目標類集合。 //通過proxy來得到代理對象。本來最有一個參數(shù)需要穿代理類對象,但因為本類實現(xiàn)了InvocationHandler,所以只需傳this Object proxy = Proxy.newProxyInstance(this.getClass().getClassLoader(), clazzs, this); return proxy;//返回的這個對象,會把加到spring的容器中。 } //返回要添加到容器里bean的類型 @Override public Class<?> getObjectType() { return this.classs; } }
自定義ImportBeanDefinitionRegistrar實現(xiàn)類,把我們自定義的FactoryBean注冊到Spring中。
//注意這里不能加注解,要通過Import導入進去。 public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { //通過工具類生成一個bd,只是這個Db對象比較純潔沒有綁定任何類 BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(); //為什么要轉成GenericBeanDefinition這種類型。因為GenericBeanDefinition有更多修改bd屬性的方法。后面我會介紹為什么要修改屬性。 GenericBeanDefinition beanDefinition = (GenericBeanDefinition) beanDefinitionBuilder.getBeanDefinition(); //這里很重要。getConstructorArgumentValues是為了獲取該bd的所有構造方法,因為我們重寫了有參構造方法,所有我們需要帶參數(shù)過去 //不然spring沒法幫我們實例化,addGenericArgumentValue是添加參數(shù),該代碼會執(zhí)行兩步 //第一步是匹配對應的構造方法,第二步是實例化。 beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(UserServiceTestInterface.class.getName()); //因為代理對象類型的,實例化的時候走的是代理類的構造方法 beanDefinition.setBeanClass(MyFactoryBean.class); //注冊bd registry.registerBeanDefinition("userServiceTest",beanDefinition); } }
測試
public static void main(String[] args) { AnnotationConfigApplicationContext AnnotationConfigApplicationContext = new AnnotationConfigApplicationContext(AppConfigClassTest.class); //通過name獲取Bean Object userServiceTestInterface = AnnotationConfigApplicationContext.getBean("userServiceTest"); //針對這種場景Bean的類型是,通過FactoryBean的getObjectType方法返回的。 UserServiceTestInterface u = (UserServiceTestInterface) userServiceTestInterface; u.list(); }
?到此這篇關于Spring中的ImportBeanDefinitionRegistrar接口詳解的文章就介紹到這了,更多相關ImportBeanDefinitionRegistrar接口內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Java?EasyExcel利用填充模版動態(tài)生成多個sheet頁
這篇文章主要為大家詳細介紹了Java?EasyExcel如何利用填充模版動態(tài)生成多個sheet頁,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下2023-12-12springboot使用Logback把日志輸出到控制臺或輸出到文件
這篇文章給大家介紹springboot項目使用日志工具Logback把日志不僅輸出到控制臺,也可以輸出到文件的操作方法,本文通過實例圖文相結合給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友參考下吧2020-10-10通過Java實現(xiàn)設置Word文檔頁邊距的方法詳解
頁邊距是指頁面的邊線到文字的距離。通??稍陧撨吘鄡炔康目纱蛴^(qū)域中插入文字和圖形等。今天這篇文章將為您展示如何通過編程方式,設置Word?文檔頁邊距,感興趣的可以了解一下2023-02-02深入Synchronized和java.util.concurrent.locks.Lock的區(qū)別詳解
本篇文章是對Synchronized和java.util.concurrent.locks.Lock的區(qū)別進行了詳細的分析介紹,需要的朋友參考下2013-06-06