Spring引入Spring AOP:面向切面编程(基于aspectj)
应用场景:环绕增强可以用来解决事务问题,前置/后置/返回值/异常增强可以用来添加日志处理
1、导包
<!-- Spring AOP -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.9.1</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>1.9.9.1</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.9.1</version>
</dependency>
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.18</version>
</dependency>
2、在Spring配置文件中配置信息
<!--使AspectJ注解起作用:为匹配的类自动生成代理对象-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
3、待增强类
接口
public interface Calculator {
int add(int a, int b);
int sub(int a, int b);
int div(int a, int b);
int mut(int a, int b);
}
实现类
import org.springframework.stereotype.Component;
@Component("calculator")
public class CalculatorImpl implements Calculator {
@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;
}
@Override
public int div(int a, int b) {
System.out.println("div方法被调用");
return a / b;
}
@Override
public int mut(int a, int b) {
System.out.println("mut方法被调用");
return a * b;
}
}
4、增强类
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
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;
import java.util.Arrays;
@Component //为切面加入Spring自动扫描支持
@Aspect //加入AspectJ支持,声明类为切面
public class LogAspect {
/**
* 前置增强
*/
@Before("execution(public int com.xxx.demo.Calculator.*(int,int))")
public void beforeMethod() {
System.out.println("前置增强");
}
/**
* 后置增强
*
* @param joinPoint 可以通过joinPoint获取所执行的方法以及该方法传入的参数
*/
@After("execution(public int com.xxx.demo.Calculator.*(int,int))")
public void afterMethod(JoinPoint joinPoint) {
// 获取方法名称
String methodName = joinPoint.getSignature().getName();
System.out.println("方法名称:" + methodName);
// 获取该方法传入的参数
Object[] args = joinPoint.getArgs();
System.out.println("方法参数:" + Arrays.toString(args));
System.out.println("后置增强");
}
/**
* 返回增强
*
* @param joinPoint 可以通过joinPoint获取所执行的方法以及该方法传入的参数
* @param result 获取该方法的返回值
*/
@AfterReturning(value = "execution(public int com.xxx.demo.Calculator.*(int,int))", returning = "result")
public void afterReturningMethod(JoinPoint joinPoint, int result) {
// 获取方法名称
String methodName = joinPoint.getSignature().getName();
System.out.println("方法名称:" + methodName);
// 获取该方法传入的参数
Object[] args = joinPoint.getArgs();
System.out.println("方法参数:" + Arrays.toString(args));
// 获取该方法执行的返回值
System.out.println("方法返回值:" + result);
System.out.println("返回增强");
}
/**
* 异常增强
*
* @param joinPoint 可以通过joinPoint获取所执行的方法以及该方法传入的参数
* @param ex 该方法所产生的异常
*/
@AfterThrowing(value = "execution(public int com.xxx.demo.Calculator.*(int,int))", throwing = "ex")
public void afterThrowingMethod(JoinPoint joinPoint, ArithmeticException ex) {
// 获取方法抿成
String methodName = joinPoint.getSignature().getName();
System.out.println("方法名称:" + methodName);
// 获取该方法所传入的返回值
Object[] args = joinPoint.getArgs();
System.out.println("方法参数:" + Arrays.toString(args));
// 获取该方法产生的异常
System.out.print("方法产生的异常:" + ex);
System.out.println("异常增强");
}
/**
* 环绕增强
*
* @param pjp 可以通过pjp获取方法名称、获取传入的参数以及可以通过proceed调用目标方法
* @return java.lang.Object 返回目标方法执行后的结果
*/
@Around(value = "execution(public int com.xxx.demo.Calculator.*(int,int))")
public Object AroundMethod(ProceedingJoinPoint pjp) {
Object res = null;
String methodName = pjp.getSignature().getName();
Object[] args = pjp.getArgs();
try {
System.out.println("环绕增强-->前置");
//调用目标方法
res = pjp.proceed();
System.out.println("返回值:" + res);
System.out.println("环绕增强-->返回");
} catch (Throwable throwable) {
System.out.println("环绕增强-->异常");
throw new RuntimeException(throwable);
}
System.out.println("环绕增强-->后置");
return res;
}
}
5、测试
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.stereotype.Component;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import javax.annotation.Resource;
@ExtendWith(SpringExtension.class)
@ContextConfiguration({"/spring.xml", "/mybatis.xml"})
@Component
public class DemoTest {
@Resource
private Calculator calculator;
@Test
void fun() {
System.out.println(calculator.add(3, 2));
System.out.println(calculator.sub(3, 2));
System.out.println(calculator.div(3, 0));
System.out.println(calculator.mut(3,2));
}
}
以下测试是对LogAspect.java中的增强方式依次单个或多个进行测试,单个时是将其他的增强方法注释掉
测试前置增强:
测试后置增强
测试返回增强
测试异常增强
测试正常环绕增强
测试异常环绕增强
异常时,环绕增强执行了环绕增强的前置增强和环绕增强的异常增强
所有增强均开始,测试执行顺序
无异常时,先执行环绕前置增强-->前置增强-->环绕增强调用方法-->返回增强-->后置增强-->环绕返回增强-->环绕后置增强
异常时,环绕前置增强-->前置增强-->环绕增强调用方法-->异常增强-->后置增强-->环绕异常增强
方式二:通过xml配置文件实现增强(不建议使用)
1、导入依赖,同上
2、在Spring配置文件中配置相关信息,同上
3、待增强类,同上
4、增强类
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import java.util.Arrays;
public class LogAspect2 {
/**
* 前置增强
*/
public void beforeMethod() {
System.out.println("前置增强");
}
/**
* 后置增强
*
* @param joinPoint 可以通过joinPoint获取所执行的方法以及该方法传入的参数
*/
public void afterMethod(JoinPoint joinPoint) {
// 获取方法名称
String methodName = joinPoint.getSignature().getName();
System.out.println("方法名称:" + methodName);
// 获取该方法传入的参数
Object[] args = joinPoint.getArgs();
System.out.println("方法参数:" + Arrays.toString(args));
System.out.println("后置增强");
}
/**
* 返回增强
*
* @param joinPoint 可以通过joinPoint获取所执行的方法以及该方法传入的参数
* @param result 获取该方法的返回值
*/
public void afterReturningMethod(JoinPoint joinPoint, int result) {
// 获取方法名称
String methodName = joinPoint.getSignature().getName();
System.out.println("方法名称:" + methodName);
// 获取该方法传入的参数
Object[] args = joinPoint.getArgs();
System.out.println("方法参数:" + Arrays.toString(args));
// 获取该方法执行的返回值
System.out.println("方法返回值:" + result);
System.out.println("返回增强");
}
/**
* 异常增强
*
* @param joinPoint 可以通过joinPoint获取所执行的方法以及该方法传入的参数
* @param ex 该方法所产生的异常
*/
public void afterThrowingMethod(JoinPoint joinPoint, ArithmeticException ex) {
// 获取方法抿成
String methodName = joinPoint.getSignature().getName();
System.out.println("方法名称:" + methodName);
// 获取该方法所传入的返回值
Object[] args = joinPoint.getArgs();
System.out.println("方法参数:" + Arrays.toString(args));
// 获取该方法产生的异常
System.out.print("方法产生的异常:" + ex);
System.out.println("异常增强");
}
/**
* 环绕增强
*
* @param pjp 可以通过pjp获取方法名称、获取传入的参数以及可以通过proceed调用目标方法
* @return java.lang.Object 返回目标方法执行后的结果
*/
public Object AroundMethod(ProceedingJoinPoint pjp) {
Object res = null;
String methodName = pjp.getSignature().getName();
Object[] args = pjp.getArgs();
try {
System.out.println("环绕增强-->前置");
//调用目标方法
res = pjp.proceed();
System.out.println("返回值:" + res);
System.out.println("环绕增强-->返回");
} catch (Throwable throwable) {
System.out.println("环绕增强-->异常");
throw new RuntimeException(throwable);
}
System.out.println("环绕增强-->后置");
return res;
}
}
5、增强xml配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean class="com.xxx.demo.CalculatorImpl" id="calculator"></bean>
<bean class="com.xxx.demo.LogAspect2" id="logAspect"></bean>
<aop:config>
<aop:pointcut id="pointcut"
expression="execution(public int com.xxx.demo.Calculator.*(int,int))"/>
<aop:aspect ref="logAspect" order="1">
<aop:before method="beforeMethod" pointcut-ref="pointcut"/>
<aop:after method="afterMethod" pointcut-ref="pointcut"/>
<aop:after-returning method="afterReturningMethod" pointcut-ref="pointcut"
returning="result"/>
<aop:after-throwing method="afterThrowingMethod" pointcut-ref="pointcut"
throwing="ex"/>
<aop:around method="AroundMethod" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>
</beans>
6、测试
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.stereotype.Component;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import javax.annotation.Resource;
@ExtendWith(SpringExtension.class)
@ContextConfiguration({"/spring.xml", "/mybatis.xml", "/aspectj.xml"})
@Component
public class DemoTest2 {
@Resource
private Calculator calculator;
@Test
void fun() {
//System.out.println(calculator.add(3, 2));
//System.out.println(calculator.sub(3, 2));
System.out.println(calculator.div(3, 0));
//System.out.println(calculator.mut(3,2));
}
}