Java动态代理的彻底理解(Java高级特性-动态代理)

一、概述

说起动态代理用过Spring的都应该听说过,可是细琢磨又好像是不太明白,那今天就带着大家一起学习理解理解什么是Java的动态代理。

说动态代理之前,要先搞明白什么是代理,代理的字面意思已经很容易理解了,我们这里撇开其他的解释,我们只谈设计模式中的代理模式。

什么是代理模式(Proxy) ?  ->   给目标对象提供一个代理对象,并由代理对象控制对目标对象的引用。

使用场合有两种:

  1. 客户端不想直接访问实际对象,或者访问实际的对象存在技术上的障碍,因而通过代理对象作为桥梁,来完成间接访问;
  2. 在不改变目标对象方法的情况下对方法进行增强,比如我们希望对方法的调用增加日志记录,或者对方法的调用进行拦截等。

代理又分静态代理动态代理,也是面试时经常被提问的高发,别急一个一个来。

二、静态代理

先看例子:创建一个“程序员”接口,里面有一个“撸代码”的方法。

public interface IDeveloper {
    public void writeCode();
}

再创建一个“程序员”接口的实现类(具体的程序员)

public class Developer implements IDeveloper{
    private String name;
    public Developer(String name){
    this.name = name;
    }
    @Override
    public void writeCode() {
        System.out.println("Developer " + name + " writes code");
    }
}

测试输出:Developer wtao writes code

public class DeveloperTest {
    public static void main(String[] args) {
        IDeveloper wtao = new Developer("wtao");
        wtao.writeCode();
    }
}

那么问题来了,如果即不想破坏Developer类,又想让writeCode方法增加些功能,怎么搞?引出静态代理,其实就是新创建一个实现IDeveloper接口的类如DeveloperProxy,且内部维护一个IDeveloper成员变量,指向原始IDeveloper对象通过代理对象自身的writeCode方法内部调用原始IDeveloper实例的writeCode方法

public class DeveloperProxy implements IDeveloper {
    private IDeveloper developer;
    public DeveloperProxy(IDeveloper developer) {
        this.developer = developer;
    }

    @Override
    public void writeCode() {
        System.out.println("Write documentation...");// 新增加的功能--写文章的能力
        this.developer.writeCode();// 调用原来实例的--撸代码的能力
    }
}

测试代码:

public class DeveloperProxTest {
    public static void main(String[] args) {
        IDeveloper wtaoProxy = new DeveloperProxy(new Developer("wtao"));
        wtaoProxy.writeCode();
    }
}

输出结果如下:

结果同样是一个程序员接口的实例“wtaoProxy”,调用writeCode方法后增强了原来只有“撸代码”的能力

静态代理方式的优点

  1.  易于理解和实现
  2. 代理类和真实类的关系是编译期静态决定的,和下文马上要介绍的动态代理比较起来,执行时没有任何额外开销。

静态代理方式的缺点

  • 接口与代理类是1对1的,有多个接口需要代理或接口中有多个方法,就需要创建多个代理类或实现接口中所有方法,繁琐,类爆炸性增长。故引出动态代理。

三、动态代理(JDK实现)

如静态代理的内容所描述的,静态代理受限于接口的实现。动态代理就是通过使用反射动态地获取抽象接口的类型,从而获取相关特性进行代理

使用动态代理有三个步骤,

  1. 创建一个代理执行类,必须实现 InvocationHandler 接口,表明该类是一个动态代理执行类
  2. 代理类需要实现InvocationHandler接口的invoke(Object proxy, Method method, Object[] args) 方法(增强被代理类的功能或逻辑的方法)
  3. 获取代理类,增强了功能,关键是要获取到代理类的对象,使用静态方法Proxy.newProxyInstance(Clas loader, Class<?>[] interfaces, InvocationHandler h) 去获取代理的对象(对没错创建一个对象竟然用奇葩的方法)。
public class JDKDeveloperProxy implements InvocationHandler {
    private Object target; // 被代理的类(真实类)
    public JDKDeveloperProxy(Object target) { // 构造器
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // **重点**--代理对象想要处理的逻辑--
        System.out.println("信春哥以后的代码无bug");
        Object result = method.invoke(target, args); // 被代理的对象执行原方法,当然可能没有返回值
        System.out.println("因为信春哥撸完代码果然没有了bug");
        return result;
    }
}

上面是创建了代理类,此代理类的功能是:再原有对象的方法前后添加了两行输出。下面在测试代码中获取代理对象(写半天就是为了获取代理对象呢)。测试代码如下:

public class JDKDeveloperTest {
    public static void main(String[] args) {
        IDeveloper wtao = new Developer("wtao");
        // 将原始对象交给代理维护
        InvocationHandler jdkProxy = new JDKDeveloperProxy(wtao);

        // 通过代理执行类获取的代理后的对象
        IDeveloper wtaoProxy = (IDeveloper) Proxy.newProxyInstance(wtao.getClass().getClassLoader(),
                wtao.getClass().getInterfaces(), jdkProxy);

        wtaoProxy.writeCode();
    }
}

输出:

获取代理类对象的方法,要传入的 3 个参数,解析如下:

Proxy.newProxyInstance(Class loader, Class<?>[] interfaces, InvocationHandler h);
  1. 第一个参数类加载器;
  2. 第二个参数是委托类的接口类型,代理类返回的是同一个实现接口下的类型,保持代理类返回的类型;
  3. 第三个参数就是代理类本身,即告诉代理类,代理类遇到某个原始类的方法时该调用哪个代理执行类下的invoke方法。

注:上面获取代理类对象实在测试代码中进行,还可以在定义JDKDeveloperProxy代理执行类时,直接在类中暴露出一个public的方法来获取代理类的对象,这样测试类中就可以通过代理执行类的public方法获取代理类的对象了,参考代码如下:

public class JDKDeveloperProxy implements InvocationHandler {
    // 被代理的类(真实类)
    private Object target;
    // 提供一个获取代理类对象的方法
    public Object getProxyInstance(Object target) {
        this.target = target;
        Object object = Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(), this);
        return object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // **重点**--代理对象想要处理的逻辑--
        System.out.println("信春哥以后的代码无bug");
        Object result = method.invoke(target, args); // 被代理的执行原方法,当然可能没有返回值
        System.out.println("因为信春哥撸完代码果然没有了bug");
        return result;
    }
}

测试类代码参考:输出和上面输出结果一样(略)。仅仅是两种获取代理类对象的地方不同而已

public class JDKDeveloperTest {
    public static void main(String[] args) {
        // 创建被代理对象
        IDeveloper wtao = new Developer("wtao");

        // 创建代理执行类对象
        JDKDeveloperProxy jdkProxy = new JDKDeveloperProxy();

        // 将被代理对象交给代理类维护且获取代理类对象
        IDeveloper wtaoProxy = (IDeveloper) jdkProxy.getProxyInstance(wtao);

        wtaoProxy.writeCode();
    }
}

四、总结:

静态代理和动态代理的区别:

  • 静态代理需要自己写代理类并一一实现目标方法,且代理类必须实现与目标对象相同的接口。
  • 动态代理不需要自己实现代理类,它是利用 JDKAPI,动态地在内存中构建代理对象(需要我们传入被代理类),并且默认实现所有目标方法。

(完)

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值