参考博客教程:最简单的基于注解进行面向切面AOP开发案例_cuipy的博客-CSDN博客
AOP:Aspect Oriented Program(面向切面编程),它可以让我们在执行某些方法之前,先执行一些代码块,是对面向对象编程发展到一定阶段的有益补充
准备工作:一普通Maven项目
目录完成结构:
1.在pom.xml中导入jar包依赖:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>AopTest</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.10</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.3.10</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.7</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.7</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<!-- 配置项目中需要的资源文件,不加这个配置, application.xml不会被编译打包 -->
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
</project>
2.在resources文件夹中加入Spring配置文件 application.xml
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--扫描spring注解所在的包-->
<context:component-scan base-package="com.wanshi.test" />
<!--开启允许使用aop注解开发-->
<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
</beans>
3.写入实体类test与切面类testAspect
test:
package com.wanshi.test.pojo;
import org.springframework.stereotype.Component;
@Component
public class test {
public void test1(){
System.out.println("执行了test实体类的test1");
}
public void test2(){
System.out.println("执行了test实体类的test2");
}
public void test3(){
System.out.println("执行了test实体类的test3,出现异常");
String a="1a3";
Integer i=Integer.valueOf(a);
}
}
testAspect
package com.wanshi.test.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class testAspect {
// 执行test中的test1之前插入
@Before("execution(* com.wanshi.test.pojo.test.test1(..))")
public void aspect1(){
System.out.println("执行之前切入了。。。");
}
// 环绕插入,即aspect-pojo-aspect
@Around("execution(* com.wanshi.test.pojo.test.test2(..))")
public void aspect2(ProceedingJoinPoint pjp) throws Throwable {
// ProceedingJoinPoint:即要执行的方法对象,proceed方法为执行此方法
System.out.println("执行之前切入了aspect2,环绕切入"); //调用test的test2方法后,先执行此切入方法
pjp.proceed(); //之后执行这个方法:test中的test2
System.out.println("执行完毕,关闭方法"); //最后再回来执行
}
// 异常切入,注意,ProceedingJoinPoint不可在此使用,因为它只适用于环绕切入
@AfterThrowing(value = "execution(* com.wanshi.test.pojo.test.test3(..))",throwing = "ex")
public void aspect3(JoinPoint join,Throwable ex){
String classname=join.getTarget().getClass().getName(); //拿到类名
String methodname=join.getSignature().getName(); //拿到类的方法名
String exname=ex.getClass().getName(); //拿到异常名
String exmsg=ex.getMessage(); //拿到异常信息
System.out.println("执行"+classname+"."+methodname);
System.out.println("出现异常:"+exname+",异常信息:"+exmsg);
}
}
注:增强类需要加上@Aspect注解,否则注解不生效无法切入
整体关键文件已完成,可复制代码到单元测试中使用并测试
理解:
(1) @Before:在执行某个方法之前进行切入操作
@Before("execution(* com.wanshi.test.pojo.test.test1(..))")
其中,execution(* com.wanshi.test.pojo.test.test1(..))为所匹配的切入方法,即我要给谁执行,* 表示匹配所有返回值,test1(..)中的 .. 表示匹配任意参数
(2) @Around:环绕切入,即 Aspect的方法>目标类的方法>Aspect的方法
@Around("execution(* com.wanshi.test.pojo.test.test2(..))")
public void aspect2(ProceedingJoinPoint pjp) throws Throwable {
环绕切入的aspect2方法需要一个参数ProceedingJoinPoint,因为这个参数是用来调用目标类的方法的
(3) @AfterThrowing:异常切入,即目标类方法出现异常时执行此aspect
@AfterThrowing(value = "execution(* com.wanshi.test.pojo.test.test3(..))",throwing = "ex")
public void aspect3(JoinPoint join,Throwable ex){
它的格式必须是 (value="..."),不可像@Before与@Around一样直接写,throwing为异常对象,并且它的进入类不是 ProceedingJoinPoint ,而是 JoinPoint , ProceedingJoinPoint只用于环绕切入
前置通知(Before):在目标方法之前执行的
后置通知(After):在目标方法之后执行的
环绕通知(Around):在目标方法之前之后都执行的
异常抛出通知(After-Throwing):在目标方法异常后执行的