设计模式(二)-代理模式

背景:

近期研究spring的源码发现源码中很多地方都用到了动态代理,比如:

1、spring中的AOP主要由动态代理实现切面编程;

2、spring中的DI(依赖注入)功能主要是通过动态代理和反射技术来实现的;

3、还有其他一些使用策略等设计模式的地方等等;

看到很多核心功能都用到了动态代理所以有感而发,觉得对于使用spring框架搭建项目的开发者来说非常有必要弄清楚动态代理实现的原理,下面请看我细细道来;

java中的动态主要有两种实现方式:基于JDK自带的IvocationHandler实现和基于开源框架CGlib实现;下面我将逐一讲解;

 

将具体实现之前,我们先捋一捋必要的理论知识,防止晕车,对于基础内功深厚的请直接跳过这段扫盲文字,直接移步动态代理;

什么是代理模式:

      代理模式属于23种设计模式中的结构型设计模式; 为某对象提供一种代理以控制对该对象的访问。即客户端通过代理间接地访问该对象,从而限制、增强或修改该对象的一些特性。

代理模式分:

1、静态代理:因在编程时就已经确定某个类的代理类以及代理类要去代理的类,不会再改变,所以称为静态代理

优点

代理使客户端不需要知道实现类是什么,怎么做,而客户端只需知道代理即可

方便增加功能,扩展业务逻辑

缺点

代理类中常出现大量冗余的代码,非常不利于扩展和维护

静态代理方式需要为每个接口实现一个代理类,而这些代理类中的代码 几乎是一致的。这在大型系统中将会产生很大的维护问题。

 

2、动态代理:在运行时,创建目标类,可以调用和扩展目标类的方法

优点:动态代理的应用使我们的类职责更加单一,复用性更强。代理逻辑与业务逻辑是互相独立的,没有耦合

缺点: 从代码上没法直观的看出代理关系;

           jdk和cglib两种实现方式有各自不同的缺点,看下面详细描述;

一、基于JDK实现

新建一个java类,implements InvocationHandler接口,然后实现invoke,在invoke方法里面写代理操作,代码如下;

代理类:

public class MyJdkProxy implements InvocationHandler {

    // 目标类,也就是被代理对象
    private Object target;

    public void setTarget(Object target)
    {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("增加一下");
        Object obj = method.invoke(target,args);
        return obj;
    }

    // 生成代理类
    public Object CreatProxyedObj()
    {
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }
}

 

被代理类:

public class HunterOne implements Hunter {
    @Override
    public void display() {
        System.out.println("HunterOne");
    }
}

 

测试代码:

    public static void main(String[] args) {

        Hunter hunter = new HunterTwo();
        MyJdkProxy proxy = new MyJdkProxy();
        proxy.setTarget(hunter);
        Hunter object = (Hunter) proxy.CreatProxyedObj();
        object.display();
    }

实现原理:

1、根据InvocationHandler 的实现类调用newProxyInstance生成一个被代理对象的代理类 $Proxy0,该对象是弱引用的;

2、在$Proxy0中的静态代码块中,通过反射首先获得被代理目标类中的调用方法方法参数,将方法拷贝一份;

3、调用InvocationHandler实现类的invoke方法来完成实际调用;

 

优点:

无需依赖三方jar;

jdk团队一直在优化升级;

生成的代理类是弱引用的,不会出现内存问题;

缺点:

必须要求代理对象实现一个接口,这个跟实现的原理有关系;

无法直接看出代理关系;

 

 

二、基于开源框架cglib

新建一个class implements MethodInterceptor ,然后实现intercept方法;代码如下:

public class MyCglibProxy   implements MethodInterceptor {

    // 根据一个类型产生代理类,此方法不要求一定放在MethodInterceptor中
    public Object CreatProxyedObj(Class<?> clazz) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);
        return enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("Cglib开始增强");
        Object obj = methodProxy.invokeSuper(o,objects);
        System.out.println("Cglib结束增强");
        return obj;
    }
}

 

被代理类:

这里面大家注意我故意写了一个用final修饰的方法,为了测试Cglib无法代理final修饰的方法;

public class CglibDemo {

    public CglibDemo() {
        System.out.println("构造器");
    }

    public void display() {
        System.out.println("CglibDemo");
    }


    final public void heihei() {
        System.out.println("嘿嘿");
    }
}

 

测试类:

    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        //设置enhancer对的父类,就是被代理对象
        enhancer.setSuperclass(CglibDemo.class);
        //设置回调对象
        enhancer.setCallback(new MyCglibProxy());
        //创建代理对象
        CglibDemo proxy = (CglibDemo) enhancer.create();
        proxy.display();
        proxy.heihei();
    }

控制台打印结果:

构造器
Cglib开始增强
CglibDemo
Cglib结束增强
嘿嘿

 

实现原理:

1、实现CGLIB动态代理必须实现MethodInterceptor(方法拦截器)接口;

2、通过Enhancer.create()方法创建代理对象;

3、代理对象继承于被代理对象,通过拦截器调用intercept()方法,

4、intercept()方法由自定义的MethodInterceptor实现类实现,所以,最后调用MyMethodInterceptor中

的intercept()方法,从而完成了由代理对象访问到目标对象的动态代理实现。
 

 

优点:

CGLib动态代理是通过字节码底层继承要代理类来实现,速度快;

相对于jdk动态代理,可以代理没有实现接口的目标类;

 

缺点:

因为是用继承的方式实现代理,所以无法代理有final修饰的类和方法;

生成的字节码class文件都是直接进入永久代,会造成内存问题;

 

以上为记录的内容;后面深入spring 的时候,还会再继续详细阐述两个动态代理底层对象创建的原理;

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值