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动态切入后=====
未完成,待续