本文次要钻研一下mybatis的Interceptor机制

Interceptor

org/apache/ibatis/plugin/Interceptor.java

public interface Interceptor {  Object intercept(Invocation invocation) throws Throwable;  default Object plugin(Object target) {    return Plugin.wrap(target, this);  }  default void setProperties(Properties properties) {    // NOP  }}
Interceptor定义了intercept办法,其参数为Invocation类型,同时默认提供了plugin办法,通过Plugin.wrap(target, this)进行包装

Invocation

org/apache/ibatis/plugin/Invocation.java

public class Invocation {  private final Object target;  private final Method method;  private final Object[] args;  public Invocation(Object target, Method method, Object[] args) {    this.target = target;    this.method = method;    this.args = args;  }  public Object getTarget() {    return target;  }  public Method getMethod() {    return method;  }  public Object[] getArgs() {    return args;  }  public Object proceed() throws InvocationTargetException, IllegalAccessException {    return method.invoke(target, args);  }}
Invocation定义了target、method、args属性,提供了proceed办法则是反射执行method办法

Plugin

org/apache/ibatis/plugin/Plugin.java

public class Plugin implements InvocationHandler {  private final Object target;  private final Interceptor interceptor;  private final Map<Class<?>, Set<Method>> signatureMap;  private Plugin(Object target, Interceptor interceptor, Map<Class<?>, Set<Method>> signatureMap) {    this.target = target;    this.interceptor = interceptor;    this.signatureMap = signatureMap;  }  public static Object wrap(Object target, Interceptor interceptor) {    Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);    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;  }  @Override  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {    try {      Set<Method> methods = signatureMap.get(method.getDeclaringClass());      if (methods != null && methods.contains(method)) {        return interceptor.intercept(new Invocation(target, method, args));      }      return method.invoke(target, args);    } catch (Exception e) {      throw ExceptionUtil.unwrapThrowable(e);    }  }  //......}  
Plugin实现了java.lang.reflect.InvocationHandler办法,其invoke办法次要是多了一层判断,判断interceptor的signatureMap有没有蕴含对应的办法,有则执行interceptor.intercept,同时包装了Invocation参数传递过来
而Plugin的wrap办法则是判断interceptor有没有拦挡target对应的接口,如果有则通过Proxy.newProxyInstance返回代理对象不便后续进行拦挡

InterceptorChain

org/apache/ibatis/plugin/InterceptorChain.java

public class InterceptorChain {  private final List<Interceptor> interceptors = new ArrayList<>();  public Object pluginAll(Object target) {    for (Interceptor interceptor : interceptors) {      target = interceptor.plugin(target);    }    return target;  }  public void addInterceptor(Interceptor interceptor) {    interceptors.add(interceptor);  }  public List<Interceptor> getInterceptors() {    return Collections.unmodifiableList(interceptors);  }}
InterceptorChain定义了interceptors,它提供了pluginAll办法对target代理所有的interceptor

Configuration

org/apache/ibatis/session/Configuration.java

  protected final InterceptorChain interceptorChain = new InterceptorChain();  public void addInterceptor(Interceptor interceptor) {    interceptorChain.addInterceptor(interceptor);  }
Configuration定义了interceptorChain,它通过addInterceptor办法往interceptorChain增加interceptor

XMLConfigBuilder

org/apache/ibatis/builder/xml/XMLConfigBuilder.java

  private void pluginElement(XNode parent) throws Exception {    if (parent != null) {      for (XNode child : parent.getChildren()) {        String interceptor = child.getStringAttribute("interceptor");        Properties properties = child.getChildrenAsProperties();        Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).getDeclaredConstructor()            .newInstance();        interceptorInstance.setProperties(properties);        configuration.addInterceptor(interceptorInstance);      }    }  }
XMLConfigBuilder在解析xml的plugin的时候,会获取定义的interceptor,实例化之后通过configuration.addInterceptor增加进去

SqlSessionFactoryBean

org/mybatis/spring/SqlSessionFactoryBean.java

  private Interceptor[] plugins;  public void addPlugins(Interceptor... plugins) {    setPlugins(appendArrays(this.plugins, plugins, Interceptor[]::new));  }  public void setPlugins(Interceptor... plugins) {    this.plugins = plugins;  }  protected SqlSessionFactory buildSqlSessionFactory() throws Exception {    final Configuration targetConfiguration;    XMLConfigBuilder xmlConfigBuilder = null;    if (this.configuration != null) {      targetConfiguration = this.configuration;      if (targetConfiguration.getVariables() == null) {        targetConfiguration.setVariables(this.configurationProperties);      } else if (this.configurationProperties != null) {        targetConfiguration.getVariables().putAll(this.configurationProperties);      }    } else if (this.configLocation != null) {      xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);      targetConfiguration = xmlConfigBuilder.getConfiguration();    } else {      LOGGER.debug(          () -> "Property 'configuration' or 'configLocation' not specified, using default MyBatis Configuration");      targetConfiguration = new Configuration();      Optional.ofNullable(this.configurationProperties).ifPresent(targetConfiguration::setVariables);    }    Optional.ofNullable(this.objectFactory).ifPresent(targetConfiguration::setObjectFactory);    Optional.ofNullable(this.objectWrapperFactory).ifPresent(targetConfiguration::setObjectWrapperFactory);    Optional.ofNullable(this.vfs).ifPresent(targetConfiguration::setVfsImpl);    //......    if (!isEmpty(this.plugins)) {      Stream.of(this.plugins).forEach(plugin -> {        targetConfiguration.addInterceptor(plugin);        LOGGER.debug(() -> "Registered plugin: '" + plugin + "'");      });    }    if (hasLength(this.typeHandlersPackage)) {      scanClasses(this.typeHandlersPackage, TypeHandler.class).stream().filter(clazz -> !clazz.isAnonymousClass())          .filter(clazz -> !clazz.isInterface()).filter(clazz -> !Modifier.isAbstract(clazz.getModifiers()))          .forEach(targetConfiguration.getTypeHandlerRegistry()::register);    }    if (!isEmpty(this.typeHandlers)) {      Stream.of(this.typeHandlers).forEach(typeHandler -> {        targetConfiguration.getTypeHandlerRegistry().register(typeHandler);        LOGGER.debug(() -> "Registered type handler: '" + typeHandler + "'");      });    }    targetConfiguration.setDefaultEnumTypeHandler(defaultEnumTypeHandler);    //......    return this.sqlSessionFactoryBuilder.build(targetConfiguration);  }
SqlSessionFactoryBean的buildSqlSessionFactory办法在判断plugins不为空时,通过targetConfiguration.addInterceptor(plugin)将interceptor注册进去

MybatisAutoConfiguration

org/mybatis/spring/boot/autoconfigure/MybatisAutoConfiguration.java

@org.springframework.context.annotation.Configuration@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })@ConditionalOnSingleCandidate(DataSource.class)@EnableConfigurationProperties(MybatisProperties.class)@AutoConfigureAfter({ DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class })public class MybatisAutoConfiguration implements InitializingBean {  private static final Logger logger = LoggerFactory.getLogger(MybatisAutoConfiguration.class);  private final MybatisProperties properties;  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;    this.interceptors = interceptorsProvider.getIfAvailable();    this.typeHandlers = typeHandlersProvider.getIfAvailable();    this.languageDrivers = languageDriversProvider.getIfAvailable();    this.resourceLoader = resourceLoader;    this.databaseIdProvider = databaseIdProvider.getIfAvailable();    this.configurationCustomizers = configurationCustomizersProvider.getIfAvailable();    this.sqlSessionFactoryBeanCustomizers = sqlSessionFactoryBeanCustomizers.getIfAvailable();  }  @Bean  @ConditionalOnMissingBean  public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {    SqlSessionFactoryBean factory = new SqlSessionFactoryBean();    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);    }    //......  }  
MybatisAutoConfiguration的sqlSessionFactory办法,在判断interceptors不为空时,通过SqlSessionFactory的setPlugins办法把interceptors增加进去;MybatisAutoConfiguration标注了@Configuration注解,该注解标注了@Component,因此这些interceptors则是通过结构器从spring中注入的

Configuration.pluginAll

org/apache/ibatis/session/Configuration.java

  public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject,      BoundSql boundSql) {    ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement,        parameterObject, boundSql);    return (ParameterHandler) interceptorChain.pluginAll(parameterHandler);  }  public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds,      ParameterHandler parameterHandler, ResultHandler resultHandler, BoundSql boundSql) {    ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler,        resultHandler, boundSql, rowBounds);    return (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);  }  public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement,      Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {    StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject,        rowBounds, resultHandler, boundSql);    return (StatementHandler) interceptorChain.pluginAll(statementHandler);  }  public Executor newExecutor(Transaction transaction, ExecutorType executorType) {    executorType = executorType == null ? defaultExecutorType : executorType;    Executor executor;    if (ExecutorType.BATCH == executorType) {      executor = new BatchExecutor(this, transaction);    } else if (ExecutorType.REUSE == executorType) {      executor = new ReuseExecutor(this, transaction);    } else {      executor = new SimpleExecutor(this, transaction);    }    if (cacheEnabled) {      executor = new CachingExecutor(executor);    }    return (Executor) interceptorChain.pluginAll(executor);  }
Configuration提供了newParameterHandler、newResultSetHandler、newStatementHandler、newExecutor办法,这些办法会对ParameterHandler、ResultSetHandler、StatementHandler、Executor执行interceptorChain.pluginAll办法,则创立作用了所有interceptor的代理对象,从而实现对这些对象的拦挡成果

小结

  • mybatis的Interceptor机制应用的是jdk的Proxy.newProxyInstance的形式
  • 在扫描xml的时候把interceptor注册到configuration中,针对spring的场景,在MybatisAutoConfiguration中注入所有托管的interceptor,之后在结构SqlSessionFactory的时候把interceptor注册到configuration中
  • 最初Configuration提供了newParameterHandler、newResultSetHandler、newStatementHandler、newExecutor办法,创立作用了所有interceptor的代理对象,从而实现对这些对象的拦挡成果