大致原理

mybatis-spring-boot-autoconfigure.jar包中,spring.factories文件配置了MybatisAutoConfiguration,spring会对其进行自动注入;MybatisAutoConfiguration初始化的时候,会在构造函数通过ObjectProvider收集所有实现Interceptor接口的bean,并在初始化SqlSessionFactory时(同类下@Bean方法),将Interceptor实例数组传入SqlSessionFactoryBean,然后再存入对应SqlSessionFactoryConfiguration.interceptorChain,每当有执行mapper接口方法时,都会走到InterceptorChain.pluginAll方法,在这里形成类似责任链模式的数据结构,pluginAll方法会遍历所有的Interceptor实例进行对比,拿到实例修饰的@Intercepts注解元数据,如果和类型对比一致,就将Interceptor实例加入该责任链,不一致则不做操作,执行的时候按照责任链顺序执行。
数据结构如下图所示(通过target拿到下一责任链指向):
在这里插入图片描述

使用限制

仅支持type为以下类型的拦截:

  • ParameterHandler

  • ResultSetHandler

  • StatementHandler

  • Executor

关键代码

<dependency>
  <groupId>org.mybatis.spring.boot</groupId>
  <artifactId>mybatis-spring-boot-starter</artifactId>
  <version>2.3.1</version>
</dependency>

初始化执行链:MybatisAutoConfiguration构造函数 -> (@Bean)MybatisAutoConfiguration.sqlSessionFactory() -> SqlSessionFactoryBean.getObject() -> SqlSessionFactoryBean.afterPropertiesSet() -> SqlSessionFactoryBean.buildSqlSessionFactory()
初始化执行部分代码:

/**
 * MybatisAutoConfiguration
 */
private final Interceptor[] interceptors;

public MybatisAutoConfiguration(MybatisProperties properties, ObjectProvider<Interceptor[]> interceptorsProvider,
                                ObjectProvider<TypeHandler[]> typeHandlersProvider, ObjectProvider<LanguageDriver[]> languageDriversProvider,
                                ResourceLoader resourceLoader, ObjectProvider<DatabaseIdProvider> databaseIdProvider,
                                ObjectProvider<List<ConfigurationCustomizer>> configurationCustomizersProvider,
                                ObjectProvider<List<SqlSessionFactoryBeanCustomizer>> sqlSessionFactoryBeanCustomizers) {
    this.properties = properties;
    // 拿到所有的Interceptor实例
    this.interceptors = interceptorsProvider.getIfAvailable();
    。。。
}

@Bean
@ConditionalOnMissingBean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
    SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
    。。。
    applyConfiguration(factory);
    // 将interceptors赋值到SqlSessionFactoryBean
    if (!ObjectUtils.isEmpty(this.interceptors)) {
        factory.setPlugins(this.interceptors);
    }
    。。。
    return factory.getObject();
}


/**
 * SqlSessionFactoryBean
 */
@Override
public SqlSessionFactory getObject() throws Exception {
    if (this.sqlSessionFactory == null) {
        // 手动调用afterPropertiesSet()
        afterPropertiesSet();
    }

    return this.sqlSessionFactory;
}

@Override
public void afterPropertiesSet() throws Exception {

    。。。
    // 调用同类下方法
    this.sqlSessionFactory = buildSqlSessionFactory();
}

protected SqlSessionFactory buildSqlSessionFactory() throws Exception {

    final Configuration targetConfiguration;

    。。。

    // 将Interceptor实例加入Configuration中
    if (!isEmpty(this.plugins)) {
        Stream.of(this.plugins).forEach(plugin -> {
            targetConfiguration.addInterceptor(plugin);
            LOGGER.debug(() -> "Registered plugin: '" + plugin + "'");
        });
    }
    。。。
    return this.sqlSessionFactoryBuilder.build(targetConfiguration);
}

/**
 * Configuration
 */
protected final InterceptorChain interceptorChain = new InterceptorChain();
public void addInterceptor(Interceptor interceptor) {
    interceptorChain.addInterceptor(interceptor);
}

/**
 * InterceptorChain
 */
private final List<Interceptor> interceptors = new ArrayList<>();
public void addInterceptor(Interceptor interceptor) {
    // 可以看到这里实际上就是个Interceptor的集合
    interceptors.add(interceptor);
}

mapper接口执行相关代码

/**
 * InterceptorChain
 */
// target的类型可能是:ParameterHandler、ResultSetHandler、StatementHandler、Executor
public Object pluginAll(Object target) {
    for (Interceptor interceptor : interceptors) {
        /*
         * 判断拦截器是否符合注解配置,如果符合则返回一个代理对象
         * 并将代理对象赋值到target,在下一次循环中将target重新传入方法
         * 这样形成代理对象套代理对象的情况,形成类似责任链模式的数据结构
         */
        target = interceptor.plugin(target);
    }
    return target;
}

/**
 * Interceptor
 */
default Object plugin(Object target) {
    // 将target和自身作为参数
    return Plugin.wrap(target, this);
}

/**
 * Plugin
 */
public static Object wrap(Object target, Interceptor interceptor) {
    // 获取实例修饰的@Intercepts注解元数据
    Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
    // 获取target的class
    Class<?> type = target.getClass();
    // 判断拦截器是否符合注解配置
    Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
    // 有则返回一个代理对象
    if (interfaces.length > 0) {
        return Proxy.newProxyInstance(type.getClassLoader(), interfaces, new Plugin(target, interceptor, signatureMap));
    }
    // 不做操作,直接返回
    return target;
}

private static Class<?>[] getAllInterfaces(Class<?> type, Map<Class<?>, Set<Method>> signatureMap) {
    Set<Class<?>> interfaces = new HashSet<>();
    while (type != null) {
        // 获取type的Interface
        for (Class<?> c : type.getInterfaces()) {
            // 这里是过滤的重点,判断注解元数据是否配置该类型,如Executor
            if (signatureMap.containsKey(c)) {
                // 如果有则加入返回结果集
                interfaces.add(c);
            }
        }
        // 没有则尝试在type的父类中寻找
        type = type.getSuperclass();
    }
    return interfaces.toArray(new Class<?>[0]);
}
Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐