一、为什么会出现 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
服务类中,为getUserInfo
和deleteUser
方法添加了RequiresPermission
注解,分别需要user:read
和user: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 的核心是自动代理创建,其主要步骤如下:
- 解析配置信息:Spring 容器启动时,会解析 XML 配置或注解配置,获取 AOP 相关的信息,如切面、切入点、通知等。
- 创建 BeanPostProcessor:Spring 会创建一个
AnnotationAwareAspectJAutoProxyCreator
类型的BeanPostProcessor
,该处理器会在 Bean 实例化后进行后置处理。 - 筛选需要代理的 Bean:
AnnotationAwareAspectJAutoProxyCreator
会遍历所有的 Bean,根据切入点表达式判断哪些 Bean 需要被代理。 - 创建代理对象:如果某个 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()
方法会生成目标类的子类,并返回代理对象。- 在
MyMethodInterceptor
的intercept
方法中,可以在目标方法执行前后进行增强。
十、总结
通过本文的介绍,我们从 AOP 的历史背景、核心概念、知识点、底层原理、实战应用以及源码分析等多个方面对 Spring AOP 进行了全面的学习。AOP 作为一种强大的编程思想和技术,在软件开发中有着广泛的应用,可以帮助我们提高代码的复用性、可维护性和可扩展性。无论是普通 Java 工程师、架构师还是专家,都可以根据自己的需求和场景,灵活运用 AOP 来解决实际问题。希望本文能够帮助读者彻底搞懂 Spring AOP,在实际项目中发挥其最大的价值。
以上文章详细地介绍了 Spring AOP 的各个方面,从基础概念到高级应用,再到源码分析和实战场景,相信能够帮助读者全面深入地理解和掌握 Spring AOP。通过清晰的代码示例和详细的解释,即使是初学者也能轻松理解,为广大 Java 开发者提供有价值的参考。