当前位置:   article > 正文

SpringBoot与MyBatis-Plus整合(01--SqlSessionFactory的注入)_mybatisplus sqlsessionfactory

mybatisplus sqlsessionfactory

前言

梳理MyBatis-Plus与SpringBoot的整合流程,SqlSessionFactory的注入。
说明:MyBatis-Plus版本:3.5.2,SpringBoot版本:2.4.13
  • 1
  • 2

一、流程分析

1、自动装配机制的运用

在pom.xml文件中引入mybatis-plus依赖

<dependency>
 <groupId>com.baomidou</groupId>
 <artifactId>mybatis-plus-boot-starter</artifactId>
 <version>${mybatis-plus.version}</version>
</dependency>
  • 1
  • 2
  • 3
  • 4
  • 5

引入后,在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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

先分析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 {
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

说明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
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64

接着分析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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

然后继续看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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

最后看mybatis的SqlSessionFactoryBuilder【4】,里面构造了一个DefaultSqlSessionFactory

public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
}
  • 1
  • 2
  • 3

2、图解分析

在这里插入图片描述

二、为啥SqlSessionFactory会被创建

1、情形一

创建了一个空项目,引入了MyBatis-Plus后,没有写任何一个Mapper的实现接口或者MyBatis-Plus相关的Service,这时候的SqlSessionFactory这个Bean被实例化仅仅是因为这个BeanDefinition被扫描并加载到了Spring容器中而已, 图解如下:
在这里插入图片描述

2、情形二

第二种情况才是最常见、最重要的,毕竟谁也不会一直跑一个空项目吧,先上一个简单的代码:

@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);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

代码层面很简单,一个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);
    }
	... ...省略部分代码
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

可以看到最后调用了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());
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

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;
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50

在这里插入图片描述
结合上面的图片,我们知道了SqlSessionFactory被注入的第二种情况。

总结

涉及的知识点:Spring的自动装配、依赖注入、FactoryBean的运用以及JDK的动态代理,后面将分析我们业务方法的执行流程以及跟MyBatis-Plus的衔接关系、SqlSessionFactory的作用,毕竟分析了SqlSessionFactory的注入,到底用在什么地方才算重点。

声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号