文章目录
- 1,MybatisPlus的使用示例
- 2,BaseMapper方法的执行
- 2,1 MybatisMapperProxy代理对象
- 2.2 InvocationHandler接口(JDK动态代理)
- 2.3 MapperMethodInvoker接口
- 2.4 MybatisMapperMethod
- 3,SqlSession的执行流程
- 3.1 SqlSessionTemplate
- 3.2 Proxy类中的newProxyInstance(JDK动态代理)
- 3.3 SqlSession的创建,执行,关闭
- 3.4 DefaultSqlSession的创建
- 3.5 DefaultSqlSession的方法执行
- 4,MybatisPlusAutoConfiguration自动装配
- 4.1 SqlSessionFactory 对象的创建
- 4.2 SqlSessionTemplate对象的创建
- 4.3 MapperScannerConfigurer
源码版本springboot3.0.2,mybatis-plus-spring-boot3-starter3.5.5,mybatis3.5.15
1,MybatisPlus的使用示例
开始前先简单定义一个mybatisPlus的简单使用示例,为后面的分析准备
要操作表的实体类
@TableName(value = "json_type", autoResultMap = true)
@Data
@Accessors(chain = true)
public class JsonType {
@TableId(type = IdType.AUTO)
private Integer id;
}
mapper接口
@Mapper
public interface JsonTypeMapper extends BaseMapper<JsonType> {}
service接口
public interface JsonTypeService {
List<JsonType> findAll();
JsonType findOne(JsonType jsonType);
}
service实现类
@Service
public class JsonTypeServiceImpl extends ServiceImpl<JsonTypeMapper, JsonType> implements JsonTypeService {
public List<JsonType> findAll() {
return baseMapper.selectList(Wrappers.lambdaQuery(JsonType.class));
}
@Override
public JsonType findOne(JsonType jsonType) {
LambdaQueryWrapper<JsonType> wrapper = Wrappers.lambdaQuery(JsonType.class)
.eq(jsonType.getId() != null, JsonType::getId, jsonType.getId())
.eq(jsonType.getName() != null && !"".equals(jsonType.getName()), JsonType::getName, jsonType.getName());
return this.baseMapper.selectOne(wrapper);
}
}
2,BaseMapper方法的执行
2,1 MybatisMapperProxy代理对象
当程序启动后,在执行service方法的时候,容器中继承BaseMapper的JsonTypeMapper 实例是MybatisMapperProxy类型的代理对象
MybatisMapperProxy实现了InvocationHandler接口
2.2 InvocationHandler接口(JDK动态代理)
InvocationHandler 它是实现JDK动态代理的关键部分之一。它包含一个方法 invoke(),用于处理在代理对象上调用方法时的行为。
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
MybatisMapperProxy在实现InvocationHandler接口后重写了invoke方法,前面的baseMapper.selectList(Wrappers.lambdaQuery(JsonType.class))就会通过invoke方法来处理
// MybatisMapperProxy.class
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else {
// 先执行cachedInvoker方法获取到MapperMethodInvoker实例
// 核心环节:再执行MapperMethodInvoker实例的invoke方法
return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
2.3 MapperMethodInvoker接口
MybatisMapperProxy的invoke方法中会先执行cachedInvoker方法获取到MapperMethodInvoker实例
private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
try {
return CollectionUtils.computeIfAbsent(methodCache, method, m -> {
// 判断是否是接口的default方法
if (m.isDefault()) {
try {
if (privateLookupInMethod == null) {
return new DefaultMethodInvoker(getMethodHandleJava8(method));
} else {
return new DefaultMethodInvoker(getMethodHandleJava9(method));
}
} catch (IllegalAccessException | InstantiationException | InvocationTargetException
| NoSuchMethodException e) {
throw new RuntimeException(e);
}
} else {
return new PlainMethodInvoker(new MybatisMapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
}
});
}
}
在MybatisMapperProxy调用cachedInvoker方法的时候,会先获取到一个MapperMethodInvoker实例,MapperMethodInvoker接口有两种实现类型,一种是PlainMethodInvoker(用于Mybatis默认方法的调用实现),另一种是DefaultMethodInvoker(用于处理有默认实现的接口方法)
public interface BaseMapper<T> extends Mapper<T> {
default T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper, boolean throwEx) {
List<T> list = this.selectList(queryWrapper);
int size = list.size();
if (size == 1) {
return list.get(0);
} else if (size > 1) {
if (throwEx) {
throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + size);
}
return list.get(0);
}
return null;
}
List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
}
例如上面BaseMapper接口中的selectList方法是没有默认实现的,而selectOne方法提供了默认实现。那selectList到底是怎么从数据库查到数据的呢,接下来就要看获取到的MapperMethodInvoker(也就是PlainMethodInvoker )实例的invoke方法都做了些什么
private static class PlainMethodInvoker implements MapperMethodInvoker {
private final MybatisMapperMethod mapperMethod;
public PlainMethodInvoker(MybatisMapperMethod mapperMethod) {
super();
this.mapperMethod = mapperMethod;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
// 核心方法,执行MybatisMapperMethod的execute方法
return mapperMethod.execute(sqlSession, args);
}
}
2.4 MybatisMapperMethod
在PlainMethodInvoker的invoke方法中,直接返回了MybatisMapperMethod实例执行execute方法的结果。
类中的两个属性:
MapperMethod.SqlCommand 用于表示 Mapper 方法对应的 SQL 语句信息。它包含了 SQL 语句的类型,ID(statement ID)、SQL 语句的字符串以及参数映射信息等。
MapperMethod.MethodSignature 用于表示 Mapper 方法的签名信息。它包含了方法的返回类型、参数类型以及其他相关信息。
在execute方法中经过一些判断,参数的转化之后,会将本次查询的操作交给SqlSession处理,也是MybatisMapperProxy中的SqlSession(这个SqlSession怎么来的后面会分析)
/**
* 从 {@link MapperMethod} copy 过来 </br>
* <p> 不要内部类 ParamMap </p>
* <p> 不要内部类 SqlCommand </p>
* <p> 不要内部类 MethodSignature </p>
*
* @author miemie
* @since 2018-06-09
*/
public class MybatisMapperMethod {
private final MapperMethod.SqlCommand command;
private final MapperMethod.MethodSignature method;
public MybatisMapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
this.command = new MapperMethod.SqlCommand(config, mapperInterface, method);
this.method = new MapperMethod.MethodSignature(config, mapperInterface, method);
}
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
switch (command.getType()) {
case INSERT: {
// 省略...
}
case UPDATE: {
// 省略...
}
case DELETE: {
// 省略...
}
case SELECT:
if (method.returnsVoid() && method.hasResultHandler()) {
// 省略...
} else if (method.returnsMany()) {
// 我们前面调用的方法会继续执行到这里
result = executeForMany(sqlSession, args);
} else {
// 省略...
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
throw new BindingException("Unknown execution method for: " + command.getName());
}
// 省略...
return result;
}
// 省略...
private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
List<E> result;
// 将 Args 转换为 Sql 命令参数
Object param = method.convertArgsToSqlCommandParam(args);
if (method.hasRowBounds()) {
RowBounds rowBounds = method.extractRowBounds(args);
result = sqlSession.selectList(command.getName(), param, rowBounds);
} else {
// 核心环节,执行sqlSession的selectList的方法
result = sqlSession.selectList(command.getName(), param);
}
// issue #510 Collections & arrays support
// 判断method.getReturnType()的类型是否可以被result.getClass()的类型赋值
if (!method.getReturnType().isAssignableFrom(result.getClass())) {
if (method.getReturnType().isArray()) {
return convertToArray(result);
} else {
return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
}
}
return result;
}
// 省略...
}
3,SqlSession的执行流程
SqlSession用于 MyBatis 的主要 Java 接口。通过此界面,您可以执行命令、获取映射器和管理事务
SqlSession有三个实现类,这里执行selectList的对象是从MybatisMapperProxy传递过来SqlSessionTemplate实例。
3.1 SqlSessionTemplate
SqlSessionTemplate用于提供对 Mapper 方法的调用和 SQL 语句的执行。
在SqlSessionTemplate的构造器中通过Proxy类的.new ProwyInstance方法又创建了一个SqlSession的代理对象,并赋值给属性sqlSessionProxy
public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
PersistenceExceptionTranslator exceptionTranslator) {
notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
notNull(executorType, "Property 'executorType' is required");
this.sqlSessionFactory = sqlSessionFactory;
this.executorType = executorType;
this.exceptionTranslator = exceptionTranslator;
this.sqlSessionProxy = (SqlSession) newProxyInstance(SqlSessionFactory.class.getClassLoader(),
new Class[] { SqlSession.class }, new SqlSessionInterceptor());
}
3.2 Proxy类中的newProxyInstance(JDK动态代理)
这个方法接收三个参数:
loader: 用于加载代理类的类加载器。
interfaces: 代理类需要实现的接口列表。
h: 代理类的调用处理程序,也就是拦截器。
当调用 newProxyInstance 方法时,它会返回一个代理对象,该对象实现了指定的接口列表,在调用代理对象的方法时,实际上会触发调用拦截器的 invoke 方法。
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h) {
Objects.requireNonNull(h);
@SuppressWarnings("removal")
final Class<?> caller = System.getSecurityManager() == null ? null : Reflection.getCallerClass();
/*
* Look up or generate the designated proxy class and its constructor.
*/
Constructor<?> cons = getProxyConstructor(caller, loader, interfaces);
return newProxyInstance(caller, cons, h);
}
this.sqlSessionProxy = (SqlSession) newProxyInstance(SqlSessionFactory.class.getClassLoader(),
new Class[] { SqlSession.class }, new SqlSessionInterceptor());
3.3 SqlSession的创建,执行,关闭
所以SqlSessionTemplate中的属性sqlSessionProxy 代理对象,它的加载器使用SqlSessionFactory的类加载器,并且需要实现SqlSession接口的方法,在对象方法被调用时,通过SqlSessionInterceptor的invoke方法处理
SqlSessionInterceptor的invoke方法处理了SqlSession的创建,执行,关闭
private class SqlSessionInterceptor implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 获取SqlSession对象
SqlSession sqlSession = getSqlSession(SqlSessionTemplate.this.sqlSessionFactory,
SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);
try {
// 通过反射执行sqlSession的方法获取结果
Object result = method.invoke(sqlSession, args);
// 判断会话是不是开启事务的
if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
// 如果没有开启事务则强制提交
// force commit even on non-dirty sessions because some databases require
// a commit/rollback before calling close()
sqlSession.commit(true);
}
return result;
} catch (Throwable t) {
Throwable unwrapped = unwrapThrowable(t);
// 判断是否设置了异常转换器(exceptionTranslator)且捕获到的异常是PersistenceException类型
if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
// release the connection to avoid a deadlock if the translator is no loaded. See issue #22
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
sqlSession = null;
// 转化异常后再尝试赋值给unwrapped
Throwable translated = SqlSessionTemplate.this.exceptionTranslator
.translateExceptionIfPossible((PersistenceException) unwrapped);
if (translated != null) {
unwrapped = translated;
}
}
throw unwrapped;
} finally {
if (sqlSession != null) {
// 关闭sqlSession
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
}
}
}
}
3.4 DefaultSqlSession的创建
这里又通过getSqlSession获取到了一个SqlSession实例,也是真正来执行接口方法(本次示例中是SqlSession的selectList方法)的是实例,先去getSqlSession方法里看看这次获取到的sqlSession又是什么类型,传入的三个参数sqlSessionFactory,executorType,exceptionTranslator都是在构建SqlSessionTemplate时赋值的,后面会分析到的。
Mybatis在同一个同一个事务中,只有存在一个sqlSession,所以这里通过sessionHolder尝试获取当前事务的sqlSession,如果没有获取到,则通过sessionFactory的openSession开启一个sqlSession
public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType,
PersistenceExceptionTranslator exceptionTranslator) {
notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);
notNull(executorType, NO_EXECUTOR_TYPE_SPECIFIED);
// 从事务同步管理器中获取一个 SqlSessionHolder 对象。SqlSessionHolder是一个包含SqlSession 实例的持有者对象,可能在事务刚开始时就已经存放在事务上下文中
SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
// 从 SqlSessionHolder 对象中取出 SqlSession 实例。如果能够取到 SqlSession 实例,则直接返回
SqlSession session = sessionHolder(executorType, holder);
if (session != null) {
return session;
}
LOGGER.debug(() -> "Creating a new SqlSession");
// 开启一个sql会话
session = sessionFactory.openSession(executorType);
// 调用 registerSessionHolder() 方法将新创建的 SqlSession 实例和其他相关信息注册到事务同步管理器中。这样,在同一个事务中的其他操作就可以通过事务上下文获取到这个 SqlSession 实例。
registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);
return session;
}
DefaultSqlSessionFactory的openSession获取到DefaultSqlSession
public class DefaultSqlSessionFactory implements SqlSessionFactory {
private final Configuration configuration;
public DefaultSqlSessionFactory(Configuration configuration) {
this.configuration = configuration;
}
// 省略...
@Override
public SqlSession openSession(TransactionIsolationLevel level) {
return openSessionFromDataSource(configuration.getDefaultExecutorType(), level, false);
}
// 省略...
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level,
boolean autoCommit) {
Transaction tx = null;
try {
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
// 通过mybatis的配置类创建一个执行器(默认是开启缓存的,所以是CachingExecutor)
final Executor executor = configuration.newExecutor(tx, execType);
// 将mybatis的配置,执行器,是否提交作为参数创建一个DefaultSqlSession
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
closeTransaction(tx); // may have fetched a connection so lets call close()
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
// 省略...
}
3.5 DefaultSqlSession的方法执行
DefaultSqlSession 是 MyBatis 框架中的一个核心类,实现了 SqlSession 接口。它用于执行 SQL 语句并与持久层交互,提供了对数据库的增删改查操作.。
在前面SqlSessionInterceptor的invoke方法里,也将具体的执行SQL语句交给了前面创建的defalutSqlSession
public class DefaultSqlSession implements SqlSession {
@Override
public <E> List<E> selectList(String statement, Object parameter) {
return this.selectList(statement, parameter, RowBounds.DEFAULT);
}
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
return selectList(statement, parameter, rowBounds, Executor.NO_RESULT_HANDLER);
}
private <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
try {
MappedStatement ms = configuration.getMappedStatement(statement);
dirty |= ms.isDirtySelect();
return executor.query(ms, wrapCollection(parameter), rowBounds, handler);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
}
具体的执行逻辑会交给executor,在CachingExecutor的query方法执行过程中,先尝试从mybatis的一级缓存中获取数据,如果没有获取到,再进行数据库查询,拿到结果
public class CachingExecutor implements Executor {
private final Executor delegate;
private final TransactionalCacheManager tcm = new TransactionalCacheManager();
// 省略...
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler)
throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameterObject);
CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler,
CacheKey key, BoundSql boundSql) throws SQLException {
Cache cache = ms.getCache();
if (cache != null) {
flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {
ensureNoOutParams(ms, boundSql);
@SuppressWarnings("unchecked")
List<E> list = (List<E>) tcm.getObject(cache, key);
if (list == null) {
list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
tcm.putObject(cache, key, list); // issue #578 and #116
}
return list;
}
}
return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
}
最后执行SqlSessionInterceptor 拦截器invoke 方法中的closeSqlSession方法,如果不存在事务则会直接关闭会话,如果存在事务不会立马关闭。
在事务结束后,在Mybatis提供SqlSessionSynchronization类的afterCompletion方法中,会关闭sqlSession。
SqlSessionSynchronization实现了TransactionSynchronization事务同步接口,重写了事务的回调方法。
private class SqlSessionInterceptor implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
SqlSession sqlSession = getSqlSession(SqlSessionTemplate.this.sqlSessionFactory,
SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);
try {
Object result = method.invoke(sqlSession, args);
// 省略...
} finally {
if (sqlSession != null) {
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
}
}
}
}
private static final class SqlSessionSynchronization implements TransactionSynchronization {
private final SqlSessionHolder holder;
private final SqlSessionFactory sessionFactory;
private boolean holderActive = true;
// 省略...
/**
* {@inheritDoc}
*/
@Override
public void afterCompletion(int status) {
if (this.holderActive) {
// afterCompletion may have been called from a different thread
// so avoid failing if there is nothing in this one
LOGGER
.debug(() -> "Transaction synchronization deregistering SqlSession [" + this.holder.getSqlSession() + "]");
TransactionSynchronizationManager.unbindResourceIfPossible(sessionFactory);
this.holderActive = false;
LOGGER.debug(() -> "Transaction synchronization closing SqlSession [" + this.holder.getSqlSession() + "]");
this.holder.getSqlSession().close();
}
this.holder.reset();
}
}
4,MybatisPlusAutoConfiguration自动装配
前面在执行我们定义的mapper方法时,涉及到了MybatisMapperProxy,SqlSession等一系列组件。
比如在使用MybatisPlus的时候我们都会定义一些添加Mapper注解的接口(如下面的代码示例),这些接口我们不会自己去实现,它们都将在应用启动后被注册为MybatisMapperProxy代理对象并存放在容器中。当这些实例是怎么创建的呢,现在我们去MybatisPlusAutoConfiguration类里看看
@Mapper
public interface JsonTypeMapper extends BaseMapper<JsonType> {}
4.1 SqlSessionFactory 对象的创建
@Bean
@ConditionalOnMissingBean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
// 省略...
}
4.2 SqlSessionTemplate对象的创建
@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);
}
}
4.3 MapperScannerConfigurer
在MybatisPlusAutoConfiguration类中配置了这样一个类,它表示如果不存在MapperFactoryBean或者MapperScannerConfigurer对象,我就要把AutoConfiguredMapperScannerRegistrar导入到容器里了,并且在属性注入之后打个debug日志
@org.springframework.context.annotation.Configuration(proxyBeanMethods = false)
@Import(AutoConfiguredMapperScannerRegistrar.class)
@ConditionalOnMissingBean({MapperFactoryBean.class, MapperScannerConfigurer.class})
public static class MapperScannerRegistrarNotFoundConfiguration implements InitializingBean {
@Override
public void afterPropertiesSet() {
logger.debug(
"Not found configuration for registering mapper bean using @MapperScan, MapperFactoryBean and MapperScannerConfigurer.");
}
}
要被导入的AutoConfiguredMapperScannerRegistrar是MybatisPlusAutoConfiguration的内部类,它实现了ImportBeanDefinitionRegistrar接口,并且重写了registerBeanDefinitions方法。
这里要先说一下registerBeanDefinitions方法会在Spring容器初始化的早期执行,用于注册BeanDefinition,而在这个我们AutoConfiguredMapperScannerRegistrar的registerBeanDefinitions方法中,它注册了一个类型为MapperScannerConfigurer的BeanDefinition
public static class AutoConfiguredMapperScannerRegistrar
implements BeanFactoryAware, EnvironmentAware, ImportBeanDefinitionRegistrar {
private BeanFactory beanFactory;
private Environment environment;
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
if (!AutoConfigurationPackages.has(this.beanFactory)) {
logger.debug("Could not determine auto-configuration package, automatic mapper scanning disabled.");
return;
}
logger.debug("Searching for mappers annotated with @Mapper");
List<String> packages = AutoConfigurationPackages.get(this.beanFactory);
if (logger.isDebugEnabled()) {
packages.forEach(pkg -> logger.debug("Using auto-configuration base package '{}'", pkg));
}
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
builder.addPropertyValue("processPropertyPlaceHolders", true);
// 这些addPropertyValue方法都是给要注册的BeanDefinition的属性赋值,瞧,这里给annotationClass属性赋了Mapper.class
builder.addPropertyValue("annotationClass", Mapper.class);
builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(packages));
BeanWrapper beanWrapper = new BeanWrapperImpl(MapperScannerConfigurer.class);
Set<String> propertyNames = Stream.of(beanWrapper.getPropertyDescriptors()).map(PropertyDescriptor::getName)
.collect(Collectors.toSet());
if (propertyNames.contains("lazyInitialization")) {
// Need to mybatis-spring 2.0.2+
builder.addPropertyValue("lazyInitialization", "${mybatis-plus.lazy-initialization:${mybatis.lazy-initialization:false}}");
}
if (propertyNames.contains("defaultScope")) {
// Need to mybatis-spring 2.0.6+
builder.addPropertyValue("defaultScope", "${mybatis-plus.mapper-default-scope:}");
}
// for spring-native
Boolean injectSqlSession = environment.getProperty("mybatis-plus.inject-sql-session-on-mapper-scan", Boolean.class);
if (injectSqlSession == null) {
injectSqlSession = environment.getProperty("mybatis.inject-sql-session-on-mapper-scan", Boolean.class, Boolean.TRUE);
}
if (injectSqlSession && this.beanFactory instanceof ListableBeanFactory) {
ListableBeanFactory listableBeanFactory = (ListableBeanFactory) this.beanFactory;
Optional<String> sqlSessionTemplateBeanName = Optional
.ofNullable(getBeanNameForType(SqlSessionTemplate.class, listableBeanFactory));
Optional<String> sqlSessionFactoryBeanName = Optional
.ofNullable(getBeanNameForType(SqlSessionFactory.class, listableBeanFactory));
if (sqlSessionTemplateBeanName.isPresent() || !sqlSessionFactoryBeanName.isPresent()) {
builder.addPropertyValue("sqlSessionTemplateBeanName",
sqlSessionTemplateBeanName.orElse("sqlSessionTemplate"));
} else {
builder.addPropertyValue("sqlSessionFactoryBeanName", sqlSessionFactoryBeanName.get());
}
}
builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(MapperScannerConfigurer.class.getName(), builder.getBeanDefinition());
}
@Override
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
@Override
public void setEnvironment(Environment environment) {
this.environment = environment;
}
private String getBeanNameForType(Class<?> type, ListableBeanFactory factory) {
String[] beanNames = factory.getBeanNamesForType(type);
return beanNames.length > 0 ? beanNames[0] : null;
}
}
这里的MapperScannerConfigurer实现了BeanDefinitionRegistryPostProcessor接口重写了postProcessBeanDefinitionRegistry方法,postProcessBeanDefinitionRegistry方法也是在BeanDefinition注册到容器之前调用的,这里在postProcessBeanDefinitionRegistry方法中创建了一个ClassPathMapperScanner扫描器,这类又继承了ClassPathBeanDefinitionScanner 用于用于扫描指定路径下的类,并将其转化为BeanDefinition。
ClassPathMapperScanner对象会在执行扫描前设置annotationClass的值为Mapper.class用于对扫描结果过滤,如果在@MapperScan注解中配置扫描路径,则会扫描该路径,否则扫描springboot默认的包扫描路径。
在执行完doScan方法之后,那些带Mapper接口就已经注册为BeanDefinition了
public class MapperScannerConfigurer
implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {
private Class<? extends MapperFactoryBean> mapperFactoryBeanClass;
// 省略...
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
if (this.processPropertyPlaceHolders) {
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));
}
if (StringUtils.hasText(defaultScope)) {
scanner.setDefaultScope(defaultScope);
}
scanner.registerFilters();
scanner.scan(
StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}
}
在ClassPathMapperScanner里有一个setMapperFactoryBeanClass的set方法,确保mapperFactoryBeanClass的属性为MapperFactoryBean
public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner {
public void setMapperFactoryBeanClass(Class<? extends MapperFactoryBean> mapperFactoryBeanClass) {
this.mapperFactoryBeanClass = mapperFactoryBeanClass == null ? MapperFactoryBean.class : mapperFactoryBeanClass;
}
}
MapperFactoryBean是一个FactoryBean并且继承了SqlSessionDaoSupport ,这个类的作用是通过getObject创建代理对象,
MybatisMapperProxy
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
private Class<T> mapperInterface;
// 省略...
@Override
public T getObject() throws Exception {
return getSqlSession().getMapper(this.mapperInterface);
}
@Override
public Class<T> getObjectType() {
return this.mapperInterface;
}
// 省略...
}