AOP,即面向切面编程,算是OOP(面向对象编程)的补充和完善。OOP引入封装、继承、多态等概念来建立一种对象层次结构。OOP主要应对纵向的关系,而AOP则负责横向的关系,如事务管理,日志等。
AOP实现的关键在于AOP框架自动创建的AOP代理,AOP代理主要分为静态代理和动态代理,静态代理的代表为AspectJ;而动态代理则以Spring AOP为代表。 这篇文章主要介绍Spring AOP。 关于AspectJ的内容可以之后再探索。
Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理。
JDK动态代理通过反射来接收被代理的类,并且要求被代理的类必须实现一个接口。
如果目标类没有实现接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成某个类的子类,注意,CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。
为动态代理和CGLIB做一个简单的比较:
- 如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
- 如果目标对象实现了接口,仍然可以强制使用CGLIB实现AOP
- 如果目标对象没有实现接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换
在使用SpringAOP的过程中,或许有时会看到@AspectJ 这样的注解。不过Spring 只是使用了与 AspectJ 5 一样的注解,但没有使用 AspectJ 的编译器(ajc编译器),底层依然是动态代理技术的实现,因此并不依赖于 AspectJ 的编译器。
动态代理
介绍了许多关于代理的基本概念之后,进行动态代理的深入探究。JDK动态代理的核心是InvocationHandler接口和Proxy类。
Proxy类
我们主要通过这个类中的一个方法生成代理对象
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler invocationHandler throws IllegalArgumentException
其中第一个参数loader 代表类加载器(即哪个类加载器来加载这个代理类到 JVM 的方法区)
第二个参数是接口(表明这个代理类需要实现哪些接口),第三个参数是调用处理器类实例
这个函数是 JDK 为了程序员方便创建代理对象而封装的一个函数,因此你调用newProxyInstance()时直接创建了代理对象(略去了创建代理类的代码)。其实他主要完成了以下几个工作:
static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler handler)
{
//1. 根据类加载器和接口创建代理类
Class clazz = Proxy.getProxyClass(loader, interfaces);
//2. 获得代理类的带参数的构造函数
Constructor constructor = clazz.getConstructor(new Class[] { InvocationHandler.class });
//3. 创建代理对象,并制定调用处理器实例为参数传入
Interface Proxy = (Interface)constructor.newInstance(new Object[] {handler});
}
Proxy 类中有一个映射表,映射关系为:(<ClassLoader>,(<Interfaces>,<ProxyClass>) ),可以看出一级key为类加载器,根据这个一级key获得二级映射表,二级key为接口数组,因此可以看出:一个类加载器对象和一个接口数组确定了一个代理类。
InvocationHandler接口
这个接口下只有一个方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
在每次代理对象调用 实际接口 中的方法时,都会触发这个 invoke 方法,其中有三个参数
proxy参数 代表代理对象, method参数代表调用的具体的方法, args参数代表这个method所需要的具体参数
定义接口与实现类
接下来通过一个具体的例子查看如何实现动态代理
接口
public interface BuyTickets {
String buy(String userName);
}
接口实现类
public class BuyTicketsImpl implements BuyTickets {
public String buy(String userName) {
String result = userName + "buy Success!";
return result;
}
}
生成代理
public class BuyTicketsProxy implements InvocationHandler {
private Object subObject; //需要代理的目标对象
public Object creatProxy(Object subObject){
this.subObject = subObject;
return Proxy.newProxyInstance(subObject.getClass().getClassLoader(), subObject.getClass().getInterfaces()
,this);
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理类名:" + proxy.getClass().getName());
System.out.println("代理方法名:" + method.getName());
Object result = null;
System.out.println("----------begin method------------"); //这里可以织入其他切面逻辑
result = method.invoke(this.subObject, args);
System.out.println("between hhh" + result);
System.out.println("----------end method ---------------");
return result;
}
}
测试
public class Main {
public static void main(String []args){
BuyTicketsProxy buyTicketsProxy = new BuyTicketsProxy();
//利用Proxy 创建一个代理实例
BuyTickets buyTickets = (BuyTickets) buyTicketsProxy.creatProxy(new BuyTicketsImpl());
String result = buyTickets.buy("LYP");
System.out.println(result);
}
}
结果
代理类名:com.sun.proxy.$Proxy0
代理方法名:buy
----------begin method------------
between hhhLYPbuy Success!
----------end method ---------------
LYPbuy Success!
网友评论