• Mybatis的核心实现Plugin、Interceptor


    本章讲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(http://www.my516.com)
    return stmt;
    }
    }
    mybatis用拦截器的设计非常巧妙的利用了jdk的动态代理,其实复杂度并不高。
    ---------------------

  • 相关阅读:
    android源码在线查看
    关于codereview工具与建议
    <转>如何进行code review
    [转] Android实时抓包分析 : 善用adb调试桥
    Swift:UIKit中Demo(一)
    Objective-C学习笔记(十)——循环语句for和do-while的使用
    一些牛人的IOS博客,mark下慢慢学习
    Visual Studio 2015速递(2)——提升效率和质量(VS2015核心竞争力)
    Web前端之基础知识
    通过金矿模型介绍动态规划
  • 原文地址:https://www.cnblogs.com/hyhy904/p/11102143.html
Copyright © 2020-2023  润新知