AOP概述(Aspect Oriented Programming)
所谓AOP,就是面向方面(切面)的编程,简单来说,就是通过面向切面,在执行的方法前后加上所需要实现的事情,比如,日志,计算方法执行的时间,实现事务等。这样做的目的一方面在于不改变原有代码,提高通用性,最重要的是通过切面可以在需要的地方加上所需要做的任何事情。
我们通过最简单例子来说明
这里我们要实现在执行加减法运算的前后,分别打印出提示信息
首先定义一个加减运算的接口
package com.markorg.top.general; /** * @Author Mark * * @Date 2018年6月10日 * * @Describe:定义进行计算的接口 */ public interface ICalculate { public int plus(int x, int y); public int minus(int x, int y); }
写出方法的实现
package com.markorg.top.general; /** * @Author Mark * * @Date 2018年6月10日 * * @Describe:加减法的实现类 */ public class Calculate implements ICalculate { /** * @加法运算 */ public int plus(int x, int y) { int m = x + y; System.out.println(m); return m; } /** * @减法运算 */ public int minus(int x, int y) { int n = x - y; System.out.println(n); return n; } }
接下来实现在计算前后在控制台打印出信息
第一种方式,直接在调用方法的时候进行打印
package com.markorg.top.general; /** * @Author Mark * * @Date 2018年6月10日 * * @Describe:不用切面的方法 */ public class General { public static void main(String[] args) { ICalculate calculate = new Calculate(); System.out.println("加法运行前"); calculate.plus(10, 1); System.out.println("加法运行后"); System.out.println("减法运行前"); calculate.minus(20, 12); System.out.println("减法运行后"); } }
方法执行后控制台打印的结果
加法运行前 11 加法运行后 减法运行前 8 减法运行后
这种方法简单粗暴,但是每次实现的时候都要写,代码复用性差
第二种方式,我们使用代理模式来实现
package com.markorg.top.general; /** * @Author Mark * * @Date 2018年6月10日 * * @Describe:静态代理实现 */ public class StaticCalculateProxy implements ICalculate { ICalculate ica = new Calculate(); public int plus(int x, int y) { System.out.println("方法执行前的切面"); int re = ica.plus(x, y); System.out.println("方法执行后的切面"); return re; } public int minus(int x, int y) { System.out.println("方法执行前的切面"); int re = ica.minus(x, y); System.out.println("方法执行后的切面"); return re; } public static void main(String[] args) { ICalculate ic = new StaticCalculateProxy(); ic.plus(10, 2); ic.minus(20, 12); } }
方法执行前的切面 12 方法执行后的切面 方法执行前的切面 8 方法执行后的切面
第三种方式,我们使用JDK的动态代理来实现
虽然说第二种方法相对第一种来说,提高了复用性,不用每次都去手动加入切面,但是它有个问题,每次都需要new实现类的对象,在代理方法中才调用加减运算,有时候也会不方便。
下面是动态代理的实现类
下面是执行后控制台的结果
package com.markorg.top.dynamic; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import com.markorg.top.general.Calculate; import com.markorg.top.general.ICalculate; /** * @Author Mark * * @Date 2018年6月10日 * * @Describe:使用JDK的动态代理来实现 */ public class DynamicCalculate implements InvocationHandler { Calculate c; /** * 使用asm生产被代理代理对象 * * @param c * @return */ public Object getProxy(Calculate c) { this.c = c; return Proxy.newProxyInstance(c.getClass().getClassLoader(), c.getClass().getInterfaces(), this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("-----jdk动态代理切面执行前"); Object result = method.invoke(c, args); System.out.println("-----jdk动态代理切面执行后"); return result; } public static void main(String[] args) { ICalculate ic = (ICalculate) new DynamicCalculate().getProxy(new Calculate()); ic.plus(10, 8); ic.minus(20, 12); } }
下面是执行后控制台的结果
-----jdk动态代理切面执行前 18 -----jdk动态代理切面执行后 -----jdk动态代理切面执行前 8 -----jdk动态代理切面执行后
第四种 使用cglib动态代理来实现,这个时候被代理类可以不需要实现接口,因为是通过继承的方法,当然,你想实现接口也是可以的
下面是代码
package com.markorg.top.dynamic; import java.lang.reflect.Method; import com.markorg.top.general.Calculate; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; /** * @Author Mark * * @Date 2018年6月10日 * * @Describe:使用cglib实现动态代理 */ public class CglibDynamicCalculate implements MethodInterceptor { Object targetObject; /** * cglib生成所需要的业务类对象,供代理来使用 * * @param target * @return */ public Object getProxyObject(Object object) { this.targetObject = object; // 增强器,动态代码生成器 Enhancer enhancer = new Enhancer(); // 回调方法 enhancer.setCallback(this); // 设置生成类的父类类型 enhancer.setSuperclass(targetObject.getClass()); // 动态生成字节码并返回代理对象 return enhancer.create(); } // 拦截方法 public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { // 被织入的横切内容,开始时间 before System.out.println("使用cglib动态切入前====="); // 调用方法 Object result = methodProxy.invoke(targetObject, args); // 被织入的横切内容,结束时间 System.out.println("使用cglib动态切入后====="); return result; } public static void main(String[] args) { Calculate ic = (Calculate) new CglibDynamicCalculate().getProxyObject(new Calculate()); ic.plus(10, 2); ic.minus(20, 12); } }
下面是控制台显示的结果
使用cglib动态切入前===== 12 使用cglib动态切入后===== 使用cglib动态切入前===== 8 使用cglib动态切入后=====
未完成,待续