代理模式是指给某个对象提供一个代理对象,用户不直接访问原对象而是通过代理对象间接访问。该UML图如下:
其中涉及到三种角色:
1.抽象主题(AbstractObject)类:通过接口或抽象类声明真实主题和代理对象实现的业务方法。
2.真实主题(Real Subject)类:实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象。
3.代理(ProxyObject)类:提供了与真实主题相同的方法,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能。
1.功能增强:在你原有的功能上,增加了额外的功能,新增加的功能,叫做功能增强。
2.控制访问:代理类不让你访问目标,例如商家不让用户访问厂家。
按照代理的创建时期,代理类可分为两种:静态代理和动态代理。
代理类是自己手工实现的,自己创建一个java类,表示代理类。同时你所要代理的目标类是确定的。
1、代码示例:(以我们平时在淘宝买鞋为例)
(1)服务类接口:ShoesSell,目标类和代理类都需要实现该接口
//表示功能的, 厂家和商家都要完成的功能 public interface ShoesSell { //定义售卖方法 Float sell(int amount); }
(2)目标类:NikeFactory
//目标类:耐克厂家,不接受用户的单独购买 public class NikeFactory implements ShoesSell { /** * @param amount:数量 * @return :单价 */ @Override public Float sell(int amount) { return 1000.0f; } }
(3)代理类:TaobaoProxy
public class TaobaoProxy implements ShoesSell { //声明商家代理的厂家具体是谁 private NikeFactory nikeFactory = new NikeFactory(); @Override public int sell(int amount) { //向厂家发送订单,告诉厂家买了鞋子,发货 Float price = nikeFactory.sell(amount); //商家需要加价,也就是代理需要加价 //增强功能,代理类在完成目标类方法调用后,增强了功能 price = price + 200; //在目标类的方法调用后,你做的其他功能,都是增强的意思 System.out.println("淘宝商家给你返回一个优惠券或者红包"); return price; } }
(4)主方法测试:ShopMain
public class ShopMain { public static void main(String[] args) { //创建代理的商家taobao对象 TaobaoProxy taobao = new TaobaoProxy(); Float price = taobao.sell(1); System.out.println("通过taobao的商家,购买鞋子的单价:" + price); } } //输出结果: 淘宝商家给你返回一个优惠券或者红包 通过taobao的商家,购买鞋子的单价:1200.0
2、静态代理的优点和缺点
(1)优点:① 实现简单
② 容易理解
(2)缺点:当项目中,目标类和代理类很多时候,
① 当目标类增加了,代理类可能也需要成倍的增加,代理类数量过多。
② 当接口中功能增加或者修改了,会影响众多的实现类,目标类,代理类都需要修改,影响比较多。
使用反射机制,在程序执行中,创建代理类对象。特点是不用创建类文件,代理的目标类是活动的,可设置的。
1、jdk动态代理:使用java反射包中的类和接口实现动态代理的功能。
将上述静态代理代码修改:
(1)新建动态代理类:JdkProxyHandler
public class JdkProxyHandler implements InvocationHandler { private Object target; public JdkProxyHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object res = null; //向厂家发送订单 res = method.invoke(target,args); //代理增加价格 if (res != null) { Float price = (Float) res; price = price + 200; res = price; } //返优惠券 System.out.println("淘宝商家,返回5元购物券"); return res; } }
(2)主方法修改:
public class ShopMain { public static void main(String[] args) { //目标类: ShoesSell nikeFactory = new NikeFactory(); //代理类: JdkProxyHandler jdkProxyHandler = new JdkProxyHandler(nikeFactory); ShoesSell result = (ShoesSell) Proxy.newProxyInstance( nikeFactory.getClass().getClassLoader(), nikeFactory.getClass().getInterfaces(), jdkProxyHandler); Float price = result.sell(1); System.out.println("通过动态代理调用,最终价格为:" + price); } }
其中Proxy.newProxyInstance()中的三个入参:
2、cglib动态代理
cglib是第三方的工具库,创建代理对象。重写父类中同名的方法,实现功能的修改。
因为cglib是继承,重写方法,所以要求目标类不能是final的,方法也不能是final的。
对上述静态代理代码修改:
(1)在pom.xml中添加依赖包
<!-- cglib --> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.2.6</version> </dependency>
(2)新建cglib代理类:CglibProxyHandler
import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; public class CglibProxyHandler implements MethodInterceptor { private Object target; public CglibProxyHandler(Object target) { this.target = target; } @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { Object res = null; //向厂家发送订单 res = method.invoke(target,objects); //代理增加价格 if (res != null) { Float price = (Float) res; price = price + 200; res = price; } //返优惠券 System.out.println("淘宝商家,返回5元购物券"); return res; } }
(3)主方法修改:
public class ShopMain { public static void main(String[] args) { //目标类: ShoesSell nikeFactory = new NikeFactory(); //代理类: CglibProxyHandler cglibProxyHandler = new CglibProxyHandler(nikeFactory); Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(nikeFactory.getClass()); enhancer.setCallback(cglibProxyHandler); ShoesSell result = (ShoesSell) enhancer.create(); Float price = result.sell(1); System.out.println("通过cglib动态代理调用,最终价格为:" + price); } }
五、Cglib和jdk动态代理的区别
1.Jdk动态代理:
① 利用拦截器(必须实现InvocationHandler)加上反射机制生成一个代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
② 只能代理实现了接口的类,没有实现接口的类不能实现JDK动态代理。
2. Cglib动态代理:
① 利用ASM框架,对代理对象类生成的class文件加载进来,通过修改其字节码生成子类来处理。
②不能对final的类代理。