要想了解Spring的AOP的实现,首先必须要了解代理模式。
如果只是简单代理一个对象,那么我们自己就可以很好的实现。
声明一个抽象主题,支付接口:
public interface ToCPayment {
void pay();
}
被代理的实现类
public class ToCPaymentImpl implements ToCPayment {
@Override
public void pay() {
System.out.println("以用户的名义进行支付");
}
}
代理角色(持有被代理角色)
public class AliToC implements ToCPayment {
private ToCPayment toCPayment;
public AliToC(ToCPayment toCPayment){
this.toCPayment = toCPayment;
}
@Override
public void pay() {
beforePay();
toCPayment.pay();
afterPay();
}
private void afterPay() {
System.out.println("支付给收钱方");
}
private void beforePay() {
System.out.println("从银行卡取款");
}
}
这样在调用接口的时候,直接就可以调用代理对象。
public class ProxyDemo {
public static void main(String[] args) {
ToCPayment toCProxy = new AliToC( new ToCPaymentImpl());
toCProxy.pay();
}
}
,但是这样的实现有一个弊端,以上只是针对ToC的代理,如果此时增加一个ToB岂不是又要自己手写一个被代理的类?这种静态代理的方式无疑在代理多的情况下维护成本指数级别增加。
上面的思路是没有问题的。只是相关的实现方式需要改进。
我们知道,类是通过类加载器加载的。
1.通过findClass获取到二进制字节流。
2.根据读取到的字节流,将其代表的静态数据结构转换成方法区运行时的数据结构。有了数据结构,才能让外界通过class对象取访问。
3.生成一个代表该类的class对象,作为方法区该类的数据访问入口。
改进的切入点
根据一定的规则取改动或者生成新的字节流,将切面逻辑织入进去。
这就是动态代理,在程序运行时动态生成类的字节码,并加载到JVM中。
她要求被代理的类必须实现接口(唯一约束),否则不能使用JDK动态代理。
其中关键的有一个核心类和一个接口。
public class Proxy implements java.io.Serializable
public interface InvocationHandler
只有实现了InvocationHandler的类,才具有代理功能。相关的切面逻辑就存在于此接口的实现类里面。此接口只有一个方法。
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
当我们铜鼓动态代理对象调用一个方法的时候 ,这个方法的调用就会被转发到实现了这个接口的本方法里面。
此方法接受三个参数。
//Object proxy 代理对象(不是被代理对象)
// Method method 调用的目标对象的方法示例
//方法的参数
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
创建代理类。
里面有一个很重要的方法
//ClassLoader loader 声明哪个加载器来加载class\
//为代理对象要加上的接口
// InvocationHandler 用来 表示动态代理的方法会关联到哪个 InvocationHandler 的实现类
//返回的时一个被改造的类 但是 继承自这些接口 所以可以偷天换日
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
下载我们将增强逻辑直接写在InvocationHandler的实现类里面。
public class AliPayInvocationHandler implements InvocationHandler {
//用来保存被代理的对象
private Object targetObject;
public AliPayInvocationHandler(Object targetObject){
this.targetObject = targetObject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
beforePay();
//目标类 方法参
Object result = method.invoke(targetObject, args);
afterPay();
return result;
}
//增强逻辑
private void afterPay() {
System.out.println("支付给收钱方");
}
//增强逻辑
private void beforePay() {
System.out.println("从银行卡取款");
}
}
public class JdkUtil {
public static <T> T newProxyInstance(T targetObject, InvocationHandler handler){
ClassLoader classLoader = targetObject.getClass().getClassLoader();
Class<?>[] interfaces = targetObject.getClass().getInterfaces();
return (T) Proxy.newProxyInstance(classLoader,interfaces,handler);
}
}
public class ProxyDemo {
public static void main(String[] args) {
//被代理的对象
ToCPayment toCPayment = new ToCPaymentImpl();
AliPayInvocationHandler handler = new AliPayInvocationHandler(toCPayment);
ToCPayment payment = JdkUtil.newProxyInstance(toCPayment, handler);
payment.pay();
}
}
运行:
通过动态代理,可以复用增强的逻辑 ,代理对象是自动生成的,不用显式的去写一个类。
JDK动态代理要求对象必须实现接口,否则不能使用动态代理。这样在于在接口调用方便偷天换日。
CGLIB全程 Code Generation Libary.不要求目标必须实现接口,内部封装了ASM字节码操作框架。
缺点在于:上手难度高 ,对JVM不熟悉直接修改字节码,会发生莫名其妙的问题。
简单来说CGLIB只是动态生成目标类的子类,覆盖非final的方法,绑定钩子回调自定义拦截器 。
使用cglib先导包。
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.9</version>
</dependency>
主要由三个部分组成。
CallBack接口
```clike
public interface Callback {
}
`一个空接口,没有方法,仅仅是标志作用。
MethodInterceptor接口
public interface MethodInterceptor extends Callback {
//参数1 动态代理对象
//被代理的方法
// 方法参数
//代理方法
Object intercept(Object var1, Method var2, Object[] var3, MethodProxy var4) throws Throwable;
}
我们可以通过实现这个接口,构造出通用的逻辑。
Enhancer类
帮助我们创建出动态代理实例出来。
定义通用切面
public class AliPayMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
beforePay();
//执行被代理方法
Object invoke = methodProxy.invokeSuper(o, objects);
afterPay();
return invoke;
}
private void afterPay() {
System.out.println("支付给收钱方");
}
private void beforePay() {
System.out.println("从银行卡取款");
}
}
创建动态代理类
public class CgLibUtil {
public static <T> T createProxy(T targetObject , MethodInterceptor interceptor){
return (T) Enhancer.create(targetObject.getClass(), interceptor);
}
}
创建被代理对象,(无接口)
public class CommonPay {
public void pay(){
System.out.println("一个通用的支付通道");
}
}
Demo
public class CgLibDemo {
public static void main(String[] args) {
//无接口的类
CommonPay commonPay = new CommonPay();
AliPayMethodInterceptor interceptor = new AliPayMethodInterceptor();
CommonPay commonPayProxy = CgLibUtil.createProxy(commonPay,interceptor);
commonPayProxy.pay();
}
}
运行结果
CGLIB方式 不仅仅支持无接口的对象代理,也支持有接口的对象代理。
public class CgLibDemo {
public static void main(String[] args) {
/* CommonPay commonPay = new CommonPay();
AliPayMethodInterceptor interceptor = new AliPayMethodInterceptor();
CommonPay commonPayProxy = CgLibUtil.createProxy(commonPay,interceptor);
commonPayProxy.pay();*/
//tocPayMent接口的实现类
ToCPayment toCPayment = new ToCPaymentImpl();
AliPayMethodInterceptor interceptor = new AliPayMethodInterceptor();
ToCPayment proxy = CgLibUtil.createProxy(toCPayment, interceptor);
proxy.pay();
}
}
但是JDK动态代理模式,如果强行代理无接口的类,就会抛出异常。
public class CgLibDemo {
public static void main(String[] args) {
/* CommonPay commonPay = new CommonPay();
AliPayMethodInterceptor interceptor = new AliPayMethodInterceptor();
CommonPay commonPayProxy = CgLibUtil.createProxy(commonPay,interceptor);
commonPayProxy.pay();*/
//tocPayMent的实现类
/* ToCPayment toCPayment = new ToCPaymentImpl();
AliPayMethodInterceptor interceptor = new AliPayMethodInterceptor();
ToCPayment proxy = CgLibUtil.createProxy(toCPayment, interceptor);
proxy.pay();*/
//尝试使用JDK动态代理
CommonPay commonPay = new CommonPay();
AliPayInvocationHandler handler = new AliPayInvocationHandler(commonPay);
CommonPay newProxyInstance = JdkUtil.newProxyInstance(commonPay, handler);
newProxyInstance.pay();
}
}