Java设计模式:代理模式

一、什么是代理模式

代理模式是指给某个对象提供一个代理对象,用户不直接访问原对象而是通过代理对象间接访问。该UML图如下:

Java设计模式:代理模式

其中涉及到三种角色:

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的类代理。

Java设计模式:代理模式

上一篇:Vue08: 计算属性computed


下一篇:django复习笔记3