本篇文章为你整理了Spring(三)(spring三大特性)的详细内容,包含有spring三级缓存如何解决循环依赖 spring三大特性 spring三大核心思想 spring三剑客 Spring(三),希望能帮助你了解 Spring(三)。
!-- spring-aspects begin --
!-- maven项目中,使用aop的AspectJ框架,只需要增加此依赖,自动添加依赖aspectjweaver(包含了aspectjrt)--
dependency
groupId org.springframework /groupId
artifactId spring-aspects /artifactId
version ${spring.version} /version
/dependency
!-- spring-aspects end --
/dependencies
2.2 切入点
通知需要在哪些方法上执行的表达式;(可以唯一匹配或模糊匹配);
2.2.1 唯一匹配
execution(public int com.kgc.spring.aspectj.ArithmeticCalculator.add(int ,int ))
execution(修饰符 返回值类型 方法全类名)
2.2.2 模糊匹配
execution(* com.kgc.spring.aspectj.*.*(..)
通用切入点表达式含义:
2.3.1 JoinPoint 对象
JoinPoint对象封装了SpringAop中切面方法的信息,在切面方法中添加JoinPoint参数,就可以获取到封装了该方法信息的JoinPoint对象。
常用api:
Signature getSignature();
获取封装了署名信息的对象,在该对象中可以获取到目标方法名,所属类的Class等信息
2.3.2 ProceedingJoinPoint对象
ProceedingJoinPoint对象是JoinPoint的子接口,该对象只用在@Around的切面方法中 添加了 两个方法.
//起别名,方便单元测试,根据别名,从容器中获取
public class ArithmeticCalculatorImpl implements ArithmeticCalculator {
@Override
public int add(int m, int n) {
return m + n;
@Override
public int sub(int m, int n) {
return m - n;
@Override
public int nul(int m, int n) {
return m*n;
@Override
public int div(int m, int n) {
System.out.println("====== 执行 div 方法 ======");
return m/n;
2.4.3 @Before 前置通知
在目标方法执行前,自动执行此方法(通过代理实现);
@Component //声明为一个普通的组件,放入spring的容器中,才可以生效
@Aspect //声明当前类是 一个切面
public class LogAspect {
//重用切入点表达式
@Pointcut( "execution(* com.kgc.spring.aspectj.*.*(..))")
public void joinPointcut(){}
//前置通知 @Before
@Before("joinPointcut()")
public void logBeforeMethod(JoinPoint joinPoint){
//获取通知作用的目标方法名
String methodName = joinPoint.getSignature().getName();
//获取通知作用的目标方法入参,返回的是参数值数组
Object[] methodParams = joinPoint.getArgs();
System.out.println("------ LogAspect "+methodName+" 方法,入参:"+ Arrays.toString(methodParams) +" ------");
2.5 配置文件
spring-aop.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="com.kgc.spring.aspectj" /context:component-scan
!-- 基于注解方式实现Aspect切面 --
!-- 作用:当spring的容器检测到此配置项,会自动将Aspect切面匹配的目标对象,放入容器,默认使用的是jdk的动态代理 --
aop:aspectj-autoproxy /aop:aspectj-autoproxy
//从容器中获取计算器的实例对象
ArithmeticCalculator arithmeticCalculator = context.getBean("arithmeticCalculator", ArithmeticCalculator.class);
System.out.println(arithmeticCalculator.getClass());
//调用切面作用的目标方法,执行操作,
int result = arithmeticCalculator.div(20, 10);
System.out.println("****** 通过单元测试,计算结果:"+result +" ******");
测试结果
class com.sun.proxy.$Proxy15
------ LogAspect div 方法,入参:[20, 10] ------
====== 执行 div 方法 ======
****** 通过单元测试,计算结果:2 ******
3、后置通知
3.1 @After
目标方法发执行之后,自动执行;
特点:
后置通知无法获取目标方法的返回值;
它的执行跟目标方法是否抛出异常无关,不影响此方法的执行;
@After("joinPointcut()")
public void logAfterMethod(JoinPoint joinPoint){
//获取通知作用的目标方法名
String methodName = joinPoint.getSignature().getName();
//获取通知作用的目标方法入参,返回的是参数值数组
Object[] methodParams = joinPoint.getArgs();
System.out.println("------ LogAspect "+methodName+" 方法执行结束 ------");
3.2 测试
3.2.1 无异常
@Test
public void testSpringAopAspectj(){
//从容器中获取计算器的实例对象
ArithmeticCalculator arithmeticCalculator = context.getBean("arithmeticCalculator", ArithmeticCalculator.class);
//调用切面作用的目标方法,执行操作,
int result = arithmeticCalculator.div(20, 10);
System.out.println("****** 通过单元测试,计算结果:"+result +" ******");
测试结果
====== 执行 div 方法 ======
------ LogAspect div 方法执行结束 ------
****** 通过单元测试,计算结果:2 ******
3.2.2 有异常
@Test
public void testSpringAopAspectj(){
//从容器中获取计算器的实例对象
ArithmeticCalculator arithmeticCalculator = context.getBean("arithmeticCalculator", ArithmeticCalculator.class);
//调用切面作用的目标方法,执行操作,
int result = arithmeticCalculator.div(20, 0);
System.out.println("****** 通过单元测试,计算结果:"+result +" ******");
测试结果
====== 执行 div 方法 ======
------ LogAspect div 方法执行结束 ------ //有异常也会执行后置通知
java.lang.ArithmeticException: / by zero
4、返回通知
4.1 @AfterReturning
目标方法返回结果后自动执行,可以获取目标方法的返回值;
但是要求@AfterReturning必须增加属性returning,指定一个参数名;
且此参数名必须跟通知方法的一个形参名一致,用于接收返回值;
@AfterReturning(value = "joinPointcut()",returning = "result")
public void afterReturningMethod(JoinPoint joinPoint,Object result){
//获取通知作用的目标方法名
String methodName = joinPoint.getSignature().getName();
System.out.println("------ LogAspect "+methodName+" 方法,执行结果:"+ result +" ------");
4.2 测试
测试结果
====== 执行 div 方法 ======
------ LogAspect div 方法,返回结果:2 ------
****** 通过单元测试,计算结果:2 ******
5、异常抛出通知
5.1 @AfterThrowing
异常抛出通知 @AfterThrowing ,在目标方法抛出异常后,可以获取目标方法发生异常后抛出的异常信息;
但是要求 @AfterThrowing 必须增加属性 throwing,指定一个参数名;
且此参数名必须跟通知方法的一个形参名一致,用于接收异常;
@AfterThrowing(value = "joinPointcut()",throwing = "ex")
public void logAfterThrowingMethod(JoinPoint joinPoint,Exception ex){
//获取通知作用的目标方法名
String methodName = joinPoint.getSignature().getName();
System.out.println("------ LogAspect "+methodName+" 方法,执行异常信息:"+ ex.getMessage() +" ------");
5.2 测试
@Test
public void testSpringAopAspectj(){
//从容器中获取计算器的实例对象
ArithmeticCalculator arithmeticCalculator = context.getBean("arithmeticCalculator", ArithmeticCalculator.class);
System.out.println(arithmeticCalculator.getClass());
//调用切面作用的目标方法,执行操作,
int result = arithmeticCalculator.div(20, 0);
System.out.println("****** 通过单元测试,计算结果:"+result +" ******");
测试结果
====== 执行 div 方法 ======
------ LogAspect div 方法,执行异常信息:/ by zero ------
java.lang.ArithmeticException: / by zero
6、环绕通知
6.1 @Around
环绕通知 @Around,可以看作是上面四种通知的结合体,一般不建议跟单个的通知共用(防止冲突失效);
作用:可以让开发人员在环绕通知的处理方法中根据不同也业务逻辑,决定是否发起对目标方法的调用;
@Around(value = "joinPointcut()")
public Object logAroundMethod(ProceedingJoinPoint joinPoint){
//获取通知作用的目标方法名
String methodName = joinPoint.getSignature().getName();
//定义获取目标方法的返回值变量
Object result = null;
try{
//实现前置通知功能
System.out.println("------ LogAspect "+methodName+" 方法 Around通知,入参:"+ Arrays.toString(joinPoint.getArgs()) +" ------");
//手动调用原目标方法(业务中决定,是否对核心方法方法发起调用)
result = joinPoint.proceed();
}catch (Throwable tx){
//实现异常抛出通知功能
System.out.println("------ LogAspect "+methodName+" 方法 Around通知,执行异常信息:"+ tx.getMessage() +" ------");
}finally {
//实现后置通知功能
System.out.println("------ LogAspect "+methodName+" 方法 Around通知,执行结束 ------");
//实现返回通知功能
System.out.println("------ LogAspect "+methodName+" 方法 Around通知,执行结果:"+ result +" ------");
return result;
6.2 测试
6.2.1 测试结果,无异常
//调用切面作用的目标方法,执行操作,
int result = arithmeticCalculator.div(20, 10);
class com.sun.proxy.$Proxy13
------ LogAspect div 方法 Around通知,入参:[20, 10] ------
====== 执行 div 方法 ======
------ LogAspect div 方法 Around通知,执行结束 ------
------ LogAspect div 方法 Around通知,返回结果:2 ------
****** 通过单元测试,计算结果:2 ******
6.2.2 测试结果,有异常
//调用切面作用的目标方法,执行操作,
int result = arithmeticCalculator.div(20, 0);
class com.sun.proxy.$Proxy13
------ LogAspect div 方法 Around通知,入参:[20, 0] ------
====== 执行 div 方法 ======
------ LogAspect div 方法 Around通知,执行异常信息:/ by zero ------
------ LogAspect div 方法 Around通知,执行结束 ------
------ LogAspect div 方法 Around通知,返回结果:null ------
6.2.3 测试结果 不调用 原方法
//调用切面作用的目标方法,执行操作,
int result = arithmeticCalculator.div(20, 0);
//(业务中决定,是否对核心方法发起调用)
//不调用核心方法
//result = joinPoint.proceed();
------ LogAspect div 方法 Around通知,入参:[20, 10] ------
------ LogAspect div 方法 Around通知,执行结束 ------
------ LogAspect div 方法 Around通知,返回结果:null ------
7、切入点优先级
当有多个前置通知时,我们想自定义前置通知顺序:使用@Order(int)
指定切面优先级,一般都是int型整数,值越小,优先级越高**(默认值 2^31 - 1 最低优先级);
7.1 多个前置通知
logBeforeMethod
@Before("joinPointcut()")
public void logBeforeMethod(JoinPoint joinPoint){
//获取通知作用的目标方法名
String methodName = joinPoint.getSignature().getName();
//获取通知作用的目标方法入参,返回的是参数值数组
Object[] methodParams = joinPoint.getArgs();
System.out.println("------ LogAspectBeforeMethod "+methodName+" 方法,入参:"+ Arrays.toString(methodParams) +" ------");
verifyBeforeMethod
@Component
@Aspect
public class VerifyParamAspect {
@Before("com.kgc.spring.aspectj.LogAspect.joinPointcut()")
public void verifyBeforeMethod( JoinPoint joinPoint){
//获取通知作用的目标方法名
String methodName = joinPoint.getSignature().getName();
//获取通知作用的目标方法入参,返回的是参数值数组
Object[] methodParams = joinPoint.getArgs();
System.out.println("------ verifyBeforeMethod "+methodName+" 方法,入参:"+ Arrays.toString(methodParams) +" ------");
7.2 测试(默认)
@Test
public void testVerifyParamAspect(){
//从容器中获取计算器的实例对象
ArithmeticCalculator arithmeticCalculator = context.getBean("arithmeticCalculator", ArithmeticCalculator.class);
System.out.println(arithmeticCalculator.getClass());
//调用切面作用的目标方法,执行操作,
int result = arithmeticCalculator.div(20, 10);
System.out.println("****** 通过单元测试,计算结果:"+result +" ******");
测试结果
------ LogAspectBeforeMethod div 方法,入参:[20, 10] ------ //LogAspectBeforeMethod 先执行
------ verifyBeforeMethod div 方法,入参:[20, 10] ------
====== 执行 div 方法 ======
****** 通过单元测试,计算结果:2 ******
7.3 测试(自定义优先级)
@Component
@Aspect
@Order(1) //指定切面优先级,一般都是int型整数,值越小,优先级越高(默认值 2^31 - 1)
public class VerifyParamAspect {
@Before("com.kgc.spring.aspectj.LogAspect.joinPointcut()")
public void verifyBeforeMethod( JoinPoint joinPoint){
//获取通知作用的目标方法名
String methodName = joinPoint.getSignature().getName();
//获取通知作用的目标方法入参,返回的是参数值数组
Object[] methodParams = joinPoint.getArgs();
System.out.println("------ verifyBeforeMethod "+methodName+" 方法,入参:"+ Arrays.toString(methodParams) +" ------");
测试结果
------ verifyBeforeMethod div 方法,入参:[20, 10] ------ //优先级高的切面中的verifyBeforeMethod,先执行
------ LogAspectBeforeMethod div 方法,入参:[20, 10] ------
====== 执行 div 方法 ======
****** 通过单元测试,计算结果:2 ******
以上就是Spring(三)(spring三大特性)的详细内容,想要了解更多 Spring(三)的内容,请持续关注盛行IT软件开发工作室。
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。