Java AOP

点 线 面

AOP(Aspect Oriented Programming) 面向方面的编程

基本概念


  • Target: 目标,目标类
  • Aspect: 跨越多个类的功能层面,比如事务管理、日志管理等等
  • Pointcut: 切点,筛选出目标方法,关联一个表达式,即哪些方法是代理范围
  • Joinpoint: 连接点,程序执行时间点,比如方法执行时、异常处理时等等
  • Advice:增强逻辑,要额外执行的动作
    • Before: 时间点前执行,不能阻止时间点执行
    • After returning: 时间点无异常完成后执行
    • After throwing: 异常后执行
    • After(Finally): 时间点后执行,不管有无异常
    • Around: 之前之后都处理,最通用最强大的类型
  • Weaving: 将增强逻辑引入目标
    • 编译时,需要特殊编译器,可以添加源码再编译,也可以编译后针对class文件直接把二进制字节码插入
    • 加载时,需要特殊类加载器,加载时添加额外逻辑
    • 运行时, 构建动态代理,针对已经加载到JVM的类
  • Introduction: 给被代理对象增加额外的方法或属性

静态代理


手动编写维护代理类,代理类和目标实现同一接口

  • 每个需要代理的类都需要手动自己进行代理实现,数量多的话需要创建大量代理类文件
  • 代理类实现接口,方法多很繁杂并且代码机械重复,而且接口变化如添加方法,代理类也要同步修改

动态代理可以动态生成代理类,无需手动编写,只需要专注于处理逻辑

JDK 动态代理


运行时生成具备一组接口的新类,该类继承了java.lang.reflect.Proxy,由于不能多继承,因此要求必须有接口才行
代理类在调用具体方法时交给handler处理

要求

  • 目标对象必须实现了接口
  • 需要InvocationHandler内部维护目标对象

生成代理所需参数:目标类的加载器,目标接口数组,处理器

Sample
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//handler需要实例参与
class UppercaseValueHandler implements InvocationHandler {
Object target;

public UppercaseValueHandler(Object obj) {
target = obj;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("put")) {
String value = String.valueOf(args[1]);
args[1] = value.toUpperCase();
}
return method.invoke(target, args);
}
}

public class Test {
public static void main(String[] args) throws IOException {
Map<String, String> target = new HashMap<>();
Map<String, String> proxy = (Map<String, String>)Proxy.newProxyInstance(
Map.class.getClassLoader(), new Class[] { Map.class }, new UppercaseValueHandler(target));
proxy.put("foo", "bar");
System.out.println(proxy.getClass().getName()); // com.sun.proxy.$Proxy0
System.out.println(target); // {foo=BAR}
}
}

CGLIB 动态代理


CGLIB字节码修改,继承生成子类,重写覆盖方法

Sample
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//interceptor不需要实例参与
class UppercaseValueInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
if (method.getName().equals("put")) {
String value = String.valueOf(args[1]);
args[1] = value.toUpperCase();
}
return proxy.invokeSuper(obj, args);
}
}

public class Test {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(HashMap.class);
enhancer.setCallback(new UppercaseValueInterceptor());

Map<String,String> proxy = (Map<String, String>) enhancer.create();
proxy.put("foo", "bar");
System.out.println(proxy.getClass().getName()); // $java.util.HashMap$$EnhancerByCGLIB$$3f4e67dd
System.out.println(proxy); // {foo=BAR}
}
}

注意点


动态代理也会把Object类的hashCode,equals,toString进行代理实现,其他Object方法不会代理作用于代理对象本身
所以代理对象和真实对象是equal的,hashCode和toString也相同

对比


JDK执行性能差,但是创建时间短,需要有接口
CGLIB执行性能好,但是创建时间长,需要能继承子类
只创建一次代理的情况下,比如单例,优先使用CGLIB

应用


  • 执行时间统计
  • Mock框架
  • ORM框架
  • IOC容器
  • 诊断工具
  • 代码生成