彻底搞懂 Spring AOP:从基础到实战再到源码剖析

一、为什么会出现 AOP:历史背景与需求驱动

1.1 传统编程模式的困境

在早期的软件开发中,我们采用的是面向对象编程(OOP),它以类和对象为核心,通过封装、继承和多态等特性来实现代码的复用和可维护性。然而,在实际开发中,我们会遇到一些横切关注点(Cross-cutting Concerns),比如日志记录、事务管理、权限验证等。这些功能在多个模块或方法中都会用到,按照传统的 OOP 方式,我们需要在每个需要的地方重复编写这些代码,这就导致了代码的冗余和可维护性的降低。

举个例子,假如我们有一个电商系统,在商品的添加、删除、修改等操作中都需要进行日志记录。如果不采用特殊的方式,我们需要在每个操作方法中都编写日志记录的代码,这样不仅代码重复,而且如果日志记录的方式发生变化,我们需要修改多个地方的代码,工作量大且容易出错。

1.2 AOP 的出现

为了解决上述问题,面向切面编程(AOP)应运而生。AOP 的主要思想是将横切关注点从业务逻辑中分离出来,形成独立的模块,这些模块被称为切面(Aspect)。通过 AOP,我们可以在不修改原有业务逻辑的前提下,对程序进行增强,提高代码的复用性和可维护性。

二、什么是 AOP:核心概念解析

2.1 核心概念

  • 切面(Aspect):切面是一个包含了通知(Advice)和切入点(Pointcut)的模块,它定义了横切关注点的实现逻辑。简单来说,切面就是我们要插入到业务逻辑中的额外功能,比如日志记录、事务管理等。
  • 通知(Advice):通知是切面中的具体操作,它定义了在何时(连接点)执行何种操作。常见的通知类型有:
    • 前置通知(Before Advice):在目标方法执行之前执行。
    • 后置通知(After Advice):在目标方法执行之后执行,无论目标方法是否抛出异常。
    • 返回通知(After Returning Advice):在目标方法正常返回之后执行。
    • 异常通知(After Throwing Advice):在目标方法抛出异常之后执行。
    • 环绕通知(Around Advice):环绕目标方法执行,可以在目标方法执行前后进行增强。
  • 切入点(Pointcut):切入点定义了哪些连接点(Join Point)会被增强,也就是确定哪些方法会被切面织入。切入点通常使用表达式来定义,比如使用 AspectJ 的表达式语法。
  • 连接点(Join Point):连接点是程序执行过程中的一个点,比如方法调用、异常抛出等。在 Spring AOP 中,连接点通常指的是方法调用。
  • 织入(Weaving):织入是将切面应用到目标对象并创建代理对象的过程。织入可以在编译时、类加载时或运行时进行。

2.2 概念理解举例

假设我们有一个银行系统,有一个转账方法transferMoney。现在我们要对这个方法进行日志记录和权限验证,那么:

  • 切面:就是包含日志记录和权限验证逻辑的模块。
  • 通知:日志记录可以是前置通知(在转账前记录开始转账的日志)和后置通知(在转账后记录转账成功或失败的日志);权限验证可以是前置通知(在转账前验证用户是否有转账权限)。
  • 切入点:就是transferMoney方法,我们只对这个方法进行增强。
  • 连接点:就是transferMoney方法的调用。
  • 织入:就是将日志记录和权限验证逻辑插入到transferMoney方法的调用过程中,生成一个代理对象来执行增强后的方法。

三、Spring AOP 知识点详解及项目对应

3.1 基于 XML 配置的 AOP

在 Spring 框架早期,我们可以通过 XML 配置文件来配置 AOP。以下是一个简单的示例:

<!-- 定义目标对象 -->
<bean id="userService" class="com.example.service.UserService"/>

<!-- 定义切面 -->
<bean id="logAspect" class="com.example.aspect.LogAspect"/>

<!-- 配置AOP -->
<aop:config>
    <!-- 定义切入点 -->
    <aop:pointcut id="serviceMethods" expression="execution(* com.example.service.*.*(..))"/>
    <!-- 定义切面 -->
    <aop:aspect ref="logAspect">
        <!-- 前置通知 -->
        <aop:before method="beforeAdvice" pointcut-ref="serviceMethods"/>
        <!-- 后置通知 -->
        <aop:after method="afterAdvice" pointcut-ref="serviceMethods"/>
    </aop:aspect>
</aop:config>
// 目标对象
package com.example.service;

public class UserService {
    public void addUser() {
        System.out.println("添加用户");
    }
}

// 切面
package com.example.aspect;

import org.aspectj.lang.JoinPoint;

public class LogAspect {
    // 前置通知
    public void beforeAdvice(JoinPoint joinPoint) {
        System.out.println("方法 " + joinPoint.getSignature().getName() + " 开始执行");
    }

    // 后置通知
    public void afterAdvice(JoinPoint joinPoint) {
        System.out.println("方法 " + joinPoint.getSignature().getName() + " 执行结束");
    }
}

代码解释

  • aop:pointcut 定义了切入点,使用execution表达式匹配com.example.service包下的所有类的所有方法。
  • aop:aspect 定义了切面,引用了logAspect bean。
  • aop:before 和 aop:after 分别定义了前置通知和后置通知,指定了通知方法和切入点。

在项目中,基于 XML 配置的 AOP 适用于需要集中管理配置的场景,比如在一些传统的企业级项目中,通过 XML 配置可以方便地进行统一管理和修改。

3.2 基于注解的 AOP

Spring 2.0 之后,支持使用注解来配置 AOP,这种方式更加简洁和方便。以下是一个示例:

// 目标对象
package com.example.service;

import org.springframework.stereotype.Service;

@Service
public class UserService {
    public void addUser() {
        System.out.println("添加用户");
    }
}

// 切面
package com.example.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class LogAspect {
    // 前置通知
    @Before("execution(* com.example.service.*.*(..))")
    public void beforeAdvice(JoinPoint joinPoint) {
        System.out.println("方法 " + joinPoint.getSignature().getName() + " 开始执行");
    }

    // 后置通知
    @After("execution(* com.example.service.*.*(..))")
    public void afterAdvice(JoinPoint joinPoint) {
        System.out.println("方法 " + joinPoint.getSignature().getName() + " 执行结束");
    }
}
// 配置类
package com.example.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@ComponentScan(basePackages = "com.example")
@EnableAspectJAutoProxy
public class AppConfig {
}

代码解释

  • @Aspect 注解表示这是一个切面类。
  • @Before 和 @After 注解分别定义了前置通知和后置通知,使用execution表达式指定切入点。
  • @EnableAspectJAutoProxy 注解开启 AOP 自动代理功能。

在项目中,基于注解的 AOP 适用于快速开发和小型项目,它可以减少配置文件的编写,提高开发效率。

3.3 切入点表达式

切入点表达式是 AOP 中非常重要的一部分,它用于指定哪些方法会被增强。常见的切入点表达式类型有:

  • execution:用于匹配方法执行连接点,语法为execution(修饰符? 返回类型 类路径?方法名(参数) throws 异常?)。例如:
    • execution(* com.example.service.*.*(..)) 匹配com.example.service包下的所有类的所有方法。
    • execution(public * com.example.service.UserService.*(..)) 匹配com.example.service.UserService类的所有公共方法。
  • within:用于匹配指定类型内的方法执行。例如:
    • within(com.example.service.*) 匹配com.example.service包下的所有类的所有方法。
  • @annotation:用于匹配带有指定注解的方法。例如:
    • @annotation(com.example.annotation.Log) 匹配带有com.example.annotation.Log注解的方法。

在项目中,切入点表达式可以根据不同的需求灵活使用,比如根据业务模块、方法名、注解等进行匹配。

四、Spring AOP 底层原理

4.1 代理模式

Spring AOP 的底层实现基于代理模式,主要有两种代理方式:JDK 动态代理和 CGLIB 代理。

  • JDK 动态代理:JDK 动态代理是基于接口的代理,它通过java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口来实现。JDK 动态代理只能代理实现了接口的类。
  • CGLIB 代理:CGLIB 代理是基于继承的代理,它通过字节码技术生成目标类的子类来实现代理。CGLIB 代理可以代理没有实现接口的类。

4.2 代理创建过程

当 Spring 容器启动时,会根据配置的 AOP 信息,判断是否需要对目标对象进行代理。如果需要代理,会根据目标对象是否实现接口来选择使用 JDK 动态代理还是 CGLIB 代理。

  • 如果目标对象实现了接口,Spring 会默认使用 JDK 动态代理。
  • 如果目标对象没有实现接口,Spring 会使用 CGLIB 代理。

4.3 示例代码理解

以下是一个简单的 JDK 动态代理示例:

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

// 接口
interface UserService {
    void addUser();
}

// 实现类
class UserServiceImpl implements UserService {
    @Override
    public void addUser() {
        System.out.println("添加用户");
    }
}

// 代理处理器
class UserServiceProxyHandler implements InvocationHandler {
    private Object target;

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

    @Override
    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;
    }
}

// 测试类
public class ProxyTest {
    public static void main(String[] args) {
        UserService target = new UserServiceImpl();
        UserServiceProxyHandler handler = new UserServiceProxyHandler(target);
        UserService proxy = (UserService) Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                handler
        );
        proxy.addUser();
    }
}

代码解释

  • UserService 是一个接口,UserServiceImpl 是实现类。
  • UserServiceProxyHandler 是代理处理器,实现了InvocationHandler接口,在invoke方法中可以进行方法的增强。
  • Proxy.newProxyInstance 方法用于创建代理对象。

五、Spring 框架和 Spring Boot 框架中使用 AOP

5.1 Spring 框架中使用 AOP

在 Spring 框架中,我们可以通过 XML 配置或注解的方式使用 AOP。前面已经给出了相应的示例,这里不再赘述。Spring 框架中使用 AOP 可以对业务逻辑进行增强,比如日志记录、事务管理等。

5.2 Spring Boot 框架中使用 AOP

Spring Boot 框架对 AOP 进行了简化和封装,我们只需要引入相应的依赖,然后使用注解配置即可。以下是一个 Spring Boot 项目中使用 AOP 进行日志记录的示例:

<!-- pom.xml -->
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
</dependencies>
// 目标对象
package com.example.service;

import org.springframework.stereotype.Service;

@Service
public class UserService {
    public void addUser() {
        System.out.println("添加用户");
    }
}

// 切面
package com.example.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class LogAspect {
    // 前置通知
    @Before("execution(* com.example.service.*.*(..))")
    public void beforeAdvice(JoinPoint joinPoint) {
        System.out.println("方法 " + joinPoint.getSignature().getName() + " 开始执行");
    }

    // 后置通知
    @After("execution(* com.example.service.*.*(..))")
    public void afterAdvice(JoinPoint joinPoint) {
        System.out.println("方法 " + joinPoint.getSignature().getName() + " 执行结束");
    }
}

// 主应用类
package com.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

代码解释

  • 引入spring-boot-starter-aop依赖,Spring Boot 会自动配置 AOP 相关的组件。
  • LogAspect 类使用@Aspect@Component注解定义切面,使用@Before@After注解定义通知。

六、Spring Boot 项目中使用 AOP 解决的业务场景问题

6.1 日志记录

在 Spring Boot 项目中,我们可以使用 AOP 来记录方法的执行日志,包括方法的开始时间、结束时间、参数、返回值等信息。以下是一个示例:

// 切面
package com.example.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class LoggingAspect {
    private static final Logger logger = LoggerFactory.getLogger(LoggingAspect.class);

    // 定义切入点
    @Pointcut("execution(* com.example.service.*.*(..))")
    public void serviceMethods() {}

    // 前置通知
    @Before("serviceMethods()")
    public void beforeAdvice(JoinPoint joinPoint) {
        logger.info("方法 {} 开始执行,参数:{}", joinPoint.getSignature().getName(), joinPoint.getArgs());
    }

    // 返回通知
    @AfterReturning(pointcut = "serviceMethods()", returning = "result")
    public void afterReturningAdvice(JoinPoint joinPoint, Object result) {
        logger.info("方法 {} 执行结束,返回值:{}", joinPoint.getSignature().getName(), result);
    }
}

代码解释

  • @Pointcut 注解定义了切入点,匹配com.example.service包下的所有方法。
  • @Before 注解定义了前置通知,在方法执行前记录方法名和参数。
  • @AfterReturning 注解定义了返回通知,在方法正常返回后记录方法名和返回值。

6.2 权限验证

在 Spring Boot 项目中,我们可以使用 AOP 来进行权限验证,确保用户有足够的权限来执行某些操作。以下是一个示例:

// 自定义权限注解
package com.example.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequiresPermission {
    String value();
}

// 切面
package com.example.aspect;

import com.example.annotation.RequiresPermission;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class PermissionAspect {
    // 前置通知
    @Before("@annotation(com.example.annotation.RequiresPermission)")
    public void beforeAdvice(JoinPoint joinPoint) throws Exception {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        RequiresPermission annotation = signature.getMethod().getAnnotation(RequiresPermission.class);
        String requiredPermission = annotation.value();
        // 这里模拟权限验证
        if (!hasPermission(requiredPermission)) {
            throw new Exception("没有权限执行该操作");
        }
    }

      private boolean hasPermission(String permission) {
        // 这里模拟权限验证逻辑,实际项目中可以从数据库、缓存等获取用户权限信息
        // 假设当前用户拥有的权限列表存储在一个集合中
        java.util.Set<String> userPermissions = java.util.Set.of("user:read", "user:update");
        return userPermissions.contains(permission);
    }
}

// 目标服务类
package com.example.service;

import com.example.annotation.RequiresPermission;
import org.springframework.stereotype.Service;

@Service
public class UserService {
    @RequiresPermission("user:read")
    public void getUserInfo() {
        System.out.println("获取用户信息");
    }

    @RequiresPermission("user:delete")
    public void deleteUser() {
        System.out.println("删除用户");
    }
}

代码解释

  • 首先定义了一个自定义注解RequiresPermission,用于标记需要进行权限验证的方法,注解的value属性表示所需的权限。
  • PermissionAspect切面类中,使用@Before注解和@annotation(com.example.annotation.RequiresPermission)切入点表达式,当目标方法被调用且带有RequiresPermission注解时,会触发前置通知。
  • 在前置通知方法beforeAdvice中,通过反射获取目标方法上的RequiresPermission注解,并提取所需的权限。然后调用hasPermission方法进行权限验证,如果验证不通过,则抛出异常。
  • hasPermission方法模拟了权限验证逻辑,实际项目中可以从数据库、缓存等获取用户的权限信息。
  • UserService服务类中,为getUserInfodeleteUser方法添加了RequiresPermission注解,分别需要user:readuser:delete权限。

性能监控

在 Spring Boot 项目中,我们可以使用 AOP 来监控方法的执行时间,以便进行性能分析和优化。以下是一个示例:

// 切面
package com.example.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class PerformanceAspect {
    private static final Logger logger = LoggerFactory.getLogger(PerformanceAspect.class);

    // 环绕通知
    @Around("execution(* com.example.service.*.*(..))")
    public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        try {
            // 执行目标方法
            return joinPoint.proceed();
        } finally {
            long endTime = System.currentTimeMillis();
            long executionTime = endTime - startTime;
            logger.info("方法 {} 执行时间:{} 毫秒", joinPoint.getSignature().getName(), executionTime);
        }
    }
}

代码解释

  • 使用@Around注解定义环绕通知,环绕通知可以在目标方法执行前后进行增强。
  • aroundAdvice方法中,首先记录开始时间,然后调用joinPoint.proceed()方法执行目标方法,最后记录结束时间并计算执行时间,将执行时间信息记录到日志中。

事务管理

在 Spring Boot 项目中,AOP 可以与 Spring 的事务管理结合使用,通过注解的方式来管理事务。以下是一个示例:

// 目标服务类
package com.example.service;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class AccountService {
    @Transactional
    public void transferMoney(long fromAccountId, long toAccountId, double amount) {
        // 模拟转账操作
        System.out.println("从账户 " + fromAccountId + " 向账户 " + toAccountId + " 转账 " + amount + " 元");
        // 这里可以添加具体的数据库操作
    }
}

// 配置类(如果需要自定义事务管理器等)
package com.example.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.sql.DataSource;

@Configuration
@EnableTransactionManagement
public class TransactionConfig {
    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
}

代码解释

  • AccountService服务类的transferMoney方法上添加@Transactional注解,表示该方法需要在事务中执行。
  • @EnableTransactionManagement注解开启 Spring 的事务管理功能。
  • TransactionConfig配置类中定义了事务管理器DataSourceTransactionManager,用于管理数据库事务。Spring AOP 会在方法调用时自动开启和提交或回滚事务。

七、为什么选择 AOP

7.1 提高代码复用性

通过将横切关注点(如日志记录、权限验证等)提取到切面中,避免了在多个业务逻辑中重复编写相同的代码。例如,在前面的日志记录和权限验证示例中,我们只需要在切面中编写一次逻辑,就可以应用到多个方法上,提高了代码的复用性。

7.2 增强可维护性

当横切关注点的逻辑发生变化时,我们只需要修改切面中的代码,而不需要修改每个业务逻辑中的相关代码。例如,如果日志记录的格式需要改变,我们只需要修改日志记录切面中的代码,而不会影响到业务逻辑的代码,降低了代码的耦合度,提高了可维护性。

7.3 不修改原有业务逻辑

AOP 可以在不修改原有业务逻辑的前提下,对程序进行增强。例如,在性能监控示例中,我们通过环绕通知来监控方法的执行时间,而不需要修改目标方法的代码,保证了业务逻辑的独立性和稳定性。

八、不同角色视角下对 AOP 的理解

8.1 普通 Java 工程师角度

对于普通 Java 工程师来说,AOP 是一种强大的工具,可以帮助他们更高效地开发和维护代码。在日常开发中,他们可以使用 AOP 来处理一些通用的功能,如日志记录、权限验证等,而不需要在每个业务方法中重复编写这些代码。同时,AOP 的注解配置方式简单易懂,降低了学习成本,提高了开发效率。

8.2 架构师角度

从架构师的角度来看,AOP 是一种重要的设计思想,可以帮助他们构建更加模块化、可维护的系统架构。通过将横切关注点分离出来,架构师可以更好地组织代码结构,提高系统的可扩展性和可维护性。此外,架构师还可以根据系统的需求,选择合适的 AOP 实现方式(如 JDK 动态代理或 CGLIB 代理),并合理配置切入点和通知,以达到最佳的性能和效果。

8.3 专家角度

专家视角下,AOP 不仅仅是一种编程技术,更是一种解决软件设计中横切关注点问题的方法论。专家会深入研究 AOP 的底层原理,如代理模式、字节码增强等,以便在复杂的场景中灵活运用 AOP。同时,专家还会关注 AOP 的性能优化和安全性问题,确保 AOP 的使用不会对系统的性能和安全性造成负面影响。

九、Spring AOP 源码讲解

9.1 AOP 自动代理创建过程

Spring AOP 的核心是自动代理创建,其主要步骤如下:

  1. 解析配置信息:Spring 容器启动时,会解析 XML 配置或注解配置,获取 AOP 相关的信息,如切面、切入点、通知等。
  2. 创建 BeanPostProcessor:Spring 会创建一个AnnotationAwareAspectJAutoProxyCreator类型的BeanPostProcessor,该处理器会在 Bean 实例化后进行后置处理。
  3. 筛选需要代理的 BeanAnnotationAwareAspectJAutoProxyCreator会遍历所有的 Bean,根据切入点表达式判断哪些 Bean 需要被代理。
  4. 创建代理对象:如果某个 Bean 需要被代理,AnnotationAwareAspectJAutoProxyCreator会根据目标 Bean 是否实现接口,选择使用 JDK 动态代理或 CGLIB 代理来创建代理对象。

9.2 JDK 动态代理源码分析

JDK 动态代理的核心是java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口。以下是Proxy.newProxyInstance方法的源码分析:

public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)
    throws IllegalArgumentException
{
    Objects.requireNonNull(h);

    final Class<?>[] intfs = interfaces.clone();
    final SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
        checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
    }

    /*
     * Look up or generate the designated proxy class.
     */
    Class<?> cl = getProxyClass0(loader, intfs);

    /*
     * Invoke its constructor with the designated invocation handler.
     */
    try {
        if (sm != null) {
            checkNewProxyPermission(Reflection.getCallerClass(), cl);
        }

        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});
    } catch (IllegalAccessException|InstantiationException e) {
        throw new InternalError(e.toString(), e);
    } catch (InvocationTargetException e) {
        Throwable t = e.getCause();
        if (t instanceof RuntimeException) {
            throw (RuntimeException) t;
        } else {
            throw new InternalError(t.toString(), t);
        }
    } catch (NoSuchMethodException e) {
        throw new InternalError(e.toString(), e);
    }
}

代码解释

  • getProxyClass0方法会根据类加载器和接口数组生成代理类的Class对象。
  • 然后通过反射获取代理类的构造方法,并传入InvocationHandler对象来创建代理对象。

9.3 CGLIB 代理源码分析

CGLIB 代理的核心是通过字节码技术生成目标类的子类。以下是一个简单的 CGLIB 代理示例及部分源码分析:

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

import java.lang.reflect.Method;

// 目标类
class TargetClass {
    public void doSomething() {
        System.out.println("执行目标方法");
    }
}

// 方法拦截器
class MyMethodInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("方法执行前");
        Object result = proxy.invokeSuper(obj, args);
        System.out.println("方法执行后");
        return result;
    }
}

// 测试类
public class CglibProxyTest {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(TargetClass.class);
        enhancer.setCallback(new MyMethodInterceptor());
        TargetClass proxy = (TargetClass) enhancer.create();
        proxy.doSomething();
    }
}

代码解释

  • Enhancer类是 CGLIB 的核心类,用于创建代理对象。
  • setSuperclass方法指定目标类,setCallback方法指定方法拦截器。
  • enhancer.create()方法会生成目标类的子类,并返回代理对象。
  • MyMethodInterceptorintercept方法中,可以在目标方法执行前后进行增强。

十、总结

通过本文的介绍,我们从 AOP 的历史背景、核心概念、知识点、底层原理、实战应用以及源码分析等多个方面对 Spring AOP 进行了全面的学习。AOP 作为一种强大的编程思想和技术,在软件开发中有着广泛的应用,可以帮助我们提高代码的复用性、可维护性和可扩展性。无论是普通 Java 工程师、架构师还是专家,都可以根据自己的需求和场景,灵活运用 AOP 来解决实际问题。希望本文能够帮助读者彻底搞懂 Spring AOP,在实际项目中发挥其最大的价值。

以上文章详细地介绍了 Spring AOP 的各个方面,从基础概念到高级应用,再到源码分析和实战场景,相信能够帮助读者全面深入地理解和掌握 Spring AOP。通过清晰的代码示例和详细的解释,即使是初学者也能轻松理解,为广大 Java 开发者提供有价值的参考。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值