一 : 什么是代理(Proxy)模式?
代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。通俗讲.就是真正的业务功能还是由委托类来实现,但是在实现业务之前的一些公共服务,例如在项目开发中忘记了加入缓冲、日志等的功能。后期想加入,就可以使用代理来实现而没有必要打开已经封装好的委托类。
现实生活中的代理模式:房东-->中介-->租客 中介就是代理方,负责看房,谈价格,签合同等琐事,房东只需收钱.
二 : 为什么要有代理模式?
三 : 怎么使用代理模式?
3.1 静态代理
静态代理定义 : 静态代理是由程序员创建或特定工具自动生成源代码,再对其编译。最大的特点就是在程序运行时代理类的.class文件就已经存在了,但是这有一个很大的缺陷即每一个代理类只能为一个接口服务。
3.1.1 静态代理示例:
①:定义服务接口
package com.canner.proxy; /** * @Auther: canner * @Date: 12:18 2018/11/02 */ public interface ICustomer { void buyHosue(); }
②:定义服务实现类
import com.canner.proxy.ICustomer; /** * @Auther: canner * @Date: 12:20 2018/11/02 */ public class CustomerImpl implements ICustomer{ @Override public void buyHosue() { System.out.println("顾客买了一套房"); } }
③:创建客户代理类
package com.canner.proxy; import com.canner.proxy.ICustomer; /** * @Auther: canner * @Date: 12:33 2018/11/02 */ public class CustomerProxy implements ICustomer{ private ICustomer customer; public CustomerProxy(final ICustomer customer) { this.customer= customer; } @Override public void buyHosue() { System.out.println("客户买房前做的事情"); customer.buyHosue(); System.out.println("客户买房后做的事情"); } }
④:编写测试
/** * @Auther: canner * @Date: 12:25 2018/11/02 */ public class ProxyTest { public static void main(String[] args) { ICustomer customer = new CustomerImpl(); CustomerProxy customerProxy = new CustomerProxy(customer); customerProxy.buyHosue(); } }
静态代理总结:代理对象和实际对象实现的是同一个接口
优点:可以做到在符合开闭原则的情况下对目标对象进行功能扩展。
缺点:1:每一个代理类只能为一个接口服务,工作量太大,不易管理。同时接口一旦发生改变,代理类也得相应修改。
2:在程序运行之前静态代理的.class文件已经存在了。
3:如果接口新增一个方法,代理类也得维护新增内容。
3.2 动态代理
3.2.1 JDK动态代理
动态代理 : 在动态代理中我们不再需要再手动的创建代理类,我们只需要编写一个动态处理器就可以了。真正的代理对象由JDK在运行时利用反射为我们动态的来创建。
JDK动态代理必须要求真实对象是有接口
JDK动态代理操作步骤 :
① 实现 InvocationHandler 接口,创建自己增强代码的处理器。
② 给 Proxy 类提供 ClassLoader 对象和代理接口类型数组,创建动态代理对象。
③ 在处理器中实现增强操作。
JDK动态代理类:
public class TransactionManagerAdvice implements java.lang.reflect.InvocationHandler { private Object target;//真实对象(对谁做增强) private TransactionManager txManager;//事务管理器(模拟) public void setTxManager(TransactionManager txManager) { this.txManager = txManager; } public void setTarget(Object target) { this.target = target; } //创建一个代理对象 public <T> T getProxyObject() { return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), //类加载器,一般跟上真实对象的类加载器 target.getClass().getInterfaces(), //真实对象所实现的接口(JDK动态代理必须要求真实对象有接口) this);//如何做事务增强的对象 } //如何为真实对象的方法做增强的具体操作 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//System.out.println(proxy); 如果打印这行会报栈溢出 if (method.getName().startsWith("get") || method.getName().startsWith("list")) { return method.invoke(target, args);//放行 } Object ret = null; txManager.begin(); try { //--------------------------------------------------------------- ret = method.invoke(target, args);//调用真实对象的方法 //--------------------------------------------------------------- txManager.commit(); } catch (Exception e) { e.printStackTrace(); txManager.rollback(); } return ret; } }
注意事项:反编译字节码文件后,发现底层对hashCode(),equals(),toString()方法和真实对象中的方法做了增强,所以在invoke()方法中如果直接打印proxy,相当于调用toString()方法,因此会递归调用导致栈溢出.调用proxy.getClass()方法则不会出现问题.
3.2.2 CGLIB动态代理
使用JDK的动态代理,只针对于目标对象有接口的情况,如果目标对象没有接口,则需要使用CGLIB的动态处理方式
CGLIB动态代理类:
public class TransactionManagerAdvice implements org.springframework.cglib.proxy.InvocationHandler { private Object target;//真实对象(对谁做增强) private TransactionManager txManager;//事务管理器(模拟) public void setTxManager(TransactionManager txManager) { this.txManager = txManager; } public void setTarget(Object target) { this.target = target; } //创建一个代理对象 public <T> T getProxyObject() { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(target.getClass());//将继承于哪一个类,去做增强 enhancer.setCallback(this);//设置增强的对象 return (T) enhancer.create();//创建代理对象 } //如何为真实对象的方法做增强的具体操作 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object ret = null; txManager.begin(); try { //--------------------------------------------------------------- ret = method.invoke(target, args);//调用真实对象的方法 //--------------------------------------------------------------- txManager.commit(); } catch (Exception e) { e.printStackTrace(); txManager.rollback(); } return ret; } }