有关Spring的所有文章都收录于我的专栏:👉Spring👈
目录
一、前言
二、演示
三、切面类中声明通知方法
四、使用
相关文章
【Spring(一)】如何获取对象(Bean) | 【Spring(一)】如何获取对象(Bean) |
【Spring(二)】java对象属性的配置(Bean的配置) | 【Spring(二)】java对象属性的配置(Bean的配置) |
【Spring(三)】熟练掌握Spring的使用 | 【Spring(三)】熟练掌握Spring的使用 |
【Spring(四)】Spring基于注解的配置方式 | 【Spring(四)】Spring基于注解的配置方式 |
【Spring(五)】引入篇:AOP的底层原理之动态代理 | 【Spring(五)】引入篇:AOP的底层原理之动态代理 |
通过我们上一节对AOP的引入和底层原理的讲解,我相信大家对AOP有了很深的理解。即使我们还没讲AOP的使用。大家肯定不希望做一个只会API的程序员。只要对原理有了解,一个技术也就能很快的上手。
我们在正式开始之前,先演示一下基本使用。
public interface Cal {public double cal1(int n);public double cal2(int n);
}
package com.jl.spring.aop.exer;import org.springframework.stereotype.Component;/*** @author long* @date 2022/9/11*/
@Component
public class MyCal implements Cal{@Overridepublic double cal1(int n) {int result=0;for (int i = 1; i <= n; i++) {result += i;}System.out.println("1+2+...=" + result);return result;}@Overridepublic double cal2(int n) {int result = 0;if (n == 0){System.out.println("1+2+...=" + result);return result;}if (n == 1){System.out.println("1+2+...=" + 1);return 1;}result = 1;for (int i = 1; i <= n; i++) {result *= i;}System.out.println("1+2+...=" + result);return result;}
}
package com.jl.spring.aop.exer;import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;/*** @author long* @date 2022/9/11*/
@Aspect
@Component
public class MyCalAspect {@Before(value = "execution(public double com.jl.spring.aop.exer.MyCal.*(..))")public void showBegin(){System.out.println("开始执行计算");}@AfterReturning(value = "execution(public double com.jl.spring.aop.exer.MyCal.*(..))")public void showEndn(){System.out.println("结束执行计算");}
}
测试类
@Test
public void MyCalAnnotation(){ApplicationContext ioc = new ClassPathXmlApplicationContext("mycal.xml");Cal bean = ioc.getBean(Cal.class);bean.cal1(2);
}
运行结果:
我们主要介绍基于注解的AOP的使用
这五个注解,我们在【使用】中会全部进行讲解。
和前面几节中使用注解一样,我们想要让Spring容器将我们的类扫描实例化到容器中,我们就得在xml配置文件中告诉Spring容器要扫描的包,同时还要开始基于注解的AOP功能。
exexution([权限修饰符][返回值类型][简单类名/全类名][方法名][参数类名])
举例:
// public修饰符 double返回值类型 com.ji.spring.aop.exer.MyCal全类名 *所有的方法 (..)匹配任意数量、任意类型的参数
execution(public double com.jl.spring.aop.exer.MyCal.*(..))
- 切入表达式也可以指向类的方法, 这时切入表达式会对该类/对象生效。
- 切入表达式也可以指向接口的方法, 这时切入表达式会对实现了接口的类/对象生效。
- 切入表达式也可以对没有实现接口的类,进行切入。
JoinPoint在调用前置通知获取到调用方法的签名, 和其它相关信息。
package com.jl.spring.aop.exer;import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;/*** @author long* @date 2022/9/11*/
@Aspect
@Component
public class MyCalAspect {@Before(value = "execution(public double com.jl.spring.aop.exer.MyCal.*(..))")public void showBegin(JoinPoint joinPoint){Signature signature = joinPoint.getSignature();System.out.println(signature.getName() +"开始执行计算");}// 其他通知也一样
}
运行结果:
joinPoint常用的方法
public void beforeMethod(JoinPoint joinPoint){joinPoint.getSignature().getName(); // 获取目标方法名joinPoint.getSignature().getDeclaringType().getSimpleName(); // 获取目标方法所属类的简单类名joinPoint.getSignature().getDeclaringTypeName(); // 获取目标方法所属类的类名joinPoint.getSignature().getModifiers(); // 获取目标方法声明类型(public、private、protected)Object[] args = joinPoint.getArgs(); // 获取传入目标方法的参数,返回一个数组joinPoint.getTarget(); // 获取被代理的对象joinPoint.getThis(); // 获取代理对象自己
}
package com.jl.spring.aop.exer;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;/*** @author long* @date 2022/9/11*/
@Aspect
@Component
public class MyCalAspect {@AfterReturning(value = "execution(public double com.jl.spring.aop.exer.MyCal.*(..))",returning = "res")public void showEndn(Object res){System.out.println("结束执行计算"+"结果是=" + res);}}
- 在@AfterReturning直接的参数中使用returning来接收返回的结果。通过方法的参数来传递,对此结果可以进行相应的后续处理。
- returning底层是通过调用MyCal类的cal1()来获取到结果的。
结果截图:
我们这里制造一个异常,看一下我们的异常通知。
package com.jl.spring.aop.exer;
import org.springframework.stereotype.Component;/*** @author long* @date 2022/9/11*/
@Component
public class MyCal implements Cal{@Overridepublic double cal1(int n) {int result=0;for (int i = 1; i <= n; i++) {result = result / 0;}System.out.println("1+2+...=" + result);return result;}
}
package com.jl.spring.aop.exer;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;/*** @author long* @date 2022/9/11*/
@Aspect
@Component
public class MyCalAspect {@AfterThrowing(value = "execution(public double com.jl.spring.aop.exer.MyCal.*(..))",throwing = "throwable")public void showExceptionLog(Throwable throwable) {System.out.println("异常通知"+throwable);}
}
结果截图
环绕通知可以完成其他四个通知可以做的事情。
package com.jl.spring.aop.exer;import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;import java.util.Arrays;
import java.util.List;
@Aspect //表示是一个切面类[底层切面编程的支撑(动态代理+反射+动态绑定...)]
@Component //会注入SmartAnimalAspect2到容器
public class MyCalAspect2 {//演示环绕通知的使用-了解//1. @Around: 表示这是一个环绕通知[完成其它四个通知的功能]//2. value = "execution(public double com.jl.spring.aop.exer.MyCal.*(..))" 切入点表达式//3. doAround 表示要切入的方法 - 调用结构 try-catch-finally@Around(value = "execution(public double com.jl.spring.aop.exer.MyCal.*(..))")public Object doAround(ProceedingJoinPoint joinPoint) {Object result = null;String methodName = joinPoint.getSignature().getName();try {//1.相当于前置通知完成的事情Object[] args = joinPoint.getArgs();List
结果截图:
package com.jl.spring.aop.exer;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;/*** @author long* @date 2022/9/11*/
@Aspect
@Component
public class MyCalAspect {@Pointcut(value = "execution(public double com.jl.spring.aop.exer.MyCal.*(..))")public void myCut(){System.out.println("123");}@Before(value = "myCut()")public void showBegin(){System.out.println("开始执行计算");}@AfterReturning(value = "myCut()")public void showEnd(){System.out.println("结束执行计算");}@AfterThrowing(value = "myCut()")public void showExceptionLog() {System.out.println("异常通知");}@After(value = "myCut()")public void showFinallyEndLog() {System.out.println("最终通知");}}
结果截图:
通过切入点表达式的重用可以统一管理切入点。
我们可以通过Order(value=值)
来控制切面的优先级,值越小,优先级越高。
package com.jl.spring.aop.exer;import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;/*** @author long* @date 2022/9/11*/
@Aspect
@Order(value = 2)
@Component
public class MyCalAspect {@Before(value = "execution(public double com.jl.spring.aop.exer.MyCal.*(..))")public void showBegin(){System.out.println("开始执行计算");}@AfterReturning(value = "execution(public double com.jl.spring.aop.exer.MyCal.*(..))")public void showEndn(){System.out.println("结束执行计算");}@AfterThrowing(value = "execution(public double com.jl.spring.aop.exer.MyCal.*(..))")public void showExceptionLog() {System.out.println("异常通知");}@After(value = "execution(public double com.jl.spring.aop.exer.MyCal.*(..))")public void showFinallyEndLog() {System.out.println("最终通知");}
}
当我们有多个切面类的时候,他的各个切面通知的顺序相当于javaWeb部分过滤器Filter过滤链
的那个顺序。
注意:在配置之前,需要将之间用注解配的切面类注销
package com.jl.spring.aop.exer;import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;/*** @author long* @date 2022/9/11*/
public class MyCalAspect {public void showBegin(){System.out.println("开始执行计算");}public void showEndn(){System.out.println("结束执行计算");}
}
测试类
package com.jl.spring.aop.exer;import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;/*** @author long* @date 2022/9/11*/
public class MyCalTest {@Testpublic void MyCalAnnotation(){ApplicationContext ioc = new ClassPathXmlApplicationContext("mycal.xml");Cal bean = ioc.getBean(Cal.class);bean.cal1(2);}
}
如果文章中有描述不准确或者错误的地方,还望指正。您可以留言📫或者私信我。🙏
最后希望大家多多 关注+点赞+收藏^_^,你们的鼓励是我不断前进的动力!!!
感谢感谢~~~🙏🙏🙏