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();

    }


}

 

 

注意:

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

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

 

posted @ 2022-02-10 12:09  yq055783  阅读(59)  评论(0编辑  收藏  举报