一、整合配置
POM
<!-- mybatis框架 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.3</version>
</dependency>
<!--mybatis-spring适配器 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.3</version>
</dependency>
需要上面两个依赖的源代码。
版本匹配:
在spring上下文中配置SqlSessionFactoryBean,并将一些配置信息都设置到SqlSessionFactoryBean中:
xml
<!-- 配置数据源 -->
<bean id="datasource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
<!--驱动类名 -->
<property name="driverClass" value="${jdbc.driver}" />
<!-- url -->
<property name="jdbcUrl" value="${jdbc.url}" />
<!-- 用户名 -->
<property name="user" value="${jdbc.uid}" />
<!-- 密码 -->
<property name="password" value="${jdbc.pwd}" />
</bean>
<!-- 会话工厂bean sqlSessionFactoryBean -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 数据源 -->
<property name="dataSource" ref="xxxxx"></property>
<!-- 别名 -->
<property name="typeAliasesPackage" value="xxxxx"></property>
<!-- sql映射文件路径 -->
<property name="mapperLocations" value="xxxxx"></property>
</bean>
javaConfig
//<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
@Bean
public SqlSessionFactoryBean sqlSessionFactory( ) throws IOException {
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(dataSource());
// 设置MyBatis配置文件路径
factoryBean.setConfigLocation(new ClassPathResource("mybatis/mybatis-config.xml"));
// 设置SQL映射文件路径
factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mybatis/mapper/*.xml"));
// 设置别名
factoryBean.setTypeAliases(User.class);
return factoryBean;
}
//<bean id="datasource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
public DataSource dataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUsername("root");
dataSource.setPassword("123456");
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/mybatis");
return dataSource;
}
二、源码分析
1.构建SqlSessionFactory
SqlSessionFactoryBean实现InitializingBean,会在初始化Bean时调用afterPropertiesSet,其中的buildSqlSessionFactory就等同于SqlSessionFactoryBuilder().build()方法,用于构建SqlSessionFactory
@Override
public void afterPropertiesSet() throws Exception {
...
//通过sqlSessionFactoryBuilder来构建sqlSessionFactory
//等同于new SqlSessionFactoryBuilder().build();
this.sqlSessionFactory = buildSqlSessionFactory();
}
buildSqlSessionFactory,将SqlSessionFactoryBean中设置的属性设置到Configuration对象中,Configuration对象用于保存mybatis的所有的配置信息。
protected SqlSessionFactory buildSqlSessionFactory() throws Exception {
final Configuration targetConfiguration;
XMLConfigBuilder xmlConfigBuilder = null;
...
//解析mapper映射文件
XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
targetConfiguration, mapperLocation.toString(), targetConfiguration.getSqlFragments());
xmlMapperBuilder.parse();
...
//通过建造者模式构建DefaultSqlSessionFactory
return this.sqlSessionFactoryBuilder.build(targetConfiguration);
}
2.mybatis-spring事务集成
如果在Mybatis中配置了事务,那么Mybatis中的事务要和Spring中的事务集成在一起。
Spring开启事务时会获取Connection,并最终存到TransactionSynchronizationManager中。Mybatis想要集成Spring的事务,就需要拿到Spring开启事务时获取的Connection,具体逻辑源码:
1.当buildSqlSessionFactory时,会设置environment,当transactionFactory为null的时候会创建一个新的mybatis-spring适配的事务工厂类SpringManagedTransactionFactory
targetConfiguration.setEnvironment(new Environment(this.environment,
this.transactionFactory == null ? new SpringManagedTransactionFactory() : this.transactionFactory,
this.dataSource));
2.当调用SpringManagedTransactionFactory的newTransaction时,会创建一个SpringManagedTransaction
public class SpringManagedTransactionFactory implements TransactionFactory {
@Override
public Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit) {
return new SpringManagedTransaction(dataSource);
...
}
3.当mybatis获取connection时就会调用SpringManagedTransaction的getConnection,最终获取到的connection就是TransactionSynchronizationManager中的Connection
public class SpringManagedTransaction implements Transaction {
...
@Override
public Connection getConnection() throws SQLException {
if (this.connection == null) {
openConnection();
}
return this.connection;
}
private void openConnection() throws SQLException {
this.connection = DataSourceUtils.getConnection(this.dataSource);
....
}
DataSourceUtils
public abstract class DataSourceUtils {
...
public static Connection getConnection(DataSource dataSource) throws CannotGetJdbcConnectionException {
try {
return doGetConnection(dataSource);
}...
}
public static Connection doGetConnection(DataSource dataSource) throws SQLException {
...
ConnectionHolder conHolder = (ConnectionHolder)TransactionSynchronizationManager.getResource(dataSource);
....
}
3.Spring管理Mapper代理对象
Mapper接口底层实现
Mapper接口底层使用JDK动态代理来实现。通过sqlSession.getMapper方法就可以获取对应的Mapper接口,由此查看源码:
//DefaultSqlSession#getMapper
@Override
public <T> T getMapper(Class<T> type) {
return configuration.getMapper(type, this);
}
//Configuration#getMapper
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
//MapperRegistry#getMapper
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
...
try {
//通过MapperProxyFactory来创建实例
return mapperProxyFactory.newInstance(sqlSession);
} ...
}
//MapperProxyFactory#newInstance
public T newInstance(SqlSession sqlSession) {
//创建代理对象
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
//创建Mapper代理对象返回
return newInstance(mapperProxy);
}
//MapperProxyFactory#newInstance
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
JDK动态代理实现类MapperProxy,当我们调用Mapper接口中的某一方法,就会来到MapperProxy的invoke方法。
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
...
return mapperMethod.execute(sqlSession, args);
}
MapperMethod#execute,底层最终还是通过sqlSession的方式来执行。
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
//sql命令的类型
switch (command.getType()) {
//insert操作
case INSERT: {
...
}
//update操作
case UPDATE: {
...
}
//delete操作
case DELETE: {
...
}
//select操作
case SELECT:
...
case FLUSH:
...
default:
...
return result;
}
*Spring管理Mapper代理对象
Mapper代理对象创建后,还需要把Mapper代理对象作为一个bean注入到Spring容器中,使得能够像普通bean一样被@Autowire自动注入。
1.通过@MapperScan(basePackages = {“com.xxx.mapper”})注解指定需要扫描的mapper接口包路径。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class)
@Repeatable(MapperScans.class)
public @interface MapperScan {
2.通过@Import导入MapperScannerRegistrar,实现了ImportBeanDefinitionRegistrar。它会在beanDefinition扫描的时候调用registerBeanDefinitions()方法往Spring容器中添加beanDefinition对象到 beanDefinitionMap中。
void registerBeanDefinitions(AnnotationAttributes annoAttrs, BeanDefinitionRegistry registry, String beanName) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
...
//为容器中注册了MapperScannerConfigurer的接口
registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
}
3.注册MapperScannerConfigurer,实现了BeanDefinitionRegistryPostProcessor,重写其postProcessBeanDefinitionRegistry方法,并会在IOC容器的refresh—>invokeBeanFactoryPostProcessors方法中调用。
public class MapperScannerConfigurer
implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {
...
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
...
//创建一个ClassPathMapperScanner包扫描器对象
//mybaits-spring适配器中的,继承了spring中的ClassPathBeanDefinitionScanner
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
...
//真正去扫描@MapperScan指定的路径下的bean定义信息
scanner.scan(
StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}
4.创建ClassPathMapperScanner对象,是mybaits-spring适配器中自定义的一个扫描类,继承了spring中的ClassPathBeanDefinitionScanner。通过doScan进行扫描,将mapper接口都扫描成beanDefinition。
@Override
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
//调用父类ClassPathBeanDefinitionScanner 来进行扫描
//会通过isCandidateComponent判断是否符合条件,该方法被重写
Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
...
//进行处理,偷天换日操作
processBeanDefinitions(beanDefinitions);
...
}
//重写,只有是接口时满足扫描条件
//spring中默认是不是接口满足条件
@Override
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
}
5.重点方法,通过processBeanDefinitions对所有mapper接口的beanDefinition循环进行处理,将BeanClass偷换成MapperFactoryBean。
因为接口没有办法进行实例化,所以创建成FactoryBean,在调用getObject时就会给mapper会创建动态代理。
但是创建动态代理需要被代理的mapper接口,MapperFactoryBean中的属性mapperInterface就用来指定mapper接口,通过构造函数的方式进行传入。
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
GenericBeanDefinition definition;
for (BeanDefinitionHolder holder : beanDefinitions) {
...
// 设置ConstructorArgumentValues 会通过构造器初始化对象
// 传入String类型的名字,Spring会根据名字获取到class
definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName);
// 将BeanClass设置成MapperFactoryBean,实现了FactoryBean
// 其中有属性mapperInterface,指向要创建动态代理的接口
// MapperFactoryBean通过构造方法设置了mapperInterface
definition.setBeanClass(this.mapperFactoryBeanClass);
...
//根据类型自动注入,会将容器中已经存在的SqlSessionFactory注入进来
//MapperFactoryBean继承了SqlSessionDaoSupport,其中有setSqlSessionFactory
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
}
...
}
}
6.当我们调用到mapper接口时(getBean),就会调用FactoryBean的getObject方法,创建动态代理。
@Override
public T getObject() throws Exception {
//进入sqlSession.getMapper逻辑
return getSqlSession().getMapper(this.mapperInterface);
}