mybatis 是一个设计十分优秀的框架, 其使用 java 原生的动态代理为使用者带来了 “仅定义接口, 无需具体实现, 完成 ORM 映射操纵数据库” 的巨大便利, 成为 java 应用中最受欢迎的轻量级 ORM 框架;
本文尝试探究一下 mybatis 动态代理的实现原理, 以及其在 spring 中的引导逻辑 (但不涉及具体的 ORM 映射逻辑);

自动扫描 & 自动替换

mybatis 实现自动扫描 Mapper 的核心类是 MapperScannerConfigurer, 这个类实现了 spring BeanDefinitionRegistryPostProcessor 接口, 从而获得了在 spring bean 实例化之前操纵修改 beanDefinition 的机会, 并进而利用代理机制实现了对用户自定义 Mapper 接口的 “偷梁换柱”;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
MapperScannerConfigurer:
-> postProcessBeanDefinitionRegistry(registry: BeanDefinitionRegistry)
-> scanner = new ClassPathMapperScanner(registry)
-> scanner.scan(basePackage)
ClassPathBeanDefinitionScanner: // super.doScan(basePackages)
-> beanDefinitions: Set<BeanDefinition> = findCandidateComponents(basePackage)
ClassPathMapperScanner:
-> beanDefinitions = super.doScan(basePackages)
-> processBeanDefinitions(beanDefinitions)
-> beanDefinitions.foreach {
// mapperInterface 是用户自定义 Mapper 类
definition.getPropertyValues().add("mapperInterface", Resources.classForName(beanClassName))
definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory)
definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate)
// MapperFactoryBean 是替换过的 beanDefinition 最终的目标类
definition.setBeanClass(MapperFactoryBean.class)
}

springboot 引导层

对于非 springboot 应用, 用户需要自己主动将 MapperScannerConfigurer 配置为 bean, spring 才能感知并加载它, 比如:

1
2
3
<bean name="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.xxx.mapper" />
</bean>

而对于一个 springboot 应用, 如果在启动类已经标记了 @EnableAutoConfiguration, 则只需在 maven pom 中引用 mybatis springboot starter, 即可实现自动注册:

1
2
3
4
5
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis-starter.version}</version>
</dependency>

因为 mybatis springboot starter 定义了 MybatisAutoConfiguration, 将其注册为自动配置类:

1
2
3
4
# spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration,\
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration

并在 MybatisAutoConfiguration 中引导 spring 加载 MapperScannerConfigurer:

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
@Configuration
@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })
@EnableConfigurationProperties(MybatisProperties.class)
public class MybatisAutoConfiguration implements InitializingBean {

public static class AutoConfiguredMapperScannerRegistrar implements BeanFactoryAware, ImportBeanDefinitionRegistrar {

private BeanFactory beanFactory;

@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
......
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
builder.addPropertyValue("processPropertyPlaceHolders", true);
builder.addPropertyValue("annotationClass", Mapper.class);
builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(packages));
......
registry.registerBeanDefinition(MapperScannerConfigurer.class.getName(), builder.getBeanDefinition());
}
......
}

@Configuration
@Import(AutoConfiguredMapperScannerRegistrar.class)
@ConditionalOnMissingBean({ MapperFactoryBean.class, MapperScannerConfigurer.class })
public static class MapperScannerRegistrarNotFoundConfiguration implements InitializingBean {......}
}

通过配置型 bean MapperScannerRegistrarNotFoundConfiguration 标记了注解 @Import(AutoConfiguredMapperScannerRegistrar.class), spring 最终调用 AutoConfiguredMapperScannerRegistrar#registerBeanDefinitions 方法, 将 MapperScannerConfigurer 的 beanDefinition 注册到了 beanDefinitionRegistry 中;
spring 会在实例化 bean 之前, 优先加载 MapperScannerConfigurer 并执行其 postProcessBeanDefinitionRegistry 方法实现 Mapper beanDefinition 的自动替换;

自动代理

在 ApplicationContext 启动并 refresh() 的过程中, MapperScannerConfigurer 已经于 spring bean 实例化之前完成了对用户自定义 Mapper 对应的 beanDefinition 的逻辑替换, 此时用户 Mapper BeanDefinition 里记录的 beanClass 是 MapperFactoryBean !!!

当 BeanFactory 开始加载各个 bean, MapperFactoryBean 将启动它的自动代理引导:

1
2
3
4
5
6
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
@Override
public T getObject() throws Exception {
return getSqlSession().getMapper(this.mapperInterface);
}
}

getSqlSession() 方法返回的是 MapperScannerConfigurer 注入的 sqlSessionTemplate:

1
2
3
4
5
6
7
8
9
10
11
public class SqlSessionTemplate implements SqlSession, DisposableBean {
@Override
public <T> T getMapper(Class<T> type) {
return getConfiguration().getMapper(type, this);
}

@Override
public Configuration getConfiguration() {
return this.sqlSessionFactory.getConfiguration();
}
}

最终是从 MapperRegistry 中拿到的代理实例:

1
2
3
4
5
6
7
8
9
10
11
12
public class DefaultSqlSessionFactory implements SqlSessionFactory {
@Override
public Configuration getConfiguration() {
return configuration;
}
}

public class Configuration {
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
}

MapperRegistry 注册了所有的 MapperProxyFactory, MapperRegistry#getMapper 方法将会代理给 MapperProxyFactory 真正实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class MapperRegistry {
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
}

代理框架

最终 mybatis 使用了 jdk 原生的 InvocationHandler / Proxy 动态代理解决方案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class MapperProxy<T> implements InvocationHandler, Serializable {
@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 {
return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
}

jdk 原生的动态代理最大的局限是只能代理 Interface, 不能代理 Class, 不过在 mybatis 的场景中已经足够用了, mybatis 给用户的使用方式就是 用纯接口定义行为规范即可; 而一旦不受该约束所困, jdk 原生动态代理的高性能优势就会凸显出来;
MapperProxyFactory 类作为引擎, 使用 Proxy.newProxyInstance 基于 mapperProxy 创建出真正的代理实例:

1
2
3
4
5
6
7
8
9
10
11
public class MapperProxyFactory<T> {

public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}

protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
}

代理实现

mybatis 在 MapperProxy 的 invoke 方法中实现了真正的 ORM 映射逻辑, 不过这就是另一个宏大的话题了, 本文不再讨论相关细节;