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所在类和method在signatureMap中,就执行拦截器的拦截方法 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中添加自定义的拦截器 * - 创建对象时要通过ObjectFactory中newXXX创建,会返回被代理的对象 */ @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();
}
}
|
注意:
加入插件后,要保证原有的业务功能不会发生变化。
这就需要原来的业务结构足够清晰 和 选准插件要拦截的方法