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

相信大家对动态代理这个词并不模式,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

Guess you like

Origin blog.csdn.net/BaymaxCS/article/details/119976781