在啃Spring的AOP之前,在复习以下动态代理及其实现方式
代理模式
在不使用代理模式的场景下,对象A想使用对象B的功能,一个方法是通过持有对对象B的引用,然后直接使用对象B提供的服务了。
而使用了代理模式,则引入一个第三方的代理对象,这个代理对象持有着对对象B的引用,可以调用对象B的服务与资源,而如果有对象希望使用对象B提供的服务的话,则不再去找对象B,而是去找这个代理对象。 如下图所示。
很多思想都是通过引入一个第三者来去实现某个功能,使得程序耦合度更低,或者提高代码复用性等等,例如发布-订阅模式,控制反转,领域模型的思想等等,而这里的代理模式,也是引入了一个第三者(代理对象),这个对象对外提供的接口仅仅是对某一主要功能的服务接口,代理对象内部持有真正提供这一服务的对象,通过它们来调用服务,同时,在代理对象这一层面对方法进行增强。。。
例如:当我们调用dao接口访问数据库的时候,我们关注的是访问数据库这一过程,而记录日志,安全管理,事务管理这一类事情尽管也很重要,但并不是我们关注的对象,并且这类事情不仅仅只是在这一次访问数据库才会去做的,而是每次访问数据库都要去做的。。
代理模式的两种实现方式
在实现代理模式之前,脑中要有2个关键核心的东西, 代理对象 = 被代理对象 + 切面逻辑, 也许这么说并不是非常严谨准确,但方便对代理模式的学习。
一、静态代理
静态代理就是在编译时就已经确定了怎样去增强方法,也就是,把切面的逻辑都写死在代理对象里面。就是说,在代理类里,就已经确定了代理对象 和 切面逻辑。(毕竟都写死在里面了)
1 2 3 4 5 6 7 8 9
| package myAOP.proxy;
public interface Pay { void pay(); }
|
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
| package myAOP.proxy;
public class StaticPayProxy implements Pay { private Pay payment;
public StaticPayProxy(Pay payment) { this.payment = payment; }
@Override public void pay() { postProcessBeforePayment(); payment.pay(); postProcessAfterPayment(); } void postProcessBeforePayment(){ System.out.println("支付前需要做的一些事情~~~ 我要支付啦!"); }
void postProcessAfterPayment(){ System.out.println("支付后需要做的一些事情~~~支付完啦!"); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| ge myAOP.proxy;
import myAOP.proxy.impl.ToCPaymentImpl;
public class proxyMain { public static void main(String[] args) { Pay userPay = new StaticPayProxy(new ToCPaymentImpl()); userPay.pay(); } }
|
静态代理带来的弊端就是,增强的代码被写死到某一个固定的代理类中,这样使得增强的代码无法被复用。 例如,上面的例子中,我们又来了一个ToC的Payment需要去实现的话,那么也许业务逻辑不一样,但是增强的方法实际上是一样的,但我们确不得不重新在新的代理对象里再写一遍这个增强方法。。 为了能够使得增强代码复用, 可以用动态代理来做。
二、动态代理
动态代理是在运行时去确定增强的方法,在运行时将一些切面逻辑给织入到我们的程序中。也就是说,在运行时才确定 被代理对象 + 切面逻辑 。 而下面的2种动态代理的实现,实际上也主要就是学习使用JDK和第三方库提供给我们的强大功能,即:
- 切面逻辑抽象化的功能 (JDK里用的InvovationHandler,Cglib里是实现了CallBack接口的Interceptor)
- 代理对象创建的功能(JDK里用的是Proxy类,Cglib里用的是Enhancer类)
2.1 使用JDK的提供的动态代理
jdk1.3之后就提供了动态代理的功能.
1. 创建将切面逻辑抽象化的Handler对象
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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| package myAOP.proxy;
import com.sun.corba.se.spi.ior.ObjectKey;
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method;
public class PaymentHandler implements InvocationHandler {
private Object targetObject;
public PaymentHandler(Object targetObject) { this.targetObject = targetObject; }
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { postProcessBeforePayment(); Object res = method.invoke(targetObject, args); postProcessAfterPayment(); return res; }
void postProcessBeforePayment(){ System.out.println("支付前需要做的一些事情~~~ 我要支付啦!"); }
void postProcessAfterPayment(){ System.out.println("支付后需要做的一些事情~~~支付完啦!"); } }
|
2. 代理对象的创建
为了使代码更加优雅,能够统一创建代理对象的接口(即提供被代理对象+抽象出来的切面对象(这里是handler)),这里就对代理对象的创建再做一层封装。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| package myAOP.proxy;
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy;
public class JDKDynamicProxyUtil { public static <T> T newProxyInstance(T proxyObject, InvocationHandler handler){ ClassLoader classLoader = proxyObject.getClass().getClassLoader(); Class<?>[] interfaces = proxyObject.getClass().getInterfaces(); Object res = Proxy.newProxyInstance(classLoader, interfaces, handler); return (T)res; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| package myAOP.proxy;
import myAOP.proxy.impl.ToCPaymentImpl;
public class proxyMain { public static void main(String[] args) { Pay userPay = new ToCPaymentImpl(); PaymentHandler paymentHandler = new PaymentHandler(userPay); Pay pay = JDKDynamicProxyUtil.newProxyInstance(userPay, paymentHandler); pay.pay(); } }
|
但使用JDK自带的动态代理库的话,被代理对象一定要实现了某一个接口,如果被代理对象没有实现任何接口的话,就不能用这个方法… 例如下面这个情况。
1 2 3 4 5 6 7 8 9 10 11
| package myAOP.proxy;
public class PayWithoutInterface { public void pay(){ System.out.println("用户进行了支付(本操作未实现接口)"); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| package myAOP.proxy;
import myAOP.proxy.impl.ToCPaymentImpl;
public class proxyMain { public static void main(String[] args) { PayWithoutInterface userPay = new PayWithoutInterface(); PaymentHandler paymentHandler = new PaymentHandler(userPay); PayWithoutInterface pay = JDKDynamicProxyUtil.newProxyInstance(userPay, paymentHandler); pay.pay(); } }
|
而如果使用Cglib的话,就不存在这一问题了。。
2.2 使用cglib实现动态代理
cglib是第三方库,基于asm实现的,即直接对字节码进行代码织入
1. 创建将切面逻辑抽象化的Callback对象
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 31 32 33 34 35 36 37
| package myAOP.proxy;
import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class PayInterceptor implements MethodInterceptor {
@Override public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { postProcessBeforePayment(); Object res = methodProxy.invokeSuper(o, args); postProcessAfterPayment(); return res; }
void postProcessBeforePayment(){ System.out.println("支付前需要做的一些事情~~~ 我要支付啦!"); }
void postProcessAfterPayment(){ System.out.println("支付后需要做的一些事情~~~支付完啦!"); } }
|
这里看似没有实现Callback接口,是因为MethodInterceptor接口已经继承了Callback接口了
1 2 3
| public interface MethodInterceptor extends Callback { Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable; }
|
2. 代理对象的创建
这里同样对创建逻辑进行了一层封装
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| package myAOP.proxy;
import net.sf.cglib.proxy.Callback; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor;
import java.lang.reflect.Method;
public class CglibUtil { public static <T> T newInstance(T targetObject, Callback methodInterceptor){ return (T) Enhancer.create(targetObject.getClass(), methodInterceptor); } }
|
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 31 32 33 34 35 36 37
| package myAOP.proxy;
import myAOP.proxy.impl.ToCPaymentImpl; import net.sf.cglib.proxy.Callback;
public class proxyMain { public static void main(String[] args) {
PayWithoutInterface userPay = new PayWithoutInterface(); Callback interceptor = new PayInterceptor(); PayWithoutInterface userPayProxy = CglibUtil.newInstance(userPay, interceptor); userPayProxy.pay(); } }
|