Spring AOP实现原理

在之前的一文中介绍过Spring AOP的功能使用,但是没有深究AOP的实现原理,今天正好看到几篇好文,于是就自己整理了一下AOP实现的几种方式,同时把代理模式相关知识也稍微整理一下。

代理模式

代理模式的UML类图如下:

可以看到还是很简单的,代理类实现了被代理类的接口,同时与被代理类是组合关系。下面看一下代理模式的实现。

静态代理

接口类:

interface Person {
void speak();
}

真实实体类:

class Actor implements Person {
private String content;
public Actor(String content) {
this.content = content;
} @Override
public void speak() {
System.out.println(this.content);
}
}

代理类:

class Agent implements Person {
private Actor actor;
private String before;
private String after;
public Agent(Actor actor, String before, String after) {
this.actor = actor;
this.before = before;
this.after = after;
}
@Override
public void speak() {
//before speak
System.out.println("Before actor speak, Agent say: " + before);
//real speak
this.actor.speak();
//after speak
System.out.println("After actor speak, Agent say: " + after);
}
}

测试方法:

public class StaticProxy {
public static void main(String[] args) {
Actor actor = new Actor("I am a famous actor!");
Agent agent = new Agent(actor, "Hello I am an agent.", "That's all!");
agent.speak();
}
}

结果:

动态代理

在讲JDK的动态代理方法之前,不妨先想想如果让你来实现一个可以任意类的任意方法的代理类,该怎么实现?有个很naive的做法,通过反射获得Class和Method,再调用该方法,并且实现一些代理的方法。我尝试了一下,很快就发现问题所在了。于是乎,还是使用JDK的动态代理接口吧。

JDK自带方法

首先介绍一下最核心的一个接口和一个方法:

首先是java.lang.reflect包里的InvocationHandler接口:

public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}

我们对于被代理的类的操作都会由该接口中的invoke方法实现,其中的参数的含义分别是:

  • proxy:被代理的类的实例
  • method:调用被代理的类的方法
  • args:该方法需要的参数

使用方法首先是需要实现该接口,并且我们可以在invoke方法中调用被代理类的方法并获得返回值,自然也可以在调用该方法的前后去做一些额外的事情,从而实现动态代理,下面的例子会详细写到。

另外一个很重要的静态方法是java.lang.reflect包中的Proxy类的newProxyInstance方法:

public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException

其中的参数含义如下:

  • loader:被代理的类的类加载器
  • interfaces:被代理类的接口数组
  • invocationHandler:就是刚刚介绍的调用处理器类的对象实例

该方法会返回一个被修改过的类的实例,从而可以自由的调用该实例的方法。下面是一个实际例子。

Fruit接口:

public interface Fruit {
public void show();
}

Apple实现Fruit接口:

public class Apple implements Fruit{
@Override
public void show() {
System.out.println("<<<<show method is invoked");
}
}

代理类Agent.java:

public class DynamicAgent {

	//实现InvocationHandler接口,并且可以初始化被代理类的对象
static class MyHandler implements InvocationHandler {
private Object proxy;
public MyHandler(Object proxy) {
this.proxy = proxy;
} //自定义invoke方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(">>>>before invoking");
//真正调用方法的地方
Object ret = method.invoke(this.proxy, args);
System.out.println(">>>>after invoking");
return ret;
}
} //返回一个被修改过的对象
public static Object agent(Class interfaceClazz, Object proxy) {
return Proxy.newProxyInstance(interfaceClazz.getClassLoader(), new Class[]{interfaceClazz},
new MyHandler(proxy));
}
}

测试类:

public class ReflectTest {
public static void main(String[] args) throws InvocationTargetException, IllegalAccessException {
//注意一定要返回接口,不能返回实现类否则会报错
Fruit fruit = (Fruit) DynamicAgent.agent(Fruit.class, new Apple());
fruit.show();
}
}

结果:

可以看到对于不同的实现类来说,可以用同一个动态代理类来进行代理,实现了“一次编写到处代理”的效果。但是这种方法有个缺点,就是被代理的类一定要是实现了某个接口的,这很大程度限制了本方法的使用场景。下面还有另外一个使用了CGlib增强库的方法。

CGLIB库的方法

CGlib是一个字节码增强库,为AOP等提供了底层支持。下面看看它是怎么实现动态代理的。

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method; public class CGlibAgent implements MethodInterceptor { private Object proxy; public Object getInstance(Object proxy) {
this.proxy = proxy;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.proxy.getClass());
// 回调方法
enhancer.setCallback(this);
// 创建代理对象
return enhancer.create();
}
//回调方法
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println(">>>>before invoking");
//真正调用
Object ret = methodProxy.invokeSuper(o, objects);
System.out.println(">>>>after invoking");
return ret;
} public static void main(String[] args) {
CGlibAgent cGlibAgent = new CGlibAgent();
Apple apple = (Apple) cGlibAgent.getInstance(new Apple());
apple.show();
}
}

结果:

可以看到结果和JDK动态代理是一样的,但是可以直接对实现类进行操作而非接口,这样会有很大的便利。

参考文献

最新文章

  1. AngularJS基础入门初探
  2. zabbix告警使用sendEmail
  3. xdebug所有相关方法函数详解(中文翻译版)
  4. soui中subscribeEvent订阅控件消息与宏订阅注意事项
  5. jquery 格式化日期
  6. Linux Basic --- The First Character in The File Properity
  7. nginx配置文件详解( 看着好长,其实不长,看了就知道了,精心整理,有些配置也是没用到呢 )
  8. android service 如何弹出dialog
  9. {Reship}{C#}{GDI+}GDI+画笔,线,区域类型
  10. HTML5客户端数据存储
  11. DHCP协议讲解
  12. hdu 1348 Wall(凸包模板题)
  13. KeCode对照表(键盘按键的获取)
  14. (Problem 49)Prime permutations
  15. 青否云 - 小程序待办事项 wxapp开源系统
  16. Exp1 PC平台逆向破解 20165235 祁瑛
  17. Hadoop 跨集群访问
  18. python 网络编程 tcp和udp 协议
  19. php装curl拓展出错
  20. LED

热门文章

  1. JavaWeb开发学习(一)-JavaWeb开发概述
  2. Swift-ImageView响应点击事件
  3. [ASE]sprint2 总结 &amp; sprint3计划
  4. ie7,8常见bug,共计257个bug汇总?如何解决ie的历史bug
  5. 如何参与一个 GitHub 开源项目?
  6. STC12C5A60S2笔记5(省电模式)
  7. VS2012下配置MPI
  8. 深入理解MVVM模式中Silverlight的Trigger、Action和Behavior及Silverlight的继承机制
  9. IOS UIView 03- 自定义 Collection View 布局
  10. jmx server 和jmx client