AOP:Aspect Oriented Programming
面向方面编程(AOP)通过提供另一种思考程序结构的方式来补充面向对象编程(OOP)。OOP中模块化的关键单元是类,而在AOP中,模块化单元是方面。方面实现了跨越多种类型和对象的关注点(例如事务管理)的模块化。(这些担忧在AOP文献中通常被称为“横切”问题。)
官方文档
专业的人做专业的事(找出多个类中有规律的代码,开发时拆分,运行时再合并)
Java配置启用@AspectJ支持
要使用Java启用@AspectJ支持@Configuration,请添加@EnableAspectJAutoProxy 注释
@Configuration
@EnableAspectJAutoProxy
public class AppConfig {
}
声明一个切面
在启用@AspectJ支持的情况下,在应用程序上下文中定义的任何bean都具有@AspectJ方面的类(具有@Aspect注释),Spring会自动检测并用于配置Spring AOP
@Slf4j
@Aspect
@Component
public class LogAspect {
}
声明切入点
切入点确定感兴趣的连接点,从而使我们能够控制建议何时执行。Spring AOP仅支持Spring bean的方法执行连接点,因此您可以将切入点视为匹配Spring bean上方法的执行。切入点声明有两个部分:一个包含名称和任何参数的签名,以及一个精确确定我们感兴趣的方法执行的切入点表达式。在AOP的@AspectJ注释样式中,切入点签名由常规方法定义提供,并使用@Pointcut注释指示切入点表达式(用作切入点签名的方法必须具有void返回类型)。
@Slf4j
@Aspect
@Component
public class LogAspect {
@Pointcut("execution(* com.aop.controller.*.*(..))")
public void myLog() {}
}
常见的切入点表达式
执行任何公共方法:
execution(public * *(..))
执行名称以以下开头的任何方法set:
execution(* set*(..))
执行AccountService接口定义的任何方法:
execution(* com.xyz.service.AccountService.*(..))
执行service包中定义的任何方法:
execution(* com.xyz.service.*.*(..))
执行服务包或其子包中定义的任何方法:
execution(* com.xyz.service..*.*(..))
服务包中的任何连接点(仅在Spring AOP中执行方法):
within(com.xyz.service.*)
服务包或其子包中的任何连接点(仅在Spring AOP中执行方法):
within(com.xyz.service..*)
代理实现AccountService接口的任何连接点(仅在Spring AOP中执行方法) :
this(com.xyz.service.AccountService)
目标对象实现AccountService接口的任何连接点(仅在Spring AOP中执行方法):
target(com.xyz.service.AccountService)
采用单个参数的任何连接点(仅在Spring AOP中执行的方法)以及在运行时传递的参数是Serializable:
args(java.io.Serializable)
目标对象具有@Transactional注释的任何连接点(仅在Spring AOP中执行方法) :
@target(org.springframework.transaction.annotation.Transactional)
任何连接点(仅在Spring AOP中执行方法),其中目标对象的声明类型具有@Transactional注释:
@within(org.springframework.transaction.annotation.Transactional)
任何连接点(仅在Spring AOP中执行方法),其中执行方法具有 @Transactional注释:
@annotation(org.springframework.transaction.annotation.Transactional)
任何连接点(仅在Spring AOP中执行的方法),它接受一个参数,并且传递的参数的运行时类型具有@Classified注释:
@args(com.xyz.security.Classified)
名为的Spring bean上的任何连接点(仅在Spring AOP中执行方法) tradeService:
bean(tradeService)
具有与通配符表达式匹配的名称的Spring bean上的任何连接点(仅在Spring AOP中执行方法)*Service:
bean(*Service)
声明Advice
Advice与切入点表达式相关联,并在切入点匹配的方法执行之前,之后或周围运行。切入点表达式可以是对命名切入点的简单引用,也可以是在适当位置声明的切入点表达式。
声明之前
@Slf4j
@Aspect
@Component
public class LogAspect {
@Pointcut("execution(* com.aop.controller.*.*(..))")
public void myLog() {}
@Before("myLog()")
public void beforeMethod(JoinPoint joinPoint) {
log.info("调用之前执行");
Object[] args = joinPoint.getArgs();
for(Object obj:args) {
log.info("{}",obj);
}
}
}
测试
新建一个UserController进行测试
@RestController
public class UserController {
@GetMapping("/{name}")
public String getUser(@PathVariable String name) {
return name;
}
@GetMapping("/login")
public String login(@RequestParam String username,@RequestParam String password) {
return "ok";
}
}
结果