目录
一、Spring AOP底层技术
二、初步实现AOP编程
三、获取切点详细信息
四、 切点表达式语法
五、重用(提取)切点表达式
一、Spring AOP底层技术
SpringAop的核心在于动态代理,那么在SpringAop的底层的技术是依靠了什么技术呢?
- 动态代理(InvocationHandler):JDK原生的实现方式,需要被代理的目标类必须实现接口。因为这个技术要求代理对象和目标对象实现同样的接口(兄弟两个拜把子模式)。
- cglib:通过继承被代理的目标类实现代理,所以不需要目标类实现接口。
- AspectJ:早期的AOP实现的框架,SpringAOP借用了AspectJ中的AOP注解。
二、初步实现AOP编程
2.1实现AOP需要以下注解:
注解 | 说明 |
---|---|
@SpringJUnitConfig | 在JUnit测试类中使用Spring测试上下文配置 |
@Aspect | 将类标记为切面类,定义切面逻辑和增强方法的位置 |
@EnableAspectJAutoProxy | 开启AspectJ自动代理,用于启用Spring AOP的功能 |
2.2需要导入以下依赖
<!-- 切面实现 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>6.0.6</version>
</dependency>
<!-- spring核心 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!-- spring-test容器测试 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>6.0.6</version>
<scope>test</scope>
</dependency>
2.3 增强(通知)注解
注解 | 说明 |
---|---|
@Before | 在目标方法执行前执行的增强逻辑 |
@AfterReturning | 在目标方法成功返回后执行的增强逻辑 |
@AfterThrowing | 在目标方法抛出异常后执行的增强逻辑 |
@After | 在目标方法执行后执行的增强逻辑 |
@Around | 包裹目标方法,在目标方法执行前后都可以执行自定义的增强逻辑 |
实现增强(通知)的步骤
- 定义方法存储增强代码
- 使用注解配置,指定插入目标的位置
- 配置切点表达式(选中插入的方法,切点)
- 补全注解,加入到ioc容器,并且设置切面@Aspect
- 开启Aspect注解注释
案例代码:
//4.补全注解
@Component
@Aspect
//1.创建增强类与增强方法start(),after,Error
public class advice {
// 2.使用注解配置,配置插入位置@Before @After @AfterThrowing
// 3.配入切点表达式execution(* com.alphamilk.Impl.*.*(..))表明需要插入的方法为所有com.alhpamilk.Impl包下所有类的所有方法
@Before("execution(* com.alphamilk.Impl.*.*(..))")
public void start(){
System.out.println("方法起始处插入");
}
@After("execution(* com.alphamilk.Impl.*.*(..))")
public void after(){
System.out.println("方法结束后插入");
}
@AfterThrowing("execution(* com.alphamilk.Impl.*.*(..))")
public void Error(){
System.out.println("方法异常时候插入");
}
}
@ComponentScan(value = "com.alphamilk")
@Configuration
//6.注解类中开启注解注释
@EnableAspectJAutoProxy
public class JavaConfig {
}
三、获取切点详细信息
虽然已经初步实现了AOP的实现,但是还不够,在调用多个方法时候如果都是输入,调用方法前,调用方法后等等,这样并不能区分是调用了哪个方法,所以为了区分我们需要获取调用这个方法的相关信息,比如参数,方法名,返回值等等。
具体实现方式:
通过JoinPoint接口的下面几个方法获取
方法 | 说明 |
---|---|
getTarget() | 获取被代理的目标对象 |
getClass() | 获取被代理的目标对象的类 |
getSimpleName() | 获取被代理的目标对象的简单类名(不含包名) |
getArgs() | 获取方法参数数组 |
getSignature() | 获取方法签名,包括方法名、返回类型、参数类型等信息 |
getModifiers() | 获取方法修饰符 |
有三个案例分别是一般情况,需要返回值情况,还有异常情况
一般情况(前置通知、后置通知)
案例代码:
需要在方法调用中参数加入JoinPoint接口实例化对象用以创建对应的动态代理,并通过动态代理获取对象相关信息。
public class advice {
@Before("execution(* com.alphamilk.*.*(..))")
public void Before(JoinPoint joinPoint) {
// 获取类名
String simpleName = joinPoint.getTarget().getClass().getSimpleName();
// 获取方法修饰符
int modifiers = joinPoint.getSignature().getModifiers();
String Moidfier = Modifier.toString(modifiers);
// 获取方法名称
String name = joinPoint.getSignature().getName();
// 获取参数
Object[] args = joinPoint.getArgs();
//
System.out.println("调用的方法是" + name);
System.out.println("调用的类是" + simpleName);
for (Object arg : args
) {
System.out.println(arg);
}
System.out.println("调用方法前");
}
@After("execution(* com.alphamilk.*.*(..))")
public void After(JoinPoint joinPoint) {
System.out.println("调用方法后");
}
}
有返回值的情况(返回通知)
在一般情况的前提下,还需要多增加Object result参数用以接收返回值.和注解增加returning输入确切的返回对象的名称。
案例代码
public class advice {
@AfterReturning(value = "execution(* com.alphamilk.*.*(..))",returning = "result")
public void AfterReturning(JoinPoint joinPoint,Object result) {
System.out.println("调用拥有返回值的方法");
System.out.println("获取到的返回值为"+result);
}
}
异常情况(异常通知)
异常通知,获取异常信息,需要在一般情况的前提下,在注解中多声明一个注解throwing,在方法参数增加一个Throwable对象,并且throwing注解对应的值就是Throwable的对象名称。
案例代码:
@AfterThrowing(value = "execution(* com.alphamilk.*.*(..))",throwing = "throwable")
public void AfterThrowing(JoinPoint joinPoint,Throwable throwable) {
System.out.println("调用有异常的方法");
System.out.println("异常对象为"+throwable.getClass().getName());
}
@SpringJUnitConfig(value = JavaConfig.class)
public class newaopTest {
@Autowired
private Caculate caculate;
@Test
public void Test(){
caculate.div(2,0);
}
}
四、 切点表达式语法
1.切点表达式作用
AOP切点表达式(Pointcut Expression)是一种用于指定切点的语言,它可以通过定义匹配规则,来选择需要被切入的目标对象。
2.切点表达式语法
-
具体值:
- (String, int):第一个参数是字符串,第二个参数是整数。
- (int, String):第一个参数是整数,第二个参数是字符串。
- ():没有参数。
-
模糊值:
- (..):任意参数,有或者没有。
-
部分具体和模糊:
- (String..):第一个参数是字符串,后面可能有其他参数。
- (..String):最后一个参数是字符串,前面可能有其他参数。
- (String..int):字符串开头,最后一个参数是整数,中间可能有其他参数。
- (..int..):包含整数类型的参数,位置不限,可能有其他参数。
具体实战案例:
1.查询某包某类下,访问修饰符是公有,返回值是int的全部方法
execution public int 某包.某类.*(..)
2.查询某包下类中第一个参数是String的方法
execution * 某包.某类.*(String..)
3.查询全部包下,无参数的方法!
execution * *..*.*( )
4.查询com包下,以int参数类型结尾的方法
execution * com..*.*(..int)
5.查询指定包下,Service开头类的私有返回值int的无参数方法
execution private int 指定包.Service*.*()
五、重用(提取)切点表达式
如果在每一个方法前都加上一个固定的切点表达式,那么将会十分麻烦,所以下面介绍切点表达式的重用
1.在当前类中提取
特定注解@Pointcut
注解 | 描述 |
---|---|
@Pointcut | 声明切点表达式的方法,用于定义切点的匹配规则。 |
通过定义一个空方法,使用@Pointcut注解并带上特定的切点表达式
案例代码:
@Component
@Aspect
public class advice {
/*
定义空方法
空方法上加上注解@Pointcut并带上相应的切点表达式
在其他增强方法上调用方法
*/
@Pointcut("execution(* com.alphamilk.*.*(..))" )
public void blank(){}
@Before("blank()")
public void Before(JoinPoint joinPoint) {
System.out.println("调用方法前");
}
@After("blank()")
public void After(JoinPoint joinPoint) {
System.out.println("调用方法后");
}
@AfterReturning(value = "blank()",returning = "result")
public void AfterReturning(JoinPoint joinPoint,Object result) {
System.out.println("调用拥有返回值的方法");
}
@AfterThrowing(value = "blank()",throwing = "throwable")
public void AfterThrowing(JoinPoint joinPoint,Throwable throwable) {
System.out.println("调用有异常的方法");
}
}
2.创建一个存储切点类
(推荐)通过创建一个单独的存储切点的类,更加容易进行维护表达式
使用时候加上特定类的方法名即可
案例:
存储切点的类
@Component
public class MyPointcut {
@Pointcut("execution(* com.alphamilk.Impl.*.*(..))")
public void pointcut1(){}
}
对应引用类
@Component
@Aspect
public class advice {
@Before("com.alphamilk.Advice.MyPointcut.pointcut1()")
public void Before(JoinPoint joinPoint) {
System.out.println("调用方法前");
}
@After("com.alphamilk.Advice.MyPointcut.pointcut1()")
public void After(JoinPoint joinPoint) {
System.out.println("调用方法后");
}
@AfterReturning(value = "com.alphamilk.Advice.MyPointcut.pointcut1()",returning = "result")
public void AfterReturning(JoinPoint joinPoint,Object result) {
System.out.println("调用拥有返回值的方法");
}
@AfterThrowing(value = "com.alphamilk.Advice.MyPointcut.pointcut1()",throwing = "throwable")
public void AfterThrowing(JoinPoint joinPoint,Throwable throwable) {
System.out.println("调用有异常的方法");
}
}
本章总结
1.SpringAop底层技术
了解底层代理技术有jdk 与 cglib
2.初步实现AOP编程
掌握增强注解(@Before、@AfterReturning、@AfterThrowing、@After、@Around)
掌握@Aspect注解的使用
3.获取切点详细信息
掌握如何通过JoinPoint接口对象获取对应方法的类,方法名称,参数,方法修饰符
掌握三种不同情况下获取对应信息的情况(一般情况、返回通知、异常通知)
4.切点表达式语法
熟悉切点表达式的格式
(execution +权限修饰 +方法返回值类型+方法所在全类名+方法名称+参数列表)
5.重用(提取)切点表达式