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

Mybatis基于MapperScan注解的動(dòng)態(tài)代理加載機(jī)制詳解

 更新時(shí)間:2023年01月28日 11:04:56   作者:xl649138628  
這篇文章主要介紹了Mybatis基于MapperScan注解的動(dòng)態(tài)代理加載機(jī)制,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧

1.如下圖在代碼開發(fā)中使用mybatis時(shí),通過(guò)一個(gè)接口UserDao對(duì)應(yīng)的方法selectUserNameById執(zhí)行xml里配置的selectUserNameById查詢sql語(yǔ)句。接口dao沒(méi)有具體的實(shí)現(xiàn)方法,那真正執(zhí)行時(shí)mybatis是通過(guò)哪個(gè)類的哪個(gè)方法去找到對(duì)應(yīng)的sql語(yǔ)句并執(zhí)行的?

package com.changshin.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.changshin.entity.po.User;
/**
 * 后臺(tái)管理用戶表 dao   注意:只是一個(gè)接口沒(méi)有對(duì)應(yīng)的實(shí)現(xiàn)類
 *
 */
public interface UserDao extends BaseMapper<User> {
    public User selectUserNameById(Integer id);
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.changshin.dao.UserDao">
    <select id="selectUserNameById" parameterType="Integer" resultType="com.changshin.entity.po.User">
        select username from t_user where id = #{id}
    </select>
</mapper>

application.yml中關(guān)于mybatis-plus的配置

mybatis-plus:
  mapper-locations: classpath*:mapper/*.xml
  typeAliasesPackage: >
    com.changshin.entity.po
  global-config:
    id-type: 0  # 0:數(shù)據(jù)庫(kù)ID自增   1:用戶輸入id  2:全局唯一id(IdWorker)  3:全局唯一ID(uuid)
    db-column-underline: false
    refresh-mapper: true
  configuration:
    map-underscore-to-camel-case: true
    cache-enabled: true #配置的緩存的全局開關(guān)
    lazyLoadingEnabled: true #延時(shí)加載的開關(guān)
    multipleResultSetsEnabled: true #開啟的話,延時(shí)加載一個(gè)屬性時(shí)會(huì)加載該對(duì)象全部屬性,否則按需加載屬性

在springboot開發(fā)過(guò)程中項(xiàng)目集成了mybatis-plus,在集成時(shí)做了如下配置,通過(guò)@MapperScan來(lái)掃描com.changshin.dao包下的dao接口,dao是接口不能直接生成實(shí)例,在調(diào)用時(shí)只能時(shí)通過(guò)代理的方式執(zhí)行代理對(duì)象的方法。

package com.changshin.config;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
/**
 * MybatisPlus配置
 */
@Configuration
@EnableTransactionManagement(order = 2)
@MapperScan(value = "com.changshin.dao")
public class MybatisPlusConfig {
    /**
     * mybatis-plus分頁(yè)插件
     */
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        return new PaginationInterceptor();
    }
}

進(jìn)入MapperScan類,在類注解上發(fā)現(xiàn)@Import(MapperScannerRegistrar.class)這行代碼,@Import注解是用來(lái)導(dǎo)入配置類或者一些需要前置加載的類,類似原來(lái)Spring XML 里面 的 一樣。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class)  //關(guān)注該行代碼
@Repeatable(MapperScans.class)
public @interface MapperScan {

在啟動(dòng)springboot項(xiàng)目時(shí),根據(jù)導(dǎo)入的配置類的類型有以下四種處理方式:

1. 如果Abc類實(shí)現(xiàn)了ImportSelector接口,spring容器就會(huì)實(shí)例化Abc類,并且調(diào)用其selectImports方法;

2. DeferredImportSelector是ImportSelector的子類,如果Abc類實(shí)現(xiàn)了DeferredImportSelector接口,spring容器就會(huì)實(shí)例化Abc類,并且調(diào)用其selectImports方法,和ImportSelector的實(shí)例不同的是,DeferredImportSelector的實(shí)例的selectImports方法調(diào)用時(shí)機(jī)晚于ImportSelector的實(shí)例,要等到@Configuration注解中相關(guān)的業(yè)務(wù)全部都處理完了才會(huì)調(diào)用(具體邏輯在ConfigurationClassParser.processDeferredImportSelectors方法中),想了解更多DeferredImportSelector和ImportSelector的區(qū)別,請(qǐng)參考《ImportSelector與DeferredImportSelector的區(qū)別(spring4) 》;

3. 如果Abc類實(shí)現(xiàn)了ImportBeanDefinitionRegistrar接口,spring容器就會(huì)實(shí)例化Abc類,并且調(diào)用其registerBeanDefinitions方法;

4. 如果Abc沒(méi)有實(shí)現(xiàn)ImportSelector、DeferredImportSelector、ImportBeanDefinitionRegistrar等其中的任何一個(gè),spring容器就會(huì)實(shí)例化Abc類。

在MapperScannerRegistrar類中實(shí)現(xiàn)了ImportBeanDefinitionRegistrar接口。

//實(shí)現(xiàn)了ImportBeanDefinitionRegistrar接口
public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {

那么spring在初始化上下文時(shí)會(huì)調(diào)用registerBeanDefinitions方法。

  @Override
  public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    AnnotationAttributes mapperScanAttrs = AnnotationAttributes
        .fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
    if (mapperScanAttrs != null) {
     //注意該方法
      registerBeanDefinitions(importingClassMetadata, mapperScanAttrs, registry,
          generateBaseBeanName(importingClassMetadata, 0));
    }
  }

加載MapperScannerConfigurer類的bean定義。

void registerBeanDefinitions(AnnotationMetadata annoMeta, AnnotationAttributes annoAttrs,
      BeanDefinitionRegistry registry, String beanName) {
    //獲取MapperScannerConfigurer的bean定義,注意該行方法
    BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
    builder.addPropertyValue("processPropertyPlaceHolders", true);
 
    Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");
    if (!Annotation.class.equals(annotationClass)) {
      builder.addPropertyValue("annotationClass", annotationClass);
    }
    //省略.....
}

MapperScannerConfigurer類實(shí)現(xiàn)了BeanDefinitionRegistryPostProcessor接口。這個(gè)接口支持自定義beanDefinition的注冊(cè),在標(biāo)準(zhǔn)的注冊(cè)完成后(解析xml或者注解),在與實(shí)例化對(duì)象之前,實(shí)現(xiàn)這個(gè)接口

public class MapperScannerConfigurer
    implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {
    //省略....
}

實(shí)現(xiàn)接口對(duì)應(yīng)的postProcessBeanDefinitionRegistry方法,可以修改增加BeanDefinition。

此特性可以用來(lái)動(dòng)態(tài)生成bean,比如讀取某個(gè)配置項(xiàng),然后根據(jù)配置項(xiàng)動(dòng)態(tài)生成bean。在代碼中

初始化ClassPathMapperScanner類來(lái)掃描包里的接口信息

@Override
  public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
    if (this.processPropertyPlaceHolders) {
      //該方法可以加載包信息
      //PropertyValues values = mapperScannerBean.getPropertyValues();
      //this.basePackage = updatePropertyValue("basePackage", values);
      processPropertyPlaceHolders();
    }
    ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
    scanner.setAddToConfig(this.addToConfig);
    scanner.setAnnotationClass(this.annotationClass);
    scanner.setMarkerInterface(this.markerInterface);
    scanner.setSqlSessionFactory(this.sqlSessionFactory);
    scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
    scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
    scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
    scanner.setResourceLoader(this.applicationContext);
    scanner.setBeanNameGenerator(this.nameGenerator);
    scanner.setMapperFactoryBeanClass(this.mapperFactoryBeanClass);
    if (StringUtils.hasText(lazyInitialization)) {
      scanner.setLazyInitialization(Boolean.valueOf(lazyInitialization));
    }
    scanner.registerFilters();
    //掃描mapperscan里配置的com.changshin.dao包
    scanner.scan(
        StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
  }

ClassPathMapperScanner繼承了ClassPathBeanDefinitionScanner,因ClassPathMapperScanner沒(méi)有scan方法,調(diào)用scan方法時(shí)調(diào)用了ClassPathBeanDefinitionScanner里的scan方法,

public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner {
    //省略。。。。。
}

scan方法中調(diào)用了doScan方法,在繼承時(shí),子類調(diào)用了父類的方法,父類的方法中調(diào)用的方法被子類重寫時(shí)會(huì)調(diào)用子類的重寫方法而不是父類自己的方法,ClassPathBeanDefinitionScanner里的doScan方法,調(diào)用的并非本類的doScan方法而是ClassPathMapperScanner重寫的doScan方法。

public int scan(String... basePackages) {
		int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
        //注意該方法調(diào)用的并非是ClassPathBeanDefinitionScanner里的doScan方法,而是ClassPathMapperScanner重寫的doScan方法
		doScan(basePackages);
		// Register annotation config processors, if necessary.
		if (this.includeAnnotationConfig) {
			AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
		}
		return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
	}

在ClassPathMapperScanner的doScan方法中調(diào)用父類ClassPathBeanDefinitionScanner的doScan加載bean定義

@Override
  public Set<BeanDefinitionHolder> doScan(String... basePackages) {
    //調(diào)用父類ClassPathBeanDefinitionScanner的doScan加載bean定義
    Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
    if (beanDefinitions.isEmpty()) {
      LOGGER.warn(() -> "No MyBatis mapper was found in '" + Arrays.toString(basePackages)
          + "' package. Please check your configuration.");
    } else {
      //注意該方法
      processBeanDefinitions(beanDefinitions);
    }
    return beanDefinitions;
  }
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
		Assert.notEmpty(basePackages, "At least one base package must be specified");
		Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
		for (String basePackage : basePackages) {
            //獲取符合要求的bean定義
			Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
			for (BeanDefinition candidate : candidates) {
				ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
				candidate.setScope(scopeMetadata.getScopeName());
				String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
				if (candidate instanceof AbstractBeanDefinition) {
					postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
				}
				if (candidate instanceof AnnotatedBeanDefinition) {
					AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
				}
				if (checkCandidate(beanName, candidate)) {
					BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
					definitionHolder =
							AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
					beanDefinitions.add(definitionHolder);
                    //將bean定義注冊(cè)到registry中
					registerBeanDefinition(definitionHolder, this.registry);
				}
			}
		}
		return beanDefinitions;
	}

在加載bean定義后執(zhí)行ClassPathMapperScanner的doScan中的processBeanDefinitions方法。在方法中將bean對(duì)應(yīng)的class替換成MapperFactoryBean,這樣調(diào)用dao接口時(shí),指向的類被替換成

MapperFactoryBean。

private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
    GenericBeanDefinition definition;
    for (BeanDefinitionHolder holder : beanDefinitions) {
      definition = (GenericBeanDefinition) holder.getBeanDefinition();
      String beanClassName = definition.getBeanClassName();
      //省略。。。。。
      //將bean對(duì)應(yīng)的class替換成MapperFactoryBean
      //private Class<? extends MapperFactoryBean> mapperFactoryBeanClass = MapperFactoryBean.class;  
      definition.setBeanClass(this.mapperFactoryBeanClass);
      //省略。。。。。
      if (!explicitFactoryUsed) {
        LOGGER.debug(() -> "Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
        //通過(guò)AUTOWIRE_BY_TYPE注入bean,注意此處在后面會(huì)用到,bean在初始化時(shí)會(huì)調(diào)用beanClass里的set方法
        definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
      }
      definition.setLazyInit(lazyInitialization);
    }
  }

替換前:

替換后:

繼續(xù)研究MapperFactoryBean,該類繼承了SqlSessionDaoSupport類,實(shí)現(xiàn)了FactoryBean接口。

public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
    //省略。。。。。。。。。。。
}

在繼承的SqlSessionDaoSupport類中有兩個(gè)set方法,分別是setSqlSessionFactory和setSqlSessionTemplate方法,在上面說(shuō)到MapperFactoryBean在設(shè)置bean定義時(shí),會(huì)將AutowireMode設(shè)置為AUTOWIRE_BY_TYPE,bean在初始化時(shí)會(huì)調(diào)用beanClass里的set方法。

上述調(diào)用set方法可以參考

AbstractAutowireCapableBeanFactory#populateBean方法中調(diào)用的autowireByType方法的邏輯。

在這兩個(gè)set方法中setSqlSessionFactory和setSqlSessionTemplate都對(duì)sqlSessionTemplate進(jìn)行了賦值,但是setSqlSessionTemplate后賦值,對(duì)setSqlSessionFactory賦的值進(jìn)行了覆蓋。繼續(xù)觀察setSqlSessionTemplate方法,該方法直接將上下文中的sqlSessionTemplate bean對(duì)象直接賦值到SqlSessionDaoSupport的sqlSessionTemplate屬性中。

public abstract class SqlSessionDaoSupport extends DaoSupport {
  private SqlSessionTemplate sqlSessionTemplate;
  /**
   * Set MyBatis SqlSessionFactory to be used by this DAO. Will automatically create SqlSessionTemplate for the given
   * SqlSessionFactory.
   *
   * @param sqlSessionFactory
   *          a factory of SqlSession
   */
  public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
    if (this.sqlSessionTemplate == null || sqlSessionFactory != this.sqlSessionTemplate.getSqlSessionFactory()) {
      this.sqlSessionTemplate = createSqlSessionTemplate(sqlSessionFactory);
    }
  }
  /**
   * Create a SqlSessionTemplate for the given SqlSessionFactory. Only invoked if populating the DAO with a
   * SqlSessionFactory reference!
   * <p>
   * Can be overridden in subclasses to provide a SqlSessionTemplate instance with different configuration, or a custom
   * SqlSessionTemplate subclass.
   * 
   * @param sqlSessionFactory
   *          the MyBatis SqlSessionFactory to create a SqlSessionTemplate for
   * @return the new SqlSessionTemplate instance
   * @see #setSqlSessionFactory
   */
  @SuppressWarnings("WeakerAccess")
  protected SqlSessionTemplate createSqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
    return new SqlSessionTemplate(sqlSessionFactory);
  }
  /**
   * Return the MyBatis SqlSessionFactory used by this DAO.
   *
   * @return a factory of SqlSession
   */
  public final SqlSessionFactory getSqlSessionFactory() {
    return (this.sqlSessionTemplate != null ? this.sqlSessionTemplate.getSqlSessionFactory() : null);
  }
  /**
   * Set the SqlSessionTemplate for this DAO explicitly, as an alternative to specifying a SqlSessionFactory.
   *
   * @param sqlSessionTemplate
   *          a template of SqlSession
   * @see #setSqlSessionFactory
   */
  public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
    this.sqlSessionTemplate = sqlSessionTemplate;
  }
  /**
   * Users should use this method to get a SqlSession to call its statement methods This is SqlSession is managed by
   * spring. Users should not commit/rollback/close it because it will be automatically done.
   *
   * @return Spring managed thread safe SqlSession
   */
  public SqlSession getSqlSession() {
    return this.sqlSessionTemplate;
  }
  /**
   * Return the SqlSessionTemplate for this DAO, pre-initialized with the SessionFactory or set explicitly.
   * <p>
   * <b>Note: The returned SqlSessionTemplate is a shared instance.</b> You may introspect its configuration, but not
   * modify the configuration (other than from within an {@link #initDao} implementation). Consider creating a custom
   * SqlSessionTemplate instance via {@code new SqlSessionTemplate(getSqlSessionFactory())}, in which case you're
   * allowed to customize the settings on the resulting instance.
   *
   * @return a template of SqlSession
   */
  public SqlSessionTemplate getSqlSessionTemplate() {
    return this.sqlSessionTemplate;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  protected void checkDaoConfig() {
    notNull(this.sqlSessionTemplate, "Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required");
  }
}

 sqlSessionTemplate bean對(duì)象的來(lái)源是通過(guò)MybatisPlusAutoConfiguration類(被@Configuration注解)中sqlSessionTemplate放到上下文管理的。在代碼中@Configuration下的@Bean注解會(huì)將方法返回的對(duì)象放入上下文管理并且beanName默認(rèn)為方法名。

    @Bean
    @ConditionalOnMissingBean
    public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
        ExecutorType executorType = this.properties.getExecutorType();
        if (executorType != null) {
            return new SqlSessionTemplate(sqlSessionFactory, executorType);
        } else {
            return new SqlSessionTemplate(sqlSessionFactory);
        }
    }

在生成SqlSessionTemplate bean對(duì)象時(shí),SqlSessionFactory作為屬性傳入,SqlSessionFactory對(duì)象的生成是在MybatisPlusAutoConfiguration類的sqlSessionFactory方法獲取的。在該方法中的

factory.getObject()調(diào)用了MybatisSqlSessionFactoryBean中的getObject方法。

    @Bean
    @ConditionalOnMissingBean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        // TODO 使用 MybatisSqlSessionFactoryBean 而不是 SqlSessionFactoryBean
        MybatisSqlSessionFactoryBean factory = new MybatisSqlSessionFactoryBean();
        factory.setDataSource(dataSource);
        factory.setVfs(SpringBootVFS.class);
        if (StringUtils.hasText(this.properties.getConfigLocation())) {
            factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));
        }
        applyConfiguration(factory);
        if (this.properties.getConfigurationProperties() != null) {
            factory.setConfigurationProperties(this.properties.getConfigurationProperties());
        }
        if (!ObjectUtils.isEmpty(this.interceptors)) {
            factory.setPlugins(this.interceptors);
        }
        if (this.databaseIdProvider != null) {
            factory.setDatabaseIdProvider(this.databaseIdProvider);
        }
        if (StringUtils.hasLength(this.properties.getTypeAliasesPackage())) {
            factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage());
        }
        if (this.properties.getTypeAliasesSuperType() != null) {
            factory.setTypeAliasesSuperType(this.properties.getTypeAliasesSuperType());
        }
        if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) {
            factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());
        }
        if (!ObjectUtils.isEmpty(this.typeHandlers)) {
            factory.setTypeHandlers(this.typeHandlers);
        }
        // 注意該行代碼獲取xml配置路徑
        Resource[] mapperLocations = this.properties.resolveMapperLocations();
        if (!ObjectUtils.isEmpty(mapperLocations)) {
            factory.setMapperLocations(mapperLocations);
        }
        // TODO 修改源碼支持定義 TransactionFactory
        this.getBeanThen(TransactionFactory.class, factory::setTransactionFactory);
        // TODO 對(duì)源碼做了一定的修改(因?yàn)樵创a適配了老舊的mybatis版本,但我們不需要適配)
        Class<? extends LanguageDriver> defaultLanguageDriver = this.properties.getDefaultScriptingLanguageDriver();
        if (!ObjectUtils.isEmpty(this.languageDrivers)) {
            factory.setScriptingLanguageDrivers(this.languageDrivers);
        }
        Optional.ofNullable(defaultLanguageDriver).ifPresent(factory::setDefaultScriptingLanguageDriver);
        // TODO 自定義枚舉包
        if (StringUtils.hasLength(this.properties.getTypeEnumsPackage())) {
            factory.setTypeEnumsPackage(this.properties.getTypeEnumsPackage());
        }
        // TODO 此處必為非 NULL
        GlobalConfig globalConfig = this.properties.getGlobalConfig();
        // TODO 注入填充器
        this.getBeanThen(MetaObjectHandler.class, globalConfig::setMetaObjectHandler);
        // TODO 注入主鍵生成器
        this.getBeanThen(IKeyGenerator.class, i -> globalConfig.getDbConfig().setKeyGenerator(i));
        // TODO 注入sql注入器
        this.getBeanThen(ISqlInjector.class, globalConfig::setSqlInjector);
        // TODO 注入ID生成器
        this.getBeanThen(IdentifierGenerator.class, globalConfig::setIdentifierGenerator);
        // TODO 設(shè)置 GlobalConfig 到 MybatisSqlSessionFactoryBean
        factory.setGlobalConfig(globalConfig);
        //注意該行方法
        return factory.getObject();
    }

MybatisSqlSessionFactoryBean#getObject方法中的afterPropertiesSet方法中的buildSqlSessionFactory方法有一段這樣的代碼

final SqlSessionFactory sqlSessionFactory = new MybatisSqlSessionFactoryBuilder().build(targetConfiguration);

在代碼中通過(guò)builder方法可以找到MybatisSqlSessionFactoryBuilder類里的builder方法

SqlSessionFactory sqlSessionFactory = super.build(configuration);

通過(guò)該方法里的super.build發(fā)現(xiàn)調(diào)用的是父類SqlSessionFactoryBuilder里的builder方法

 public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
  }

該方法返回的是一個(gè)DefaultSqlSessionFactory對(duì)象。作為sqlSessionFactory Bean對(duì)應(yīng)的對(duì)象。也就是說(shuō)sqlSessionTemplate bean實(shí)例里的sqlSessionFactory屬性是一個(gè)DefaultSqlSessionFactory類型的實(shí)例。

分析完SqlSessionDaoSupport,繼續(xù)分析MapperFactoryBean實(shí)現(xiàn)的FactoryBean,FactoryBean是一個(gè)工廠Bean,可以生成某一個(gè)類型Bean實(shí)例,它最大的一個(gè)作用是:可以讓我們自定義Bean的創(chuàng)建過(guò)程。在FactoryBean中有一個(gè)方法getObject可以返回該bean定義的實(shí)例。在MapperFactoryBean中查看getObject方法的實(shí)現(xiàn)方法。該方法中g(shù)etSqlSession返回sqlSessionTemplate對(duì)象。

  @Override
  public T getObject() throws Exception {
    return getSqlSession().getMapper(this.mapperInterface);
  }

getMapper方法執(zhí)行的是sqlSessionTemplate的getMapper方法。其中g(shù)etConfiguration方法執(zhí)行的是SqlSessionFactory的getConfiguration方法,SqlSessionFactory是一個(gè)接口,實(shí)現(xiàn)類是DefaultSqlSessionFactory。getConfiguration方法獲取的是getConfiguration方法獲取的是DefaultSqlSessionFactory中的configuration屬性,該屬性是MybatisConfiguration類型的。

  @Override
  public <T> T getMapper(Class<T> type) {
    return getConfiguration().getMapper(type, this);
  }

MybatisConfiguration中的getMapper方法是從MybatisMapperRegistry類的getMapper方法。

   public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
        return mybatisMapperRegistry.getMapper(type, sqlSession);
    }

MybatisMapperRegistry#getMapper方法通過(guò)type(mapperInterface即接口全路徑)從knownMappers(HashMap中存儲(chǔ)的MybatisMapperProxyFactory)中獲取MybatisMapperProxyFactory

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
        // TODO 這里換成 MybatisMapperProxyFactory 而不是 MapperProxyFactory
        final MybatisMapperProxyFactory<T> mapperProxyFactory = (MybatisMapperProxyFactory<T>) knownMappers.get(type);
        if (mapperProxyFactory == null) {
            throw new BindingException("Type " + type + " is not known to the MybatisPlusMapperRegistry.");
        }
        try {
            //注意該行代碼
            return mapperProxyFactory.newInstance(sqlSession);
        } catch (Exception e) {
            throw new BindingException("Error getting mapper instance. Cause: " + e, e);
        }
    }

通過(guò)newInstance方法返回一個(gè)MybatisMapperProxy的實(shí)例。

 @SuppressWarnings("unchecked")
    protected T newInstance(MybatisMapperProxy<T> mapperProxy) {
        return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[]{mapperInterface}, mapperProxy);
    }
    public T newInstance(SqlSession sqlSession) {
        //該sqlSession是sqlSessionTemplate類型的對(duì)象
        final MybatisMapperProxy<T> mapperProxy = new MybatisMapperProxy<>(sqlSession, mapperInterface, methodCache);
        return newInstance(mapperProxy);
    }

這樣,這樣客戶只用調(diào)用dao接口實(shí)際上調(diào)用的是MybatisMapperProxy類里的invoke方法。

總結(jié):該文章講述了springboot在啟動(dòng)時(shí)是如何通過(guò)MapperScan注解的來(lái)實(shí)現(xiàn)Mybatis動(dòng)態(tài)代理的,dao接口世界上的代理對(duì)象其實(shí)就是MybatisMapperProxy類型的實(shí)例。下一篇文章我們會(huì)講述dao方法是如何同調(diào)用的sql語(yǔ)句對(duì)應(yīng)起來(lái)的。

到此這篇關(guān)于Mybatis基于MapperScan注解的動(dòng)態(tài)代理加載機(jī)制詳解的文章就介紹到這了,更多相關(guān)Mybatis動(dòng)態(tài)代理加載內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java中easypoi導(dǎo)入excel文件列名相同的處理方案

    Java中easypoi導(dǎo)入excel文件列名相同的處理方案

    這篇文章主要介紹了Java中easypoi導(dǎo)入excel文件列名相同的處理方案,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-06-06
  • Java中List與數(shù)組之間的相互轉(zhuǎn)換

    Java中List與數(shù)組之間的相互轉(zhuǎn)換

    在日常Java學(xué)習(xí)或項(xiàng)目開發(fā)中,經(jīng)常會(huì)遇到需要int[]數(shù)組和List列表相互轉(zhuǎn)換的場(chǎng)景,然而往往一時(shí)難以想到有哪些方法,最后可能會(huì)使用暴力逐個(gè)轉(zhuǎn)換法,往往不是我們所滿意的,下面這篇文章主要給大家介紹了關(guān)于Java中List與數(shù)組之間的相互轉(zhuǎn)換,需要的朋友可以參考下
    2023-05-05
  • Java注解實(shí)現(xiàn)動(dòng)態(tài)數(shù)據(jù)源切換的實(shí)例代碼

    Java注解實(shí)現(xiàn)動(dòng)態(tài)數(shù)據(jù)源切換的實(shí)例代碼

    本篇文章主要介紹了Java注解實(shí)現(xiàn)動(dòng)態(tài)數(shù)據(jù)源切換的實(shí)例代碼,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-06-06
  • SpringBoot基于MyBatis-Plus實(shí)現(xiàn)Lambda Query查詢的示例代碼

    SpringBoot基于MyBatis-Plus實(shí)現(xiàn)Lambda Query查詢的示例代碼

    MyBatis-Plus 是 MyBatis 的增強(qiáng)工具,簡(jiǎn)化了數(shù)據(jù)庫(kù)操作,并提高了開發(fā)效率,它提供了多種查詢方式,包括常規(guī)的 SQL 查詢、Lambda Query 查詢、分頁(yè)查詢、條件查詢等,在本篇博客中,我們將詳細(xì)講解如何使用 MyBatis-Plus 的各種查詢方式,需要的朋友可以參考下
    2025-01-01
  • 如何實(shí)現(xiàn)Java線程安全問(wèn)題

    如何實(shí)現(xiàn)Java線程安全問(wèn)題

    這篇文章主要介紹了如何實(shí)現(xiàn)Java線程安全問(wèn)題,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-12-12
  • Mybatis結(jié)果集映射與生命周期詳細(xì)介紹

    Mybatis結(jié)果集映射與生命周期詳細(xì)介紹

    結(jié)果集映射指的是將數(shù)據(jù)表中的字段與實(shí)體類中的屬性關(guān)聯(lián)起來(lái),這樣 MyBatis 就可以根據(jù)查詢到的數(shù)據(jù)來(lái)填充實(shí)體對(duì)象的屬性,幫助我們完成賦值操作
    2022-10-10
  • 解讀@EventListener工作原理

    解讀@EventListener工作原理

    這篇文章主要介紹了@EventListener工作原理,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-08-08
  • Java使用正則提取字符串中的內(nèi)容的詳細(xì)步驟

    Java使用正則提取字符串中的內(nèi)容的詳細(xì)步驟

    這篇文章主要介紹了Java中使用正則表達(dá)式提取字符串內(nèi)容的方法,通過(guò)Pattern和Matcher類實(shí)現(xiàn),涵蓋編譯正則、查找匹配、分組捕獲、數(shù)字與郵箱提取場(chǎng)景,以及命名分組、非貪婪匹配等技巧,需要的朋友可以參考下
    2025-08-08
  • java如何獲取本機(jī)IP地址

    java如何獲取本機(jī)IP地址

    這篇文章主要為大家詳細(xì)介紹了java如何獲取本機(jī)IP地址,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-03-03
  • Java.toCharArray()和charAt()的效率對(duì)比分析

    Java.toCharArray()和charAt()的效率對(duì)比分析

    這篇文章主要介紹了Java.toCharArray()和charAt()的效率對(duì)比分析,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-10-10

最新評(píng)論

苍山县| 喀什市| 襄垣县| 泌阳县| 合山市| 金湖县| 龙游县| 威远县| 德保县| 丹棱县| 泰宁县| 阿城市| 新昌县| 甘孜| 金寨县| 丘北县| 勃利县| 巨野县| 西林县| 吐鲁番市| 靖西县| 黄石市| 宁城县| 尉犁县| 松滋市| 翁牛特旗| 商都县| 苏尼特左旗| 竹溪县| 航空| 探索| 怀远县| 五台县| 仁寿县| 夏津县| 鄄城县| 安庆市| 丰城市| 西充县| 伊川县| 读书|