深入理解AOP机制

辛苦各位,点一下广告呀~thx
Hard work, click on the advertisement~thx

0x00 概述

AOP(Aspect Orient Programming)面向切面编程是面向对象的补充。一般用于处理系统中模块的横切关注点,例如:事务管理、日志、缓存等。AOP的核心在于AOP代理实现,主要分为动态代理静态代理。其中静态代理主要以AspectJ为代表,静态代理是在编译期实现的,性能更佳,而动态代理以Spring AOP为代表,动态代理是在运行期实现的,性能相比静态代理而言较差。

0x01 实现原理
1、静态代理

静态代理是最普通的实现方式,是基于接口编程的,在代码层面便编写相关代理类。
在设计原则上,基于多用组合,少用继承的原则,一般步骤如下:
(1)定义核心接口类

1
2
3
public interface Animal {
void eat();
}

(2) 定义主类,真正的逻辑处理

1
2
3
4
5
6
7
public class Dog implements animal {
@Override
public void eat() {
// TODO Auto-generated method stub
System.out.println("Dogs eat bones");
}
}

(3)定义代理的核心类,主要用于拦截请求核心类的请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Proxy implements animal {

private Animal animal;

public Proxy(Animal animal) {
this.animal = animal;
}

@Override
public void eat() {
.... // do something...
animal.eat();
.... // do something...
}
}

(4)客户端测试类

1
2
3
4
5
6
7
public class Client {
public static void main(String[] args) {
Animal animal = new Dog();
Animal proxy = new Proxy(animal);
proxy.eat();
}
}

2、动态代理

本文主要介绍Spring AOP 的动态代理实现,在其具体实现上,主要分为两部分介绍,一部分基于JDK动态代理,一部分是CGLIB动态代理。
(1)JDK动态代理
jdk动态代理主要通过反射来接收被代理的类。其要求被代理的类必须实现一个接口。所以jdk动态代理的核心在于InvocationHandler和Proxy类。具体实现例子如下:
a. 定义核心接口类

1
2
3
public interface Animal {
void eat();
}

b.核心的业务逻辑类

1
2
3
4
5
6
7
public class Dog implements animal {
@Override
public void eat() {
// TODO Auto-generated method stub
System.out.println("Dogs eat bones");
}
}

c.核心代理类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class ProxyTarget implements InvocationHandler {
private Object target;
public ProxyTarget(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// do something...
method.invoke(target, args);
// do something...
return null;
}

}

d.测试类

1
2
3
4
5
6
7
8
9
public class Test {
public static void main(String[] args) {
Dog dog = new Dog();
InvocationHandler target = new ProxyKind(dog);
Animal dynamicProxy = (Animal) Proxy.newProxyInstance(Dog.class.getClassLoader(),
Dog.class.getInterfaces(), kind);
dynamicProxy.eat();
}
}

综上,由代码步骤可知,并未实现代理类,但是实现了相似的功能。动态代理主要依赖于反射,每个代理的实例都有一个与之关联的 InvocationHandler 实现类,如果代理的方法被调用,那么代理便会通知和转发给内部的 InvocationHandler 实现类,由它决定处理。相当于统一处理了代理类。
(2)CGLIB动态代理
相对于jdk动态代理而言,若未实现相关的接口,Spring AOP会选择使用CGLIB来动态代理目标类。
CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成某个类的子类,注意,CGLIB是通过继承的方式做的动态代理,因此需要在满足继承的条件基础上才能使用此方式,例如如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的,同时private的方法也是不可以作为切面的。
a.定义具体的实现类

1
2
3
4
5
6
public class Dog {
public void eat() {
// TODO Auto-generated method stub
System.out.println("Dogs eat bones");
}
}

b.基于Spring 拦截器实现对应的cglib代理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class CGLibProxy implements MethodInterceptor {
private static CGLibProxy instance = new CGLibProxy();
private CGLibProxy() {
}
public static CGLibProxy getInstance() {
return instance;
}

public <T> T getProxy(Class<T> clazz) {
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return (T) enhancer.create();
}
public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable {
// do something...
Object obj = arg3.invokeSuper(arg0, arg2);
// do something...
return obj;
}
}

c.测试类

1
2
3
4
5
6
7
8
public class Test {
private final static Enhancer enhancer = new Enhancer();
public static void main(String[] args) {
CGLibProxy cglibProxy = CGLibProxy.getInstance();
Dog dog = cglibProxy.getProxy(Dog.class);
dog.eat();
}
}

0x02 总结

1、代理分为静态代理和动态代理两种。
2、静态代理,代理类需要自己编写代码写成。
3、动态代理,代理类通过 Proxy.newInstance() 方法生成。
4、不管是静态代理还是动态代理,代理与被代理者都要实现两样接口,它们的实质是面向接口编程。
5、静态代理和动态代理的区别是在于要不要开发者自己定义 Proxy 类。
6、动态代理通过 Proxy 动态生成 proxy class,但是它也指定了一个 InvocationHandler 的实现类。
7、代理模式本质上的目的是为了增强现有代码的功能
9、jdk动态代理创建的对象性能低于cglib动态代理创建的对象,而cglib创建对象所花费的时间高于jdk创建代理对象的时间。

后续出一篇文章专门写二者的性能对比(TODO)