• Mybatis插件机制拆分


    1. 介绍

    Mybatis的插件机制使用了拦截器模式 动态代理模式

    把里面的关键代码拆分出来借鉴。

    1.1 组件

    核心组件:

       ObjectFactory:

        对象工厂

        功能:

          添加自定义过滤器

          创建对象。对象可能是真实对象,也可能是动态代理对象

      MyInterceptor接口:

        拦截器接口。

          intercept()拦截方法,实现拦截的逻辑

          plugin()插件方法,生成动态代理对象

      WrapUtil工具类:

            生成动态代理对象

        实现invoke方法,在调用动态代理的方法时,调用拦截器的拦截方法

      MyInterceptorChain:

        拦截器链

        存储所有拦截器

        提供方法 用所有拦截器包装 目标对象

        提供添加拦截器的方法

    非核心组件:

      MyIntercepts注解:标注类为过滤器

      Signature注解:用在MyIntercepts注解中,存储要标注的类、方法和参数

      MyInvocation:

        存储目标对象的调用信息:对象、方法和参数

        提供调用目标对象真实方法 的接口

    客户端:

      ClientTest:

        生成ObjectFactory -> 添加自定义的拦截器 -> 根据ObjectFactory生成对象 -> 调用对象的方法(拦截)

    2. 代码实现

    ObjectFacytory类


    public class ObjectFactory {

        private MyInterceptorChain interceptorChain;


        public ObjectFactory() {
            interceptorChain = new MyInterceptorChain();
        }

        public Business newBusiness() {
            Business business = new BusinessImpl();
            return (Business)interceptorChain.pluginAll(business);
        }


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

    MyInterceptorChain类

    /**
     * 所有生成新的代理对象的入口,用来拦截该对象的指定方法
     * 在创建对象时调用
     */
    public class MyInterceptorChain {
        private final List<MyInterceptor> interceptors = new ArrayList<>();

        /**
         * 对原始目标对象 用 所有过滤器 的 代理 包装 成代理对象
         */
        public Object pluginAll(Object target) {
            for (MyInterceptor interceptor : interceptors) {
                target = interceptor.plugin(target);
            }
            return target;
        }

        public void addInterceptor(MyInterceptor myInterceptor) {
            interceptors.add(myInterceptor);
        }

    }

    插件接口类

    MyInterceptor:


    public interface MyInterceptor {


        Object intercept(MyInvocation myInvocation) throws Throwable;

        /* 返回代理对象*/
        Object plugin(Object object);
    }

    插件注解

    MyIntercepts:

    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    public @interface MyIntercepts {

        // Signature是自己定义的注解
        Signature[] value();
    }

    Signature注解

    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target({})
    public @interface Signature {
      Class<?> type();

      String method();

      Class<?>[] args();
    }

    业务接口及其实现类

    Business接口


    /**
     * 目标对象一定要有接口
     */
    public interface Business {
        String business1(String arg);

        void business2();
    }

    BusinessImpl实现类:

    /**
     * jdk自带的动态代理,如果想要被拦截,这个类必须有接口
     */
    public class BusinessImpl implements Business {

        @Override
        public String business1(String arg) {
            System.out.println("正在做business1, 参数:" + arg);
            return arg;
        }


        @Override
        public void business2() {
            System.out.println("正在做business2");
        }

    }

    业务过滤器

    业务过滤器1

    BusinessInterceptor:

    /**
     * 拦截的类一定要写接口Business.class
     */
    @MyIntercepts({@Signature(type = Business.class, method = "business1", args={String.class})})
    public class Business1Interceptor implements MyInterceptor{

        @Override
        public Object intercept(MyInvocation myInvocation) throws Throwable {
            Method method = myInvocation.getMethod();
            System.out.println("拦截的business1方法:" + method.getName());

            return myInvocation.proceed();
        }

        @Override
        public Object plugin(Object target) {
            // 如果目标对象 是要拦截的Business类,就返回代理对象
            if (target instanceof Business) {
                return WrapUtil.warp(target, this);
            }
            return target;
        }
    }

    业务过滤器2:


    @MyIntercepts({@Signature(type = Business.class, method = "business2", args={})})
    public class BusinessInterceptor implements MyInterceptor{


        @Override
        public Object intercept(MyInvocation myInvocation) throws Throwable {
            Method method = myInvocation.getMethod();
            System.out.println("拦截的business2方法:" + method.getName());

            return myInvocation.proceed();
        }

        @Override
        public Object plugin(Object target) {
            // 如果目标对象 是要拦截的Business类,就返回代理对象
            if (target instanceof Business) {
                return WrapUtil.warp(target, this);
            }
            return target;
        }
    }

    WrapUtil工具类(Plugin工具类)


    public class WrapUtil implements InvocationHandler {

        /* 目标对象 */
        private Object target;
        /* 拦截器*/
        private MyInterceptor interceptor;
        /* 要拦截的类 以及 对应的 方法*/
        private Map<Class<?>, Set<Method>> signatureMap;


        public WrapUtil(Object target, MyInterceptor interceptor,

     Map<Class<?>, Set<Method>> signatureMap) {
            this.target = target;
            this.interceptor = interceptor;
            this.signatureMap = signatureMap;

        }

        /**
         *
         * @param target 代理前的对象
         * @param interceptor 拦截器
         * @return 代理后的对象
         */
        public static Object warp(Object target, MyInterceptor interceptor) {
            // 获取interceptor类要 拦截的类和方法
            Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);

            Class<?> type = target.getClass();

            // 要拦截的对象及其父类 在 signatureMap 中,就统计
            Class<?>[] interfaces = getAllInterfaces(type, signatureMap);

            if (interfaces.length > 0) {
                return Proxy.newProxyInstance(type.getClassLoader(), interfaces,

     new WrapUtil(target, interceptor, signatureMap));
            }
            return target;


        }

        /**
         * 获取type类及其父类 所有 在signatureMap 中的类
         */
        private static Class<?>[] getAllInterfaces(Class<?> type, Map<Class<?>, Set<Method>> signatureMap) {

            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<?>[0]);
        }

        private static Map<Class<?>, Set<Method>> getSignatureMap(MyInterceptor interceptor) {
            MyIntercepts interceptsAnnotation =

    interceptor.getClass().getAnnotation(MyIntercepts.class);
            if (interceptsAnnotation == null) {
                throw new RuntimeException("拦截的方法必须加@MyIntercepts注解");
            }

            Signature[] sigs = interceptsAnnotation.value();
            HashMap<Class<?>, Set<Method>> signatureMap = new HashMap<>();
            for (Signature sig : sigs) {
                Set<Method> methods = signatureMap.computeIfAbsent(

    sig.type(), k -> new HashSet<>());
                Method method = null;
                try {
                    method = sig.type().getMethod(sig.method(), sig.args());
                    methods.add(method);
                } catch (NoSuchMethodException e) {
                    throw new RuntimeException("Could not find method on " + sig.type() + " named " + sig.method() + ". Cause: " + e, e);
                }
            }
            return signatureMap;
        }

        /**
         * 调用代理后的对象的方法,都会走到这个方法
         */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

            // 如果method所在类和methodsignatureMap中,就执行拦截器的拦截方法
            Set<Method> methods = signatureMap.get(method.getDeclaringClass());
            if (methods != null && methods.contains(method)) {
                // 将参数封装到 MyInvocation中,传给拦截器 处理
                return interceptor.intercept(new MyInvocation(target, method, args));
            }
            // 要调用target原始对象,而不是proxy对象
            return method.invoke(target, args);
        }
    }

    MyInvocation类


    public class MyInvocation {

        private Object target;
        private Method method;
        private Object[] args;


        public MyInvocation(Object target, Method method, Object[] args) {
            this.target = target;
            this.method = method;
            this.args = args;
        }

        public Object getTarget() {
            return target;
        }

        public void setTarget(Object target) {
            this.target = target;
        }

        public Method getMethod() {
            return method;
        }

        public void setMethod(Method method) {
            this.method = method;
        }

        public Object[] getArgs() {
            return args;
        }

        public void setArgs(Object[] args) {
            this.args = args;
        }

        /**
         * 调用目标方法
         */
        public Object proceed() throws InvocationTargetException, IllegalAccessException {
            return method.invoke(target, args);
        }
    }

    客户端测试类 

    最后,客户端类进行测试:

    /**
     * 插件机制。
     * 对象的创建都在 ObjectFactory,相当于一个简单工厂
     *  - 在对象创建时,返回拦截器动态代理过的对象
     *  - 当执行这些代理对象的方法时,就会执行拦截器中的方法
     */
    public class ClientTest {

        @Test
        public void test() {
            Business business = new BusinessImpl();
            Business1Interceptor businessInterceptor = new Business1Interceptor();

            Business warpBusiness = (Business) WrapUtil.warp(business, businessInterceptor);
            String name = warpBusiness.business1("name");


        }


        /**
         * 添加插件的关键:
         *   - 自定义拦截器,指定拦截类和方法
         *   - ObjectFactory中添加自定义的拦截器
         *   - 创建对象时要通过ObjectFactorynewXXX创建,会返回被代理的对象
         */
        @Test
        public void testObjectFactory() {
            ObjectFactory objectFactory = new ObjectFactory();
            objectFactory.addInterceptor(new Business1Interceptor());
            objectFactory.addInterceptor(new BusinessInterceptor());

            // 经过过滤器包装的Business对象
            Business business = objectFactory.newBusiness();
            business.business1("name");
            business.business2();

        }


    }

    注意:

    加入插件后,要保证原有的业务功能不会发生变化。

    这就需要原来的业务结构足够清晰 和 选准插件要拦截的方法

  • 相关阅读:
    TODO C++ lambda表达式
    C++ Map实践
    【转】C++ typedef typename 作用
    C++ Vector实践
    再学引用
    设置table中的td一连串内容自动换行
    JavaScript中基本数据类型和引用数据类型的区别
    “浏览器模式”和“文档模式”之间的区别
    浏览器模式与文档模式区别
    HTML5中的data-*属性和jQuery中的.data()方法使用
  • 原文地址:https://www.cnblogs.com/yq055783/p/15878516.html
Copyright © 2020-2023  润新知