探究Java spring中jdk代理和cglib代理!

简介: 探究Java spring中jdk代理和cglib代理!

一.介绍二者代理模式

1.1.Jdk代理模式

JDK代理是通过接口实现的动态代理方式。当目标类实现了至少一个接口时,Spring AOP会使用JDK代理。JDK代理通过在运行时创建一个实现了目标接口的代理类来实现代理功能。代理对象和目标对象实现了同一个接口,因此只能代理接口中定义的方法

1.2cglib代理模式

CGLIB代理是通过继承实现的动态代理方式。当目标类没有实现任何接口时,Spring AOP会使用CGLIB代理。CGLIB代理通过在运行时创建目标类的子类来实现代理功能。代理对象继承自目标对象,并覆盖了目标对象的非final方法,因此可以代理目标对象的所有方法。

1.3二者区别

1.3.1有无接口

JDK代理要求目标类必须实现接口,而CGLIB代理不需要,可以代理普通的Java类

1.3.2灵活性

JDK代理基于接口,适用于对接口的代理,更加灵活,但无法代理没有接口的类

CGLIB代理是通过生成目标类的子类来实现代理,适用于对类的代理。CGLIB代理具有更高的性能,因为它不需要通过反射调用目标对象的方法

1.4对于两种代理模式的总结

1.4.1jdk代理模式

JDK 动态代理是基于接口的代理方式。它通过创建一个实现了目标接口的代理类,并在代理类中实现代理逻辑。代理类在运行时动态生成,并在其中调用原始对象的方法

JDK 动态代理通过java.lang.reflect.Proxy 类和 java.lang.reflect.InvocationHandler 接口来实现。代理对象是在运行时动态生成的。

无法代理非公有类:JDK 动态代理不能代理那些声明为 final 的类,因为 final 类不能被继承。另外,由于代理类是在运行时动态生成的,因此也无法代理那些没有默认构造函数的类

方法拦截:代理对象在调用方法时,会将方法调用转发给 InvocationHandler 的实现类,在这个实现类中可以添加自定义的逻辑,完成方法的拦截、增强等操作。

性能相对较低:相比于 CGLIB 动态代理,JDK 动态代理的性能较低。这主要是因为 JDK 动态代理需要通过反射来调用目标对象的方法。

总的来说,JDK 动态代理是一种比较常用的代理方式,适用于代理接口的场景,易于使用和理解。但它的局限性在于只能代理实现了接口的类,并且在性能方面稍逊于 CGLIB 动态代理。

1.4.2cglib代理模式

CGLIB 动态代理是一种基于继承的代理方式。它通过创建目标类的子类来实现代理。代理类继承自目标类,并重写其中的方法,在重写的方法中添加了增强逻辑

类代理:CGLIB 动态代理可以代理类,无论是否实现了接口。这使得它可以代理那些没有实现接口的类,不包括 final 类

代理生成:CGLIB 动态代理使用字节码生成库来创建目标类的子类。代理类在运行时动态生成,并且不需要目标类实现任何接口。

方法拦截:被代理的方法在执行时,会调用代理类中的方法。这样,我们可以在代理类中添加拦截器(MethodInterceptor),并在拦截器中实现自定义的逻辑。

性能相对较高:相比于 JDK 动态代理,CGLIB 动态代理的性能更高。这是因为 CGLIB 通过继承来实现代理,避免了反射调用的开销。

对于 final 方法和私有方法的限制:CGLIB 默认无法代理 final 方法,因为 final 方法无法被重写。此外,CGLIB 也不能代理私有方法,因为代理类无法访问目标类的私有方法。但是可以通过设置 CGLIB 的 Enhancer 对象的 setUseReflection(true) 方法来强制代理私有方法。这样一来,CGLIB 将使用反射调用私有方法,但这可能会导致性能下降。

需要注意的是,CGLIB 代理依赖于字节码生成,在代理类生成时会占用一定的时间和内存

总结来说,CGLIB 动态代理适用于代理类的场景,可以代理没有实现接口的类,并且具有较高的性能。

二.两种代理模式应用场景

2.1jdk代理模式应用场景

2.1.1基于接口的代理

如果目标对象实现了接口,可以使用JDK动态代理。它要求目标对象实现一个接口,并且生成的代理对象也会实现相同的接口。

2.1.2细粒度的方法拦截

JDK动态代理可以在目标对象的方法调用前后添加额外的逻辑。例如,在方法调用前打印日志、进行权限验证或事务管理等

2.2cglib代理模式应用场景

2.2.1非基于接口的代理

CGLIB动态代理可以代理没有实现接口的类。因此,当目标对象没有实现任何接口时,可以使用CGLIB动态代理。

2.2.2覆盖父类中的方法

CGLIB动态代理通过继承目标类并覆盖其中的方法来创建代理对象。这使得它可以代理目标类中的非公有方法。

2.2.3高性能的代理

相比JDK动态代理,CGLIB动态代理通常具有更高的性能,因为它直接操作字节码而不需要通过反射调用方法。

三.代码演示

3.1jdk代理模式

3.1.1源码

package com.daili.jdk;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
 * @author lz
 * @create 2023-08-21 9:33
 */
public interface UserService {
    void saveUser(String username);
}
class UserServiceImpl implements UserService {
    public void saveUser(String username) {
        System.out.println("Saving user: " + username);
    }
}
class UserServiceProxy implements InvocationHandler {
    private Object target;
    public UserServiceProxy(Object target) {
        this.target = target;
    }
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Before method: " + method.getName());
        Object result = method.invoke(target, args);
        System.out.println("After method: " + method.getName());
        return result;
    }
}
class Main {
    public static void main(String[] args) {
        UserService userService = new UserServiceImpl();
        UserService proxy = (UserService) Proxy.newProxyInstance(
                userService.getClass().getClassLoader(),
                userService.getClass().getInterfaces(),
                new UserServiceProxy(userService)
        );
        proxy.saveUser("刘兵");
    }
}

3.2思路解释

  1. 定义了一个接口 UserService,其中包含了一个抽象方法 saveUser(String username)
  2. 实现了接口 UserService 的具体类 UserServiceImpl,该类实现了 saveUser 方法,用于保存用户信息。
  3. 创建了一个代理类 UserServiceProxy,实现了 InvocationHandler 接口。在 invoke 方法中,对目标方法进行前置和后置处理,即在目标方法执行前输出 "Before method: " + 方法名,在目标方法执行后输出 "After method: " + 方法名。
  4. Main 类的 main 方法中,创建了一个 UserServiceImpl 的实例。
  5. 通过 Proxy.newProxyInstance() 方法创建代理对象,传入目标对象的类加载器、接口数组和 UserServiceProxy 的实例。返回的代理对象将会实现 UserService 接口。
  6. 调用代理对象的 saveUser("John") 方法,实际上会触发代理对象的 invoke 方法,从而实现了动态代理。

在代码执行过程中,代理对象的 invoke 方法会先输出 "Before method: saveUser",然后调用目标对象的 saveUser 方法,最后输出 "After method: saveUser"。

通过 JDK 动态代理,可以在不修改目标对象代码的情况下,对其方法进行增强、添加日志等操作。

3.2cglib代理模式

3.2.1源码

package com.daili.cglib;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
 * @author lz
 * @create 2023-08-21 9:49
 */
public class UserService {
    public void saveUser(String username) {
        System.out.println("Saving user: " + username);
    }
}
class UserServiceInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("Before method: " + method.getName());
        Object result = proxy.invokeSuper(obj, args);
        System.out.println("After method: " + method.getName());
        return result;
    }
static class Main {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(UserService.class);
        enhancer.setCallback(new UserServiceInterceptor());
        UserService proxy = (UserService) enhancer.create();
        proxy.saveUser("刘兵");
    }
}
}

3.2.1思路解释

  1. 定义了一个类 UserService,其中包含了一个方法 saveUser(String username),用于保存用户信息。
  2. 创建了一个代理类 UserServiceInterceptor,实现了 MethodInterceptor 接口。在 intercept 方法中,对目标方法进行前置和后置处理,即在目标方法执行前输出 "Before method: " + 方法名,在目标方法执行后输出 "After method: " + 方法名。使用 MethodProxy 对象调用原始方法。
  3. Main 类的 main 方法中,创建了一个 Enhancer 对象。
  4. 通过 enhancer.setSuperclass(UserService.class) 设置目标类为 UserService
  5. 通过 enhancer.setCallback(new UserServiceInterceptor()) 设置回调处理器为 UserServiceInterceptor 的实例。
  6. 调用 enhancer.create() 方法返回代理对象,将其转换为 UserService 类型。
  7. 调用代理对象的 saveUser("John") 方法,会触发代理对象的 intercept 方法,从而实现了动态代理。

在代码执行过程中,代理对象的 intercept 方法会先输出 "Before method: saveUser",然后调用目标对象的 saveUser 方法,最后输出 "After method: saveUser"。

通过 CGLIB 动态代理,可以在不修改目标对象代码的情况下,对其方法进行增强、添加日志等操作。与 JDK 动态代理不同的是,CGLIB 动态代理可以代理类而不仅限于接口。

相关文章
|
Java Spring
JDK动态代理和CGLIB动态代理的区别
Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理: ● JDK动态代理只提供接口的代理,不支持类的代理Proxy.newProxyInstance(类加载器, 代理对象实现的所有接口, 代理执行器) ● CGLIB是通过继承的方式做的动态代理 , 如果某个类被标记为final,那么它是无法使用 CGLIB做动态代理的。Enhancer.create(父类的字节码对象, 代理执行器)
|
监控 Java API
JDK动态代理和CGLIB动态代理
Java动态代理允许在运行时创建代理对象,增强或拦截目标类方法的执行。主要通过两种方式实现:JDK动态代理和CGLIB动态代理。JDK动态代理基于接口,利用`java.lang.reflect.Proxy`类和`InvocationHandler`接口;CGLIB则通过字节码技术生成目标类的子类作为代理,适用于未实现接口的类。两者均用于在方法执行前后添加额外逻辑,如日志记录、权限控制等,广泛应用于AOP框架中。
449 2
|
监控 Java API
JDK动态代理和CGLIB动态代理
Java动态代理允许在运行时创建代理对象,增强或拦截目标类的方法调用,无需修改原代码。它有两种主要实现方式:JDK动态代理和CGLIB动态代理。 - **JDK动态代理**:通过`java.lang.reflect.Proxy`类和`InvocationHandler`接口实现,适用于实现了接口的类。它在方法调用前后插入额外逻辑,如日志记录、权限控制等。 - **CGLIB动态代理**:基于字节码技术,为未实现接口的类生成子类作为代理,重写父类方法以添加增强逻辑。适用于没有接口的类,但要求目标类不能是`final`类或方法。
197 1
|
Java API 数据安全/隐私保护
探索Java动态代理的奥秘:JDK vs CGLIB
动态代理是一种在 运行时动态生成代理类的技术,无需手动编写代理类代码。它通过拦截目标方法的调用,实现对核心逻辑的 无侵入式增强(如日志、事务、权限控制等)。
455 0
探索Java动态代理的奥秘:JDK vs CGLIB
|
存储 缓存 Java
Spring高手之路23——AOP触发机制与代理逻辑的执行
本篇文章深入解析了Spring AOP代理的触发机制和执行流程,从源码角度详细讲解了Bean如何被AOP代理,包括代理对象的创建、配置与执行逻辑,帮助读者全面掌握Spring AOP的核心技术。
397 3
Spring高手之路23——AOP触发机制与代理逻辑的执行
|
安全 Java 开发者
AOP中的JDK动态代理与CGLIB动态代理:深度解析与实战模拟
【11月更文挑战第21天】面向切面编程(AOP,Aspect-Oriented Programming)是一种编程范式,它通过将横切关注点(cross-cutting concerns)与业务逻辑分离,以提高代码的可维护性和可重用性。在Java开发中,AOP的实现离不开动态代理技术,其中JDK动态代理和CGLIB动态代理是两种常用的方式。本文将从背景、历史、功能点、业务场景、底层逻辑等多个维度,深度解析这两种代理方式的区别,并通过Java示例进行模拟和比较。
1012 5
|
设计模式 Java 测试技术
spring复习04,静态代理动态代理,AOP
这篇文章讲解了Java代理模式的相关知识,包括静态代理和动态代理(JDK动态代理和CGLIB),以及AOP(面向切面编程)的概念和在Spring框架中的应用。文章还提供了详细的示例代码,演示了如何使用Spring AOP进行方法增强和代理对象的创建。
spring复习04,静态代理动态代理,AOP
|
Java
【编程进阶知识】静态代理、JDK动态代理及Cglib动态代理各自存在的缺点及代码示例
本文介绍了三种Java代理模式:静态代理、JDK动态代理和Cglib动态代理。静态代理针对特定接口或对象,需手动编码实现;JDK动态代理通过反射机制实现,适用于所有接口;Cglib动态代理则基于字节码技术,无需接口支持,但需引入外部库。每种方法各有优缺点,选择时应根据具体需求考虑。
236 1
|
Java 关系型数据库 开发工具
idea创建不了spring2.X版本,无法使用JDK8,最低支持JDK17 , 如何用idea创建spring2.X版本,使用JDK8解决方案
本文提供了解决方案,如何在IDEA中创建Spring 2.X版本的项目并使用JDK8,尽管Spring 2.X已停止维护且IDEA不再直接支持,通过修改pom.xml或使用阿里云的国内源来创建项目。
2510 0
idea创建不了spring2.X版本,无法使用JDK8,最低支持JDK17 , 如何用idea创建spring2.X版本,使用JDK8解决方案
下一篇
开通oss服务