源码梳理(3)MybatisPlus启动流程

文章目录

          • 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));}@Overridepublic 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@Overridepublic 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;}@Overridepublic 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 方法。

    @CallerSensitivepublic 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 {@Overridepublic 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 #22closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);sqlSession = null;// 转化异常后再尝试赋值给unwrappedThrowable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException) unwrapped);if (translated != null) {unwrapped = translated;}}throw unwrapped;} finally {if (sqlSession != null) {// 关闭sqlSessioncloseSqlSession(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;}// 省略...@Overridepublic 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的配置,执行器,是否提交作为参数创建一个DefaultSqlSessionreturn 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 {@Overridepublic <E> List<E> selectList(String statement, Object parameter) {return this.selectList(statement, parameter, RowBounds.DEFAULT);}@Overridepublic <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();// 省略...@Overridepublic <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);}@Overridepublic <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 {@Overridepublic 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}*/@Overridepublic 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 oneLOGGER.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@ConditionalOnMissingBeanpublic SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {// 省略...}
4.2 SqlSessionTemplate对象的创建
    @Bean@ConditionalOnMissingBeanpublic 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 {@Overridepublic 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 AutoConfiguredMapperScannerRegistrarimplements BeanFactoryAware, EnvironmentAware, ImportBeanDefinitionRegistrar {private BeanFactory beanFactory;private Environment environment;@Overridepublic 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.classbuilder.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-nativeBoolean 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());}@Overridepublic void setBeanFactory(BeanFactory beanFactory) {this.beanFactory = beanFactory;}@Overridepublic 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 MapperScannerConfigurerimplements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {private Class<? extends MapperFactoryBean> mapperFactoryBeanClass;// 省略...@Overridepublic 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;// 省略...@Overridepublic T getObject() throws Exception {return getSqlSession().getMapper(this.mapperInterface);}@Overridepublic Class<T> getObjectType() {return this.mapperInterface;}// 省略...
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/676102.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

js基础(1)

操作数组 数组.push() 将一个或多个元素添加到数组末尾&#xff0c;返回数组新长度 数组.unshift() 将一个或多个元素添加到数组末尾&#xff0c;返回数组新长度 数组.pop() 删除最后一个元素&#xff0c;返回该元素的值 更灵活的删除方法&#xff0c;删除指定元素 数组.spli…

Github 2024-02-09 开源项目日报 Top10

根据Github Trendings的统计&#xff0c;今日(2024-02-09统计)共有10个项目上榜。根据开发语言中项目的数量&#xff0c;汇总情况如下&#xff1a; 开发语言项目数量Python项目4Go项目2Scala项目1PLpgSQL项目1Ruby项目1HTML项目1Solidity项目1Lua项目1 开源个人理财应用 Mayb…

如何连接ChatGPT?无需科学上网,使用官方GPT教程

随着AI的发展&#xff0c;ChatGPT也越来越强大了。 它可以帮你做你能想到的几乎任何事情&#xff0c;妥妥的生产力工具。 然而&#xff0c;对于许多国内的用户来说&#xff0c;并不能直接使用ChatGPT&#xff0c;不过没关系&#xff0c;我最近发现了一个可以直接免科学上网连…

【图形图像的C++ 实现 01/20】 2D 和 3D 贝塞尔曲线

目录 一、说明二、贝塞尔曲线特征三、模拟四、全部代码如下​五、资源和下载 一、说明 以下文章介绍了用 C 计算和绘制的贝塞尔曲线&#xff08;2D 和 3D&#xff09;。    贝塞尔曲线具有出色的数学能力来计算路径&#xff08;从起点到目的地点的曲线&#xff09;。曲线的形…

Java汽车销售管理

技术架构&#xff1a; springboot mybatis Mysql5.7 vue2 npm node 有需要该项目的小伙伴可以私信我你的Q。 功能描述&#xff1a; 针对汽车销售提供客户信息、车辆信息、订单信息、销售人员管理、财务报表等功能&#xff0c;提供经理和销售两种角色进行管理 效果图&…

医院挂号预约|医院挂号预约小程序|基于微信小程序的医院挂号预约系统设计与实现(源码+数据库+文档)

医院挂号预约小程序目录 目录 基于微信小程序的医院挂号预约系统设计与实现 一、前言 二、系统功能设计 三、系统实现 1、小程序用户端 2、系统服务端 &#xff08;1&#xff09; 用户管理 &#xff08;2&#xff09;医院管理 &#xff08;3&#xff09;医生管理 &…

Cisco firepower2100系列使用FDM管理FTD

Cisco firepower2100系列使用FDM管理FTD 啥是FDM Firepower Device Manager 当思科Firepower系列运行的FTD镜像时&#xff0c;可以通过2种方式进行管理 第1种方式&#xff1a; FMC (Firepower management Center) 可以进行统一管理&#xff0c;一台FMC可以管理多个FTD&…

[office] 怎么在Excel2003菜单栏自定义一个选项卡 #其他#微信#知识分享

怎么在Excel2003菜单栏自定义一个选项卡 怎么在Excel2003菜单栏自定义一个选项卡 ①启动Excel2003&#xff0c;单击菜单栏--工具--自定义。 ②在自定义界面&#xff0c;我们单击命令标签&#xff0c;在类别中选择新菜单&#xff0c;鼠标左键按住新菜单&#xff0c;拖放到菜单栏…

设计模式巡礼:多板适配案例解析与深度重构

theme: cyanosis 月黑风高&#xff0c;好兄弟发给我一个重构需求&#xff0c;咨询我的意见。 一、 场景分析 开发的产品是需要运行到不同的定制Android板子&#xff0c;不同板子有对应的不同SDK提供的API&#xff0c;目前的业务端&#xff0c;业务流程基本是确定的&#xff0…

Python中HTTP隧道的基本原理与实现

HTTP隧道是一种允许客户端和服务器之间通过中间代理进行通信的技术。这种隧道技术允许代理服务器转发客户端和服务器之间的所有HTTP请求和响应&#xff0c;而不需要对请求或响应内容进行任何处理或解析。Python提供了强大的网络编程能力&#xff0c;可以使用标准库中的socket和…

单片机学习笔记---DS1302实时时钟工作原理

目录 DS1302介绍 学会读芯片手册&#xff08;DS1302芯片手册&#xff09; 封装 引脚定义 电源部分 时钟部分 通信部分 总结列表 内部结构图 电源控制部分 时钟控制部分 寄存器部分 访问部分 寄存器部分的详细定义 命令字 时序的定义 单字节读 单字节写 提前预…

【JAVA WEB】CSS

目录 CSS是什么&#xff1f; 基本语法规范 引入方式 内部样式表 行内样式表 外部样式表 常用选择器的种类 基础选择器 标签选择器 类选择器 id选择器 通配符选择器 复合选择器 后代选择器 伪类选择器 常用元素属性&#xff1a; 字体属性&#xff1a; 文本属性…

Sklearn、TensorFlow 与 Keras 机器学习实用指南第三版(一)

原文&#xff1a;Hands-On Machine Learning with Scikit-Learn, Keras, and TensorFlow 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 前言 机器学习海啸 2006 年&#xff0c;Geoffrey Hinton 等人发表了一篇论文&#xff0c;展示了如何训练一个能够以最先进的精度…

Mac 下JDK环境变量配置 及 JDK多版本切换

一、推荐官网下载&#xff1a; 二、环境变量配置 1、查看JDK地址&#xff0c;在终端输入以下命令&#xff1a; /usr/libexec/java_home -V 我的路径&#xff1a; /Library/Java/JavaVirtualMachines/jdk-17.jdk/Contents/Home /Library/Java/JavaVirtualMachines/zulu-11.j…

图像批量重命名(基于Python,本地运行)

图像批量重命名(基于Python&#xff0c;本地运行) &#x1f335;文章目录&#x1f335; &#x1f333;引言&#x1f333;&#x1f333;场景假设&#x1f333;&#x1f333;知识储备&#x1f333;os.path.splitext方法语法示例 os.listdir方法语法示例 &#x1f333;解决方案&am…

Springboot 整合 Elasticsearch(二):使用HTTP请求来操作ES

&#x1f4c1;前情提要&#xff1a;Springboot整合Elasticsearch&#xff08;一&#xff09;&#xff1a;Linux下安装 Elasticsearch 8.x 目录 一、使用 elasticsearch-head 插件连接 1、下载压缩包 2、在 chrome 浏览器中添加扩展程序 3、修改IP地址&#xff0c;点击连接 …

深度学习系列57: 清华大模型MiniCPM上手

MiniCPM 是面壁智能与清华大学自然语言处理实验室共同开源的系列端侧大模型&#xff0c;主体语言模型 MiniCPM-2B 仅有 24亿&#xff08;2.4B&#xff09;的非词嵌入参数量 1. 上手对比测试 mps比cpu大概快了9倍左右。 也可以在modelspore上测试&#xff1a;

【机器学习】数据清洗之处理缺失点

&#x1f388;个人主页&#xff1a;甜美的江 &#x1f389;欢迎 &#x1f44d;点赞✍评论⭐收藏 &#x1f917;收录专栏&#xff1a;机器学习 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共同学习、交流进步…

【CC++】内存管理1:new + delete

前言 之前我们学习过C语言中的内存管理&#xff08;各种函数&#xff09;今天我们来学习C中的内存管理 引入 我们先来看下面的一段代码和相关问题 int globalVar 1; static int staticGlobalVar 1; void Test() {static int staticVar 1;int localVar 1;int num1[10] {…

Bee+SpringBoot稳定的Sharding、Mongodb ORM功能(同步 Maven)

Hibernate/MyBatis plus Sharding JDBC Jpa Spring data GraphQL App ORM (Android, 鸿蒙) Bee 小巧玲珑&#xff01;仅 860K, 还不到 1M, 但却是功能强大&#xff01; V2.2 (2024春节・LTS 版) 1.Javabean 实体支持继承 (配置 bee.osql.openEntityCanExtendtrue) 2. 增强批…