目录
一.四个点:
1.PointCut 切入点,表示在哪个位置切入
2.Advice 通知,表示在这个位置要执行的方法
3.Joinpoint 连接点,表示该方法
4 .Aspect 切面,(PointCut + Advice) 这俩构成切面
二.切点表达式:
execution( 返回值类型 包名 方法名(..)异常信息 )
..表示任意参数,*表示任意,例如:
* com.example.service..Order*(..)
表示返回值类型任意,包com.example.service下以Order开头的任意方法
// 匹配public方法
execution(public * *(..))
// 匹配名称以set开头的方法
execution(* set*(..))
// 匹配AccountService接口或类的方法
execution(* com.xyz.service.AccountService.*(..))
// 匹配service包及其子包的类或接口
execution(* com.xyz.service..*(..))
三.项目使用
首先,要引入依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.0.3</version>
</dependency>
<!-- aspects依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>6.0.3</version>
</dependency>
然后在配置类上写上注解EnableAspecetJAutoProxy开启aop支持
@Configuration
@ComponentScan("com.jjh")
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class SpringConfig {
}
然后写一个Service
@Service
public class OrderService {
public void updateOrder(){
System.out.println("订单已修改...");
}
public void deleteOrder(){
System.out.println("订单已删除...");
}
public void insertOrder(){
System.out.println("订单已创建...");
}
public void selectOrder(){
System.out.println("已查询到订单信息...");
if(true){
throw new RuntimeException("出现运行时异常!!!");
}
}
}
接着写自定义的Advice
@Component
@Aspect
@Order(1)
public class MyAdvice {
//在方法执行前执行
@Before("execution(* com.jjh.service..select*(..))")
public void beforeAdvice() {
System.out.println("在执行前执行");
}
//在方法执行后执行
@AfterReturning("execution(* com.jjh.service..select*(..))")
public void afterReturningAdvice() {
System.out.println("在执行后执行...");
}
//在方法执行前后进行环绕
@Around("execution(* com.jjh.service..select*(..))")
public void aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕前执行...");
joinPoint.proceed();
System.out.println("环绕后执行...");
}
//在方法最后执行
@After("execution(* com.jjh.service..select*(..))")
public void afterAdvice() {
System.out.println("执行后执行...");
}
//出现异常时执行
@AfterThrowing("execution(* com.jjh.service..select*(..))")
public void afterThrowing() {
System.out.println("异常出现后执行...");
}
}
然后写测试程序进行测试
public class ServiceTest {
@Test
public void TimeLogTest(){
ApplicationContext applicationContext=new AnnotationConfigApplicationContext(SpringConfig.class);
OrderService orderService = applicationContext.getBean("orderService", OrderService.class);
orderService.insertOrder();
orderService.deleteOrder();
orderService.updateOrder();
orderService.selectOrder();
}
}
运行结果
订单已创建...
订单已删除...
订单已修改...
环绕前执行...
在执行前执行
已查询到订单信息...
异常出现后执行...
执行后执行...
java.lang.RuntimeException: 出现运行时异常!!!
这里因为出现了异常,所有环绕后就不会执行了!!!
四.安全日志
aop例子:安全日志
给增,删,改加入时间,以及需要知道是谁在操作,可以使用AOP
@Component
@Aspect
@Order(2)
public class SecurityLogAdvice {
//切入点为修改方法时
@Pointcut("execution(* com.jjh.service..update*(..))")
public void updatePointcut(){}
@Pointcut("execution(* com.jjh.service..delete*(..))")
public void deletePointcut(){}
@Pointcut("execution(* com.jjh.service..insert*(..))")
public void insertPointcut(){}
@Before("updatePointcut() || deletePointcut() || insertPointcut()")
public void ShowTimelog(JoinPoint joinPoint){
SimpleDateFormat dateFormat=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String now = dateFormat.format(new Date());
/**
* joinPoint.getSignature()方法获取该连接点对象的签名,可以从签名中获取它
* 所属的类名,方法名等信息
*/
System.out.println(now + " "+joinPoint.getSignature().getDeclaringTypeName() + "."
+joinPoint.getSignature().getName()+"()");
}
}
这里pointcut是公共的切入点,可以简化写法
运行结果:
2023-02-27 22:02:21 com.jjh.service.OrderService.insertOrder()
订单已创建...
2023-02-27 22:02:21 com.jjh.service.OrderService.deleteOrder()
订单已删除...
2023-02-27 22:02:21 com.jjh.service.OrderService.updateOrder()
订单已修改...
已查询到订单信息...
这样就知道哪个方法在哪个时间做了什么事,到时在吧user信息传入即可
五.多个Advice顺序:
若定义了多个Advice,则可以使用@Order()注解,数值小的优先级大于数值大的