代理模式,拿下!!!

小郎码知答

共 9382字,需浏览 19分钟

 ·

2021-09-12 13:38

代理模式是给一个对象提供一个代理对象,并由代理对象控制对原对象的引用

通俗来讲,代理模式就是我们所熟知的中介。

以我们熟知的商品代购为例:

商品代购

假如我们需要买一个物品,我们可以直接去工厂里购买;也可以找代购。

如果直接去工厂购买,我们在购买前需要对自己要买的物品做一些调研,然后去工厂直接去提货,这样什么事情都需要自己亲力亲为。

如果我们通过代购购买,我们只需要告诉代购我们需要什么,剩下的事情代购会帮我们处理(调研、拿货),最终给我们需要的相应的物品。

因此,代理模式的目标如下:

(1)通过引用代理对象的方式来间接访问目标对象,防止直接访问目标对象给系统带来不必要的复杂性。

(2)通过代理对象对原有的业务进行增强。

通常情况下,按照代理的创建时期,一般可以分为两种:

  • 静态代理

静态代理是由程序员或者特定的工具自动生成的源代码,再对其编译,在程序运行之前,代理类编译的生成的.class文件就已经存在了

  • 动态代理

动态代理是在程序运行时,通过反射机制动态创建而成。

1、静态代理模式

静态代理中的代理类和委托类的关系在运行前就确定了,如图所示:

静态代理

特别注意几个概念:

  • 抽象对象

抽象对象声明了真实对象和代理对象的公共接口

  • 真实对象

代理对象所代表的真实对象,最终被引用的对象

  • 代理对象

包含真实对象进而操作真实对象,相当于访问者与真实对象直接的中介

下面,我们来举个例子:

(1)创建服务类接口

public interface BuyCar {
    void buycar();
}

(2)服务实现类

public class BuyCarImpl implements BuyCar{
    public void buycar() {
        System.out.println("买一辆奥迪");
    }
}

(3)创建代理类

public class BuyCarProxy implements BuyCar{
    
    private BuyCar buyCar;

    public BuyCarProxy(BuyCar buyCar){
        this.buyCar = buyCar;
    }
    public void buycar() {
        System.out.println("买车前的调研......");
        buyCar.buycar();
        System.out.println("买车后的保养......");


    }
}

(4)编写测试类

public class ProxyTest {
    public static void main(String[] args) {
        BuyCarImpl buyCar = new BuyCarImpl();
        BuyCarProxy buyCarProxy = new BuyCarProxy(buyCar);
        buyCarProxy.buycar();
    }
}

优点:静态代理在不修改目标对象的前提下,可以通过代理对象对目标对象进行扩展。

代理类可以使得客户端不需要知道具体的实现类是什么,怎么做的,客户端只需知道代理即可(解耦合)

缺点:代理类和具体的实现类实现了相同的接口,代理类通过实现类实现了相同的方法。这样就出现了大量的代码重复。如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。

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

2、动态代理模式

2.1 JDK自带

事实上,单一的代理是不存的,一个代理可以同时身兼数职。既可以代购车,也可以代购房。

在动态代理中我们不再需要手动的创建代理类,我们只需要一个动态处理器就可以了,而真正的代理对象由JDK运行时动态的创建。

(1)创建服务类接口

//买车接口
public interface BuyCar {
    void buycar();
}
//买房接口
public interface BuyHouse {
    void buyHouse();
}

(2)服务实现类

//买车接口的实现类
public class BuyCarImpl implements BuyCar {
    public void buycar() {
        System.out.println("买一辆奥迪");
    }
}
//买房接口的实现类
public class BuyHouseImpl implements BuyHouse{
    public void buyHouse() {
        System.out.println("买一栋大别墅");
    }
}

(3)动态代理类

//通过实现 InvocationHandler 接口创建自己的调用处理器;
public class ProxyHandler implements InvocationHandler {

    private Object object;
    //通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。
    public ProxyHandler(Object object){
        this.object = object;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Before invoke "+method.getName());
        method.invoke(object,args);
        System.out.println("After invoke "+method.getName());
        return null;
    }
}

(4)测试类

public class DynamicProxyTest {
    public static void main(String[] args){
        BuyHouse buyHouse = new BuyHouseImpl();
        BuyCar buyCar = new BuyCarImpl();

        InvocationHandler handler = new ProxyHandler(buyHouse);
        InvocationHandler handler1 = new ProxyHandler(buyCar);

        /**
         * 通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类;
         */

        BuyHouse proxyHouse = (BuyHouse) Proxy.newProxyInstance(buyHouse.getClass().getClassLoader(), buyHouse.getClass().getInterfaces(),handler);
        BuyCar proxyCar = (BuyCar) Proxy.newProxyInstance(buyCar.getClass().getClassLoader(), buyCar.getClass().getInterfaces(),handler1);
        proxyHouse.buyHouse();
        proxyCar.buycar();
        
    }
}

注意Proxy.newProxyInstance()方法接受三个参数:

 public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)

  • ClassLoader loader:指定当前目标对象使用类加载器,获取加载器的方法是固定的
  • Class<?>[] interfaces:指定目标对象实现的接口的类型,使用泛型方式确认类型
  • InvocationHandler h:指定动态处理器,执行目标对象的方法时,会触发事件处理器的方法。

2.2 CGLIB

CGLIB相比于JDK动态代理更加强大,JDK动态代理虽然简单易用,但是其有一个致命缺陷是,只能对接口进行代理。如果要代理的类为一个普通类、没有接口,那么Java动态代理就没法使用了。

在使用cglib前,需要先添加依赖。

        <dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.12</version>
</dependency>

(1)目标类

  • Dao
public class Dao {
    public void update() {
        System.out.println("PeopleDao.update()");
    }
}
  • Dao1
public class Dao1 {
    public void select(){
        System.out.println("PeopleDao.select");
    }
}

(2)代理类

public class DaoProxy implements MethodInterceptor {
    public Object intercept(Object object, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("Befor Metod Invoke");
        methodProxy.invokeSuper(object,objects);
        System.out.println("After Method Invoke");
        return null;
    }
}

参数解释:

  • Object表示要进行增强的对象
  • Method表示拦截的方法
  • Object[]数组表示参数列表,基本数据类型需要传入其包装类型,如int-->Integer、long-Long、double-->Double
  • MethodProxy表示对方法的代理,invokeSuper方法表示对被代理对象方法的调用

(3)测试

public class CglibProxyTest {
    public static void main(String[] args) {
        DaoProxy daoProxy = new DaoProxy();
        Enhancer enhancer = new Enhancer();
        Enhancer enhancer1 = new Enhancer();
  //设置要继承的父类
        enhancer.setSuperclass(Dao.class);
        enhancer1.setSuperclass(Dao1.class);
        //设置回调方法
        enhancer.setCallback(daoProxy);
        enhancer1.setCallback(daoProxy);

        //创建动态代理类
        Dao dao = (Dao)enhancer.create();
        Dao1 dao1= (Dao1) enhancer1.create();
        dao.update();
        System.out.println("...................................");
        dao1.select();
    }
}

公众号回复Java全套Java架构大数据电子书算法和刷题笔记面经,即可获得对应的学习资源。


点个在看你最好看



浏览 26
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报