AOP浅析

aop 是现代web框架底层核心技术之一,很多人能熟练运用 @aspect @joinpoint 去实现业务代码,但未必明白 aop 的来龙去脉。考虑这样一个say()

1
2
3
4
5
public class Hello0 {
public void say(){
System.out.println("hello");
}
}

如果想在say()前后执行一些增强代码,该怎么做呢?

实现方案一

小A采用了下面的写法。很不幸,小A这段代码写完后,就被辞退了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Hello0plus {
private Hello0 hello0;
public Hello0plus() {
this.hello0 = new Hello0();
}
public void say(){
before();
hello0.say();
after();
}
private void before(){
System.out.println("before");
}
private void after(){
System.out.println("after");
}
}

实现方案二

小B这个时候想起了代理模式,使用代理模式重构了代码:

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
29
30
public interface Hello {
void say();
}
public class HelloImpl implements Hello{
@Override
public void say() {
System.out.println("hello");
}
}
public class HelloProxy implements Hello{
private Hello hello;
public HelloProxy() {
this.hello = new HelloImpl();
}
@Override
public void say() {
before();
hello.say();
after();
}
private void before(){
System.out.println("before");
}
private void after(){
System.out.println("after");
}
}

学以致用,小B很兴奋,大量使用了这种代理方式,XXXProxy满天飞。架构师看到项目里的代码目瞪口呆,冲小B咆哮到:你就不会动态代理吗?赶紧给我重构!

实现方案三

小B查阅了资料,终于搞明白动态代理是啥,动态代理是利用Java 6 引入的InvocationHandler接口的实现。主要涉及到两个类:

  • java.lang.reflect Proxy,主要方法为
1
2
3
4
5
6
static Object newProxyInstance(ClassLoader loader, //指定当前目标对象使用类加载器
Class<?>[] interfaces, //目标对象实现的接口的类型
InvocationHandler h //事件处理器
)
//返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。
  • java.lang.reflect InvocationHandler,主要方法为
1
2
Object invoke(Object proxy, Method method, Object[] args)
// 在代理实例上处理方法调用并返回结果。

小B弄明白后兴奋的重写了代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class HelloProxyPlus implements InvocationHandler {
private Object target;
public HelloProxyPlus(Object target) {
this.target = target;
}
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
before();
Object result = method.invoke(target,objects);
after();
return result;
}
private void before(){
System.out.println("before");
}
private void after(){
System.out.println("after");
}
}

在使用的时候:

1
2
3
4
Hello helloImpl = new HelloImpl();
HelloProxyPlus helloProxyPlus = new HelloProxyPlus(helloImpl);
Hello dynamicHello = (Hello)Proxy.newProxyInstance(helloImpl.getClass().getClassLoader(),helloImpl.getClass().getInterfaces(),helloProxyPlus);
dynamicHello.say();

这种方式能将一个增强方法运用到所有的欲增强类中。大大精简了项目代码。

实现方案四

小B的兴奋劲没过几天,又遇到了难题。Proxy.newProxyInstance的第二个参数是getInterfaces(),只能代理接口方法。但并不是所有需要增强的方法都是接口方法呀。小B没招了,去请教架构师,架构师给了他一个建议,为什么不用cglib呢?小B如获至宝,查阅资料后才明白cglib是一个代码生成库,能在运行时动态生成一个类的子类,在生成过程中提供了callback织入自定义代码。小B搞明白后改写了代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class HelloProxyPlusPlus implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
before();
Object result = methodProxy.invokeSuper(o,objects);
after();
return result;
}
private void before(){
System.out.println("before");
}
private void after(){
System.out.println("after");
}
}

在使用的时候:

1
2
3
HelloProxyPlusPlus helloProxyPlusPlus = new HelloProxyPlusPlus();
Hello cglibHello = (Hello)Enhancer.create(HelloImpl.class,helloProxyPlusPlus);
cglibHello.say();

Enhancer.create()接受两个参数:第一个参数是需要继承的父类,第二个参数是MethodInterceptor对象,在MethodInterceptor对象的intercept中实现对父类方法的增强。

小B志得意满了,等着评审会好好秀一下。

END

终于到了开会的日子,架构师看完了小B的代码,向小B建议到:为什么不用注解去指明要代理谁,在生成子类后用子类的实例替换掉父类的实例呢?这样就能让被代理的方法和使用者都毫无感知了。

是的,这就是 springaop