需求:
对原有系统中的方法进行'拦截', 在方法执行的前后添加新的处理逻辑.
分析:
不是办法的办法就是, 对原有的每个方法进行修改, 添加上新的逻辑; 如果需要拦截的方法比较少, 选择此方法到是会节省成本. 但是面对成百上千的方法怎么办? 此时需要用到动态代理来实现.
场景:
例如: 对原有的系统添加日志记录, 添加性能分析等等...
举例:
如下, 需要对 Sleep 对象的 sleep 方法进行 "拦截", 并在此方法的执行前后添加新的逻辑. 想知道'睡觉前干了什么? 睡觉后干了什么?'
- interface Sleep {
- public void sleep();
- }
- public class SleepImpl implements Sleep{
- public void sleep() {
- System.out.println("我于"+new SimpleDateFormat("yyyy-MM-dd HH:mm").format(new Date())+"开始睡觉");
- }
- }
创建动态代理类, 实现 InvocationHandler 接口即可. 下面的 wrap 方法: 传入要被代理的对象 target. 返回包装后的代理对象.$Proxy 打断点会看到这样的对象. 针对下面的 sleepProxy 对象, sleepProxy.sleep() 调用需要拦截的方法. 实际上调用的是 Plugin 中的 invoke 方法. invoke 方法中的 method.invoke(target,args) 是真是的调用被代理对象的 sleep 方法. 所以直接在此语句的前后添加相应的逻辑即可满足需要.
- public class Plugin implements InvocationHandler {
- private Object target;
- Plugin(Object target){
- this.target = target;
- }
- public Object invoke(Object proxy, Method method, Object[] args)
- throws Throwable {
- // 睡觉前做的事
- Object result = method.invoke(target, args);
- // 睡觉后做的事
- return result;
- }
- public static Object wrap(Object target){
- return Proxy.newProxyInstance(target.getClass().getClassLoader(),
- target.getClass().getInterfaces(),
- new Plugin(target));
- }
- }
- public class Main {
- public static void main(String[] args) {
- // 要被代理的对象
- Sleep sleep = new SleepImpl();
- // 代理对象
- Sleep sleepProxy = (Sleep)Plugin.wrap(sleep);
- sleepProxy.sleep();
- }
- }
到此, 你以为就结束了? 不 , 这个仅仅是 说了在睡觉 前后做了什么事, 加入还想知道, 你在睡觉前后吃了什么东西? 当然睡觉后吃东西有点说不通. 但 意会就可以了. 还有其他巴拉巴拉的需求. 你该怎么做? 是不是要把所有的 新的逻辑都方法 Plugin 中 invoke 方法中去? 这样不合适吧! 乱 乱 乱 这样. 那咱们能不能抽象出来一个拦截接口, 接口中有拦截后要做什么的方法. 各种需求只需要实现这个拦截接口即可!
- interface Interceptor {
- public void interceptBefore()throws Exception;
- public void interceptAfter()throws Exception;
- }
- public class SleepBeforeAndAfter implements Interceptor {
- public void interceptBefore() throws Exception {
- System.out.println("之前...");
- }
- public void interceptAfter() throws Exception {
- System.out.println("之后...");
- }
- }
然后动态代理类 Plugin 需要修改
- /**
- * 动态代理
- *
- * @author 魏正迪
- * 2018 年 10 月 13 日
- */
- public class Plugin implements InvocationHandler {
- private Object target;
- private List<Interceptor> iList = new ArrayList<Interceptor>();
- Plugin(Object target , List<Interceptor> iList){
- this.target = target;
- this.iList = iList;
- }
- public Object invoke(Object proxy, Method method, Object[] args)
- throws Throwable {
- for(Interceptor i :iList){
- i.interceptBefore();
- }
- Object result = method.invoke(target, args);
- for(Interceptor i :iList){
- i.interceptAfter();
- }
- return result;
- }
- public static Object wrap(Object target,List<Interceptor> iList){
- return Proxy.newProxyInstance(target.getClass().getClassLoader(),
- target.getClass().getInterfaces(),
- new Plugin(target,iList)
- );
- }
- }
- public class Main {
- public static void main(String[] args) {
- Sleep sleep = new SleepImpl();
- List<Interceptor> iList = new ArrayList<Interceptor>();
- iList.add(new SleepBeforeAndAfter());
- Sleep sleepProxy = (Sleep)Plugin.wrap(sleep,iList);
- sleepProxy.sleep();
- }
- }
现在想对每个对象的方法进行拦截, 直接实现 Interceptor 接口即可! 实现其中的两个方法. 此时我们新加的逻辑和原有的逻辑并没有什么交集. 假如我们想在 interceptor 中的两个方法中使用被代理对象的各种属性, 此时该怎么做? 首先想到是将 interceptor 接口的两个方法添加参数.
- public class SleepBeforeAndAfter implements Interceptor {
- public void interceptBefore(Object target, Method method, Object[] args)
- throws Exception {
- System.out.println("之前...interceptBefore(Object target, Method method, Object[] args)");
- }
- public void interceptAfter(Object target, Method method, Object[] args)
- throws Exception {
- System.out.println("之后...interceptAfter(Object target, Method method, Object[] args)");
- }
- }
到此, 个人感觉没啥问题了 [大牛如发现明显不符的请指出] . 但但但但是我们奔着简单明了, 面向对象的思想 (其实就是 mybatis 源码插件设计). 我们做出进一步的精简. 于是 Invocation 对象产生了. 看到 Method 对象传进来了. 我们是不是可以想到, 我们不再 在 Plugin 中的 invoke 方法中调用 method.invoke(target,args); 了, 而是在 Intercetpor 中处理完前后逻辑后进行调用. 这样分工明确了.
- /**
- * 拦截对象的包装
- * @author 魏正迪
- * 2018 年 10 月 13 日
- */
- public class Invocation {
- private Object target;
- private Object []args;
- private Method method;
- Invocation(Object target,Method method,Object[] args){
- this.target = target;
- this.args = args;
- this.method = method;
- }
- /**
- * 执行拦截对象的对应的方法
- * @return
- * @throws Exception
- */
- public Object process() throws Exception{
- return method.invoke(target, args);
- }
- }
此时拦截器 Interceptor 应该是这样的
- interface Interceptor {
- public Object intercept(Invocation invocation)throws Exception;
- }
- public class SleepBeforeAndAfter implements Interceptor {
- public Object intercept(Invocation invocation) throws Exception{
- System.out.println("拦截 sleep 方法要执行的方法之前");
- Object result = invocation.process();
- System.out.println("拦截 sleep 方法要执行的方法之后");
- return result;
- }
- }
此时 Plugin 应该是这样的
- public class Plugin implements InvocationHandler {
- private Object target;
- private Interceptor interceptor;
- Plugin(Object target,Interceptor interceptor){
- this.target = target;
- this.interceptor = interceptor;
- }
- public Object invoke(Object proxy, Method method, Object[] args)
- throws Throwable {
- Invocation invocation = new Invocation(target,method,args);
- return interceptor.intercept(invocation);
- }
- public static Object wrap(Object target,Interceptor interceptor){
- return Proxy.newProxyInstance(target.getClass().getClassLoader(),
- target.getClass().getInterfaces(),
- new Plugin(target,interceptor)
- );
- }
- }
- public class Main {
- public static void main(String[] args) {
- Sleep sleep = new SleepImpl();
- SleepBeforeAndAfter s = new SleepBeforeAndAfter();
- Sleep sleepProxy1 = (Sleep)Plugin.wrap(sleep,s);
- sleepProxy1.sleep();
- Sleep sleepProxy2 = (Sleep)Plugin.wrap(sleepProxy1, s);
- sleepProxy2.sleep();
- }
- }
到此, mybatis 插件开发的引言完毕! 其实是使用了动态代理和责任链结合的方式.
来源: https://www.cnblogs.com/oldwei/p/9784708.html