切面编程的艺术:Spring动态代理解析与实战

简介: 切面编程的艺术:Spring动态代理解析与实战


Spring 动态代理

Spring 动态代理是 Spring 框架提供的一种代理机制,它可以在运行时动态地创建代理对象。

在 Spring 中,有两种常用的动态代理方式:JDK 动态代理和 CGLIB 动态代理。Spring 会根据具体情况选择使用 JDK 动态代理还是 CGLIB 动态代理来创建代理对象。在配置文件中,可以通过配置 aop:config 元素来声明需要使用代理的类和代理方式。也可以使用基于注解的方式,通过在目标类或方法上添加相关注解来实现动态代理。

动态代理不需要创建代理类的文件,代理类是在 JVM 运行期间动态生成的。

JDK 动态代理

JDK 动态代理是通过 Java 反射机制来实现的。当目标对象实现了接口时,Spring 会使用 JDK 动态代理来创建代理对象。代理对象会实现与目标对象相同的接口,并将方法的调用委托给目标对象。

JDK 动态代理的主要步骤如下:

  • 定义一个 InvocationHandler 接口的实现类,在 invoke 方法中编写代理逻辑。
  • 使用 Proxy 类的 newProxyInstance 静态方法创建代理对象。需要传入目标对象的类加载器、目标对象实现的接口以及 InvocationHandler 实例。

优点:JDK 动态代理不需要依赖第三方库,能够直接使用 Java 的标准库实现。

缺点:只能为实现了接口的类创建代理对象。

CGLIB 动态代理

当目标对象没有实现任何接口时,Spring 会使用 CGLIB 动态代理来创建代理对象。CGLIB 是一个强大的字节码生成库,它通过继承目标对象来创建代理对象,并重写目标对象的方法。

CGLIB 动态代理的主要步骤如下:

  • 定义一个 MethodInterceptor 接口的实现类,通过实现 intercept 方法编写代理逻辑。
  • 使用 Enhancer 类创建代理对象。需要设置目标对象的类和 MethodInterceptor 实例。

优点:CGLIB 动态代理可以为没有实现接口的类创建代理对象。

缺点:CGLIB 动态代理需要依赖 CGLIB 库,生成的代理对象继承了目标对象,对 final 方法和 private 方法无法进行代理。

开发 Demo

创建原始对象

package world.xuewei.proxy;
/**
 * 用户服务接口
 *
 * @author 薛伟
 * @since 2023/10/18 17:48
 */
public interface UserService {
    void register();
    void login();
}
package world.xuewei.proxy;
/**
 * 用户服务实现
 *
 * @author 薛伟
 * @since 2023/10/18 17:49
 */
public class UserServiceImpl implements UserService {
    @Override
    public void register() {
        System.out.println("UserServiceImpl.register");
    }
    @Override
    public void login() {
        System.out.println("UserServiceImpl.login");
    }
}

创建通知类

需要实现 MethodBeforeAdvice 接口,并实现 before 方法。

package world.xuewei.proxy;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
/**
 * 额外功能 MethodBeforeAdvice 接⼝
 *
 * @author 薛伟
 * @since 2023/10/18 17:53
 */
public class Before implements MethodBeforeAdvice {
    /**
     * 需要把运⾏在原始⽅法执⾏之前运⾏的额外功能,书写在 before ⽅法中
     */
    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println("Before.before");
    }
}

Spring 配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
  <!-- 注册原始对象 -->
    <bean id="userService" class="world.xuewei.proxy.UserServiceImpl"/>
    <!-- 注册通知 -->
    <bean id="before" class="world.xuewei.proxy.Before"/>
    <!-- 配置动态代理 -->
    <aop:config>
        <!-- 所有方法都作为切入点,加入额外功能 -->
        <aop:pointcut id="p1" expression="execution(* *(..))"/>
        <!-- 整合组装 -->
        <aop:advisor advice-ref="before" pointcut-ref="p1"/>
    </aop:config>
</beans>

程序测试

/**
  * 测试动态代理
  */
@Test
public void test7() {
    // 通过原始对象的 id 就可以获取到代理对象
    UserService p = context.getBean("userService", UserService.class);
    p.login();
}

MethodBeforeAdvice 接口

Spring 的 MethodBeforeAdvice 接口是 Spring AOP 框架提供的一个通知接口,用于在被拦截的方法执行之前执行特定的逻辑操作。MethodBeforeAdvice 通常用于实现前置通知(Before Advice),在目标方法执行之前执行一些预处理操作。例如,可以在 before 方法中添加日志记录、参数校验、权限验证等操作。

MethodBeforeAdvice 只能进行前置通知,如果需要在方法执行结束后进行操作,可以使用其他类型的通知,如 AfterReturningAdvice、ThrowsAdvice 等。

MethodBeforeAdvice 接口定义了一个 before 方法。方法签名如下:

void before(Method method, Object[] args, Object target) throws Throwable;
  • method:表示要被执行的方法对象。
  • args:表示执行方法时所需的参数数组,可以为空数组(即方法没有参数)。
  • target:表示被代理的目标对象,即要执行方法的对象。
  • throws Throwable:表示方法可能抛出的异常。

注意:如果 before 方法抛出异常,将阻止被拦截的方法的执行。

package world.xuewei.proxy;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
/**
 * 额外功能 MethodBeforeAdvice接⼝
 *
 * @author 薛伟
 * @since 2023/10/18 17:53
 */
public class Before implements MethodBeforeAdvice {
    /**
     * 需要把运⾏在原始⽅法执⾏之前运⾏的额外功能,书写在 before ⽅法中
     */
    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println("Before.before");
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
  <!-- 注册原始对象 -->
    <bean id="userService" class="world.xuewei.proxy.UserServiceImpl"/>
    <!-- 注册通知 -->
    <bean id="before" class="world.xuewei.proxy.Before"/>
    <!-- 配置动态代理 -->
    <aop:config>
        <!-- 所有方法都作为切入点,加入额外功能 -->
        <aop:pointcut id="p1" expression="execution(* *(..))"/>
        <!-- 整合组装 -->
        <aop:advisor advice-ref="before" pointcut-ref="p1"/>
    </aop:config>
</beans>

MethodInterceptor 接口(aopalliance)

MethodInterceptor 接口是 Spring AOP 框架提供的一个通知接口,用于在被拦截的方法执行前后执行一些特定的逻辑操作。和其他类型的通知(如MethodBeforeAdvice)不同,MethodInterceptor 接口通常用于实现环绕通知(Around Advice),可以在目标方法执行前后以及出现异常时执行特定的逻辑操作,并且对方法的返回值进行处理。比如对数据库事务的控制都需要在原始方法之前和之后都需要处理。

MethodInterceptor 接口定义了一个 invoke 方法,方法签名如下:

Object invoke(MethodInvocation invocation) throws Throwable;
  • invocation:表示方法调用对象,包含被调用的方法、目标对象以及方法参数等信息。
  • throws Throwable:表示方法可能抛出的异常。
  • Object:原始方法执行后的返回值,通过 invocation.proceed() 获取。

注意:在 MethodInterceptor 中,我们需要手动调用 invocation.proceed() 方法来执行被拦截的方法。

注意:MethodInterceptor 可以对返回值进行处理,并且允许我们在出现异常时执行特定的逻辑。

注意:由于 MethodInterceptor 是在目标方法执行前后都会被调用,因此需要特别注意对性能的影响。

package world.xuewei.proxy;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
/**
 * 自定义方法拦截器
 *
 * @author 薛伟
 * @since 2023/10/19 17:32
 */
public class Around implements MethodInterceptor {
    /**
     * 编写自定义处理逻辑
     *
     * @param methodInvocation 表示方法调用对象,包含被调用的方法、目标对象以及方法参数等信息。
     * @return 原始方法执行后的返回值,通过 invocation.proceed() 获取。
     * @throws Throwable 表示方法可能抛出的异常。
     */
    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        // 在此处编写前置操作
        System.out.println("Before");
        Object result = null;
        try {
            result = methodInvocation.proceed();
        } catch (Exception e) {
            // 在此处编写异常操作
            System.out.println("Exception");
        }
        // 在此处编写后置操作
        System.out.println("After");
        // 可以在此处编写新的返回值并返回
        return result;
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
  <!-- 注册原始对象 -->
    <bean id="userService" class="world.xuewei.proxy.UserServiceImpl"/>
    <!-- 注册通知 -->
    <bean id="around" class="world.xuewei.proxy.Around"/>
    <!-- 配置动态代理 -->
    <aop:config>
        <!-- 所有方法都作为切入点,加入额外功能 -->
        <aop:pointcut id="p1" expression="execution(* *(..))"/>
        <!-- 整合组装 -->
        <aop:advisor advice-ref="around" pointcut-ref="p1"/>
    </aop:config>
</beans>



相关文章
|
7月前
|
缓存 安全 Java
Spring Security通用权限管理模型解析
Spring Security作为Spring生态的核心安全框架,结合RBAC与ACL权限模型,基于IoC与AOP构建灵活、可扩展的企业级权限控制体系,涵盖认证、授权流程及数据库设计、性能优化等实现策略。
511 0
|
7月前
|
缓存 安全 Java
Spring Security权限管理解析
Spring Security是Spring生态中的核心安全框架,采用认证与授权分离架构,提供高度可定制的权限管理方案。其基于过滤器链实现认证流程,通过SecurityContextHolder管理用户状态,并结合RBAC模型与动态权限决策,支持细粒度访问控制。通过扩展点如自定义投票器、注解式校验与前端标签,可灵活适配多租户、API网关等复杂场景。结合缓存优化与无状态设计,适用于高并发与前后端分离架构。
501 0
|
7月前
|
人工智能 Java 开发者
【Spring】原理解析:Spring Boot 自动配置
Spring Boot通过“约定优于配置”的设计理念,自动检测项目依赖并根据这些依赖自动装配相应的Bean,从而解放开发者从繁琐的配置工作中解脱出来,专注于业务逻辑实现。
2496 0
|
6月前
|
监控 Cloud Native Java
Spring Boot 3.x 微服务架构实战指南
🌟蒋星熠Jaxonic,技术宇宙中的星际旅人。深耕Spring Boot 3.x与微服务架构,探索云原生、性能优化与高可用系统设计。以代码为笔,在二进制星河中谱写极客诗篇。关注我,共赴技术星辰大海!(238字)
1171 2
Spring Boot 3.x 微服务架构实战指南
|
6月前
|
XML Java 数据格式
《深入理解Spring》:AOP面向切面编程深度解析
Spring AOP通过代理模式实现面向切面编程,将日志、事务等横切关注点与业务逻辑分离。支持注解、XML和编程式配置,提供五种通知类型及丰富切点表达式,助力构建高内聚、低耦合的可维护系统。
|
6月前
|
XML Java 测试技术
《深入理解Spring》:IoC容器核心原理与实战
Spring IoC通过控制反转与依赖注入实现对象间的解耦,由容器统一管理Bean的生命周期与依赖关系。支持XML、注解和Java配置三种方式,结合作用域、条件化配置与循环依赖处理等机制,提升应用的可维护性与可测试性,是现代Java开发的核心基石。
|
6月前
|
前端开发 Java 微服务
《深入理解Spring》:Spring、Spring MVC与Spring Boot的深度解析
Spring Framework是Java生态的基石,提供IoC、AOP等核心功能;Spring MVC基于其构建,实现Web层MVC架构;Spring Boot则通过自动配置和内嵌服务器,极大简化了开发与部署。三者层层演进,Spring Boot并非替代,而是对前者的高效封装与增强,适用于微服务与快速开发,而深入理解Spring Framework有助于更好驾驭整体技术栈。
|
7月前
|
Java 数据库 数据安全/隐私保护
Spring Boot四层架构深度解析
本文详解Spring Boot四层架构(Controller-Service-DAO-Database)的核心思想与实战应用,涵盖职责划分、代码结构、依赖注入、事务管理及常见问题解决方案,助力构建高内聚、低耦合的企业级应用。
1440 1
|
7月前
|
Kubernetes Java 微服务
Spring Cloud 微服务架构技术解析与实践指南
本文档全面介绍 Spring Cloud 微服务架构的核心组件、设计理念和实现方案。作为构建分布式系统的综合工具箱,Spring Cloud 为微服务架构提供了服务发现、配置管理、负载均衡、熔断器等关键功能的标准化实现。本文将深入探讨其核心组件的工作原理、集成方式以及在实际项目中的最佳实践,帮助开发者构建高可用、可扩展的分布式系统。
635 0

推荐镜像

更多
  • DNS
  • 下一篇
    开通oss服务