亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

深入探究Java?@MapperScan實(shí)現(xiàn)原理

 更新時(shí)間:2023年01月04日 09:51:29   作者:程序員小潘  
之前是直接在Mapper類上面添加注解@Mapper,這種方式要求每一個(gè)mapper類都需要添加此注解,麻煩。通過(guò)使用@MapperScan可以指定要掃描的Mapper類的包的路徑,這篇文章深入探究Java?@MapperScan的實(shí)現(xiàn)原理

1. 前言

MyBatis在整合Spring的時(shí)候,只需要加如下 注解,就可以將Mapper實(shí)例注冊(cè)到IOC容器交給Spring管理,它是怎么做到的呢???

@MapperScan("com.xxx.mapper")

提出幾個(gè)問(wèn)題:

  • Mapper接口不能實(shí)例化,對(duì)象是怎么來(lái)的?
  • Mapper接口沒有加任何Spring相關(guān)注解,Spring憑什么管理這些Bean?

2. ImportBeanDefinitionRegistrar

ImportBeanDefinitionRegistrar是Spring提供的接口,屬于Spring的擴(kuò)展點(diǎn)之一。該接口會(huì)暴露 BeanDefinitionRegistry對(duì)象,Spring允許我們手動(dòng)往容器注冊(cè)自定義的BeanDefinition。

public interface ImportBeanDefinitionRegistrar {
	/**
	 * 注冊(cè)自定義BeanDefinition
	 *
	 * @param importingClassMetadata 導(dǎo)入類的元數(shù)據(jù),被誰(shuí)導(dǎo)入的
	 * @param registry BeanDefinition注冊(cè)器
	 */
	void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);
}

使用起來(lái)也很簡(jiǎn)單,新建類實(shí)現(xiàn)ImportBeanDefinitionRegistrar接口,重寫registerBeanDefinitions()方法實(shí)現(xiàn)注冊(cè)自定義BeanDefinition的相關(guān)邏輯,然后通過(guò)@Import注解引入即可。

ImportBeanDefinitionRegistrar實(shí)例本身并不會(huì)注冊(cè)到容器,Spring僅僅是通過(guò)反射實(shí)例化對(duì)象,然后觸發(fā)registerBeanDefinitions()方法而已。

3. ConfigurationClassPostProcessor

ImportBeanDefinitionRegistrar擴(kuò)展點(diǎn)是在哪里被觸發(fā)的呢???

AnnotationConfigApplicationContext類的構(gòu)造函數(shù)里會(huì)創(chuàng)建AnnotatedBeanDefinitionReader對(duì)象用來(lái)讀取并注冊(cè)基于注解的BeanDefinition,AnnotatedBeanDefinitionReader的構(gòu)造函數(shù)有一個(gè)特別重要的功能,就是往容器手動(dòng)注冊(cè)Spring內(nèi)置的幾個(gè)非常重要的,用來(lái)支撐Spring底層核心功能的BeanDefinition,分別是:

  • ConfigurationClassPostProcessor
  • AutowiredAnnotationBeanPostProcessor
  • CommonAnnotationBeanPostProcessor
  • PersistenceAnnotationBeanPostProcessor
  • EventListenerMethodProcessor
  • DefaultEventListenerFactory

ConfigurationClassPostProcessor這個(gè)類特別特別重要,它做的事情包括:

  • 解析@ComponentScan注解掃描自定義的Bean。
  • 解析@PropertySources@Value注解讀取配置文件屬性。
  • 解析@Import注解引入自定義類。
  • 解析@ImportResource注解引入外部Spring配置文件。
  • 處理@Bean注解方法。

ConfigurationClassPostProcessor實(shí)現(xiàn)了BeanFactoryPostProcessor和BeanDefinitionRegistryPostProcessor接口。BeanFactoryPostProcessor也是Spring的擴(kuò)展點(diǎn)之一,它允許開發(fā)者對(duì)BeanFactory進(jìn)行擴(kuò)展;BeanDefinitionRegistryPostProcessor擴(kuò)展的語(yǔ)義更明確一些,它表示要對(duì)BeanFactory完成BeanDefinition的注冊(cè)。BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry()會(huì)比BeanFactoryPostProcessor#postProcessBeanFactory()先執(zhí)行。

Spring啟動(dòng)時(shí),準(zhǔn)備好BeanFactory后就會(huì)開始觸發(fā)BeanFactoryPostProcessor擴(kuò)展點(diǎn),ConfigurationClassPostProcessor因?yàn)樵跇?gòu)造函數(shù)里已經(jīng)被注冊(cè)到容器中,所以會(huì)被執(zhí)行到。它會(huì)去解析ConfigurationClass是否有加@Import注解,如果加了該注解,且引入的類是ImportBeanDefinitionRegistrar子類,就會(huì)去實(shí)例化子類對(duì)象,然后執(zhí)行它的registerBeanDefinitions()方法。

4. MapperScannerRegistrar

查看@MapperScan注解發(fā)現(xiàn),它的確加了@Import注解,且引入的MapperScannerRegistrar類就是ImportBeanDefinitionRegistrar的子類。

@Retention(RetentionPolicy . RUNTIME)

@Target(ElementType.TYPE)

@Documented

@Import(MapperScannerRegistrar.class)

public @interface MapperScan {

也就是說(shuō)Spring在啟動(dòng)時(shí),觸發(fā)ImportBeanDefinitionRegistrar擴(kuò)展點(diǎn)的時(shí)候,會(huì)執(zhí)行MyBatis寫的MapperScannerRegistrar的擴(kuò)展邏輯。其實(shí)從名字就可以看的出來(lái),這個(gè)類的作用是完成MapperScanner的注冊(cè)工作,MapperScanner是啥?就是Mapper接口的掃描器了。

MapperScannerRegistrar的擴(kuò)展邏輯很簡(jiǎn)單,創(chuàng)建自定義的Bean掃描器ClassPathMapperScanner,然后掃描@MapperScan注解指定的包路徑。

public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
  private ResourceLoader resourceLoader;
  @Override
  public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
  	// 注解屬性
    AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
    // 創(chuàng)建自定義的Mapper掃描器,用來(lái)掃描Mapper接口
    ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
    // this check is needed in Spring 3.1
    if (resourceLoader != null) {
      scanner.setResourceLoader(resourceLoader);
    }
    Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");
    if (!Annotation.class.equals(annotationClass)) {
      // 如果指定了注解,則只掃描加了指定注解的Mapper接口
      scanner.setAnnotationClass(annotationClass);
    }
    Class<?> markerInterface = annoAttrs.getClass("markerInterface");
    if (!Class.class.equals(markerInterface)) {
      scanner.setMarkerInterface(markerInterface);
    }
    Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator");
    if (!BeanNameGenerator.class.equals(generatorClass)) {
      // 指定BeanName生成器,如果有
      scanner.setBeanNameGenerator(BeanUtils.instantiateClass(generatorClass));
    }
    Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");
    if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {
      scanner.setMapperFactoryBean(BeanUtils.instantiateClass(mapperFactoryBeanClass));
    }
    scanner.setSqlSessionTemplateBeanName(annoAttrs.getString("sqlSessionTemplateRef"));
    scanner.setSqlSessionFactoryBeanName(annoAttrs.getString("sqlSessionFactoryRef"));
    List<String> basePackages = new ArrayList<String>();
    for (String pkg : annoAttrs.getStringArray("value")) {
      if (StringUtils.hasText(pkg)) {
        basePackages.add(pkg);
      }
    }
    for (String pkg : annoAttrs.getStringArray("basePackages")) {
      if (StringUtils.hasText(pkg)) {
        basePackages.add(pkg);
      }
    }
    for (Class<?> clazz : annoAttrs.getClassArray("basePackageClasses")) {
      basePackages.add(ClassUtils.getPackageName(clazz));
    }
    // 注冊(cè)過(guò)濾器,定義Bean的掃描規(guī)則
    scanner.registerFilters();
    // 開始掃描
    scanner.doScan(StringUtils.toStringArray(basePackages));
  }
  /**
* {@inheritDoc}
*/
  @Override
  public void setResourceLoader(ResourceLoader resourceLoader) {
    this.resourceLoader = resourceLoader;
  }
}

具體的掃描工作交給了ClassPathMapperScanner類,它繼承自Spring提供的ClassPathBeanDefinitionScanner,就不用自己去實(shí)現(xiàn)掃描Class的邏輯了,這里用到了模板方法模式,子類通過(guò)重寫部分方法,來(lái)自定義Bean的掃描和注冊(cè)規(guī)則。

@Override
  public Set<BeanDefinitionHolder> doScan(String... basePackages) {
    // 父類完成掃描,得到一組BeanDefinition
    Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
    if (beanDefinitions.isEmpty()) {
      // 沒有符合的Bean,不做處理
      logger.warn("No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
    } else {
      // 處理BeanDefinition,因?yàn)镸apper接口不能被實(shí)例化
      processBeanDefinitions(beanDefinitions);
    }
    return beanDefinitions;
  }

調(diào)用父類的doScan()方法完成掃描得到一組BeanDefinition,如果有符合規(guī)則的BeanDefinition,這里需要做處理,不能直接返回。因?yàn)榇藭r(shí)BeanDefinition的beanClass指向的是Mapper接口,直接注冊(cè)到容器的話,Spring不知道怎么實(shí)例化Bean。 所以,MyBatis還需要做點(diǎn)小動(dòng)作,對(duì)BeanDefinition做一些修改。主要是重設(shè)beanClass,將其指向MapperFactoryBean。因?yàn)镸apperFactoryBean是類,可以被實(shí)例化。

private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
  GenericBeanDefinition definition;
  for (BeanDefinitionHolder holder : beanDefinitions) {
    definition = (GenericBeanDefinition) holder.getBeanDefinition();
    // MapperFactoryBean構(gòu)造函數(shù)需要MapperClass
    // 這里是告訴Spring實(shí)例化MapperFactoryBean時(shí)構(gòu)造函數(shù)傳哪個(gè)Class
    definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName());
    // 重設(shè)beanClass 指向MapperFactoryBean
    definition.setBeanClass(this.mapperFactoryBean.getClass());
    definition.getPropertyValues().add("addToConfig", this.addToConfig);
    boolean explicitFactoryUsed = false;
    if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
      definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
      explicitFactoryUsed = true;
    } else if (this.sqlSessionFactory != null) {
      definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
      explicitFactoryUsed = true;
    }
    if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
      if (explicitFactoryUsed) {
        logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
      }
      definition.getPropertyValues().add("sqlSessionTemplate", new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
      explicitFactoryUsed = true;
    } else if (this.sqlSessionTemplate != null) {
      if (explicitFactoryUsed) {
        logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
      }
      definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
      explicitFactoryUsed = true;
    }
    if (!explicitFactoryUsed) {
      if (logger.isDebugEnabled()) {
        logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
      }
      definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
    }
  }
}

5. MapperFactoryBean

Mapper接口不能被實(shí)例化,所以MyBatis會(huì)將掃描到的屬于MapperClass的BeanDefinition做些修改,將beanClass指向MapperFactoryBean,這樣Spring在實(shí)例化Bean的時(shí)候就會(huì)去創(chuàng)建MapperFactoryBean實(shí)例了。

MapperFactoryBean實(shí)現(xiàn)了FactoryBean接口,SpringgetBean()時(shí)會(huì)判斷,如果一個(gè)BeanClass實(shí)現(xiàn)了FactoryBean接口,則不直接返回bean,而是返回FactoryBean#getObject()方法返回的對(duì)象。也就是說(shuō),本該由Spring完成的Bean實(shí)例化過(guò)程,交給了MyBatis自己來(lái)實(shí)現(xiàn)。

@Override
  public T getObject() throws Exception {
    // 基于Mapper接口生成代理對(duì)象
    return getSqlSession().getMapper(this.mapperInterface);
}

通過(guò)MapperFactoryBean#getObject()發(fā)現(xiàn),MyBatis會(huì)調(diào)用SqlSession#getMapper()方法基于Mapper接口創(chuàng)建JDK動(dòng)態(tài)代理對(duì)象。也就是說(shuō),Mapper接口對(duì)應(yīng)的BeanDefinition,對(duì)應(yīng)的在Spring容器里的對(duì)象是MyBatis生成的代理對(duì)象。

到此這篇關(guān)于深入探究Java @MapperScan實(shí)現(xiàn)原理的文章就介紹到這了,更多相關(guān)Java @MapperScan內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java Spring詳解如何配置數(shù)據(jù)源注解開發(fā)以及整合Junit

    Java Spring詳解如何配置數(shù)據(jù)源注解開發(fā)以及整合Junit

    Spring 是目前主流的 Java Web 開發(fā)框架,是 Java 世界最為成功的框架。該框架是一個(gè)輕量級(jí)的開源框架,具有很高的凝聚力和吸引力,本篇文章帶你了解如何配置數(shù)據(jù)源、注解開發(fā)以及整合Junit
    2021-10-10
  • IDEA2023.1.3安裝教程及下載(圖文)

    IDEA2023.1.3安裝教程及下載(圖文)

    最新變化是在IDEA?2023.1中,對(duì)新UI做出了大量改進(jìn),本文主要介紹了IDEA2023.1.3安裝教程及下載,具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-10-10
  • ConstraintValidator類如何實(shí)現(xiàn)自定義注解校驗(yàn)前端傳參

    ConstraintValidator類如何實(shí)現(xiàn)自定義注解校驗(yàn)前端傳參

    這篇文章主要介紹了ConstraintValidator類實(shí)現(xiàn)自定義注解校驗(yàn)前端傳參的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-06-06
  • FeignMultipartSupportConfig上傳圖片配置方式

    FeignMultipartSupportConfig上傳圖片配置方式

    這篇文章主要介紹了FeignMultipartSupportConfig上傳圖片配置方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • JAVA反射機(jī)制實(shí)例教程

    JAVA反射機(jī)制實(shí)例教程

    這篇文章主要介紹了JAVA反射機(jī)制,包括了Java反射機(jī)制的各種應(yīng)用技巧,非常具有實(shí)用價(jià)值,需要的朋友可以參考下
    2014-09-09
  • Java編程實(shí)現(xiàn)的模擬行星運(yùn)動(dòng)示例

    Java編程實(shí)現(xiàn)的模擬行星運(yùn)動(dòng)示例

    這篇文章主要介紹了Java編程實(shí)現(xiàn)的模擬行星運(yùn)動(dòng),涉及java基于swing組建繪制動(dòng)態(tài)效果及數(shù)值運(yùn)算相關(guān)操作技巧,并總結(jié)分析了java面向?qū)ο蟮南嚓P(guān)特性,需要的朋友可以參考下
    2018-04-04
  • Java NIO原理圖文分析及代碼實(shí)現(xiàn)

    Java NIO原理圖文分析及代碼實(shí)現(xiàn)

    本文主要介紹Java NIO原理的知識(shí),這里整理了詳細(xì)資料及簡(jiǎn)單示例代碼和原理圖,有需要的小伙伴可以參考下
    2016-09-09
  • Java中WeakHashMap的回收問(wèn)題詳解

    Java中WeakHashMap的回收問(wèn)題詳解

    這篇文章主要介紹了Java中WeakHashMap的回收問(wèn)題詳解,WeakHashMap弱鍵大致上是通過(guò)WeakReference和ReferenceQueue實(shí)現(xiàn),WeakHashMap的key是"弱鍵",即是WeakReference類型的,ReferenceQueue是一個(gè)隊(duì)列,它會(huì)保存被GC回收的"弱鍵",需要的朋友可以參考下
    2023-09-09
  • Mybatis Properties 配置優(yōu)先級(jí)詳解

    Mybatis Properties 配置優(yōu)先級(jí)詳解

    這篇文章主要介紹了Mybatis Properties 配置優(yōu)先級(jí),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • JAVA入門教學(xué)之快速搭建基本的springboot(從spring boot到spring cloud)

    JAVA入門教學(xué)之快速搭建基本的springboot(從spring boot到spring cloud)

    本文主要入門者介紹怎么搭建一個(gè)基礎(chǔ)的springboot環(huán)境,本文通過(guò)圖文并茂的形式給大家介紹從spring boot到spring cloud的完美搭建過(guò)程,適用java入門教學(xué),需要的朋友可以參考下
    2021-02-02

最新評(píng)論