Mybatis基于MapperScan注解的動態(tài)代理加載機制詳解
1.如下圖在代碼開發(fā)中使用mybatis時,通過一個接口UserDao對應(yīng)的方法selectUserNameById執(zhí)行xml里配置的selectUserNameById查詢sql語句。接口dao沒有具體的實現(xiàn)方法,那真正執(zhí)行時mybatis是通過哪個類的哪個方法去找到對應(yīng)的sql語句并執(zhí)行的?
package com.changshin.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.changshin.entity.po.User;
/**
* 后臺管理用戶表 dao 注意:只是一個接口沒有對應(yīng)的實現(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ù)庫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 #延時加載的開關(guān)
multipleResultSetsEnabled: true #開啟的話,延時加載一個屬性時會加載該對象全部屬性,否則按需加載屬性
在springboot開發(fā)過程中項目集成了mybatis-plus,在集成時做了如下配置,通過@MapperScan來掃描com.changshin.dao包下的dao接口,dao是接口不能直接生成實例,在調(diào)用時只能時通過代理的方式執(zhí)行代理對象的方法。
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分頁插件
*/
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
}進入MapperScan類,在類注解上發(fā)現(xiàn)@Import(MapperScannerRegistrar.class)這行代碼,@Import注解是用來導(dǎo)入配置類或者一些需要前置加載的類,類似原來Spring XML 里面 的 一樣。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class) //關(guān)注該行代碼
@Repeatable(MapperScans.class)
public @interface MapperScan {在啟動springboot項目時,根據(jù)導(dǎo)入的配置類的類型有以下四種處理方式:
1. 如果Abc類實現(xiàn)了ImportSelector接口,spring容器就會實例化Abc類,并且調(diào)用其selectImports方法;
2. DeferredImportSelector是ImportSelector的子類,如果Abc類實現(xiàn)了DeferredImportSelector接口,spring容器就會實例化Abc類,并且調(diào)用其selectImports方法,和ImportSelector的實例不同的是,DeferredImportSelector的實例的selectImports方法調(diào)用時機晚于ImportSelector的實例,要等到@Configuration注解中相關(guān)的業(yè)務(wù)全部都處理完了才會調(diào)用(具體邏輯在ConfigurationClassParser.processDeferredImportSelectors方法中),想了解更多DeferredImportSelector和ImportSelector的區(qū)別,請參考《ImportSelector與DeferredImportSelector的區(qū)別(spring4) 》;
3. 如果Abc類實現(xiàn)了ImportBeanDefinitionRegistrar接口,spring容器就會實例化Abc類,并且調(diào)用其registerBeanDefinitions方法;
4. 如果Abc沒有實現(xiàn)ImportSelector、DeferredImportSelector、ImportBeanDefinitionRegistrar等其中的任何一個,spring容器就會實例化Abc類。
在MapperScannerRegistrar類中實現(xiàn)了ImportBeanDefinitionRegistrar接口。
//實現(xiàn)了ImportBeanDefinitionRegistrar接口
public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {那么spring在初始化上下文時會調(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類實現(xiàn)了BeanDefinitionRegistryPostProcessor接口。這個接口支持自定義beanDefinition的注冊,在標(biāo)準(zhǔn)的注冊完成后(解析xml或者注解),在與實例化對象之前,實現(xiàn)這個接口
public class MapperScannerConfigurer
implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {
//省略....
}實現(xiàn)接口對應(yīng)的postProcessBeanDefinitionRegistry方法,可以修改增加BeanDefinition。
此特性可以用來動態(tài)生成bean,比如讀取某個配置項,然后根據(jù)配置項動態(tài)生成bean。在代碼中
初始化ClassPathMapperScanner類來掃描包里的接口信息
@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沒有scan方法,調(diào)用scan方法時調(diào)用了ClassPathBeanDefinitionScanner里的scan方法,
public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner {
//省略。。。。。
}scan方法中調(diào)用了doScan方法,在繼承時,子類調(diào)用了父類的方法,父類的方法中調(diào)用的方法被子類重寫時會調(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定義注冊到registry中
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}在加載bean定義后執(zhí)行ClassPathMapperScanner的doScan中的processBeanDefinitions方法。在方法中將bean對應(yīng)的class替換成MapperFactoryBean,這樣調(diào)用dao接口時,指向的類被替換成
MapperFactoryBean。
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
GenericBeanDefinition definition;
for (BeanDefinitionHolder holder : beanDefinitions) {
definition = (GenericBeanDefinition) holder.getBeanDefinition();
String beanClassName = definition.getBeanClassName();
//省略。。。。。
//將bean對應(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() + "'.");
//通過AUTOWIRE_BY_TYPE注入bean,注意此處在后面會用到,bean在初始化時會調(diào)用beanClass里的set方法
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
}
definition.setLazyInit(lazyInitialization);
}
}替換前:

替換后:

繼續(xù)研究MapperFactoryBean,該類繼承了SqlSessionDaoSupport類,實現(xiàn)了FactoryBean接口。
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
//省略。。。。。。。。。。。
}在繼承的SqlSessionDaoSupport類中有兩個set方法,分別是setSqlSessionFactory和setSqlSessionTemplate方法,在上面說到MapperFactoryBean在設(shè)置bean定義時,會將AutowireMode設(shè)置為AUTOWIRE_BY_TYPE,bean在初始化時會調(diào)用beanClass里的set方法。
上述調(diào)用set方法可以參考
AbstractAutowireCapableBeanFactory#populateBean方法中調(diào)用的autowireByType方法的邏輯。
在這兩個set方法中setSqlSessionFactory和setSqlSessionTemplate都對sqlSessionTemplate進行了賦值,但是setSqlSessionTemplate后賦值,對setSqlSessionFactory賦的值進行了覆蓋。繼續(xù)觀察setSqlSessionTemplate方法,該方法直接將上下文中的sqlSessionTemplate bean對象直接賦值到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對象的來源是通過MybatisPlusAutoConfiguration類(被@Configuration注解)中sqlSessionTemplate放到上下文管理的。在代碼中@Configuration下的@Bean注解會將方法返回的對象放入上下文管理并且beanName默認為方法名。
@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對象時,SqlSessionFactory作為屬性傳入,SqlSessionFactory對象的生成是在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 對源碼做了一定的修改(因為源碼適配了老舊的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);
在代碼中通過builder方法可以找到MybatisSqlSessionFactoryBuilder類里的builder方法
SqlSessionFactory sqlSessionFactory = super.build(configuration);
通過該方法里的super.build發(fā)現(xiàn)調(diào)用的是父類SqlSessionFactoryBuilder里的builder方法
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}該方法返回的是一個DefaultSqlSessionFactory對象。作為sqlSessionFactory Bean對應(yīng)的對象。也就是說sqlSessionTemplate bean實例里的sqlSessionFactory屬性是一個DefaultSqlSessionFactory類型的實例。
分析完SqlSessionDaoSupport,繼續(xù)分析MapperFactoryBean實現(xiàn)的FactoryBean,FactoryBean是一個工廠Bean,可以生成某一個類型Bean實例,它最大的一個作用是:可以讓我們自定義Bean的創(chuàng)建過程。在FactoryBean中有一個方法getObject可以返回該bean定義的實例。在MapperFactoryBean中查看getObject方法的實現(xiàn)方法。該方法中g(shù)etSqlSession返回sqlSessionTemplate對象。
@Override
public T getObject() throws Exception {
return getSqlSession().getMapper(this.mapperInterface);
}getMapper方法執(zhí)行的是sqlSessionTemplate的getMapper方法。其中g(shù)etConfiguration方法執(zhí)行的是SqlSessionFactory的getConfiguration方法,SqlSessionFactory是一個接口,實現(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方法通過type(mapperInterface即接口全路徑)從knownMappers(HashMap中存儲的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);
}
}通過newInstance方法返回一個MybatisMapperProxy的實例。
@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類型的對象
final MybatisMapperProxy<T> mapperProxy = new MybatisMapperProxy<>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}這樣,這樣客戶只用調(diào)用dao接口實際上調(diào)用的是MybatisMapperProxy類里的invoke方法。
總結(jié):該文章講述了springboot在啟動時是如何通過MapperScan注解的來實現(xiàn)Mybatis動態(tài)代理的,dao接口世界上的代理對象其實就是MybatisMapperProxy類型的實例。下一篇文章我們會講述dao方法是如何同調(diào)用的sql語句對應(yīng)起來的。
到此這篇關(guān)于Mybatis基于MapperScan注解的動態(tài)代理加載機制詳解的文章就介紹到這了,更多相關(guān)Mybatis動態(tài)代理加載內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java8中關(guān)于Function.identity()的使用
這篇文章主要介紹了Java8中關(guān)于Function.identity()的使用,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-05-05
Spring需要三個級別緩存解決循環(huán)依賴原理解析
這篇文章主要為大家介紹了Spring需要三個級別緩存解決循環(huán)依賴原理解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-02-02
解決SpringMvc中普通類注入Service為null的問題
這篇文章主要介紹了解決SpringMvc中普通類注入Service為null的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-07-07
SpringBoot集成netty實現(xiàn)websocket通信功能
Netty是一個高性能、異步事件驅(qū)動的網(wǎng)絡(luò)應(yīng)用框架,用于快速開發(fā)可維護的高性能協(xié)議服務(wù)器和客戶端,WebSocket 是一種網(wǎng)絡(luò)通信協(xié)議,相比傳統(tǒng)的HTTP協(xié)議,本文給大家介紹了SpringBoot集成netty實現(xiàn)websocket通信功能,需要的朋友可以參考下2024-03-03
詳解Lombok安裝及Spring Boot集成Lombok
這篇文章主要介紹了詳解Lombok安裝及Spring Boot集成Lombok,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-03-03

