下面我为大家详细讲解“SpringBoot项目使用aop案例详解”的完整攻略。
一、什么是AOP
AOP(Aspect Oriented Programming),即面向切面编程,是一种编程思想,它的原理就是在不改变原有代码结构的基础上,对横切关注点进行描述,便于将这些非功能性的需求模块化,降低系统耦合度。在Spring Framework中,AOP通过切面(Aspect)和连接点(Join Point)来构建。
二、SpringBoot项目中的AOP应用
2.1 依赖配置
在SpringBoot项目中使用AOP,需要添加一个Spring AOP的依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2.2 定义切面
在使用AOP时,一般先定义一个类,这个类用来描述需要实现的非功能性需求,也就是所谓的切面(Aspect)。切面使用@Aspect
注解来进行标识。
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class LogAspect {
@Before("execution(* com.example.demo.service..*(..))")
public void log(JoinPoint point) {
System.out.println("Before execution:" + point.getSignature().getName());
}
}
上面的代码定义了一个切面类LogAspect
,这个类使用@Component
注解将它注册到容器中。@Aspect
注解表示这个类是一个切面。
2.3 定义切点
在定义了切面之后,就需要定义切点(Join Point),切点是一部分应用程序的执行过程,这个过程可以使用切面进行增强。切点使用@Pointcut
注解来进行标识。
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
@Component
@Aspect
public class LogAspect {
@Pointcut("execution(* com.example.demo.service..*(..))")
public void pointcut() {
}
@Before("pointcut()")
public void log() {
System.out.println("Before execution...");
}
}
上面的代码中,我们定义了一个切点pointcut
,这个切点是所有用户Service中方法执行前都要切入的。在切面类中,我们可以使用这个切点来引用所有满足条件的切点。
2.4 增强代码
在切面类中定义切点之后,就需要定义增强代码。
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class LogAspect {
@Pointcut("execution(* com.example.demo.service..*(..))")
public void pointcut() {
}
@Before("pointcut()")
public void log(JoinPoint point) {
System.out.println("Before execution:" + point.getSignature().getName());
}
}
上面的代码中,我们使用@Before
注解来定义了一个增强方法log()
,这个方法会在所有匹配到的切点执行之前被执行。
2.5 示例
下面我们通过一个示例来演示SpringBoot项目中使用AOP的过程。
定义一个UserService,完成对用户的注销操作。
@Service("userService")
public class UserService {
public void logout() {
System.out.println("User Logout...");
}
}
定义切面。
@Aspect
@Component
public class LogAspect {
@Pointcut("execution(* com.example.demo.service..*(..))")
public void pointcut() {
}
@Before("pointcut()")
public void log(JoinPoint joinPoint) {
System.out.println("Before execution:" + joinPoint.getTarget() + "#" + joinPoint.getSignature().getName());
}
}
使用 UserService。
@RestController
public class UserController {
@Autowired
private UserService userService;
@RequestMapping("/logout")
public String logout() {
userService.logout();
return "Logout Success.";
}
}
当调用/logout
接口时,控制台会输出如下信息,说明切面已经生效:
Before execution:com.example.demo.service.UserService#logout
User Logout...
2.6 AOP的应用
通过AOP技术,可以在不改变原有代码结构的情况下,通过将横切关注点进行描述和模块化,让系统的各个组件能够分离,高聚合,低耦合地协同工作。AOP还可以让我们的代码更易于维护和测试,因为不同的关注点可以被划分到不同的切面中。
三、SpringBoot项目中aop解决事务重复提交
3.1 需求
在一个添加用户的接口中,如果没有在120秒内完成修改,视为无效操作。
3.2 解决思路
在添加操作前,先查询Database中是否已经存在用户,若没有则进行添加,并对添加过程设置一个超时时间,超时后自动撤销添加操作。
3.3 前置检查
首先实现一个检查用户是否存在的方法。
@Service
public class UserService {
public boolean exists(User user) {
// 在这里实现数据查询验证
return false;
}
}
然后定义一个切面检查用户是否存在并执行创建操作。
@Aspect
@Component
public class UserExistsAspect {
@Autowired
UserService userService;
@Pointcut("execution(* com.example.demo.service.UserService.addUser(..))")
public void pointcut() {
}
@Before("pointcut()")
public void doBefore(JoinPoint joinPoint) {
Object[] args = joinPoint.getArgs();
User user = (User) args[0];
if (userService.exists(user)) {
throw new RuntimeException("用户已存在");
}
}
}
3.4 事务
定义一个事务管理切面,作用是在创建用户时自动设置超时时间,并在超时后自动撤销操作。
@Aspect
@Component
public class TransactionAspect {
@Autowired
UserService userService;
@Pointcut("execution(* com.example.demo.service.UserService.addUser(..))")
public void pointcut() {
}
@Around("pointcut()")
public void around(ProceedingJoinPoint point) {
Object[] args = point.getArgs();
User user = (User) args[0];
// 秒数
int seconds = 120;
Thread thread = null;
try {
thread = new Thread(() -> {
try {
Thread.sleep(1000 * seconds);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Do Nothing Because " + seconds + " Seconds Expires...");
});
thread.start();
userService.addUser(user);
thread.interrupt();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
try {
thread.interrupt();
point.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
}
3.5 局限性
在这个示例中,我们手动启动了一个线程,并通过sleep()
函数来实现超时控制。这种方式在并发量较大时,可能会导致表单资源迅速耗尽,进而导致程序崩溃。解决这个问题的方式是利用Spring框架的TaskExecutor
功能,将线程池与AOP的切面功能进行结合。
四、总结
以上就是SpringBoot项目使用AOP的详细攻略。AOP可以帮助我们更好地将各个模块分离开来,提高程序的聚焦性和清晰性,同时维护程序也变得更加容易。同时也给出了AOP在事务管理场景下的应用示例,以此来更加深入地了解AOP的工作方式。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:SpringBoot项目使用aop案例详解 - Python技术站