想要使用spring中的任何技术 第一步都需要导包
第一步导包
- spring核心包
- spring整个aspectj
- lombok偷懒神器
- 测试包junit
- spring集合junit4
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.9</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.3.17</version>
</dependency>
</dependencies>
第二步 开始创建使用aop
- 首先是创建spring的配置文件
- 目的就是开启注解配置 包扫描
<?xml version="1.0" encoding="UTF-8"?>
<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 http://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
">
<context:component-scan base-package="com.cong"></context:component-scan>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
创建一个目标对象
- 目标对象解释:被通知的对象称之为目标对象
接口
/**
* @ClassName CalculatorDao
* @Author Dongyu Zhou
* @Date 2022/4/24 19:25
* @Version 1.0
*/
public interface CalculatorDao {
/**
* 加法
* @param a
* @param b
*/
public int add(int a ,int b);
/**
* 减法
* @param a
* @param b
*/
public int sub(int a ,int b);
}
实现类
/**
* @ClassName CalucDaoImpl
* @Author Dongyu Zhou
* @Date 2022/4/24 19:25
* @Version 1.0
*/
@Repository
public class CalucDaoImpl implements CalculatorDao {
@Override
public int add(int a, int b) {
System.out.println("add-------------");
return a+b;
}
@Override
public int sub(int a, int b) {
System.out.println("sub-------------");
return a-b;
}
}
创建切面
- 名词解释 切面:非核心业务代码称之为:通知【提取到切面之后】
/**
* @ClassName MyProxy
* @Author Dongyu Zhou
* @Date 2022/4/24 19:27
* @Version 1.0
*/
@Component
@Aspect
public class MyProxy {
//加一个日志功能
/**
* 提取公共代码
*/
@Pointcut("execution(* com.cong.dao.impl.*.*(..))")
public void myPorxy(){}
/**
* 前置通知
* joinPoint.getSignature() 获取签名 public int add(int a, int b) 方法名加参数
*/
@Before("myPorxy()")
public void before(JoinPoint joinPoint){
Signature signature = joinPoint.getSignature();
String name = signature.getName();
Object[] args = joinPoint.getArgs();
System.out.println("前置通知 方法名是:"+name+" 参数是"+ Arrays.toString(args));
}
/**
* 后置通知
* @param joinPoint
*/
@After("myPorxy()")
public void after(JoinPoint joinPoint){
Signature signature = joinPoint.getSignature();
String name = signature.getName();
Object[] args = joinPoint.getArgs();
System.out.println("后置通知 方法名是:"+name+" 参数是"+ Arrays.toString(args));
}
/**
* 返回通知 定义一个返回参数
* @param joinPoint
*/
@AfterReturning(value = "myPorxy()",returning = "ret")
public void afterReturning(JoinPoint joinPoint,Object ret){
Signature signature = joinPoint.getSignature();
String name = signature.getName();
Object[] args = joinPoint.getArgs();
System.out.println("返回通知 方法名是:"+name+" 参数是"+ Arrays.toString(args)+" 返回值是:"+ret);
}
/**
* 异常通知
* @param joinPoint
* @param e
*/
@AfterThrowing(value = "myPorxy()",throwing = "e")
public void afterReturning(JoinPoint joinPoint,Throwable e){
Signature signature = joinPoint.getSignature();
String name = signature.getName();
Object[] args = joinPoint.getArgs();
System.out.println("返回通知 方法名是:"+name+" 参数是"+ Arrays.toString(args));
}
/**
* 终极大法 环绕通知
*/
@Around(value = "myPorxy()")
public Object around(ProceedingJoinPoint proceedingJoinPoint){
String name = proceedingJoinPoint.getSignature().getName();
Object[] args = proceedingJoinPoint.getArgs();
Object proceed = null;
try {
//前置通知
System.out.println("前置通知 方法名是:"+name+" 参数是"+ Arrays.toString(args));
proceed = proceedingJoinPoint.proceed();
//返回通知
System.out.println("返回通知 方法名是:"+name+" 参数是"+ Arrays.toString(args));
} catch (Throwable e) {
//异常通知
System.out.println("返回通知 方法名是:"+name+" 异常是"+ e);
} finally {
//后置通知
System.out.println("后置通知 方法名是:"+name+" 参数是"+ Arrays.toString(args));
}
return proceed;
}
}
测试
/**
* @ClassName MyTest
* @Author Dongyu Zhou
* @Date 2022/4/24 19:36
* @Version 1.0
*/
@ContextConfiguration(locations = "classpath:applicationContext.xml")
@RunWith(SpringJUnit4ClassRunner.class)
public class MyTest {
@Autowired
private CalculatorDao calculatorDao;
@Test
public void before() {
int add = calculatorDao.add(1, 2);
}
@Test
public void after() {
int sub = calculatorDao.sub(20, 2);
}
}
这里 总结下
前置通知: 没什么可注意的 加一个参数 JoinPoint 这里可以获取类的签名
类的签名就是类名加参数 都可以获取到
注意的是在切点执行之前执行 所以有异常也会执行
后置通知
需要注意的是 后置通知是最后执行的 不管有没有异常都会执行
返回通知
这里返回通知就是可以有一个返回值
需要加一个参数
@AfterReturning(value = "myPorxy()",returning = "ret")这个参数名字ret可以随便叫 你喜欢就行 但是必须和方法中的Object中的形参名形同
有异常不会执行
异常通知
顾名思义就是有异常才会执行 没有异常不会执行
这里和返回通知同样有一个参数值 是异常类型的形参 可以随便叫什么名字
前面四个通知有一个总结性的通知
叫做环绕通知 需要注意两点
第一点就是必须要有一个返回值类型 必须和目标方法返回值类型一直 用Object
第二点就是必须加一个参数
ProceedingJoinPoint执行方法
proceed = proceedingJoinPoint.proceed();代表目标对象方法的执行 所以给他一个try catch finally
分别加上前置通知 返回通知 异常通知 和后置通知
四个通知的执行流程
有异常的情况下 : 前置通知->异常->后置
无异常的情况下 : 前置 ->返回->后置