您现在的位置是:首页 > 正文

2.代理模式

2024-01-31 00:05:42阅读 0

1. 什么是代理模式??作用是什么??有哪几种代理模式??

1.1 什么是代理模式

代理模式是指,为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户类和目标对象之间起到中介的作用。

换句话说,使用代理对象,是为了在不修改目标对象的基础上,增强主业务逻辑。

例如1: 有 A,B,C 三个类, A 原来可以调用 C 类的方法, 现在因为某种原因 C 类不允许A 类调用其方法,但 B 类可以调用 C 类的方法。A 类通过 B 类调用 C 类的方法。这里 B 是 C的代理。 A 通过代理 B 访问 C.

例如2:在实际开发过程中,有A和C两个类,我们通过A类去调用C类中的某个功能,但是现在C类中的功能不够用了(即,需要在C类功能的基础上添加部分功能),在这种状态中,我们所想到的是更改C类的功能,但在实际开发中不允许更改C类。因此,可以通过类B去对C类的功能进行扩展。即功能增强。

注意:在代理模式中主要包含三种类关系:

  • 客户类
  • 代理类
  • 目标类

代理类完成的功能:

  • 目标类中方法的调用
  • 功能增强

1.2 代理模式 的作用

  • 控制访问:控制客户端 访问目标类
  • 增强功能:增强目标类的功能

1.3 代理模式分类

  • 静态代理
  • 动态代理:分为JDK动态代理和Cglib代理

2 静态代理

2.1 什么是静态代理

  • 代理类是自己手工实现的,自己创建一个java类,表示代理类。
  • 同时你所要代理的目标类是确定的。

特点

  • 1)实现简单
  • 2)容易理解。

缺点: 当你的项目中,目标类和代理类很多时候,有以下的缺点:

  • 代码复杂,难于管理

代理类和目标类实现了相同的接口,每个代理都需要实现目标类的方法,这样就出现了大量的代码重复。如果接口增加一个方法,除了所有目标类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。

  • 代理类依赖目标类,代理类过多

代理类只服务于一种类型的目标类,如果要服务多个类型。势必要为每一种目标类都进行代理,静态代理在程序规模稍大时就无法胜任了,代理类数量过多。

2.2 静态代理实例

  • 需求:

     用户需要购买 u 盘,u 盘厂家不单独接待零散购买,厂家规定一次最少购买 1000
     个以上,用户可以通过淘宝的代理商,或者微商哪里进行购买。
     淘宝上的商品,微商都是 u 盘工厂的代理商, 他们代理对 u 盘的销售业		务。
     用户购买-------代理商(淘宝,微商)----- u 厂家(金士顿,闪迪等不同的厂家)
    
  • 分析:

     用户是客户端类
     商家:代理,代理某个品牌的u盘。
     厂家:目标类。
    
     三者的关系: 用户(客户端)---商家(代理)---厂家(目标)
     商家和厂家都是卖u盘的,他们完成的功能是一致的,都是卖u盘。
    

实现步骤

  • 创建一个接口,定义卖u盘的方法, 表示你的厂家和商家做的事情。
  • 创建厂家类,实现1步骤的接口
  • 创建商家,就是代理,也需要实现1步骤中的接口。
  • 创建客户端类,调用商家的方法买一个u盘。

第一步:定义业务接口 UsbSell(目标接口),其中含有抽象方法 sell(int amount), sell 是目标方法。

package com.bipowernode.service;
//表示功能,厂家和商家都要完成的功能
public interface UsbSell {
    //定义方法
    /**
     * @param amount  :表示一次购买的数量
     * @return  表示一个u盘的价格
     */
   float sell(int amount);
}

第二步: 目标类 UsbKingFactory(金士顿 u 盘),该类实现了业务接口。

package com.bipowernode.factory;

import com.bipowernode.service.UsbSell;
public class UsbKingFactory implements UsbSell {
    @Override
    public float sell(int amount) {
        //一个128G的U盘是85元
        return 85.0f;
    }
}

第三步:TaoBao 就是一个代理类, 代理厂家销售 u 盘

package com.bipowernode.shangjia;

import com.bipowernode.factory.UsbKingFactory;
import com.bipowernode.service.UsbSell;
public class TaoBao implements UsbSell {
    //声明 商家代理的厂家具体是谁
    private UsbKingFactory factory = new UsbKingFactory();
    @Override
    //实现销售u盘功能
    public float sell(int amount) {
        //向厂家发送订单,告诉厂家,请求发货。
        float price = factory.sell(amount);

        //商家需要加价,也就是代理要增加价格
        price = price + 25;  //增强功能,代理类在完成目标类方法调用后增强了功能。
        //在目标类方法调用之后,商家所做的其他功能都是增强的意思。
        System.out.println("淘宝商家,返给你一个优惠券,或者红包");

        //增加后的价格
        return price;
    }
}

第四步:创建客户端类,调用商家的方法买一个u盘。

package com.bipowernode;

import com.bipowernode.shangjia.TaoBao;

public class ShopMain {
    public static void main(String[] args) {
        //创建代理的商家淘宝对象
        TaoBao taoBao = new TaoBao();

        float price = taoBao.sell(1);
        System.out.println("通过淘宝的商家,购买u盘单价:" + price);
    }
}

3 动态代理

3.1 什么是动态代理

在静态代理中目标类很多时候,可以使用动态代理,避免静态代理的缺点。

动态代理是指代理类对象在程序运行时由 JVM 根据反射机制动态生成的。动态代理不需要定义代理类的.java 源文件。

动态代理: 在程序执行过程中,使用jdk的反射机制,创建代理类对象, 并动态的指定要代理目标类。
换句话说: 动态代理是一种创建java对象的能力,让你不用创建TaoBao类,就能创建代理类对象。

动态代理的分类:

  • Jdk动态代理: 使用java反射包中的类和接口实现动态代理的功能。反射包 java.lang.reflect , 里面有三个类 : InvocationHandler , Method, Proxy.
  • cglib动态代理: cglib的原理是继承, cglib通过继承目标类,创建它的子类,在子类中 重写父类中同名的方法, 实现功能的修改。因为cglib是继承,重写方法,所以要求目标类不能是final的, 方法也不能是final的。 cglib的要求目标类比较宽松, 只要能继承就可以了。cglib在很多的框架中使用,比如 mybatis ,spring框架中都有使用。

3.2 JDK动态代理

jdk 动态代理是基于 Java 的反射机制实现的。使用 jdk 中接口和类实现代理对象的动态创建。

Jdk 的动态要求目标对象必须实现接口,这是 java 设计上的要求。

从 jdk1.3 以来,java 语言通过 java.lang.reflect 包提供三个类支持代理模式 Proxy, Method和 InovcationHandler

  • InvocationHandler 接口

     InvocationHandler 接口叫做调用处理器,负责完调用目标方法,并增强功能。
     
     通 过 代 理 对 象 执 行 目 标 接 口 中 的 方 法 , 会 把 方 法 的 调 用 分 派 给 调 用 处 理 器
    
     (InvocationHandler)的实现类,执行实现类中的 invoke()方法,我们需要把功能代理写在 invoke()方法中 。
     
     public interface InvocationHandler {
     	    public Object invoke(Object proxy, Method method, Object[] args)
     	        throws Throwable;
     	}
     	
     public Object invoke ( Object proxy, Method method, Object[] args)
     	proxy:代表生成的代理对象
     	method:代表目标方法
     	args:代表目标方法的参数
     		第一个参数 proxy 是 jdk 在运行时赋值的,在方法中直接使用,
     		第二个参数后面介绍,
     		第三个参数是方法执行的参数,		
     注意:这三个参数都是 jdk 运行时赋值的,无需程序员给出。
    
  • Method 类

     invoke()方法的第二个参数为 Method 类对象,该类有一个方法也叫 invoke(),可以调用
     目标方法。这两个 invoke()方法,虽然同名,但无关。
     	public Object invoke ( Object obj, Object... args) 
     		obj:表示目标对象
     		args:表示目标方法参数,就是其上一层 invoke 方法的第三个参数
     该方法的作用是:调用执行 obj 对象所属类的方法,这个方法由其调用者 Method 对象确定。
     在代码中,一般的写法为
     	method.invoke(target, args);
     其中,method 为上一层 invoke 方法的第二个参数。这样,即可调用了目标类的目标方法。
    
  • Proxy 类

    通 过 JDK 的 java.lang.reflect.Proxy 类 实 现 动 态 代 理 , 会 使 用 其 静 态 方 法newProxyInstance(),
    依据目标对象、业务接口及调用处理器三者,自动生成一个动态代理对象。
    	public static newProxyInstance ( ClassLoader loader, Class<?>[]interfaces, InvocationHandler handler) 
    	
    	loader:目标类的类加载器,通过目标对象的反射可获取
    	interfaces:目标类实现的接口数组,通过目标对象的反射可获取
    	handler:调用处理器。
    

JDK动态代理实现步骤:jdk 动态代理是代理模式的一种实现方式,其只能代理接口。

  1. 新建一个接口,作为目标接口
  2. 为接口创建一个实现类,是目标类
  3. 创建类实现 java.lang.reflect.InvocationHandler 接口,调用目标方法并增加其他功能代码.
  4. 创建动态代理对象,使用 Proxy.newProxyInstance()方法,并把返回值强制转为接口类型。

第一步: 新建一个接口,作为目标接口

package com.bjpowernode.service;
//目标接口
public interface UsbSell {

    /**
     * @param amount  :表示一次购买的数量
     * @return  表示一个u盘的价格
     */
   float sell(int amount);
}

第二步: 为接口创建一个实现类,是目标类

package com.bjpowernode.factory;

import com.bjpowernode.service.UsbSell;
//目标类
public class UsbKingFactory implements UsbSell {
    @Override
    //目标方法
    public float sell(int amount) {
        System.out.println("目标类中,执行了sell目标方法");
        return 85.0f;
    }
}

第三步: 创建类实现 java.lang.reflect.InvocationHandler 接口,调用目标方法并增加其他功能代码.

package com.bjpowernode.handler;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

//必须实现InvocationHandler接口,完成代理类要做的功能(1.调用目标方法,2. 功能增强)
public class MySellHandler implements InvocationHandler {
    private Object target = null;

    //动态代理:目标对象时活动的,不是固定的,需要传入进来
    //传入是谁就给谁创建代理
    public MySellHandler(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 + 25;
            res = price;
        }
        //在目标类方法调用之后,商家所做的其他功能都是增强的意思。
        System.out.println("淘宝商家,返给你一个优惠券,或者红包");
        //增加后的价格
        return res;
    }
}

第四步: 创建动态代理对象,使用 Proxy.newProxyInstance()方法,并把返回值强制转为接口类型。

package com.bjpowernode;

import com.bjpowernode.factory.UsbKingFactory;
import com.bjpowernode.handler.MySellHandler;
import com.bjpowernode.service.UsbSell;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public class MainShop {
    public static void main(String[] args) {
        //创建代理对象,使用proxy
        // 1. 创建目标对象
        UsbSell factory = new UsbKingFactory();
        // 2. 创建InvocationHandler对象
        InvocationHandler handler = new MySellHandler(factory);
        // 3. 创建代理对象
        UsbSell proxy = (UsbSell) Proxy.newProxyInstance(factory.getClass().getClassLoader(),
                factory.getClass().getInterfaces(), handler);
        // 4. 通过代理执行方法
        float price = proxy.sell(1);
        System.out.println("通过动态代理,调用方法:" + price);
    }
}

网站文章