Java动态代理模式jdk和cglib(上)

简介: Java动态代理模式jdk和cglib(上)

动态代理 V.S 静态代理

  • Proxy类的代码被固定下来,不会因为业务的逐渐庞大而庞大
  • 可以实现AOP编程,这是静态代理无法实现的
  • 解耦,如果用在web业务下,可以实现数据层和业务层的分离
  • 动态代理的优势就是实现无侵入式的代码扩展。

静态代理这个模式本身有个大问题,若类方法数量越来越多的时候,代理类的代码量十分庞大的。所以引入动态代理

动态代理

Java中动态代理的实现的关键:

  • Proxy
  • InvocationHandler

InvocationHandler#invoke

  • method
    调用的方法,即需要执行的方法
  • args
    方法的参数
  • proxy
    代理类的实例
  • image.png

JDK动态代理

JDK动态代理模式里有个拦截器,在JDK中,只要实现了InvocationHandler接口的类就是一个拦截器类

假如写了个请求到action,经过拦截器,然后才会到action,执行后续操作。

拦截器就像一个过滤网,一层层过滤,只有满足一定条件,才能继续向后执行。

拦截器的作用:控制目标对象的目标方法的执行。


拦截器的具体操作步骤:


引入类

目标类和一些扩展方法相关的类

赋值

调用构造器,给相关对象赋值

合并逻辑处理

在invoke方法中把所有的逻辑结合在一起。最终决定目标方法是否被调用

示例

image.png

image.png

image.png

image.png

思考如下问题:

代理对象由谁产生

JVM,不像静态代理,我们得自己new个代理对象。

代理对象实现了什么接口

实现的接口是目标对象实现的接口。

同静态代理中代理对象实现的接口。那个继承关系图还是相同的。

代理对象和目标对象都实现一个共同的接口。就是这个接口。

所以Proxy.newProxyInstance()方法返回的类型就是这个接口类型。

代理对象的方法体是什么

代理对象的方法体中的内容就是拦截器中invoke方法中的内容。

所有代理对象的处理逻辑,控制是否执行目标对象的目标方法。都是在这个方法里面处理的。

拦截器中的invoke方法中的method参数是在什么时候赋值的

在客户端,代理对象调用目标方法的时候,此实例中为:

proxyObj.business();

实际上进入的是拦截器中的invoke方法,这时拦截器中的invoke方法中的method参数会被赋值。


为啥这叫JDK动态代理

因为该动态代理对象是用JDK相关代码生成。


很多同学对动态代理迷糊,在于proxyObj.business();理解错了,至少是被表面所迷惑,没有发现这个proxyObj和Proxy之间的联系,一度纠结最后调用的这个business()是怎么和invoke()联系上的,而invoke又怎么知道business存在的。


其实上面的true和class P r o x y 0 就 能 解 决 很 多 的 疑 问 , 再 加 上 下 面 将 要 说 的 Proxy0就能解决很多的疑问,再加上下面将要说的Proxy0就能解决很多的疑问,再加上下面将要说的Proxy0的源码,完全可以解决动态代理的疑惑了。


我们并没有显式调用invoke(),但是这个方法确实执行了。下面分析:


从Client中的代码看,可以从newProxyInstance这个方法作为突破口,我们先来看一下Proxy类中newProxyInstance方法的源代码:

public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h) {
    final Class<?>[] intfs = interfaces.clone();
    final SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
        checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
    }
    /*
     * 查找或生成指定的代理类
     * 创建代理类$Proxy0
     * $Proxy0类实现了interfaces的接口,并继承了Proxy类
     */
    Class<?> cl = getProxyClass0(loader, intfs);
    /*
     * 使用指定的调用处理程序调用其构造器
     */
    try {
        if (sm != null) {
            checkNewProxyPermission(Reflection.getCallerClass(), cl);
        }
        // 形参为InvocationHandler类型的构造器
        final Constructor<?> cons = cl.getConstructor(constructorParams);
        final InvocationHandler ih = h;
        if (!Modifier.isPublic(cl.getModifiers())) {
            AccessController.doPrivileged(new PrivilegedAction<Void>() {
                public Void run() {
                    cons.setAccessible(true);
                    return null;
                }
            });
        }
        return cons.newInstance(new Object[]{h});
    } ...
}

Proxy.newProxyInstance做了如下事:


根据参数loader和interfaces调用方法 getProxyClass(loader, interfaces)创建代理类$Proxy0。$Proxy0类 实现了interfaces的接口,并继承了Proxy类

实例化$Proxy0,并在构造器把DynamicSubject传过去,接着$Proxy0调用父类Proxy的构造器,为h赋值

$Proxy0的源码:

package com.sun.proxy;
public final class $Proxy0 extends Proxy implements TargetInterface {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m0;
    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }
    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        }...
    }
    public final void business() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        }...
    }
    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        }...
    }
    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        }...
    }
    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m3 = Class.forName("com.javaedge.design.pattern.structural.proxy.dynamicproxy.jdkdynamicproxy.TargetInterface").getMethod("business");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        }...
    }
}

接着把得到的$Proxy0实例强转成TargetInterface,并将引用赋给TargetInterface。当执行proxyObj.business(),就调用了$Proxy0类中的business()方法,进而调用父类Proxy中的h的invoke()方法。即InvocationHandler.invoke()。


Proxy#getProxyClass返回的是Proxy的Class类,而非“被代理类的Class类”!

image.png

目录
相关文章
|
2月前
|
Java Linux
java基础(3)安装好JDK后使用javac.exe编译java文件、java.exe运行编译好的类
本文介绍了如何在安装JDK后使用`javac.exe`编译Java文件,以及使用`java.exe`运行编译好的类文件。涵盖了JDK的安装、环境变量配置、编写Java程序、使用命令行编译和运行程序的步骤,并提供了解决中文乱码的方法。
59 2
|
8天前
|
安全 Java 编译器
JDK 10中的局部变量类型推断:Java编程的简化与革新
JDK 10引入的局部变量类型推断通过`var`关键字简化了代码编写,提高了可读性。编译器根据初始化表达式自动推断变量类型,减少了冗长的类型声明。虽然带来了诸多优点,但也有一些限制,如只能用于局部变量声明,并需立即初始化。这一特性使Java更接近动态类型语言,增强了灵活性和易用性。
90 53
|
21天前
|
设计模式 Java API
[Java]静态代理与动态代理(基于JDK1.8)
本文介绍了代理模式及其分类,包括静态代理和动态代理。静态代理分为面向接口和面向继承两种形式,分别通过手动创建代理类实现;动态代理则利用反射技术,在运行时动态创建代理对象,分为JDK动态代理和Cglib动态代理。文中通过具体代码示例详细讲解了各种代理模式的实现方式和应用场景。
17 0
[Java]静态代理与动态代理(基于JDK1.8)
|
1月前
|
Java
【编程进阶知识】静态代理、JDK动态代理及Cglib动态代理各自存在的缺点及代码示例
本文介绍了三种Java代理模式:静态代理、JDK动态代理和Cglib动态代理。静态代理针对特定接口或对象,需手动编码实现;JDK动态代理通过反射机制实现,适用于所有接口;Cglib动态代理则基于字节码技术,无需接口支持,但需引入外部库。每种方法各有优缺点,选择时应根据具体需求考虑。
18 1
|
1月前
|
Java
让星星⭐月亮告诉你,jdk1.8 Java函数式编程示例:Lambda函数/方法引用/4种内建函数式接口(功能性-/消费型/供给型/断言型)
本示例展示了Java中函数式接口的使用,包括自定义和内置的函数式接口。通过方法引用,实现对字符串操作如转换大写、数值转换等,并演示了Function、Consumer、Supplier及Predicate四种主要内置函数式接口的应用。
25 1
|
1月前
|
Java
深入理解Java动态代理
深入理解Java动态代理
19 1
|
25天前
|
Java
Java代码解释静态代理和动态代理的区别
### 静态代理与动态代理简介 **静态代理**:代理类在编译时已确定,目标对象和代理对象都实现同一接口。代理类包含对目标对象的引用,并在调用方法时添加额外操作。 **动态代理**:利用Java反射机制在运行时生成代理类,更加灵活。通过`Proxy`类和`InvocationHandler`接口实现,无需提前知道接口的具体实现细节。 示例代码展示了两种代理方式的实现,静态代理需要手动创建代理对象,而动态代理通过反射机制自动创建。
|
2月前
|
Oracle Java 关系型数据库
Linux下JDK环境的配置及 bash: /usr/local/java/bin/java: cannot execute binary file: exec format error问题的解决
如果遇到"exec format error"问题,文章建议先检查Linux操作系统是32位还是64位,并确保安装了与系统匹配的JDK版本。如果系统是64位的,但出现了错误,可能是因为下载了错误的JDK版本。文章提供了一个链接,指向Oracle官网上的JDK 17 Linux版本下载页面,并附有截图说明。
Linux下JDK环境的配置及 bash: /usr/local/java/bin/java: cannot execute binary file: exec format error问题的解决
|
1月前
|
设计模式 缓存 Java
从源码学习Java动态代理|8月更文挑战
从源码学习Java动态代理|8月更文挑战
|
2月前
|
监控 Java 大数据
【Java内存管理新突破】JDK 22:细粒度内存管理API,精准控制每一块内存!
【9月更文挑战第9天】虽然目前JDK 22的确切内容尚未公布,但我们可以根据Java语言的发展趋势和社区的需求,预测细粒度内存管理API可能成为未来Java内存管理领域的新突破。这套API将为开发者提供前所未有的内存控制能力,助力Java应用在更多领域发挥更大作用。我们期待JDK 22的发布,期待Java语言在内存管理领域的持续创新和发展。