Mybatis插件原理,源码解读

一.插件功能基本说明 Mybatis的sql执行过程中有四大组件, Executor ParameterHandler ResultSetHandler StatementHandler 针对执行器, 参数处理器,结果处理器,sql处理器预留增强扩展点,支持用户自定义实现增强逻辑, 如pageHelper,就在interceptor中完成增强逻辑,   二.实践 自定义简单插件,演示配置和实现   以下配置在StatementHandler.prepare(Connection,Integer)方法前进行调用,完成增强,
 1 1)配置文件注册插件
 2     <plugins>
 3         <plugin interceptor="com.lagou.interceptor.ExamplePlugin">
 4             <property name="name" value="dajgh"/>
 5         </plugin>
 6     </plugins>
 7 2)插件实现
 8 @Intercepts({@Signature(type = StatementHandler.class,
 9         method = "prepare", args = {Connection.class,Integer.class})})
10 public class ExamplePlugin implements Interceptor {
11     @Override
12     public Object intercept(Invocation invocation) throws Throwable {//拦截器方法,
13         System.out.println("增强行为");
14         return invocation.proceed();
15     }
16     @Override
17     public Object plugin(Object target) {//惯例的写法,将target封装后继续返回
18         System.out.println("进行了增强初始化...");
19         return Plugin.wrap(target,this);
20     }
21     @Override
22     public void setProperties(Properties properties) {//扩展属性配置,xml中注册时可以增加属性配置
23         System.out.println("properties = " + properties);
24     }
25 }

 

三.mybatis的加载和调用

 1 //xml文件配置略,注册插件见实践部分,
 2 
 3 //加载和调用
 4 String file = "sqlMapConfig.xml";
 5 InputStream inputStream = Resources.getResourceAsStream(file);
 6 SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
 7 sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
 8 SqlSession sqlSession = sqlSessionFactory.openSession();
 9 //得到mapper,进行调用
10 UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
11 List<User> list = user1Mapper.selectAll();
12 sqlSession.close();

四.源码剖析

1)分析加载过程

sqlSessionFactoryBuilder.build中会解析xml配置,得到configuration对象,

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
  try {
    XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
    return build(parser.parse());//解析xml,得到configuration
  } catch (Exception e) {
    throw ExceptionFactory.wrapException("Error building SqlSession.", e);
  } finally {
    ErrorContext.instance().reset();
    try {
      inputStream.close();
    } catch (IOException e) {
      // Intentionally ignore. Prefer previous error.
    }
  }
}

2)实际调用

public Configuration parse() {
  if (parsed) {
    throw new BuilderException("Each XMLConfigBuilder can only be used once.");
  }
  parsed = true;
  parseConfiguration(parser.evalNode("/configuration"));//解析配置文件下configuration标签
  return configuration;
}

3)进入parseConfiguration()

private void parseConfiguration(XNode root) {
  try {
    //issue #117 read properties first
    propertiesElement(root.evalNode("properties"));
    Properties settings = settingsAsProperties(root.evalNode("settings"));
    loadCustomVfs(settings);
    typeAliasesElement(root.evalNode("typeAliases"));
    pluginElement(root.evalNode("plugins"));//解析插件并进行注册
    objectFactoryElement(root.evalNode("objectFactory"));
    objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
    reflectorFactoryElement(root.evalNode("reflectorFactory"));
    settingsElement(settings);
    // read it after objectFactory and objectWrapperFactory issue #631
    environmentsElement(root.evalNode("environments"));
    databaseIdProviderElement(root.evalNode("databaseIdProvider"));
    typeHandlerElement(root.evalNode("typeHandlers"));
    mapperElement(root.evalNode("mappers"));
  } catch (Exception e) {
    throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
  }
}

4)插件解析过程

  /**
  <plugins>
        <plugin interceptor="com.lagou.interceptor.ExamplePlugin">
            <property name="name" value="dajgh"/>
        </plugin>
    </plugins>
*/
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).newInstance();//实例化拦截器,
      interceptorInstance.setProperties(properties);
      configuration.addInterceptor(interceptorInstance);
    }
  }
}

5)观察插件注册configuration.addInterceptor();

public void addInterceptor(Interceptor interceptor) {
  interceptorChain.addInterceptor(interceptor);
}

6)观察InterceptorChain,

维护了一个拦截器数组,通过addInterceptor()进行添加, 提供一个pluginAll(),支持对入参obj进行链式调用,
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);
  }
}
7)观察Interceptor接口
public interface Interceptor {
  Object intercept(Invocation invocation) throws Throwable;//在这里完成增强功能,
  default Object plugin(Object target) {//这个方法,一般实现类保持不变即可,逻辑基本一致,
    return Plugin.wrap(target, this);
  }
  default void setProperties(Properties properties) {
  }
}
8)观察Plugin.wrap(target,interceptor) 如果target实现的接口和interceptor注解的接口一致,则得到target的代理对象,否则不做处理, target的代理对象的代理逻辑是,如果调用target的方法,且方法在拦截器中配置了,则执行拦截器interceptor.intercept,
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);//得到type所有实现的与signatureMap中一致的接口,
    if (interfaces.length > 0) {
      return Proxy.newProxyInstance(
          type.getClassLoader(),
          interfaces,
          new Plugin(target, interceptor, signatureMap));//一致即走代理,不一致就原样返回target,一致就new一个本对象, 实质是一次动态代理,代理targt对象,
    }
    return target;
  }

  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//代理核心方法,如果被调用方法method,在signatureMap中已注册,则调用拦截器进行增强,否则直接执行方法,
    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) {//读取拦截器注解中的信息,标注针对xx接口,yy方法,进行增强
    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<>();
    for (Signature sig : sigs) {
      Set<Method> methods = signatureMap.computeIfAbsent(sig.type(), k -> new HashSet<>());
      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;
  }

  private static Class<?>[] getAllInterfaces(Class<?> type, Map<Class<?>, Set<Method>> signatureMap) {//遍历type及其父类,得到实现的与拦截器注解一致的所有接口,
    Set<Class<?>> interfaces = new HashSet<>();
    while (type != null) {
      for (Class<?> c : type.getInterfaces()) {
        if (signatureMap.containsKey(c)) {
          interfaces.add(c);
        }
      }
      type = type.getSuperclass();
    }
    return interfaces.toArray(new Class<?>[interfaces.size()]);
  }
}
9)观察自定义插件实现intercept() 进行增强处理,然后调用入参invocation, 注意
@Override
public Object intercept(Invocation invocation) throws Throwable {
    System.out.println("增强行为");
    return invocation.proceed();
}
10)观察Invocation.proceed(),可知内部就是正常调用反射
public Object proceed() throws InvocationTargetException, IllegalAccessException {
  return method.invoke(target, args);
}
11)补充查看拦截器链被调用入口 在configuration中总工有四个类似方法,针对四种接口调用链式拦截器链进行代理封装,逻辑基本一致,这里举一例, 对StatementHandler进行链式代理, 而以下方法在对应组件被使用时会被调用,得到链式代理后的对象完成业务操作,
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;
}

五.总结

结合8,9,10,可知,拦截器中实现增强的具体逻辑, 然后invocation.proceed()完成正常的反射调用原方法; 综上拦截器的解析过程可知, 解析拦截器的注解信息,得到拦截器拦截的接口的指定方法, 然后将拦截器添加到拦截器链中, 拦截器链支持对Object进行链式代理,通过InterceptorChain.pluginAll(tatrget)方法, 对target进行层层链式代理,拦截器提供两个方法,一个plugin()由拦截器链调用,进行链式代理,一个intercept()进行业务调用和增强逻辑, 链式代理过程中,如果target匹配拦截器注解类注解指定的接口,则进行代理封装,返回代理对象继续进行代理封装,否则直接返回target原对象---至此完成target的拦截器链式代理封装处理 target就这样被层层代理, 如果target方法被调用,若符合拦截器中定义的方法,则调用拦截器定义的增强方法,  
上一篇:【Mybatis框架】输入映射-pojo包装类型


下一篇:如何在Interceptor中使用@Autowired