Mybatis的核心实现Plugin、Interceptor_布道-CSDN博客

本章讲Interceptor其实在分享怎样的设计才是具备良好的拓展性的程序,注重内功修为的童靴可能更感兴趣。Mybatis中的插件允许你针对核心组件接口Executor 、StatementHandler、ParameterHandler、ResultSetHandler中任何一个方法进行拦截调用。而每个Interceptor(拦截的接口)其实是通过JDK的动态代理技术生成的代理类,每当执行这4种接口中的方法时,就会进入拦截方法(具体就是InvocationHandler的invoke()方法)。

关于Mybatis的系统架构及Executor、ParameterHandler、ResultSetHandler、StatementHandler可参阅之前的相关的分享

责任链模式

责任链模式是一种对象行为模式,插件用的是它,很多对象由每一个对象对其下家的引用而连接起来形成一条链,请求在这个链上传递,直到链上的某一个对象决定处理此请求。

用法

用法一点也不复杂:只需实现 Interceptor 接口,并指定想要拦截的方法签名即可,更多用法可参阅官方教程

除了用插件来修改 MyBatis 核心行为之外,还可以通过完全覆盖配置类来达到目的。只需继承后覆盖其中的每个方法,再把它传递到 SqlSessionFactoryBuilder.build(myConfig) 方法即可。再次重申,这可能会严重影响 MyBatis 的行为,务请慎之又慎。

<!-- mybatis-config.xml --><plugins>  <plugin interceptor="org.mybatis.example.ExamplePlugin">    <property name="someProperty" value="100"/>  </plugin></plugins>
//执行Executor.update时拦截@Intercepts({@Signature(type= Executor.class, method = "update", args = {MappedStatement.class,Object.class})})public class ExamplePlugin implements Interceptor {  public Object intercept(Invocation invocation) throws Throwable {//干你想干的事情    return invocation.proceed();  }  public Object plugin(Object target) {    return Plugin.wrap(target, this);  }  public void setProperties(Properties properties) {  }}

源码

在上面的源码中Plugin.wrap(),它就是当前拦截器(ExamplePlugin)的代理类。而InterceptorChain.pluginAll()则定义了一个组件接口(比如:ParameterHandler)执行的责任链。

public interface Interceptor {  //拦截器具体实现  Object intercept(Invocation invocation) throws Throwable;  //拦截器的代理类  Object plugin(Object target);  //添加属性  void setProperties(Properties properties);}//源码Plugin.wrap其实是一个代理public class Plugin implements InvocationHandler {  private Object target;  private Interceptor interceptor;//拦截器  private Map<Class<?>, Set<Method>> 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);    }  }  //处理拦截器类上的各种注解  private static Map<Class<?>, Set<Method>> getSignatureMap(Interceptor interceptor) {    Intercepts interceptsAnnotation = interceptor.getClass().getAnnotation(Intercepts.class);    // issue #251    if (interceptsAnnotation == null) {      throw new PluginException("No @Intercepts annotation was found in interceptor " + interceptor.getClass().getName());          }    Signature[] sigs = interceptsAnnotation.value();    Map<Class<?>, Set<Method>> signatureMap = new HashMap<Class<?>, Set<Method>>();    for (Signature sig : sigs) {      Set<Method> methods = signatureMap.get(sig.type());      if (methods == null) {        methods = new HashSet<Method>();        signatureMap.put(sig.type(), methods);      }      try {        Method method = sig.type().getMethod(sig.method(), sig.args());        methods.add(method);      } catch (NoSuchMethodException e) {        throw new PluginException("Could not find method on " + sig.type() + " named " + sig.method() + ". Cause: " + e, e);      }    }    return signatureMap;  }}//拦截器的请求public class Invocation {  private Object target;  private Method method;  private Object[] args;  //调用原先真实方法(非代理)  public Object proceed() throws InvocationTargetException, IllegalAccessException {    return method.invoke(target, args);  }}
拦截器链执行过程

拦截器代理类对象->拦截器->目标方法,即:InvocationHandler.invoke()->Interceptor.intercept()->Invocation.proceed()。

也就是说其实在执行每个Executor、ParameterHandler、ResultSetHandler、StatementHandler都会构建一个拦截器链,如果针对每个配置了plugin,一个sql执行下来会陆续经过四个拦截器链。

//拦截器管理类public class InterceptorChain {  private final List<Interceptor> interceptors = new ArrayList<Interceptor>();  //构建责任链  public Object pluginAll(Object target) {    for (Interceptor interceptor : interceptors) {//存在拦截器则构建      //有多个同类型的拦截器则构成链,将代理类作为target再成代理类      target = interceptor.plugin(target);    }    return target;//可以没有拦截器  }  public void addInterceptor(Interceptor interceptor) {    interceptors.add(interceptor);  }  public List<Interceptor> getInterceptors() {    return Collections.unmodifiableList(interceptors);  }}//重要的配置类public class Configuration {  protected final InterceptorChain interceptorChain = new InterceptorChain();  //有匹配的拦截器则构建责任链,没有则不生成  public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {    ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);    parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);    return parameterHandler;  }  //同newParameterHandler  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);    resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);    return resultSetHandler;  }  //同newParameterHandler  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);    statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);    return statementHandler;  }   public Executor newExecutor(Transaction transaction) {    return newExecutor(transaction, defaultExecutorType);  }  //同newParameterHandler  public Executor newExecutor(Transaction transaction, ExecutorType executorType) {    executorType = executorType == null ? defaultExecutorType : executorType;    executorType = executorType == null ? ExecutorType.SIMPLE : 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);    }    executor = (Executor) interceptorChain.pluginAll(executor);    return executor;  }}//负责解析Mybatis 全局配置文件public class XMLConfigBuilder extends BaseBuilder {  private void pluginElement(XNode parent) throws Exception {//解析interceptor    if (parent != null) {      for (XNode child : parent.getChildren()) {        String interceptor = child.getStringAttribute("interceptor");        Properties properties = child.getChildrenAsProperties();        Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();        interceptorInstance.setProperties(properties);        configuration.addInterceptor(interceptorInstance);      }    }  }}public class SimpleExecutor extends BaseExecutor {  @Override  public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {    Statement stmt = null;    try {      Configuration configuration = ms.getConfiguration();      //生成责任链      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);      stmt = prepareStatement(handler, ms.getStatementLog());      return handler.<E>query(stmt, resultHandler);//可拦截query()    } finally {      closeStatement(stmt);    }  }  private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {    Statement stmt;    Connection connection = getConnection(statementLog);    stmt = handler.prepare(connection, transaction.getTimeout());//可拦截prepare()    handler.parameterize(stmt);//可拦截parameterize()    return stmt;  }}

mybatis用拦截器的设计非常巧妙的利用了jdk的动态代理,其实复杂度并不高。


Original url: Access
Created at: 2019-12-25 11:35:30
Category: default
Tags: none

请先后发表评论
  • 最新评论
  • 总共0条评论