最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring框架中ImportBeanDefinitionRegistrar的應(yīng)用詳解

 更新時(shí)間:2024年01月29日 10:15:17   作者:Smallc0de  
這篇文章主要介紹了Spring框架中ImportBeanDefinitionRegistrar的應(yīng)用詳解,如果實(shí)現(xiàn)了ImportSelector接口,在配置類中被@Import加入到Spring容器中以后,Spring容器就會(huì)把ImportSelector接口方法返回的字符串?dāng)?shù)組中的類new出來對(duì)象然后放到工廠中去,需要的朋友可以參考下

前言

我們講過如果一個(gè)類實(shí)現(xiàn)了ImportSelector接口,并且在配置類中被@Import加入到Spring容器中以后。

Spring容器就會(huì)把ImportSelector接口方法返回的字符串?dāng)?shù)組中的類new出來對(duì)象然后放到工廠中去。

并且做了一個(gè)功能開關(guān)的例子輔助講解其功能。這次我們就接著上次講解ImportSelector接口的內(nèi)容繼續(xù)擴(kuò)展講解ImportBeanDefinitionRegistrar的用法。

ImportBeanDefinitionRegistrar

按照慣例我們還是先介紹一下這個(gè)接口里面最重要的方法:registerBeanDefinitions。

public interface ImportBeanDefinitionRegistrar {
	//雖然是倆方法,但是等于一個(gè)方法
	default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry,
         BeanNameGenerator importBeanNameGenerator) {
	//直接調(diào)用了下面的方法
      registerBeanDefinitions(importingClassMetadata, registry);
   }
   default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
   }
}

里面一共兩個(gè)方法而且都被default注解了,可見確實(shí)用的不多。但是這個(gè)方法卻擁有ImportSelector接口內(nèi)方法的一切功能,而且更強(qiáng)大。這倆方法是重載方法,區(qū)別就在于有沒有BeanNameGenerator,這個(gè)接口是Spring內(nèi)置的BeanName生成器,無關(guān)大雅。但是注意到第一個(gè)方法其實(shí)是調(diào)用了第二個(gè)方法去實(shí)現(xiàn)的,可以說方法一是一個(gè)擴(kuò)展,也可以說方法一等于方法二。那就直接解析參數(shù)。

  1. 第一個(gè)參數(shù)AnnotationMetadata importingClassMetadata:這個(gè)參數(shù)和ImportSelector中的一樣,可以拿到被@Import注解過的類的元數(shù)據(jù),具體到例子就是筆者一直寫的配置類AppConfig.class。因?yàn)橐膊淮蛩氵M(jìn)行修改,所以這個(gè)不多說。
  2. 第二個(gè)參數(shù)BeanDefinitionRegistry registry:這個(gè)參數(shù)厲害了。BeanDefinitionRegistry這個(gè)接口我們以前說過,Spring想要把一個(gè)類變成對(duì)象就一定會(huì)把這個(gè)類變成一個(gè)BeanDefinition對(duì)象。這個(gè)過程怎么來的呢?就是通過實(shí)現(xiàn)BeanDefinitionRegistry接口類的構(gòu)造方法做的。

Spring把在方法中把這個(gè)接口開放出來,就意味著我們可以在這里手動(dòng)添加一個(gè)BeanDefinition給Spring容器,然后構(gòu)建對(duì)象出來。通過registry我們就可以注冊(cè)一個(gè)BeanDefinition進(jìn)入Spring容器,就使用下面的這個(gè)方法:

registry.registerBeanDefinition(String beanName, BeanDefinition beanDefinition)

ImportBeanDefinitionRegistrar例子

按照常規(guī)我們先把必要的類都先創(chuàng)建出來。一個(gè)業(yè)務(wù)接口ImportTestDao,一個(gè)業(yè)務(wù)類依賴該接口ImportTestService,一個(gè)配置類AppConfig,一個(gè)測(cè)試類Test,以及一個(gè)實(shí)現(xiàn)了ImportBeanDefinitionRegistrar的類MyImportDBR。

public interface ImportTestDao {
   public void query();
}
@Component
public class ImportTestService {
    @Autowired
    ImportTestDao importTestDao;
    public void find(){
        System.out.println("ImportTestService importTestDao.query()");
        importTestDao.query();
    }
}
@ComponentScan("com.demo")
public class AppConfig {
}
public class Test {
   public static void main(String[] args) {
     AnnotationConfigApplicationContext anno= new AnnotationConfigApplicationContext(AppConfig.class);
     ImportTestDao importTestDao= (ImportTestDao) anno.getBean("importTestDao");
     importTestDao.query();
   }
}
public class MyImportDBR implements ImportBeanDefinitionRegistrar {
   @Override
   public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
   }
}

要完成MyImportDBR最重要的就是實(shí)現(xiàn)里面的方法。所以先分析一下如何使用這個(gè)方法:首先看參數(shù)需要一個(gè)beanName和一個(gè)beanDefinition,那么第一步就是需要得到要注冊(cè)的bean的beanDefinition。Spring也給我們提供了相應(yīng)的類BeanDefinitionBuilder。那么最終這個(gè)類構(gòu)造成這個(gè)樣子:

public class MyImportDBR implements ImportBeanDefinitionRegistrar {
   @Override
   public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
      //得到BD,掃描接口
      BeanDefinitionBuilder builder= BeanDefinitionBuilder.genericBeanDefinition(ImportTestDao.class);
      GenericBeanDefinition beanDefinition= (GenericBeanDefinition) builder.getBeanDefinition();
      beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(beanDefinition.getBeanClassName());
      registry.registerBeanDefinition("importTestDao",beanDefinition);
   }
}

但是這里有一個(gè)問題:因?yàn)槲覀冞@里雖然把ImportTestDao.class寫在這里了,想要構(gòu)建一個(gè)BeanDefinition。但是我們沒有辦法使用,因?yàn)镮mportTestDao是一個(gè)接口, Spring沒有辦法給你new一個(gè)出來,也就是說沒有辦法實(shí)例化。那么怎么辦呢?我們知道這里用的不應(yīng)該是一個(gè)類,而是應(yīng)該是ImportTestDao這個(gè)接口的一個(gè)代理類。所以要怎么樣才能獲取到這個(gè)代理呢?這需要提起來我們很早以前就提到的一個(gè)知識(shí)點(diǎn)FactoryBean。那我們就需要構(gòu)造這么一個(gè)FactoryBean,以及為了構(gòu)造一個(gè)代理對(duì)象還需要一個(gè)InvocationHandler。

public class MyfactoryBean implements FactoryBean {
   private Class clazz;
   public MyfactoryBean(Class clazz) {
      this.clazz=clazz;
   }
   @Override
   public Object getObject() throws Exception {
      Class[] clazzes=new Class[]{this.clazz};
      Object proxy= Proxy.newProxyInstance(this.getClass().getClassLoader(),clazzes,new MyInvocation());
      return proxy;
   }
   @Override
   public Class<?> getObjectType() {
      return this.clazz;
   }
   @Override
   public boolean isSingleton() {
      return false;
   }
}
public class MyInvocation implements InvocationHandler {
   public MyInvocation() {
   }
   @Override
   public Object invoke(Object proxy, Method method, Object[] args) {
   	  System.out.println("This is a proxy of ImportTestDao");
      return null;
   }
}

然后把這個(gè)FactoryBean加入進(jìn)去。

public class MyImportDBR implements ImportBeanDefinitionRegistrar {
   @Override
   public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
      //得到BD,掃描接口,這里筆者寫死了,但是其實(shí)可以做一個(gè)包掃描,把某一個(gè)包下的所有類都掃描進(jìn)來,就像Mybatis的@MapperScan注解一樣
      BeanDefinitionBuilder builder= BeanDefinitionBuilder.genericBeanDefinition(ImportTestDao.class);
      //拿到一個(gè)BeanDefinition, 這里使用一個(gè)其中一個(gè)子類來接收,代表這里構(gòu)建的就是一個(gè)普通的BeanDefinition
      GenericBeanDefinition beanDefinition= (GenericBeanDefinition) builder.getBeanDefinition();
      //這里打印是因?yàn)楣P者當(dāng)時(shí)不確定類名要不要包含包名
      System.out.println(beanDefinition.getBeanClassName());
      //給我們的beanDefinition添加一個(gè)構(gòu)造方法,并且傳入我們需要的bean名字
      beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(beanDefinition.getBeanClassName());
      //把代理對(duì)象給賦予給BeanDefinition
      beanDefinition.setBeanClass(MyfactoryBean.class);
      //這里的名字可以隨便取,這里是隨著Spring名規(guī)范取的。話說這個(gè)接口的另一個(gè)方法有BeanNameGenerator這個(gè)參數(shù)就是讓大家自由發(fā)揮的
      registry.registerBeanDefinition("importTestDao",beanDefinition);
   }
}

為了更有逼格一些,我們把這個(gè)ImportBeanDefinitionRegistrar封裝成為一個(gè)注解MyScaner,這個(gè)是仿照@MapperScan的

@Retention(RetentionPolicy.RUNTIME)
@Import(MyImportDBR.class)
public @interface MyScaner {
}

直接加載到AppConfig類上。

@ComponentScan("com.demo")
@MyScaner
public class AppConfig {
}
運(yùn)行,拿到結(jié)果:
This is a proxy of ImportTestDao
query

這樣就完成了從外部直接加載一個(gè)BeanDefinition到Spring容器的過程。但是小伙伴們看到這里一定會(huì)覺得:你這一頓操作猛如虎,一看戰(zhàn)績(jī)0比5。搞這么多有個(gè)毛線用???

ImportBeanDefinitionRegistrar作用

看起來筆者的例子卵用沒有,但是仔細(xì)想想,大家經(jīng)常使用的Mybatis是不是就是這個(gè)原理?我特意把Mybatis官網(wǎng)的代碼調(diào)出來,想必大家都配置過。

<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
  <property name="mapperInterface" value="org.mybatis.spring.sample.mapper.UserMapper" />
  <property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>

一般的博客會(huì)說,這個(gè)是把我們自己定義的UserMapper注冊(cè)給MapperFactoryBean,看到這里的同學(xué),筆者可以明確的告訴你,這個(gè)解釋是錯(cuò)的。我們要想轉(zhuǎn)換UserMapper成為MapperFactoryBean,你就必須顯式的告訴Spring他們之間的關(guān)系。然后Spring拿到UserMapper接口傳入MapperFactoryBean,再由MapperFactoryBean動(dòng)態(tài)產(chǎn)生UserMapper的代理對(duì)象,然后你程序里使用的一直都是這個(gè)代理對(duì)象,這一切的原理就是我們寫的MyfactoryBean的一系列操作。

為了驗(yàn)證我的說法咱們?nèi)ybatis的MapperFactoryBean的源碼看下,是不是和我們寫的基本上解構(gòu)一樣。

public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
//通過這個(gè)接口反射代理對(duì)象,就是我們例子中的clazz
   private Class<T> mapperInterface;  
   public MapperFactoryBean() {
      // intentionally empty
   }
   public MapperFactoryBean(Class<T> mapperInterface) {
      this.mapperInterface = mapperInterface;
   }
   ...無關(guān),略...
   /**
    * {@inheritDoc}
    */
   @Override
   public T getObject() throws Exception {
	//通過傳入接口反射得到代理對(duì)象,如果沒有接口你就沒有辦法獲取代理對(duì)象
	//這個(gè)接口怎么拿到,就是通過上面的xml配置的
      return getSqlSession().getMapper(this.mapperInterface);
   }
   /**
    * {@inheritDoc}
    */
   @Override
   public Class<T> getObjectType() {
      return this.mapperInterface;
   }
   /**
    * {@inheritDoc}
    */
   @Override
   public boolean isSingleton() {
      return true;
   }
 // ...無關(guān),略...
}

所以我們配置的這個(gè)Mybatis的xml是干嘛的呢?就是為了讓MapperFactoryBean生成我們需要的(UserMapper)代理對(duì)象而已,根本就不是什么注冊(cè)。

到此這篇關(guān)于Spring框架中ImportBeanDefinitionRegistrar的應(yīng)用詳解的文章就介紹到這了,更多相關(guān)ImportBeanDefinitionRegistrar應(yīng)用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SpringBoot登錄認(rèn)證前后端實(shí)現(xiàn)方案:SpringBoot + Mybatis + JWT(圖文實(shí)例)

    SpringBoot登錄認(rèn)證前后端實(shí)現(xiàn)方案:SpringBoot + Mybatis +&

    本文展示如何在Spring生態(tài)系統(tǒng)中整合IOC/DI、MyBatis、MD5加密、Session/Cookie管理、JWT令牌和攔截器,以實(shí)現(xiàn)一個(gè)企業(yè)級(jí)的認(rèn)證方案,前端使用HTML/CSS/JavaScript/JQuery,后端使用SpringBoot+MyBatis+JWT,通過統(tǒng)一返回結(jié)果封裝和圖形驗(yàn)證碼,提高了API的可維護(hù)性和安全性
    2025-10-10
  • springboot日志的使用與多個(gè)日志框架沖突問題及解決

    springboot日志的使用與多個(gè)日志框架沖突問題及解決

    文章介紹了日志級(jí)別、日志分類、logback、log4j、log4j2的使用方法,并討論了統(tǒng)一日志標(biāo)準(zhǔn)的重要性,還提到了各種日志類Jar包的用途,以及如何實(shí)現(xiàn)SLF4J門面以兼容不同的日志框架
    2025-02-02
  • 詳解maven中央倉(cāng)庫(kù)連不上的解決辦法

    詳解maven中央倉(cāng)庫(kù)連不上的解決辦法

    這篇文章主要介紹了詳解maven中央倉(cāng)庫(kù)連不上的解決辦法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09
  • springboot注冊(cè)bean的三種方法

    springboot注冊(cè)bean的三種方法

    這篇文章主要介紹了springboot注冊(cè)bean的三種方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2019-02-02
  • java發(fā)起http請(qǐng)求調(diào)用post與get接口的方法實(shí)例

    java發(fā)起http請(qǐng)求調(diào)用post與get接口的方法實(shí)例

    在實(shí)際開發(fā)過程中,我們經(jīng)常需要調(diào)用對(duì)方提供的接口或測(cè)試自己寫的接口是否合適,下面這篇文章主要給大家介紹了關(guān)于java發(fā)起http請(qǐng)求調(diào)用post與get接口的相關(guān)資料,需要的朋友可以參考下
    2022-08-08
  • java框架之maven是用來做什么的

    java框架之maven是用來做什么的

    這篇文章主要介紹了java之maven是用來做什么的,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-07-07
  • 列舉java語言中反射的常用方法及實(shí)例代碼

    列舉java語言中反射的常用方法及實(shí)例代碼

    反射機(jī)制指的是程序在運(yùn)行時(shí)能夠獲取自身的信息。這篇文章主要介紹了列舉java語言中反射的常用方法,需要的朋友可以參考下
    2019-07-07
  • SpringBoot入門系列之JPA mysql

    SpringBoot入門系列之JPA mysql

    這篇文章主要介紹了SpringBoot入門系列之JPA mysql的相關(guān)資料,需要的朋友可以參考下
    2017-04-04
  • RocketMQ消息過濾與查詢的實(shí)現(xiàn)

    RocketMQ消息過濾與查詢的實(shí)現(xiàn)

    這篇文章主要介紹了RocketMQ消息過濾與查詢的實(shí)現(xiàn)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • Java實(shí)現(xiàn)自動(dòng)生成縮略圖片

    Java實(shí)現(xiàn)自動(dòng)生成縮略圖片

    這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)自動(dòng)生成縮略圖片,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-04-04

最新評(píng)論

夏邑县| 永平县| 紫云| 赤水市| 肥乡县| 东阿县| 大新县| 凤山市| 新营市| 青龙| 高碑店市| 旌德县| 章丘市| 渑池县| 建宁县| 长乐市| 公主岭市| 湖州市| 连云港市| 盘山县| 大邑县| 吉林市| 湛江市| 达拉特旗| 长海县| 蒙山县| 中牟县| 芜湖县| 吐鲁番市| 镇赉县| 乳山市| 山阳县| 吉水县| 礼泉县| 二连浩特市| 利津县| 荣昌县| 扶绥县| 通道| 沿河| 灵丘县|