介绍spring aop 的使用方式,使用的框架是spring-boot
核心概念
Aspect:即切面,切面一般定义为一个 Java 类, 每个切面侧重于特定的跨领域功能,比如,事务管理或者日志打印等。
Joinpoint:即连接点,程序执行的某个点,比如方法执行。构造函数调用或者字段赋值等。在 Spring AOP 中,连接点只会有 方法调用 (Method execution)。
Advice:即通知,在连接点要的代码。
Pointcut:即切点,一个匹配连接点的正则表达式。当一个连接点匹配到切点时,一个关联到这个切点的特定的 通知 (Advice) 会被执行。
Weaving:即编织,负责将切面和目标对象链接,以创建通知对象,在 Spring AOP 中没有这个东西。
代码
废话不多说,我们查看下代码
pom.xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
服务类
下面我们测试aop 的各种情况都在一个类中体现
@Service
public class AppService {
@CaculateExecuteTime // 这个是自定义的注解,用来计算方法的执行时间
public void share(String articleUrl){
// 这个方法是我们的核心测试方法
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("share article"+articleUrl);
}
public void onebyone(){
System.out.println("拥有全部的拦截器");
}
}
切面类
@Component
@Aspect //标记为一个切面
public class ServiceAspect {
/**
* execution 该指示器用来匹配方法执行连接点,即匹配哪个方法执行
* within 如果要匹配一个类中所有方法的调用,便可以使用 within 指示器 还可以匹配某个包下面的所有类的所有方法调用
* target 如果目标对象实现了任何接口,Spring AOP 会创建基于CGLIB 的动态代理,这时候需要使用 target 指示器
* this 如果目标对象没有实现任何接口,Spring AOP 会创建基于JDK的动态代理,这时候需要使用 this 指示器
* @target 该指示器用于匹配连接点所在的类是否拥有指定类型的注解,注意是所在类
* @annotation 匹配连接点的某个方法是否有某个注解
*/
@Pointcut("execution(public void WeiXinService.share(String))") //定义切点 在指定方法上拦截
public void shareCut(){
}
@Pointcut("within(com.zhibinwang.WeiXinService)") //定义切点 拦截这个类的所有方法
public void shareCutAll(){
}
@Pointcut("@annotation(CaculateExecuteTime)") // 拦截自定义的注解,这个是方法上的,用于确认方法的执行时间
public void shareAnntion(){}
/**
* @AfterReturing:该 Advice 会在方法正常返回以后执行
* @AfterThrowing: 该 Advice 会在方法抛出异常以后执行
* @After: 该 Advice 无论如何,在方法执行以后都会执行
* @param joinPoint
*/
@AfterReturning("shareCut()") //在业务返回后执行
public void log(JoinPoint joinPoint){
System.out.println(joinPoint.getSignature()+" executed");
}
@Before("shareCutAll()") // 在方法执行前执行
public void log2(JoinPoint joinPoint){
System.out.println(WeiXinService.class.getName()+"这个类的方法执行前全部被拦截");
}
@Around("shareAnntion()") // 在方法执行前后执行
public void arround(ProceedingJoinPoint joinPoint){
long l = System.currentTimeMillis();
try{ Object proceed = joinPoint.proceed();}catch (Exception e){} catch (Throwable throwable) {
throwable.printStackTrace();
}
System.out.println("消耗时间="+(System.currentTimeMillis() - l));
}
}
自定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CaculateExecuteTime {
}
测试类
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {AppAspect.class})
public class TestBoot {
@Autowired
ApplicationContext context;
@Test
public void test(){
AppService bean = context.getBean(AppService.class);
bean.share("baidu");
}
}
执行结果
com.zhibinwang.WeiXinService这个类的方法执行前全部被拦截 # 方法执行前
share articlebaidu #方法执行
消耗时间=3004 # 方法执行前后
void com.zhibinwang.WeiXinService.share(String) executed # 方法返回以后