代理模式的应用场景:springAOP、旧业务代码改造
静态代理:
首先有个业务接口IPayChannelService,里面有2个功能:扣款、退款
public interface IPayChannelService {
public boolean pay(String orderId);
public boolean refund(String orderId);
}
旧有实现类AlipayChannelService,处理扣款、退款业务
public class AlipayChannelService implements IPayChannelService {
@Override
public boolean pay(String orderId) {
System.out.println("orderId: " + orderId + " ,pay success !");
return true;
}
@Override
public boolean refund(String orderId) {
System.out.println("orderId: " + orderId + " ,refund success !");
return true;
}
}
业务要求:新增一个订单查询功能并且增加退款的处理逻辑,扣款功能保持不变
我们首先想到的是修改AlipayChannelService类的refund()方法,并新增一个query()方法。但是前辈们的旧代码如果晦涩难懂,那么直接改动AlipayChannelService类的勇气是值得商榷的。
这时候可以新写一个类StaticProxyService ,兼具旧类的功能,并且增加一定的功能扩展
public class StaticProxyService implements IPayChannelService {
private AlipayChannelService alipayChannelService;
public StaticProxyService() {
this.alipayChannelService = new AlipayChannelService();
}
@Override
public boolean pay(String orderId) {
return alipayChannelService.pay(orderId);
}
@Override
public boolean refund(String orderId) {
/**
* 增加新的退款处理逻辑
*/
System.out.println("StaticProxyService - orderId: " + orderId + " , refund success !");
return alipayChannelService.refund(orderId);
}
public boolean query(String orderId) {
System.out.println("StaticProxyService - orderId: " + orderId + " ,query success !");
return true;
}
}
StaticProxyService 类兼具了旧AlipayChannelService 类的扣款功能、并且增加了退款处理逻辑、查询功能。这样做不需要将AlipayChannelService类中的代码复制粘贴一遍到新类,也不用冒改动旧代码的风险。
public static void main(String[] args) {
IPayChannelService staticProxyService = new StaticProxyService();
((StaticProxyService) staticProxyService).query("123");
staticProxyService.refund("456");
staticProxyService.pay("789");
}
执行结果可以看到,依旧执行了旧类中的pay()和refund(),同时在执行refund()前加入了新退款逻辑,并且执行了新增的查询功能。
JDK动态代理:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* Description 动态代理类
* Created by szd
* Email 1911150175@qq.com
* date on 2019/8/7
**/
public class DynamicProxyService implements InvocationHandler {
private Object proxyTarget;
public Object getProxyInstance(Object target) {
this.proxyTarget = target;
return Proxy.newProxyInstance(proxyTarget.getClass().getClassLoader(), proxyTarget.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/**
* 1.这里不能用入参中的proxy对象,不然会一直循环代理
* 2.使用proxyTarget的原因,在执行getProxyInstance()方法时,已经获得了代理对象的实例,用代理对象来invoke()
*/
return method.invoke(proxyTarget, args);
}
}
public static void main(String[] args) {
DynamicProxyService dynamicProxyService = new DynamicProxyService();
IPayChannelService payChannelService = (IPayChannelService) dynamicProxyService.getProxyInstance(new AlipayChannelService());
payChannelService.pay("123");
}
动态代理,运用场景在于做切面,尤其是springAOP,日志框架等,在执行目标方法method.invoke(proxyTarget,args)前(before)、后(after),进行其他处理
CGLIB动态代理:
对于无继承(实现接口)的类,JDK动态代理就无法实现了,这时候就只能靠cglib动态代理
public class AlipayChannelV2Service {
public boolean pay(String orderId) {
System.out.println("AlipayChannelV2Service orderId: " + orderId + " ,pay success !");
return true;
}
}
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* Description CGLIB代理
* Created by szd
* Email 1911150175@qq.com
* date on 2019/8/8
**/
public class CglibProxyService implements MethodInterceptor {
private Object proxyTarget;
public Object getInstance(Object target) {
this.proxyTarget = target;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.proxyTarget.getClass());
// 设置回调方法
enhancer.setCallback(this);
// 创建代理对象
return enhancer.create();
}
@Override
public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
return methodProxy.invokeSuper(object, args);
}
}
public static void main(String[] args) {
CglibProxyService cglib = new CglibProxyService();
AlipayChannelV2Service alipayChannelV2Service = (AlipayChannelV2Service) cglib.getInstance(new AlipayChannelV2Service());
alipayChannelV2Service.pay("123");
}
要注意的是,cglib代理对于final修饰的类是无效的,对于final修饰的方法并无影响(网上帖子说对final修饰的方法也不能代理,我没法重现,不知道是我哪里不对,希望有知道的大神可以指点一二。)
(动态代理往往用于做切面,并不去override方法,也就没有重写一说,个人觉得重写在静态代理中运用的普遍)