Spring-Aop
Aop概念
pring AOP(面向切面编程)是Spring框架的一个重要组成部分,它提供了在应用的多个模块中复用某些行为的能力。AOP允许开发者定义“切面”,这些切面可以封装横切关注点,如事务管理、日志记录、安全检查等,这些关注点通常分散在应用程序的多个部分。通过使用AOP,开发人员可以在不修改对象本身的代码的情况下,将这些关注点添加到应用程序中。
Spring AOP的基本组成部分
1.切面(Aspect):
切面是横切关注点的模块化。一个切面封装了许多关注点,比如日志记录、事务管理等。一个切面包含了一系列的通知(Advice),这些通知会在特定的连接点(Joinpoint)被激活。
2.通知(Advice):
这是在特定连接点上采取的动作。Spring支持多种类型的通知,包括但不限于:
@Before
:在方法执行前执行。- @After:无论方法是否成功执行都会被执行。
- @AfterReturning:如果方法成功执行,则会被执行。
- @AfterThrowing:如果方法抛出了异常,则会被执行。
- @Around:环绕通知,可以控制方法的执行流程。
3.连接点(Joinpoint):
应用程序执行过程中的某个特定点,例如方法执行、字段访问等。
4.切入点(Pointcut):
一个或多个连接点的集合。它是通知将要被应用的地方的定义。可以通过表达式来指定切入点,比如execution(* com.example.service.*.*(..))。
5.目标对象(Target Object):
被一个或多个切面所通知的对象。
6.代理(Proxy):
被织入了通知的对象。客户端代码与代理交互,而不是直接与目标对象交互。
7.织入(Weaving):
将切面连接到其他应用程序类型或对象上的过程。织入可以在编译时、加载时或运行时进行。
环境准备
新建module:
- 填写Name
- 选择语言
- 选择build工具Maven
- ,选择jdk
- 填写GroupdId
- 填写ArtifactId
- 点击create
注:项目创建完毕后不要忘记刷新maven,否则依赖不会导入
导入依赖
在项目的pom文件中导入以下依赖
<dependencies> <!--导入spring-context依赖--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>6.1.12</version> </dependency> <!--导入测试依赖--><!--spring对junit--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>6.1.12</version> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> <version>5.11.0</version> <scope>test</scope> </dependency><!--aop相关的jar包--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>6.1.12</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>6.1.12</version> </dependency> </dependencies>
新建springConfig.xml文件
下面是springConfig.xml文件中的内容
<?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="org.xiji"/> </beans>
实体准备
package org.xiji;
import org.springframework.stereotype.Component;
/**
* 计算类
*/
@Component
public class Calculation {/**
* 相加
*/
public int add(int a, int b)
{
return a + b;
}
/**
* 相减
*/
public int sub(int a, int b)
{
return a - b;
}/**
* 相乘
*/
public int mul(int a, int b)
{
return a * b;
}/**
* 相除
*/
public int div(int a, int b)
{
return a / b;
}
}
编写springConfig.xml文件
文件内容如下:
<?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="org.xiji"/> <aop:aspectj-autoproxy /> </beans>
注:需要开启包扫描和aop自动代理
Spring测试类配置
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; import org.xiji.Calculation; /** * * JUNIT5第一种 * @ExtendWith(SpringExtension.class) * @ContextConfiguration(locations = {"classpath:/SpringConfig.xml"}) * * * JUNIT5第二种 * @SpringJUnitConfig(locations = {"classpath:/SpringConfig.xml"}) */ //@ExtendWith(SpringExtension.class) //@ContextConfiguration(locations = {"classpath:/SpringConfig.xml"}) @SpringJUnitConfig(locations = {"classpath:/springConfig.xml"}) public class SpringTestJunit5 { @Autowired private Calculation calculation; @Test public void test() { System.out.println(calculation.add(1, 2)); } }
切点配置类
切点表达式
"execution(权限修饰符 包名.类名.方法名(参数)) "
* 代表任意一个
.. 代表任意参数
前置通知
/** * 方法执行前执行的代码 */ @Before("execution(* org.xiji.Calculation.*(..))") public void before(JoinPoint joinPoint) { /** * 遍历输出参数 */ System.out.println("=========================="); Object[] args = joinPoint.getArgs(); for (Object arg : args) { System.out.println(arg); } System.out.println(joinPoint.getSignature().getName()); System.out.println(joinPoint.getTarget()); System.out.println("Aop前置处理器"); System.out.println("before"); System.out.println("=========================="); }
后置通知
/** * 后置方法,无论方法执行是否成功都会返回 */ @After("execution(* org.xiji.Calculation.*(..))") public void after(JoinPoint joinPoint) { System.out.println("=========================="); System.out.println(joinPoint.getTarget()); System.out.println("Aop后置处理器"); System.out.println("after"); System.out.println("=========================="); }
返回通知
注:returning 的名字,与参数中接受返回结果的值是一致的
/** * * 返回处理器 */ @AfterReturning(pointcut = "execution(* org.xiji.Calculation.*(..))",returning = "returning") public void afterReturning(JoinPoint joinPoint,Object returning) { System.out.println("=========================="); System.out.println(returning.toString()); System.out.println(joinPoint.getTarget()); System.out.println("Aop返回处理器"); System.out.println("afterReturning"); System.out.println("=========================="); }
异常通知
注:throwing 定义的参数是throwable 那么在接受参数时的名字也是 throwable
/** * 异常处理器 */ @AfterThrowing(pointcut = "execution(* org.xiji.Calculation.*(..))",throwing = "throwable") public void afterThrowing(JoinPoint joinPoint,Throwable throwable) { System.out.println("=========================="); System.out.println(throwable.getMessage()); System.out.println(joinPoint.getTarget()); System.out.println("Aop异常处理器"); System.out.println("afterThrowing"); System.out.println("=========================="); }
定义切点
/** * 定义切点 */ @Pointcut("execution(* org.xiji.Calculation.*(..))") public void pointCut() { }
注:使用切点时直接 输入pointCut() 即可
环绕通知
/** * 环绕通知 */ @Around("pointCut()") public Object around(ProceedingJoinPoint joinPoint) { Object result = null; try { System.out.println("环绕通知前"); result = joinPoint.proceed(); System.out.println("result = " + result); System.out.println("环绕通知后"); } catch (Throwable throwable) { System.out.println("环绕通知异常"); throwable.printStackTrace(); }finally { System.out.println("无论结果好坏必然运行"); }return result; }
完整的Point类
package org.xiji;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class PointCutAop {
/**
* 方法执行前执行的代码
*/
@Before("execution(* org.xiji.Calculation.*(..))")
public void before(JoinPoint joinPoint)
{
/**
* 遍历输出参数
*/
System.out.println("==========================");
Object[] args = joinPoint.getArgs();
for (Object arg : args) {
System.out.println(arg);
}
System.out.println(joinPoint.getSignature().getName());
System.out.println(joinPoint.getTarget());
System.out.println("Aop前置处理器");
System.out.println("before");
System.out.println("==========================");
}
/**
* 后置方法,无论方法执行是否成功都会返回
*/
@After("execution(* org.xiji.Calculation.*(..))")
public void after(JoinPoint joinPoint)
{
System.out.println("==========================");
System.out.println(joinPoint.getTarget());
System.out.println("Aop后置处理器");
System.out.println("after");
System.out.println("==========================");
}
/**
*
* 返回处理器
*/
@AfterReturning(pointcut = "execution(* org.xiji.Calculation.*(..))",returning = "returning")
public void afterReturning(JoinPoint joinPoint,Object returning)
{
System.out.println("==========================");
System.out.println(returning.toString());
System.out.println(joinPoint.getTarget());
System.out.println("Aop返回处理器");
System.out.println("afterReturning");
System.out.println("==========================");
}
/**
* 异常处理器
*/
@AfterThrowing(pointcut = "execution(* org.xiji.Calculation.*(..))",throwing = "throwable")
public void afterThrowing(JoinPoint joinPoint,Throwable throwable)
{
System.out.println("==========================");
System.out.println(throwable.getMessage());
System.out.println(joinPoint.getTarget());
System.out.println("Aop异常处理器");
System.out.println("afterThrowing");
System.out.println("==========================");
}
/**
* 定义切点
*/
@Pointcut("execution(* org.xiji.Calculation.*(..))")
public void pointCut()
{
}
/**
* 环绕通知
*/
@Around("pointCut()")
public Object around(ProceedingJoinPoint joinPoint)
{
Object result = null;
try {
System.out.println("环绕通知前");
result = joinPoint.proceed();
System.out.println("result = " + result);
System.out.println("环绕通知后");
} catch (Throwable throwable) {
System.out.println("环绕通知异常");
throwable.printStackTrace();
}finally {
System.out.println("无论结果好坏必然运行");
}
return result;
}
}
xml自定义AOP
xml文件中的内容
注:开启包扫描,注入切点,配置aop,配置前置方法,或其他的等等
并且注入的切面类不需要加上@Aspect 和 @Before注解,需要加入@Component
<?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="org.xiji"/> <aop:config> <aop:aspect ref="pointCutAop" > <!--配置前置--> <aop:before method="before" pointcut="execution(* org.xiji.Calculation.*(..))"></aop:before> </aop:aspect> </aop:config> <!--导入aop切点--> <bean id="pointCutAop" class="org.xiji.PointCutAop"> </bean> </beans>