代理模式——案例分析与代码演示

1、代理模式定义

      代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗的来讲代理模式就是我们生活中常见的中介。比如租房中介,我们想租房的时候找租房中介,租房中介是户主的代理,代替户主带着租客看房、签约等后续后续操作。此模式的UML图如下:

 

2、代理模式的使用场景

(1)控制访问:在某些情况下,一个客户类不想或者不能直接引用一个委托对象,而代理类对象可以在客户类和委托对象之间起到中介的作用,其特征是代理类和委托类实现相同的接口。

2)功能增强(符合开闭原则):代理类除了是客户类和委托类的中介之外,我们还可以通过给代理类增加额外的功能来扩展委托类的功能,这样做我们只需要修改代理类而不需要再修改委托类,符合代码设计的开闭原则。代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后对返回结果的处理等。代理类本身并不真正实现服务,而是同过调用委托类的相关方法,来提供特定的服务。真正的业务功能还是由委托类来实现,但是可以在业务功能执行的前后加入一些公共的服务。例如我们想给项目加入缓存、日志这些功能,我们就可以使用代理类来完成,而没必要打开已经封装好的委托类。(也即,代理类一方面调用了被代理对象的方法,同时可以增加一些新的功能,与装饰者模式有一些类似之处)

3、三种动态代理方式

(1)静态代理

      a、新建一个接口interface

      b、创建被代理对象类(目标类),需要实现interface

      c、创建代理类,需要实现interface

      d、测试类

新建一个接口interface
public interface CommonInterface {
    void print(String string);
}
创建被代理对象类(目标类),需要实现interface
public class Target implements CommonInterface {
    public void print(String string) {
        System.out.println("this is CommonInterface and param is "+ string);
    }
}
创建代理类,需要实现interface
public class ProxyClass implements CommonInterface {
    // 静态代理的静态就体现在这里,把被代理的类对象硬编码。
    private Target target = new Target();
    public void print(String string) {
        System.out.println("before target");
        target.print(string);
        System.out.println("after target");
    }
}
测试类
public class Main {
    public static void main(String[] args) {
        ProxyClass proxyClass = new ProxyClass();
        proxyClass.print("wangwang");
    }
}

打印结果:

before target

this is CommonInterface and param is wangwang

after target

 

(2)动态代理

      a、新建一个接口interface

      b、创建被代理对象类(目标类),需要实现interface

      c、创建代理类,需要实现InvocationHandler 

      d、测试类

新建一个接口interface
public interface CommonInterface {
    int caculate(int a);
}
创建被代理对象类(目标类),需要实现interface
public class Target implements CommonInterface {
    public int caculate(int a) {
        return a * 2;
    }
}
创建代理类,需要实现interface
public class DynamicProxy implements InvocationHandler {
    private Object target = null;
    public DynamicProxy(Object target) {
        this.target = target;
    }
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = null;
        // 调用目标的方法
        result = method.invoke(target,args);
        // 方法增强,增加新的功能逻辑
        if(result != null){
            Integer res = (Integer)result;
            res += 5;
            result = res;
        }
        return result;
    }
}
 
测试类
public class Main {
    public static void main(String[] args) {
        // 1、创建目标对象
        Target target = new Target();
        // 2、创建handler对象
        DynamicProxy dynamicProxy = new DynamicProxy(target);
        ///3、创建代理对象
        CommonInterface proxy = (CommonInterface)Proxy.newProxyInstance(target.getClass().getClassLoader()
                                ,target.getClass().getInterfaces(),
                                dynamicProxy);
        System.out.println(proxy.caculate(2));
    }
}

数据结果:9

注:Proxy.newProxyInstance()方法接受三个参数:
ClassLoader loader : 指定当前目标对象使用的类加载器,获取加载器的方法是固定的
Class<?>[] interfaces : 指定目标对象实现的接口的类型,使用泛型方式确认类型
InvocationHandler : 指定动态处理器,执行目标对象的方法时,会触发事件处理器的方法

      虽然相对于静态代理,动态代理大大减少了我们的开发任务,同时减少了对业务接口的依赖,降低了耦合度。但是还是有一点点小小的遗憾之处,那就是它始终无法摆脱仅支持interface代理的桎梏,因为它的设计注定了这个遗憾。回想一下那些动态生成的代理类的继承关系图,它们已经注定有一个共同的父类叫Proxy。Java的继承机制注定了这些动态代理类们无法实现对class的动态代理,原因是多继承在Java中本质上就行不通。有很多条理由,人们可以否定对 class代理的必要性,但是同样有一些理由,相信支持class动态代理会更美好。接口和类的划分,本就不是很明显,只是到了Java中才变得如此的细化。如果只从方法的声明及是否被定义来考量,有一种两者的混合体,它的名字叫抽象类。实现对抽象类的动态代理,相信也有其内在的价值。此外,还有一些历史遗留的类,它们将因为没有实现任何接口而从此与动态代理永世无缘。如此种种,不得不说是一个小小的遗憾。但是,不完美并不等于不伟大,伟大是一种本质,Java动态代理就是佐例。

(3)cglib实现代理

      JDK实现动态代理需要实现类通过接口定义业务方法,对于没有接口的类,如何实现动态代理呢,这就需要CGLib了。CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。但因为采用的是继承,所以不能对final修饰的类进行代理。JDK动态代理与CGLib动态代理均是实现Spring AOP的基础。

需要先引入cglib的包或者依赖

如果是maven项目,通过在pom文件中加入如下依赖引入:

引入cglib依赖
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.1</version>
</dependency>

被代理的类

public class OldMethod {
    public void print(){
        System.out.println("hello world");
    }
}
代理过程,实现接口MethodInterceptor 
public class  CglibProxy implements MethodInterceptor {
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("before cglib");
        Object result = methodProxy.invokeSuper(o, objects);
        System.out.println("after cglib");
        return result;
    }
}
测试类
public class CglibProxyTest {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(OldMethod.class);
        enhancer.setCallback(new CglibProxy());
        OldMethod cglibProxy = (OldMethod)enhancer.create();
        cglibProxy.print();
    }
}

输入结果:

before cglib
hello world
after cglib

       CGLIB创建的动态代理对象比JDK创建的动态代理对象的性能更高,但是CGLIB创建代理对象时所花费的时间却比JDK多得多。所以对于单例的对象,因为无需频繁创建对象,用CGLIB合适,反之使用JDK方式要更为合适一些。同时由于CGLib由于是采用动态创建子类的方法,对于final修饰的方法无法进行代理。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值