赞
踩
梳理MyBatis-Plus与SpringBoot的整合流程,SqlSessionFactory的注入。
说明:MyBatis-Plus版本:3.5.2,SpringBoot版本:2.4.13
在pom.xml文件中引入mybatis-plus依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
引入后,在classpath类路径下就会有mybatis-plus的相关依赖,我们首先关注其starter的jar包中的METE-INF/spring.factories文件,内容如下:
org.springframework.boot.env.EnvironmentPostProcessor=\
com.baomidou.mybatisplus.autoconfigure.SafetyEncryptProcessor
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.baomidou.mybatisplus.autoconfigure.IdentifierGeneratorAutoConfiguration,\
com.baomidou.mybatisplus.autoconfigure.MybatisPlusLanguageDriverAutoConfiguration,\
com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration
先分析MybatisPlusAutoConfiguration【1】,
第一,先看该类上的注解:
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class})
@ConditionalOnSingleCandidate(DataSource.class)
@EnableConfigurationProperties(MybatisPlusProperties.class)
@AutoConfigureAfter({DataSourceAutoConfiguration.class, MybatisPlusLanguageDriverAutoConfiguration.class})
public class MybatisPlusAutoConfiguration implements InitializingBean {
说明MyBatis-Pllus需要依赖SqlSessionFactory,SqlSessionFactoryBean,DataSource等。
第二,我们看类上的关键方法sqlSessionFactory,通过Bean结合FactoryBean机制进行SqlSessionFactory的注入(FactoryBean的实际运用实例)
@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); } 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.getBeansThen(IKeyGenerator.class, i -> globalConfig.getDbConfig().setKeyGenerators(i)); // TODO 注入sql注入器 this.getBeanThen(ISqlInjector.class, globalConfig::setSqlInjector); // TODO 注入ID生成器 this.getBeanThen(IdentifierGenerator.class, globalConfig::setIdentifierGenerator); // TODO 设置 GlobalConfig 到 MybatisSqlSessionFactoryBean factory.setGlobalConfig(globalConfig); return factory.getObject();//生成SqlSessionFactory }
接着分析MybatisSqlSessionFactoryBean【2】,看factory.getObject方法(FactoryBean的getObject)
public SqlSessionFactory getObject() throws Exception { if (this.sqlSessionFactory == null) { this.afterPropertiesSet(); } return this.sqlSessionFactory; } @Override public void afterPropertiesSet() throws Exception { notNull(dataSource, "Property 'dataSource' is required"); state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null), "Property 'configuration' and 'configLocation' can not specified with together"); //TODO 清理掉资源 建议不要保留这个玩意了 SqlRunner.DEFAULT.close(); this.sqlSessionFactory = buildSqlSessionFactory(); } protected SqlSessionFactory buildSqlSessionFactory() throws Exception { MybatisXMLConfigBuilder xmlConfigBuilder = null; Object targetConfiguration; ... ... 省略部分代码 SqlSessionFactory sqlSessionFactory = (new MybatisSqlSessionFactoryBuilder()).build((Configuration)targetConfiguration); SqlHelper.FACTORY = sqlSessionFactory; if (this.globalConfig.isBanner()) { System.out.println(" _ _ |_ _ _|_. ___ _ | _ "); System.out.println("| | |\\/|_)(_| | |_\\ |_)||_|_\\ "); System.out.println(" / | "); System.out.println(" " + MybatisPlusVersion.getVersion() + " "); } return sqlSessionFactory; }
然后继续看MybatisSqlSessionFactoryBuilder【3】的build方法, 里面调用了super.build(configuration),该类继承了mybatis的SqlSessionFactoryBuilder类
public SqlSessionFactory build(Configuration configuration) { GlobalConfig globalConfig = GlobalConfigUtils.getGlobalConfig(configuration); Object identifierGenerator; if (null == globalConfig.getIdentifierGenerator()) { identifierGenerator = new DefaultIdentifierGenerator(); globalConfig.setIdentifierGenerator((IdentifierGenerator)identifierGenerator); } else { identifierGenerator = globalConfig.getIdentifierGenerator(); } IdWorker.setIdentifierGenerator((IdentifierGenerator)identifierGenerator); if (globalConfig.isEnableSqlRunner()) { (new SqlRunnerInjector()).inject(configuration); } SqlSessionFactory sqlSessionFactory = super.build(configuration); globalConfig.setSqlSessionFactory(sqlSessionFactory); return sqlSessionFactory; }
最后看mybatis的SqlSessionFactoryBuilder【4】,里面构造了一个DefaultSqlSessionFactory
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
创建了一个空项目,引入了MyBatis-Plus后,没有写任何一个Mapper的实现接口或者MyBatis-Plus相关的Service,这时候的SqlSessionFactory这个Bean被实例化仅仅是因为这个BeanDefinition被扫描并加载到了Spring容器中而已, 图解如下:
第二种情况才是最常见、最重要的,毕竟谁也不会一直跑一个空项目吧,先上一个简单的代码:
@Mapper public interface OrderMapper extends BaseMapper<OrderEntity> { OrderEntity queryTheOne(@Param("id") Long id); } public interface OrderService extends IService<OrderEntity> { OrderEntity getTheOne(Long id); } @Service public class OrderServiceImpl extends ServiceImpl<OrderMapper, OrderEntity> implements OrderService { @Override public OrderEntity getTheOne(Long id) { OrderMapper mapper = this.getBaseMapper(); return mapper.queryTheOne(id); } }
代码层面很简单,一个Mapper接口,一个Service接口,一个Service实现类(继承了ServiceImpl, 且里面注入了一个BaseMapper,这一点很重要,重点强调一下),就这么三个东西。那么这跟SqlSessionFactory有啥关系呢?先从BaseMapper说起,BaseMapper只是一个继承了他的接口OrderMapper,那么这个OrderMapper是怎么被实例化成类的呢?debug模式下,可以看到:
那肯定是在注入当前Service时,要先去注入BaseMapper,在注入BaseMapper时,进行了动态代理(涉及Spring的依赖注入与动态代理),我们在AbstractBeanFactory的doGetBean处打上断点,得到如下截图:
spring根据beanName得到了一个bean实例,但是这个bean实例是一个MapperFactoryBean(实现了FactoryBean),根据spring源码所以要调用FactoryBean的getObject方法取到真正的实例。(备注:又出现了FactoryBean的getObject方法)
//MapperFactoryBean public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> { ... ...省略部分代码 public T getObject() throws Exception { return this.getSqlSession().getMapper(this.mapperInterface); } ... ...省略部分代码 } //MybatisMapperRegistry public <T> T getMapper(Class<T> type, SqlSession sqlSession) { MybatisMapperProxyFactory<T> mapperProxyFactory = (MybatisMapperProxyFactory)this.knownMappers.get(type); if (mapperProxyFactory == null) { mapperProxyFactory = (MybatisMapperProxyFactory)this.knownMappers.entrySet().stream().filter((t) -> { return ((Class)t.getKey()).getName().equals(type.getName()); }).findFirst().map(Map.Entry::getValue).orElseThrow(() -> { return new BindingException("Type " + type + " is not known to the MybatisPlusMapperRegistry."); }); } try { return mapperProxyFactory.newInstance(sqlSession); } catch (Exception var5) { throw new BindingException("Error getting mapper instance. Cause: " + var5, var5); } } public class MybatisMapperProxyFactory<T> { ... ...省略部分代码 protected T newInstance(MybatisMapperProxy<T> mapperProxy) { return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy); } public T newInstance(SqlSession sqlSession) { MybatisMapperProxy<T> mapperProxy = new MybatisMapperProxy(sqlSession, this.mapperInterface, this.methodCache); return this.newInstance(mapperProxy); } ... ...省略部分代码 }
可以看到最后调用了jdk的Proxy.newProxyInstance生成了OrderMapper的代理类,并将MybatisMapperProxy作为代理接口实现类被传入,即属性h,以下是生成的代理类$Proxy60的部分代码:
package com.sun.proxy; ... ... public final class $Proxy60 extends Proxy implements OrderMapper { private static Method m3; public $Proxy60(InvocationHandler var1) throws { super(var1); } public final OrderEntity queryTheOne(Long var1) throws { try { return (OrderEntity)super.h.invoke(this, m3, new Object[]{var1}); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException(var4); } } static { try { ... ... m3 = Class.forName("com.series.business.dao.mapper.OrderMapper").getMethod("queryTheOne", Class.forName("java.lang.Long")); ... ... } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } } }
SqlSessionTemplate部分代码:
public class SqlSessionTemplate implements SqlSession, DisposableBean { private final SqlSessionFactory sqlSessionFactory; private final ExecutorType executorType; private final SqlSession sqlSessionProxy; private final PersistenceExceptionTranslator exceptionTranslator; public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory) { this(sqlSessionFactory, sqlSessionFactory.getConfiguration().getDefaultExecutorType()); } public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType) { this(sqlSessionFactory, executorType, new MyBatisExceptionTranslator(sqlSessionFactory.getConfiguration().getEnvironment().getDataSource(), true)); } public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) { Assert.notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required"); Assert.notNull(executorType, "Property 'executorType' is required"); this.sqlSessionFactory = sqlSessionFactory; this.executorType = executorType; this.exceptionTranslator = exceptionTranslator; this.sqlSessionProxy = (SqlSession)Proxy.newProxyInstance(SqlSessionFactory.class.getClassLoader(), new Class[]{SqlSession.class}, new SqlSessionInterceptor()); } private class SqlSessionInterceptor implements InvocationHandler { private SqlSessionInterceptor() { } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { SqlSession sqlSession = SqlSessionUtils.getSqlSession(SqlSessionTemplate.this.sqlSessionFactory, SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator); Object unwrapped; try { Object result = method.invoke(sqlSession, args); if (!SqlSessionUtils.isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) { sqlSession.commit(true); } unwrapped = result; } catch (Throwable var11) { ... ... } finally { if (sqlSession != null) { SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory); } } return unwrapped; } } }
结合上面的图片,我们知道了SqlSessionFactory被注入的第二种情况。
涉及的知识点:Spring的自动装配、依赖注入、FactoryBean的运用以及JDK的动态代理,后面将分析我们业务方法的执行流程以及跟MyBatis-Plus的衔接关系、SqlSessionFactory的作用,毕竟分析了SqlSessionFactory的注入,到底用在什么地方才算重点。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。