AOP面向切面编程
AOP概念与原理
什么是AOP
AOP(Aspect-Oriented Programming,面向切面编程)是一种编程范式,用于将横切关注点与业务逻辑分离。
为什么需要AOP
传统方式的问题:
java
public class UserService {
public void addUser(User user) {
// 日志记录
System.out.println("开始添加用户");
long start = System.currentTimeMillis();
try {
// 业务逻辑
userRepository.save(user);
// 日志记录
System.out.println("添加用户成功,耗时: " + (System.currentTimeMillis() - start));
} catch (Exception e) {
// 异常处理
System.out.println("添加用户失败: " + e.getMessage());
throw e;
}
}
}使用AOP后:
java
public class UserService {
public void addUser(User user) {
// 纯粹的业务逻辑
userRepository.save(user);
}
}
// 切面处理日志、异常等
@Aspect
@Component
public class LoggingAspect {
@Around("execution(* com.example.service.*.*(..))")
public Object log(ProceedingJoinPoint pjp) throws Throwable {
// 日志、异常处理等
}
}AOP核心概念
| 概念 | 说明 |
|---|---|
| 切面(Aspect) | 横切关注点的模块化 |
| 切点(Pointcut) | 定义在哪些连接点执行通知 |
| 通知(Advice) | 在切点执行的具体操作 |
| 连接点(Joinpoint) | 程序执行的特定点(方法调用、异常抛出等) |
| 目标对象(Target) | 被通知的对象 |
| 代理(Proxy) | AOP框架创建的对象 |
| 织入(Weaving) | 将切面应用到目标对象的过程 |
代理模式
JDK动态代理
java
public class JdkProxyHandler implements InvocationHandler {
private Object target;
public JdkProxyHandler(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 static void main(String[] args) {
UserService target = new UserServiceImpl();
UserService proxy = (UserService) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new JdkProxyHandler(target)
);
proxy.addUser(new User());
}
}CGLIB代理
java
public class CglibProxyHandler 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 static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserService.class);
enhancer.setCallback(new CglibProxyHandler());
UserService proxy = (UserService) enhancer.create();
proxy.addUser(new User());
}
}Spring 7默认使用CGLIB
Spring 7默认使用CGLIB代理,无需接口:
java
@Service
public class UserService {
public void addUser(User user) {
// 业务逻辑
}
}@AspectJ注解
启用AspectJ
java
@Configuration
@EnableAspectJAutoProxy
public class AopConfig {
}定义切面
java
@Aspect
@Component
public class LoggingAspect {
// 切点定义
@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceMethod() {}
// 通知
@Before("serviceMethod()")
public void before(JoinPoint joinPoint) {
System.out.println("执行方法: " + joinPoint.getSignature().getName());
}
}切点表达式
execution表达式
java
// 匹配所有public方法
execution(public * *(..))
// 匹配com.example.service包下所有方法
execution(* com.example.service.*.*(..))
// 匹配com.example.service包及子包下所有方法
execution(* com.example.service..*.*(..))
// 匹配UserService的所有方法
execution(* com.example.service.UserService.*(..))
// 匹配所有以save开头的方法
execution(* save*(..))
// 匹配只有一个参数且参数类型为String的方法
execution(* *(String))
// 匹配返回值为User的方法
execution(com.example.entity.User *(..))其他切点表达式
java
// 匹配带有@Log注解的方法
@annotation(com.example.annotation.Log)
// 匹配带有@Service注解的类中的所有方法
@within(org.springframework.stereotype.Service)
// 匹配参数带有@Valid注解的方法
@args(javax.validation.Valid)
// 匹配Bean名称
bean(userService)
// 组合表达式
execution(* com.example.service.*.*(..)) && @annotation(Log)
execution(* com.example.service.*.*(..)) || execution(* com.example.repository.*.*(..))
!execution(* com.example.service.*.get*(..))切点表达式示例
java
@Aspect
@Component
public class PointcutDemo {
// 匹配service包下所有方法
@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceLayer() {}
// 匹配repository包下所有方法
@Pointcut("execution(* com.example.repository.*.*(..))")
public void repositoryLayer() {}
// 匹配service或repository层
@Pointcut("serviceLayer() || repositoryLayer()")
public void serviceOrRepository() {}
// 匹配带有@Transactional注解的方法
@Pointcut("@annotation(org.springframework.transaction.annotation.Transactional)")
public void transactionalMethod() {}
}通知类型
@Before(前置通知)
java
@Aspect
@Component
public class LoggingAspect {
@Before("execution(* com.example.service.*.*(..))")
public void before(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
Object[] args = joinPoint.getArgs();
System.out.println("执行方法: " + methodName + ", 参数: " + Arrays.toString(args));
}
}@AfterReturning(返回通知)
java
@Aspect
@Component
public class LoggingAspect {
@AfterReturning(
pointcut = "execution(* com.example.service.*.*(..))",
returning = "result"
)
public void afterReturning(JoinPoint joinPoint, Object result) {
String methodName = joinPoint.getSignature().getName();
System.out.println("方法 " + methodName + " 返回: " + result);
}
}@AfterThrowing(异常通知)
java
@Aspect
@Component
public class ExceptionAspect {
@AfterThrowing(
pointcut = "execution(* com.example.service.*.*(..))",
throwing = "ex"
)
public void afterThrowing(JoinPoint joinPoint, Exception ex) {
String methodName = joinPoint.getSignature().getName();
System.out.println("方法 " + methodName + " 抛出异常: " + ex.getMessage());
}
}@After(后置通知)
java
@Aspect
@Component
public class FinallyAspect {
@After("execution(* com.example.service.*.*(..))")
public void after(JoinPoint joinPoint) {
System.out.println("方法执行完成: " + joinPoint.getSignature().getName());
}
}@Around(环绕通知)
java
@Aspect
@Component
public class AroundAspect {
@Around("execution(* com.example.service.*.*(..))")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
String methodName = pjp.getSignature().getName();
// 前置处理
System.out.println("开始执行: " + methodName);
long start = System.currentTimeMillis();
try {
// 执行目标方法
Object result = pjp.proceed();
// 返回处理
long end = System.currentTimeMillis();
System.out.println("执行完成: " + methodName + ", 耗时: " + (end - start) + "ms");
return result;
} catch (Exception e) {
// 异常处理
System.out.println("执行异常: " + methodName + ", 异常: " + e.getMessage());
throw e;
} finally {
// 最终处理
System.out.println("最终处理: " + methodName);
}
}
}实战示例
日志切面
java
@Aspect
@Component
@Slf4j
public class LoggingAspect {
@Around("@annotation(com.example.annotation.Log)")
public Object log(ProceedingJoinPoint pjp) throws Throwable {
String className = pjp.getTarget().getClass().getSimpleName();
String methodName = pjp.getSignature().getName();
Object[] args = pjp.getArgs();
log.info("{}.{} 开始执行, 参数: {}", className, methodName, args);
long start = System.currentTimeMillis();
try {
Object result = pjp.proceed();
long end = System.currentTimeMillis();
log.info("{}.{} 执行成功, 耗时: {}ms, 返回: {}",
className, methodName, end - start, result);
return result;
} catch (Exception e) {
log.error("{}.{} 执行失败, 异常: {}", className, methodName, e.getMessage());
throw e;
}
}
}
// 自定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {
String value() default "";
}
// 使用
@Service
public class UserService {
@Log("添加用户")
public void addUser(User user) {
// 业务逻辑
}
}性能监控切面
java
@Aspect
@Component
public class PerformanceAspect {
@Around("execution(* com.example.service.*.*(..))")
public Object monitor(ProceedingJoinPoint pjp) throws Throwable {
String methodName = pjp.getSignature().getName();
StopWatch stopWatch = new StopWatch();
stopWatch.start();
try {
return pjp.proceed();
} finally {
stopWatch.stop();
long time = stopWatch.getTotalTimeMillis();
if (time > 1000) {
System.out.println("警告: 方法 " + methodName + " 执行时间过长: " + time + "ms");
}
}
}
}事务切面
java
@Aspect
@Component
public class TransactionAspect {
@Around("@annotation(Transactional)")
public Object transaction(ProceedingJoinPoint pjp) throws Throwable {
// 开启事务
System.out.println("开启事务");
try {
Object result = pjp.proceed();
// 提交事务
System.out.println("提交事务");
return result;
} catch (Exception e) {
// 回滚事务
System.out.println("回滚事务");
throw e;
}
}
}下一步
继续学习 JDBC数据访问,了解Spring的数据访问能力。