详解Spring的两种代理方式:JDK动态代理和CGLIB动态代理

Spring的两种代理方式

在使用Spring框架时,我们常常会使用到AOP(面向切面编程)的相关技术,而代理是AOP中必不可少的一个环节。在Spring中,支持两种代理方式:JDK动态代理和CGLIB动态代理。这两种代理方式都有各自的特点和优劣,具体使用哪种方式则要根据具体的情况而定。

JDK动态代理

JDK动态代理是基于接口的代理,它要求目标对象必须实现一个接口。Spring提供JDK动态代理主要是为了解决AOP中的问题,例如安全控制、事务管理等等。JDK动态代理通过Proxy类来实现,它不需要任何第三方依赖,只需要一个接口即可。

JDK动态代理的核心类是java.lang.reflect.Proxy,使用方式如下:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class JdkProxyDemo {
    public static void main(String[] args) {
        // 创建目标对象
        UserService userService = new UserServiceImpl();
        // 创建InvocationHandler对象
        InvocationHandler handler = new MyInvocationHandler(userService);
        // 创建代理对象
        UserService proxy = (UserService) Proxy.newProxyInstance(
                userService.getClass().getClassLoader(),
                userService.getClass().getInterfaces(),
                handler
        );
        // 调用代理对象的方法
        proxy.saveUser();
    }
}

interface UserService {
    void saveUser();
}

class UserServiceImpl implements UserService {
    public void saveUser() {
        System.out.println("保存用户信息");
    }
}

class MyInvocationHandler implements InvocationHandler {
    private Object target;

    public MyInvocationHandler(Object target) {
        this.target = target;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("方法调用前");
        Object result = method.invoke(target, args);
        System.out.println("方法调用后");
        return result;
    }
}

上述代码中,我们创建了一个UserService接口及其实现类UserServiceImpl。MyInvocationHandler是我们自己实现的InvocationHandler接口的实现类。在main方法中,我们创建了一个UserService的代理对象,当我们调用代理对象的saveUser方法时,实际上是通过invoke方法来执行了UserServiceImpl的saveUser方法,并在调用前后分别输出了一句话。

CGLIB动态代理

CGLIB动态代理是基于类的代理,它要求目标对象不需要实现接口。它通过动态生成一个子类来覆盖目标对象的方法,并使用字节码技术生成被代理对象的子类。

CGLIB动态代理的核心类是net.sf.cglib.proxy.Enhancer,使用方式如下:

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class CglibProxyDemo {
    public static void main(String[] args) {
        // 创建目标对象
        UserService userService = new UserServiceImpl();
        // 创建MethodInterceptor对象
        MethodInterceptor interceptor = new MyMethodInterceptor();
        // 创建代理对象
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(UserServiceImpl.class);
        enhancer.setCallback(interceptor);
        UserService proxy = (UserService) enhancer.create();
        // 调用代理对象的方法
        proxy.saveUser();
    }
}

class MyMethodInterceptor implements MethodInterceptor {
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("方法调用前");
        Object result = methodProxy.invokeSuper(o, objects);
        System.out.println("方法调用后");
        return result;
    }
}

上述代码中,我们使用CGLIB动态代理实现了一个UserService的代理对象,方法调用前后分别输出了一句话。

选择合适的代理方式

JDK动态代理和CGLIB动态代理各有优劣,应该根据具体情况选择合适的代理方式。一般来说,如果目标对象实现了接口,那么使用JDK动态代理;如果目标对象没有实现接口,那么使用CGLIB动态代理。

示例一

假设我们有一个接口UserService和它的实现类UserServiceImpl,我们想在UserServiceImpl中增加一个方法,记录方法调用次数。

我们可以先定义一个计数器类Counter,然后定义一个切面类LogAspect,在其中通过反射获取了目标方法的信息,并记录了方法调用次数。

import java.lang.reflect.Method;

public class Counter {
    private static int count;

    public synchronized static void addCount() {
        count++;
    }

    public static void printCount() {
        System.out.println("方法调用次数:" + count);
    }
}

public class LogAspect {
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("调用方法:" + method.getName());
        Counter.addCount();
    }

    public void after(Method method, Object[] args, Object target) throws Throwable {
        Counter.printCount();
    }
}

接下来,我们使用JDK动态代理和CGLIB动态代理实现对UserServiceImpl的代理。

使用JDK动态代理的代码如下:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class JdkProxyExample {
    public static void main(String[] args) {
        // 创建目标对象
        UserService userService = new UserServiceImpl();
        // 创建InvocationHandler对象
        InvocationHandler handler = new MyInvocationHandler(userService);
        // 创建代理对象
        UserService proxy = (UserService) Proxy.newProxyInstance(
                userService.getClass().getClassLoader(),
                userService.getClass().getInterfaces(),
                handler
        );
        // 调用代理对象的方法
        proxy.saveUser();
    }
}

interface UserService {
    void saveUser();
}

class UserServiceImpl implements UserService {
    public void saveUser() {
        System.out.println("保存用户信息");
    }
}

class MyInvocationHandler implements InvocationHandler {
    private Object target;

    public MyInvocationHandler(Object target) {
        this.target = target;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        LogAspect aspect = new LogAspect();
        aspect.before(method, args, target);
        Object result = method.invoke(target, args);
        aspect.after(method, args, target);
        return result;
    }
}

使用CGLIB动态代理的代码如下:

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class CglibProxyExample {
    public static void main(String[] args) {
        // 创建目标对象
        UserServiceImpl userServiceImpl = new UserServiceImpl();
        // 创建MethodInterceptor对象
        MethodInterceptor interceptor = new MyMethodInterceptor();
        // 创建代理对象
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(UserServiceImpl.class);
        enhancer.setCallback(interceptor);
        UserServiceImpl proxy = (UserServiceImpl) enhancer.create();
        // 调用代理对象的方法
        proxy.saveUser();
    }
}

class MyMethodInterceptor implements MethodInterceptor {
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        LogAspect aspect = new LogAspect();
        aspect.before(method, objects, o);
        Object result = methodProxy.invokeSuper(o, objects);
        aspect.after(method, objects, o);
        return result;
    }
}

这两个示例中,我们使用相同的切面实现了对UserServiceImpl方法调用次数的监控。可以看到,使用JDK动态代理时我们需要实现接口,而使用CGLIB动态代理则不需要实现接口,但需要实现类必须支持继承。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:详解Spring的两种代理方式:JDK动态代理和CGLIB动态代理 - Python技术站

(0)
上一篇 2023年5月20日
下一篇 2023年5月20日

相关文章

  • spring mvc中直接注入的HttpServletRequst安全吗

    Spring MVC中直接注入的HttpServletRequest安全吗? 在Spring MVC中,我们可以直接在控制器方法中注入HttpServletRequest对象,以便在方法中访问请求信息。但是,这种做法是否安全呢?本文将对此进行详细讲解,并提供两个示例说明。 直接注入HttpServletRequest的安全问题 直接注入HttpServlet…

    Java 2023年5月17日
    00
  • java 中Spring task定时任务的深入理解

    对于Java中Spring task定时任务的深入理解,我们可以通过以下步骤来进行实现: 1. 添加依赖 首先,我们需要在项目中添加Spring task的相关依赖,该依赖包括: <dependency> <groupId>org.springframework</groupId> <artifactId>sp…

    Java 2023年6月15日
    00
  • SpringBoot中处理的转发与重定向方式

    SpringBoot中处理转发与重定向的方式有以下几种: 转发(forward) 使用转发的方式可以将请求转发给另一个URL处理,同时请求的地址栏不会发生改变。SpringBoot中使用ModelAndView来实现请求转发。示例如下: @RequestMapping("/test") public ModelAndView test()…

    Java 2023年6月15日
    00
  • Spring Boot 优雅整合多数据源

    接下来我将为您详细讲解“Spring Boot 优雅整合多数据源”的完整攻略。 一、前置知识 在学习 Spring Boot 优雅整合多数据源之前,需要掌握以下知识点: Spring Boot 和 Spring Data JPA 的基础知识。 数据库连接池的使用,例如 HikariCP、Druid 等。 多数据源的基本概念。 二、多数据源的基本概念 在 Sp…

    Java 2023年6月2日
    00
  • MyBatis使用Zookeeper保存数据库的配置可动态刷新的实现代码

    下面我将为你详细讲解使用Zookeeper保存数据库的配置并实现动态刷新的实现过程。本文主要分为以下几个部分: MyBatis使用Zookeeper保存数据库的配置的原理 实现动态刷新的流程 代码实现及示例说明 1. MyBatis使用Zookeeper保存数据库的配置的原理 MyBatis使用Zookeeper保存数据库的配置,可以将配置信息保存在Zook…

    Java 2023年6月16日
    00
  • IntelliJ IDEA下Maven创建Scala项目的方法步骤

    下面是详细的攻略步骤: 一、前置条件 在开始之前,需要你已经将IntelliJ IDEA和Maven安装并配置好。如果还没有安装和配置,请先安装和配置。 二、创建Maven项目 打开IntelliJ IDEA,选择“File”-“New”-“Project”,在选择窗口中选择Maven,并点击“Next”; 在“New Project”对话框中,填写项目相关…

    Java 2023年5月20日
    00
  • Springboot使用influxDB时序数据库的实现

    接下来我将详细讲解“Springboot使用influxDB时序数据库的实现”的完整攻略。首先需要明确的是,influxDB是一个高性能的时序数据库,专门用于处理时间序列数据。而Springboot是一个基于Spring框架的应用程序快速开发框架。 引入influxDB依赖 在Springboot项目的pom.xml文件中,添加以下依赖: <depen…

    Java 2023年5月20日
    00
  • Java中高效的判断数组中某个元素是否存在详解

    Java中高效的判断数组中某个元素是否存在的方法,一般有以下两种: 方法一:使用Arrays类中的binarySearch()方法 Arrays类中的binarySearch()方法可以对已排序的数组进行二分查找,返回匹配元素的索引,若未找到则返回负数。该方法需要先对数组进行排序,时间复杂度为 O(log n)。 下面是一个使用binarySearch()方…

    Java 2023年5月26日
    00
合作推广
合作推广
分享本页
返回顶部