AOP是面向切面编程。全称:Aspect Oriented Programming
面向切面编程指的是:程序是运行期间,动态地将某段代码插入到原来方法代码的某些位置中。这就叫面向切面编程。
准备计算器相关类
计算接口
public interface Calculate {
public int add(int num1, int num2);
public int mul(int num1, int num2);
public int div(int num1, int num2);
public int sub(int num1, int num2);
}
计算机类
public class Calculator implements Calculate {
public int add(int num1, int num2) {
System.out.println("日记 :【add】 方法调用前 。参数1是:" + num1 + " , 参数2是:" + num2);
return num1 + num2;
}
public int mul(int num1, int num2) {
System.out.println("日记 :【mul】 方法调用前 。参数1是:" + num1 + " , 参数2是:" + num2);
return num1 * num2;
}
public int div(int num1, int num2) {
System.out.println("日记 :【div】 方法调用前 。参数1是:" + num1 + " , 参数2是:" + num2);
return num1 / num2;
}
public int sub(int num1, int num2) {
System.out.println("日记 :【sub】 方法调用前 。参数1是:" + num1 + " , 参数2是:" + num2);
return num1 - num2;
}
}
测试的代码
public class CalculatorTest {
public static void main(String[] args) {
Calculate calculate = new Calculator();
int result = calculate.add(12, 12);
System.out.println("相加的结果:" + result);
result = calculate.mul(12, 12);
System.out.println("相乘的结果:" + result);
}
}
上面这种方法加日记处理操作。日记的代码就会耦合到业务代码中。而且后期如果需要修改日记就需要去指的修改所有方法中的日记操作。这个维护操作非常不方便。
把日记的内容封装到一个类去中集中处理。
编写一个日记处理工具类
public class LogUtil {
public static void log(String method, int num1, int num2) {
System.out.println("日记 :【" + method + "】 方法调用前 。参数1是:" + num1 + " , 参数2是:" + num2);
}
}
修改原来Calculator中的日记代码
@Override
public int add(int num1, int num2) {
LogUtil.log("add", num1, num2);
return num1 + num2;
}
@Override
public int mul(int num1, int num2) {
LogUtil.log("mul", num1, num2);
return num1 * num2;
}
但是这种方式的不足之处是,每有一个需要加日记的类,都需要到类的代码中去添加日记功能代码。
无法做到所有对象都统一处理。
创建一个计算器代理工具类
public class CalculateProxyFactory {
public static Calculate getProxy(Calculate target) {
// 定义一个计算器拦截处理类
class CalculateInvocationHandler implements InvocationHandler {
Calculate target;
public CalculateInvocationHandler(Calculate calculate) {
this.target = calculate;
}
/**
* proxy 代理对象
* method 调用的方法
* args 方法的参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getDeclaringClass().equals(Object.class)) {
return method.invoke(target, args);
}
LogUtil.log(method.getName(), (int) args[0], (int) args[1]);
Object result = null;
try {
result = method.invoke(target, args);
System.out.println("后置日记");
} catch (Exception e) {
System.out.println("异常日记");
} finally {
System.out.println("finally日记");
}
return result;
}
}
return (Calculate) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass()
.getInterfaces(), new CalculateInvocationHandler(target));
}
}
public class Calculator implements Calculate {
@Override
public int add(int num1, int num2) {
return num1 + num2;
}
@Override
public int mul(int num1, int num2) {
return num1 * num2;
}
@Override
public int div(int num1, int num2) {
return num1 / num2;
}
@Override
public int sub(int num1, int num2) {
return num1 - num2;
}
}
public static void main(String[] args) {
Calculate calculate = CalculateProxyFactory.getProxy( new Calculator() );
int result = calculate.add(12, 12);
System.out.println("相加的结果:" + result);
result = calculate.mul(12, 12);
System.out.println("相乘的结果:" + result);
}
优点:这种方式已经解决我们前面所有日记需要的问题。非常的灵活。而且可以方便的在后期进行维护和升级。
缺点:当然使用jdk动态代理,需要有接口。如果没有接口。就无法使用jdk动态代理。
public class CGLibProxyFactory implements MethodInterceptor {
public static Object getCGLibProxy(Object target, Callback callback) {
// 创建一个CGLig生成器
Enhancer enhancer = new Enhancer();
// 设置父类。因为cglib是通过类,进行代码,不是通过接口
enhancer.setSuperclass(target.getClass());
// 设置拦截的代理方法
enhancer.setCallback(callback);
// create 方法创建一个代理对象并返回
return enhancer.create();
}
@Override
public Object intercept(Object proxy, Method method, Object[] params, MethodProxy methodProxy)
throws Throwable {
LogUtil.log(method.getName(), (int) params[0], (int) params[1]);
// 调用实际的对象的方法
// 一定要使用methodProxy对象
// 第一个参数是proxy代码对象的父类方法
Object result = methodProxy.invokeSuper(proxy, params);
System.out.println("这是后置代码");
return result;
}
public static void main(String[] args) {
Calculator calculator = (Calculator) CGLibProxyFactory.getCGLibProxy(new Calculator(),
new CGLibProxyFactory());
calculator.add(12, 13);
}
}
优点:在没有接口的情况下,同样可以实现代理的效果。
缺点:同样需要自己编码实现代理全部过程。
但是为了更好的整合Spring框架使用。所以我们需要学习一下Spring 的AOP 功能。也就是学习Spring提供的AOP功能。