Java中的代理模式
代理模式
在代理模式里面,使用代理类控制对目标类的访问,通过代理对象控制对原对象的访问。这里的代理对象其实就是相当于是生活中的中介。
举个例子来说明:假如说我现在想买一辆二手车,虽然我可以自己去找车源,做质量检测等一系列的车辆过户流程,但是这确实太浪费我得时间和精力了。我只是想买一辆车而已为什么我还要额外做这么多事呢?于是我就通过中介公司来买车,他们来给我找车源,帮我办理车辆过户流程,我只是负责选择自己喜欢的车,然后付钱就可以了。
代理对象的存在隔离了用户和目标对象,并且可以通过给代理对象添加额外的功能来扩展目标类的功能,并且不用修改目标类的代码。总而言之,代理对象解决了直接访问对象而带来的问题。比如,要访问的对象在远程机器上面。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层。
用户只会访问代理对象,代理对象是目标对象的扩展,代理对象会调用目标对象,用户不会直接访问目标对象。
静态代理
静态代理是由程序员或是特定工具生成源代码,再对其进行编译,在程序员运行之前代理类的.class文件就已经被创建了。
BuyHouse接口:
public interface BuyHouse { void buyHouse(); }
BuyHouse接口实现:
public class BuyHouseImpl implements BuyHouse{ @Override public void buyHouse() { System.out.println("I want to buy a house!"); } }
代理对象的实现:
public class BuyHouseProxy implements BuyHouse{ private BuyHouse buyHouse; public BuyHouseProxy(BuyHouse buyHouse) { this.buyHouse=buyHouse; } @Override public void buyHouse() { System.out.println("before buy house"); buyHouse.buyHouse(); System.out.println("after buy house"); } }
测试类实现:
public class ProxyTest { public static void main(String[] args) { BuyHouse buyHouse = new BuyHouseImpl(); buyHouse.buyHosue(); BuyHouseProxy buyHouseProxy = new BuyHouseProxy(buyHouse); buyHouseProxy.buyHosue(); } }
JDK动态代理
动态代理的代理对象是是在程序运行时通过java的反射机制生成的。在动态代理里面,我们并不需要为每一个服务都创建代理类,我们只需要实现一个代理处理器,真正的对象由JDK动态的为我们创建。
动态处理器:
public class DynamicProxyHandler implements InvocationHandler{ private Object object; public DynamicProxyHandler(Object object) { // TODO Auto-generated constructor stub this.object=object; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("before method"); Object res=method.invoke(object, args); System.out.println("after method"); return res; } }
测试代码:
public class DynamicProxyTest { public static void main(String[] args) { BuyHouse buyHouse=new BuyHouseImpl(); BuyHouse proxyBuyHouse=(BuyHouse) Proxy.newProxyInstance(BuyHouse.class.getClassLoader(), new Class[]{BuyHouse.class}, new DynamicProxyHandler(buyHouse)); proxyBuyHouse.buyHouse(); } }
也可以直接将动态处理器写在Proxy.newProxyInstance方法中,这样代码比较简洁。
public class DynamicProxyTest { public static void main(String[] args) { final BuyHouse buyHouse=new BuyHouseImpl(); BuyHouse proxyBuyHouse=(BuyHouse) Proxy.newProxyInstance(BuyHouse.class.getClassLoader(), new Class[]{BuyHouse.class}, new InvocationHandler(){@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // TODO Auto-generated method stub System.out.println("before buy house"); Object resObject=method.invoke(buyHouse, args); System.out.println("after buy house"); return resObject; }}); proxyBuyHouse.buyHouse(); } }
JDK实现代理,只需要实现newProxyInstance方法,该方法需要接受三个参数。
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
loader:目标类的类加载器。
interfaces:目标类实现的接口集合,可以是一个,可以是多个。
invocationHandler:动态处理器,当调用目标类方法时会调用动态处理器的invoke方法,这时invoke(Object proxy, Method method,Object[] args)的参数proxy是代理类,method是调用的代理类对象方法,args则是方法的参数。
JDK动态代理的目标类一定要实现接口,否则不能用JDK的动态代理。
CGlib动态代理
创建CGlib代理类:
public class CglibProxy implements MethodInterceptor { private Object target; public Object getInstance(final Object target) { this.target = target; Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(this.target.getClass()); enhancer.setCallback(this); return enhancer.create(); } public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println("买房前准备"); Object result = methodProxy.invoke(object, args); System.out.println("买房后装修"); return result; } }
创建测试类:
public class CglibProxyTest { public static void main(String[] args){ BuyHouse buyHouse = new BuyHouseImpl(); CglibProxy cglibProxy = new CglibProxy(); BuyHouseImpl buyHouseCglibProxy = (BuyHouseImpl) cglibProxy.getInstance(buyHouse); buyHouseCglibProxy.buyHosue(); } }
这里CGLib和JDK动态代理实现代理模式的方法不同,JDK动态代理实现是通过代理类和目标类实现相同的接口实现的,而CGLib是通过让代理类继承目标类来实现的。
CGLIB创建的动态代理对象比JDK创建的动态代理对象的性能更高,但是CGLIB创建代理对象时所花费的时间却比JDK多得多。所以对于单例的对象,因为无需频繁创建对象,用CGLIB合适,反之使用JDK方式要更为合适一些。同时由于CGLib由于是采用动态创建子类的方法,对于final修饰的方法无法进行代理。