今天重新学习了一下spring aop的相关知识,结合springBoot对Aop的支持,整理了一下学习成果。这只是简单的入门学习,下面演示下代码。
1. 声明一个aop注解
import java.lang.annotation.*;
/**
* 声明一个注解
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Action {
String name();
}
这个注解中有一个属性name,为了后面演示注解式切面的时候使用。
2. 引入springboot aop的pom文件
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
这里我使用的springboot版本是2.1.2.RELEASE,需要在parent中指定其版本号:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.2.RELEASE</version>
<relativePath/>
</parent>
3.编写被切方法:
在这里写两个类,第一个用来演示注解式切面,第二个用来演示方法切面。
- 注解式
import com.xqnode.learning.common.aop.annotation.Action;
import org.springframework.stereotype.Service;
@Service
public class AopAnnotationService {
@Action(name = "注解式拦截doSomething操作")
public void doSomething() {}
}
2.方法式:
import org.springframework.stereotype.Service;
@Service
public class AopMethodService {
public void doSomething() {}
}
4.编写切面类
import java.lang.reflect.Method;
@Aspect
@Component
public class LogAspect {
@Pointcut("@annotation(com.xqnode.learning.common.aop.annotation.Action)")
public void annotationPointCut() {
}
@Before("annotationPointCut()")
public void before(JoinPoint joinPoint) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
Action annotation = method.getAnnotation(Action.class);
System.out.println("注解式拦截:" + annotation.name());
}
@After("execution(* com.xqnode.learning.service.AopMethodService.* (..))")
public void after(JoinPoint joinPoint) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
System.out.println("方法式拦截:" + method.getName());
}
}
使用注解式切面需要声明一个方法annotationPointCut(),指定切面注解的类型,这里我们指定的是前面我编写的注解@Pointcut("@annotation(com.xqnode.learning.common.aop.annotation.Action)")
,在AopAnnotationService 的doSomething方法上我对其标记了注解,所以这个注解式切面会作用于这个类的这个方法上。
@Before是表示在方法执行之前执行的代码,这里我们简单打印了一句注解的名称。
@After(“execution(* com.xqnode.learning.service.AopMethodService.* (…))”)
中的表达式的意思是对AopMethodService的所有方法都进行切面操作,并且是在方法执行完成之后执行after方法中的代码,这里我打印了所有方法的名称。
在切面类中需要定义切面方法用于响应响应的目标方法,切面方法即为通知方法,通知方法需要用注解标识,AspectJ 支持 5 种类型的通知注解:
@Before: 前置通知, 在方法执行之前执行
@After: 后置通知, 在方法执行之后执行 。
@AfterRunning: 返回通知, 在方法返回结果之后执行
@AfterThrowing: 异常通知, 在方法抛出异常之后
@Around: 环绕通知, 围绕着方法执行
5. 验证
我们编写一个测试类验证下结果:
import com.xqnode.learning.service.AopAnnotationService;
import com.xqnode.learning.service.AopMethodService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
public class LearningApplicationTests {
@Autowired
AopAnnotationService aopAnnotationService;
@Autowired
AopMethodService aopMethodService;
@Test
public void contextLoads() {
//注解式切面
aopAnnotationService.doSomething();
//方法切面1
aopMethodService.doSomething();
//方法切面2
aopMethodService.otherThing();
}
}
可以看到,我们的注解式切面和方法式切面都生效了。注解式的切面比较灵活,而方法式的切面作用的范围广,大家可以根据实际需要选择适合自己的切面方式。