Spring AOP是Spring框架中的一个模块,它允许开发人员使用面向切面编程(AOP)的思想来解耦系统的不同层次。
Spring AOP的核心概念是切面(aspect)、连接点(join point)、通知(advice)、切点(pointcut)和引入(introduction)。
Spring AOP底层使用动态代理和字节码生成来实现。切面由通知和切点组成,连接点是程序执行的某个时间点,切点根据表达式匹配连接点,通知是在连接点上执行的代码片段,在方法调用前或调用后执行某些操作。
Spring AOP的优点是可以通过配置实现解耦,不用修改经常变动的业务逻辑代码,而且可以重用通知,降低代码冗余程度。
以下是一个使用Spring Boot AOP实现日志记录的例子:
1.创建一个Spring Boot项目并添加如下依赖:
org.springframework.boot
spring-boot-starter-aop
2.定义一个"UserService"接口和一个实现类"UserServiceImpl",实现类中包含一个方法"getUserById":
public interface UserService {
User getUserById(long id);
}
@Service
public class UserServiceImpl implements UserService {
@Override
public User getUserById(long id) {
System.out.println("调用getUserById方法,id=" + id);
return new User(id, "张三");
}
}
3.定义一个切面"LogAspect",
使用注解@Aspect标识切面,
使用@Pointcut定义切点,
使用
@Before、
@After、
@AfterReturning、
@AfterThrowing、
@Around注解定义通知:
@Aspect
@Component
public class LogAspect {
@Pointcut("execution(* com.example.demo.service.UserService.*(..))")
public void userServicePointcut() {}
@Before("userServicePointcut()")
public void before(JoinPoint joinPoint) {
System.out.println("调用" + joinPoint.getSignature().getName() + "方法");
}
@AfterReturning(pointcut = "userServicePointcut()", returning = "result")
public void afterReturning(JoinPoint joinPoint, Object result) {
System.out.println("调用" + joinPoint.getSignature().getName() + "方法完成,返回值=" + result);
}
@AfterThrowing(pointcut = "userServicePointcut()", throwing = "exception")
public void afterThrowing(JoinPoint joinPoint, Exception exception) {
System.out.println("调用" + joinPoint.getSignature().getName() + "方法出现异常,异常信息=" + exception.getMessage());
}
}
在上面的切面中,我们使用@Pointcut注解定义切点,使用@Before、@AfterReturning、@AfterThrowing等注解定义通知,在通知中使用JoinPoint获取方法签名、参数等信息,输出日志。
4.运行应用程序并测试:
@RestController
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/user/{id}")
public User getUser(@PathVariable long id) {
return userService.getUserById(id);
}
}
在控制台中可以看到如下输出:
调用getUserById方法,id=1
调用getUserById方法完成,返回值=User [id=1, name=张三]
Pointcut是基于表达式的描述符,用于指定应该在哪些方法上执行通知。Pointcut表达式可以使用AspectJ的语法,也可以使用Spring AOP的语法。
以下是常用的Pointcut表达式:
execution(public * com.example.demo.service.UserService.*(..)):匹配com.example.demo.service.UserService类中所有public修饰符的方法。
execution(* com.example.demo.service..(..)):匹配com.example.demo.service包下所有类的任意方法。
execution(* com.example.demo.service...(..)):匹配com.example.demo.service包以及其子包下所有类的任意方法。
within(com.example.demo.service.*):匹配所有com.example.demo.service包中的类型。
within(com.example.demo.service..*):匹配com.example.demo.service包以及其子包中的所有类型。
@annotation(org.springframework.web.bind.annotation.RequestMapping):匹配带有@RequestMapping注解的方法。
@within(org.springframework.stereotype.Service):匹配带有@Service注解的类中的所有方法。
args(java.lang.String):匹配一个参数类型为String的方法。
args(java.lang.String, ...):匹配至少一个参数类型为String的方法。
bean(userService):匹配名称为userService的bean。
bean(userService*):匹配名称以userService开头的bean。
除了以上的表达式外,还有很多其他的表达式,可以根据具体的需求选择使用。需要注意的是,在使用Pointcut表达式时,需要注意匹配的粒度,匹配过于精确会导致无法匹配到需要的切点,匹配过于宽泛则会匹配到不需要的切点。