一、什么是代理模式
代理模式是指给某个对象提供一个代理对象,用户不直接访问原对象而是通过代理对象间接访问。该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()中的三个入参:
- ClassLoader loader:目标对象的类加载器
- Class<?>[] interfaces:目标对象实现的接口
- InvocationHandler h:事件处理器,代理对象的具体代理操作
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的类代理。