原理
由于某些原因需要给一些对象提供一个代理来控制对该对象的访问。此时,访问对象不是直接访问目标对象而是通过代理对象作为中介进行访问
Java中的代理按照代理类生成时机不同又分为静态代理和动态代理。静态代理代理类在编译期生成,而动态代理代理类在Java运行时动态生成。动态代理又有JDK代理 和CGLib代理两种
结构
- 抽象主题(Subject)类:通过接口或抽象类声明真实主题和代理对象实现的业务方法。(规定代理类代理目标对象的规则)
- 真实主题(Real Subject)类:实现抽象主题中的具体业务,是代理对象所代表的真实对象,即目标对象
- 代理(Proxy)类:提供与真实主题相同的接口,其内部含有对真实主题的引用,可以访问、控制、扩展真实主题的功能
1.静态代理案例
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
| public interface SellTickets { void sell(); }
public class TrainStation implements SellTickets{ @Override public void sell() { System.out.println("买票成功....."); } }
public class ProxyPoint implements SellTickets{ private TrainStation trainStation = new TrainStation();
@Override public void sell() { System.out.println("代售点收取服务费"); trainStation.sell(); }
}
public class Client { public static void main(String[] args) { ProxyPoint proxyPoint = new ProxyPoint(); proxyPoint.sell(); } }
|
2.动态代理案例
Java中提供一个动态代理类Proxy,Proxy不是上述的代理对象的类,而是创建代理对象类的类,Proxy类中提供的静态方法newProxyInstance可以获取代理对象类(这是由于动态代理代理类在Java程序运行时动态生成这一特性决定的
JDK动态代理要求必须定义接口,对接口进行代理
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 47 48 49 50 51 52 53 54 55 56
| public interface SellTickets { void sell(); }
public class TrainStation implements SellTickets{ @Override public void sell() { System.out.println("买票成功....."); } }
public class ProxyFactory { private TrainStation trainStation = new TrainStation(); public SellTickets getProxyObject(){
return (SellTickets) Proxy.newProxyInstance(trainStation.getClass().getClassLoader(), trainStation.getClass().getInterfaces(), new InvocationHandler() {
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("代售点收取服务费。。。。");
Object invoke = method.invoke(trainStation, args); return invoke; } }); } }
public class Client { public static void main(String[] args) { ProxyFactory proxyFactory = new ProxyFactory(); SellTickets proxyObject = proxyFactory.getProxyObject(); proxyObject.sell(); } }
|
3.CGLib动态代理
CGLib是一个功能强大,高性能的代码生成包。它为没有实现接口的类提供代理,为JDK动态代理提供了很好的补充
CGLib是第三方提供的包,需要导入jar包
1 2 3 4 5
| <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>2.2.2</version> </dependency>
|
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
| public interface SellTickets { void sell(); }
public class TrainStation implements SellTickets{ @Override public void sell() { System.out.println("买票成功....."); } }
public class ProxyFactory implements MethodInterceptor {
private TrainStation trainStation = new TrainStation(); public TrainStation getProxyObject(){ Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(TrainStation.class); enhancer.setCallback(this); TrainStation proxyObject = (TrainStation) enhancer.create(); return proxyObject; }
@Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("代售点收取服务费...");
Object obj = method.invoke(trainStation,objects);
return null; } }
|
在JDK1.8及之后,JDK代理效率高于CGLib代理。所以如果有接口使用JDK动态代理,如果没有接口使用CGLIE代理。
动态代理和静态代理:
动态代理与静态代理相比较,最大的好处是接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理(InvocationHandler.invoke)。这样,在接口方法数量比较多的时候,我可以进集中处理,而不需要像静态代理那样每一个方法进行中转。
如果接口增加一个方法,静态代理模式除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。而动态代理不会出现该问题。
4.代理模式的优缺点及使用场景
优点:
代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用
代理对象可以扩展目标对象的功能
代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度
缺点:
增加了系统的复杂度
使用场景:
远程(Remote)代理
本地服务通过网络请求远程服务。为了实现本地到远程的通信,我们需要实现网络通信,处理其中可能的异常。为良好的代码设计和可维护性,我们将网络通信部分隐藏起来,只暴露给本地服务一个接口,通过该接口即可访问远程服务提供的功能,而不必过多关心通信部分的细节。(RPC思想,例如:dubbo)
防火墙(Firewall)代理
当你将浏览器配置成使用代理功能时,防火墙就将你的浏览器的请求转给互联网;当互联网返回响应时,代理服务器再把它转给你的浏览器。(VPN)
保护(Protect or Access)代理
控制对一个对象的访问,如果需要,可以给不同的用户提供不同级别的使用权限。