假设我们需要转账,转账有支付宝和微信支付两种方式,两种方式的转账前都需要检查一下账户信息,转账后显示余额。我们可以提供一个代理人,专门帮我们做检查账户信息和显示余额这种切面工作,而转账类(无论是支付宝还是微信支付)只负责转账本身。这就是代理模式。
静态代理
首先提供一个转账接口
1 2 3 4
| public interface Transfer { void transfer(int amount); }
|
支付宝类和微信支付类分别有他们自己的转账实现
支付宝
1 2 3 4 5 6 7
| public class AliPayTransfer implements Transfer { @Override public void transfer(int amount) { System.out.println("使用支付宝转出了 " + amount + " 元"); }
}
|
微信支付
1 2 3 4 5 6
| public class WechatPayTransfer implements Transfer { @Override public void transfer(int amount) { System.out.println("使用微信支付转出了 " + amount + " 元"); } }
|
我们还需要一个支付代理人,帮我们做转账前的检查账户和转账后的显示余额:
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
| public class TransferProxy implements Transfer {
private Transfer transfer;
public TransferProxy(Transfer transfer) { this.transfer = transfer; }
@Override public void transfer(int amount) { before(); this.transfer.transfer(amount); after(); }
private void before(){ System.out.println("检查账户"); }
private void after(){ System.out.println("显示余额"); }
}
|
现在,可以开始转账了:
1 2 3 4 5 6 7 8 9 10 11 12 13
| public static void main(String[] args) {
Transfer aliTransfer = new AliPayTransfer(); Transfer transferProxy = new TransferProxy(aliTransfer); transferProxy.transfer(100);
Transfer wechatTransfer = new WechatPayTransfer(); Transfer transferProxy2 = new TransferProxy(wechatTransfer); transferProxy2.transfer(100);
}
|
这就是静态代理,无论我们选用何种方式转账,都交给代理帮我们负责售前、转账、售后服务。但是,假设未来我们增加了现金交易,需要在 TransferProxy
中加入现金交易的构造方法,再之后,加入网银、云闪付等等,无疑我们的代理类会越来越沉重。于是,有没有一种方法,可以让代理类在运行时动态地知道即将进行的是何种方式的转账,这样就不用在代理类编写很多转账类了。动态代理就是这样来的。
动态代理
首先,还是一个接口
1 2 3 4
| public interface Transfer { void transfer(int amount); }
|
支付宝转账
1 2 3 4 5 6 7
| public class AliPayTransfer implements Transfer { @Override public void transfer(int amount) { System.out.println("使用支付宝转出了 " + amount + " 元"); }
}
|
微信支付转账
1 2 3 4 5 6
| public class WechatPayTransfer implements Transfer { @Override public void transfer(int amount) { System.out.println("使用微信支付转出了 " + amount + " 元"); } }
|
动态代理人
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
| import java.lang.reflect.Proxy;
public class TransferDynamicProxy {
public Object getTransferWay(Transfer transfer){ return Proxy.newProxyInstance(transfer.getClass().getClassLoader(), new Class[]{Transfer.class}, (proxy, method, args) -> { before(); Object invoke = method.invoke(transfer, args); after(); return invoke; });
}
private void before(){ System.out.println("检查账户"); }
private void after(){ System.out.println("显示余额"); }
}
|
开始转账
1 2 3 4 5 6 7 8 9 10
| public static void main(String[] args) {
Transfer transferDynamicProxy = (Transfer) new TransferDynamicProxy().getTransferWay(new AliPayTransfer()); transferDynamicProxy.transfer(100);
Transfer transferDynamicProxy2 = (Transfer) new TransferDynamicProxy().getTransferWay(new WechatPayTransfer()); transferDynamicProxy2.transfer(100); }
|
我们只需要给代理人 Transferproxy
类传入不同的转账类实例(微信还是支付宝),动态代理类就会对应地去生成具体代理类,然后通过具体代理类进行相关转账前、中、后操作。这就是动态代理。
Proxy 类
在上面 getTransferWay
中,我们接收一个真实的转账类(支付宝转账类),并动态地生成对应的代理类(支付宝转账代理类)。这个工作由 java.lang.reflect.Proxy 来实现。
在 Proxy 类中,提供了 static 方法 newProxyInstance :
1 2 3 4 5
| @CallerSensitive public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
|
该方法通过反射生成具体的代理类,接收三个参数:
- 类加载器
- 代理类实现的所有接口
- 要处理的事情(InvocationHandler)
InvocationHandler 本质上是一个函数式接口,表示要处理的事情
1 2 3
| public interface InvocationHandler { public void invoke(Object o, Method m); }
|
面向切面编程(AOP)
在 Spring AOP 中,正是通过动态代理来实现切面功能的,例如日志记录,事务等。
MyBatis 为什么通过一个接口就能访问数据库?