动态代理有什么用? -- 《JAVA编程思想》47

本文详细讲解了动态代理在Java中的应用,包括静态代理的局限性,然后通过JDK原生的InvocationHandler和Proxy.newProxyInstance方法实现动态代理,演示了如何在方法间进行代码增强。重点讨论了使用动态代理过滤特定方法执行的日志控制。
摘要由CSDN通过智能技术生成

相信大家对动态代理这个词并不模式,Spring 中的 AOP 的实现和 RPC 框架都使用到了动态代理。

在此之前,我们先来复习一下代理设计模式,也被称为静态代理

假设现在有一个需求:要在已有的实现类前后加入日志打印功能,但是不能修改原类的代码,这时可以使用静态代理解决。

通过往代理类 ModuleAProxy 中传入 ModuleA 中的引用,每次调用具体方法时,不再调用实现类,而改为调用代理类的同名方法,并在原有业务代码的基础上,实现代码增强

public interface BusinessInterface {
    void business();
}
public class ModuleA implements BusinessInterface{

    @Override
    public void business() {
        System.out.println("business doing");
    }

}
public class ModuleAProxy implements BusinessInterface {

    BusinessInterface module;

    public ModuleAProxy(BusinessInterface module) {
        this.module = module;
    }

    @Override
    public void business() {
        System.out.println("------业务执行前------");
        module.business();
        System.out.println("------业务执行后------");
    }

}
public class ModuleAProxyTest {

    public static void consumer(BusinessInterface businessInterface) {
        businessInterface.business();
    }

    public static void main(String[] args) {
        consumer(new ModuleA());
        consumer(new ModuleAProxy(new ModuleA()));
    }

}
business doing
------业务执行前------
business doing
------业务执行后------

但静态代理还是存在一定的局限性,如果接口中有多个实现方法,则需要为每个方法编写新的代理方法。

接下来,我们一起来看看动态代理,动态代理可通过 JDK 原生类或 CGLIB 实现,本文采用 JDK 原生方法。

代理类需要实现 InvocationHandler 接口,再通过 Proxy.newProxyInstance() 方法创建动态代理,该方法需要的三个参数分别为:

  • 一个类加载器(可以为被代理类或其实现接口)
  • 希望代理实现的接口表
  • 一个 InvocationHandler 接口的实现
public interface ModuleImp {

    void methodA();

    void methodB(String str);

    void methodC(String str, int num);

}
public class ModuleB implements ModuleImp {


    @Override
    public void methodA() {
        System.out.println("methodA doing");
    }

    @Override
    public void methodB(String str) {
        System.out.println("methodB doing str:" + str);
    }

    @Override
    public void methodC(String str, int num) {
        System.out.println("methodC doing str:" + str + " num:" + num);
    }

}

public class ModuleDynamicHandler implements InvocationHandler {

    private Object proxied;

    public ModuleDynamicHandler(Object proxied) {
        this.proxied = proxied;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("------业务执行前------");
        // 可获取方法中的参数
        if (args != null) {
            for (Object arg : args) {
                System.out.println(arg);
            }
        }
        Object result = method.invoke(proxied, args);
        System.out.println("------业务执行后------");
        return result;
    }
    
}
public class ModuleDynamicProxyTest {

    public static void consumer(ModuleImp moduleImp) {
        moduleImp.methodA();
        moduleImp.methodB("ABC");
        moduleImp.methodC("ABC", 123);
    }

    public static void main(String[] args) {
        ModuleB moduleB = new ModuleB();
        consumer(moduleB);
        ModuleImp proxyInstance = (ModuleImp) Proxy.newProxyInstance(ModuleB.class.getClassLoader(), new Class[]{ModuleImp.class},
                new ModuleDynamicHandler(moduleB));
        consumer(proxyInstance);
    }
    
}
methodA doing
methodB doing str:ABC
methodC doing str:ABC num:123
------业务执行前------
methodA doing
------业务执行后------
------业务执行前------
ABC
methodB doing str:ABC
------业务执行后------
------业务执行前------
ABC
123
methodC doing str:ABC num:123

值得一提的是,使用 method.invoke() 调用代理方法时,接口中的所有方法都默认会在代理类中执行。倘若想对部分方法进行过滤,希望 methodA 方法不执行日志打印,可通过方法名进行判断,参考以下代码:

public class ModuleDynamicHandler implements InvocationHandler {

    private Object proxied;

    public ModuleDynamicHandler(Object proxied) {
        this.proxied = proxied;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (method.getName().equals("methodA")){
           return method.invoke(proxied, args);
        }
        System.out.println("------业务执行前------");
        if (args != null) {
            for (Object arg : args) {
                System.out.println(arg);
            }
        }
        Object result = method.invoke(proxied, args);
        System.out.println("------业务执行后------");
        return result;
    }

}
methodA doing
methodB doing str:ABC
methodC doing str:ABC num:123
methodA doing
------业务执行前------
ABC
methodB doing str:ABC
------业务执行后------
------业务执行前------
ABC
123
methodC doing str:ABC num:123
------业务执行后------

Process finished with exit code 0

上述是基于JDK原生的动态代理实现方法,存在一定的限制条件,实现类和代理类必须拥有相同接口,若实现类未拥有接口,则无法生效(CGLIB可以实现此类需要)。

本次分享至此结束,希望本文对你有所帮助,若能点亮下方的点赞按钮,在下感激不尽,谢谢您的【精神支持】。

若有任何疑问,也欢迎与我交流,若存在不足之处,也欢迎各位指正!

参考资料引用地址
Java 动态代理作用是什么?https://www.zhihu.com/question/20794107
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BaymaxCS

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值