spring之面向切面:AOP(2)

本文涉及的产品
容器镜像服务 ACR,镜像仓库100个 不限时长
性能测试 PTS,5000VUM额度
应用实时监控服务-用户体验监控,每月100OCU免费额度
简介: 【1月更文挑战第15天】一、基于注解的AOP1、技术说明2、准备工作3、创建切面类并配置4、各种通知5、切入点表达式语法6、重用切入点表达式7、获取通知的相关信息8、环绕通知9、切面的优先级二、基于XML的AOP1、准备工作2、实现

文章目录

前言

一、基于注解的AOP

1、技术说明

2、准备工作

3、创建切面类并配置

4、各种通知

5、切入点表达式语法

6、重用切入点表达式

7、获取通知的相关信息

8、环绕通知

9、切面的优先级

二、基于XML的AOP

1、准备工作

2、实现

总结


前言

一、基于注解的AOP

1、技术说明

2、准备工作

3、创建切面类并配置

4、各种通知

5、切入点表达式语法

6、重用切入点表达式

7、获取通知的相关信息

8、环绕通知

9、切面的优先级

二、基于XML的AOP

1、准备工作

2、实现


一、基于注解的AOP

1、技术说明

  • 动态代理分为JDK动态代理和cglib动态代理
  • 当目标类有接口的情况使用JDK动态代理和cglib动态代理,没有接口时只能使用cglib动态代理
  • JDK动态代理动态生成的代理类会在com.sun.proxy包下,类名为$proxy1,和目标类实现相同的接口
  • cglib动态代理动态生成的代理类会和目标在在相同的包下,会继承目标类
  • 动态代理(InvocationHandler):JDK原生的实现方式,需要被代理的目标类必须实现接口。因为这个技术要求代理对象和目标对象实现同样的接口(兄弟两个拜把子模式)。
  • cglib:通过继承被代理的目标类(认干爹模式)实现代理,所以不需要目标类实现接口。
  • AspectJ:是AOP思想的一种实现。本质上是静态代理,将代理逻辑“织入”被代理的目标类编译得到的字节码文件,所以最终效果是动态的。weaver就是织入器。Spring只是借用了AspectJ中的注解。

2、准备工作

①添加依赖

在IOC所需依赖基础上再加入下面依赖即可:

<dependencies><!--spring context依赖--><!--当你引入Spring Context依赖之后,表示将Spring的基础依赖引入了--><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>6.0.2</version></dependency><!--spring aop依赖--><dependency><groupId>org.springframework</groupId><artifactId>spring-aop</artifactId><version>6.0.2</version></dependency><!--spring aspects依赖--><dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId><version>6.0.2</version></dependency><!--junit5测试--><dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter-api</artifactId><version>5.3.1</version></dependency><!--log4j2的依赖--><dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-core</artifactId><version>2.19.0</version></dependency><dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-slf4j2-impl</artifactId><version>2.19.0</version></dependency></dependencies>

②准备被代理的目标资源

接口:

publicinterfaceCalculator {
intadd(inti, intj);
intsub(inti, intj);
intmul(inti, intj);
intdiv(inti, intj);
}

实现类:

@ComponentpublicclassCalculatorImplimplementsCalculator {
@Overridepublicintadd(inti, intj) {
intresult=i+j;
System.out.println("方法内部 result = "+result);
returnresult;
    }
@Overridepublicintsub(inti, intj) {
intresult=i-j;
System.out.println("方法内部 result = "+result);
returnresult;
    }
@Overridepublicintmul(inti, intj) {
intresult=i*j;
System.out.println("方法内部 result = "+result);
returnresult;
    }
@Overridepublicintdiv(inti, intj) {
intresult=i/j;
System.out.println("方法内部 result = "+result);
returnresult;
    }
}

3、创建切面类并配置

// @Aspect表示这个类是一个切面类@Aspect// @Component注解保证这个切面类能够放入IOC容器@ComponentpublicclassLogAspect {
@Before("execution(public int com.atguigu.aop.annotation.CalculatorImpl.*(..))")
publicvoidbeforeMethod(JoinPointjoinPoint){
StringmethodName=joinPoint.getSignature().getName();
Stringargs=Arrays.toString(joinPoint.getArgs());
System.out.println("Logger-->前置通知,方法名:"+methodName+",参数:"+args);
    }
@After("execution(* com.atguigu.aop.annotation.CalculatorImpl.*(..))")
publicvoidafterMethod(JoinPointjoinPoint){
StringmethodName=joinPoint.getSignature().getName();
System.out.println("Logger-->后置通知,方法名:"+methodName);
    }
@AfterReturning(value="execution(* com.atguigu.aop.annotation.CalculatorImpl.*(..))", returning="result")
publicvoidafterReturningMethod(JoinPointjoinPoint, Objectresult){
StringmethodName=joinPoint.getSignature().getName();
System.out.println("Logger-->返回通知,方法名:"+methodName+",结果:"+result);
    }
@AfterThrowing(value="execution(* com.atguigu.aop.annotation.CalculatorImpl.*(..))", throwing="ex")
publicvoidafterThrowingMethod(JoinPointjoinPoint, Throwableex){
StringmethodName=joinPoint.getSignature().getName();
System.out.println("Logger-->异常通知,方法名:"+methodName+",异常:"+ex);
    }
@Around("execution(* com.atguigu.aop.annotation.CalculatorImpl.*(..))")
publicObjectaroundMethod(ProceedingJoinPointjoinPoint){
StringmethodName=joinPoint.getSignature().getName();
Stringargs=Arrays.toString(joinPoint.getArgs());
Objectresult=null;
try {
System.out.println("环绕通知-->目标对象方法执行之前");
//目标对象(连接点)方法的执行result=joinPoint.proceed();
System.out.println("环绕通知-->目标对象方法返回值之后");
        } catch (Throwablethrowable) {
throwable.printStackTrace();
System.out.println("环绕通知-->目标对象方法出现异常时");
        } finally {
System.out.println("环绕通知-->目标对象方法执行完毕");
        }
returnresult;
    }
}

在Spring的配置文件中配置:

<?xmlversion="1.0" encoding="UTF-8"?><beansxmlns="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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsd"><!--基于注解的AOP的实现:1、将目标对象和切面交给IOC容器管理(注解+扫描)2、开启AspectJ的自动代理,为目标对象自动生成代理3、将切面类通过注解@Aspect标识--><context:component-scanbase-package="com.atguigu.aop.annotation"></context:component-scan><aop:aspectj-autoproxy/></beans>

执行测试:

publicclassCalculatorTest {
privateLoggerlogger=LoggerFactory.getLogger(CalculatorTest.class);
@TestpublicvoidtestAdd(){
ApplicationContextac=newClassPathXmlApplicationContext("beans.xml");
Calculatorcalculator=ac.getBean( Calculator.class);
intadd=calculator.add(1, 1);
logger.info("执行成功:"+add);
    }
}

4、各种通知

  • 前置通知:使用@Before注解标识,在被代理的目标方法执行
  • 返回通知:使用@AfterReturning注解标识,在被代理的目标方法成功结束后执行(寿终正寝
  • 异常通知:使用@AfterThrowing注解标识,在被代理的目标方法异常结束后执行(死于非命
  • 后置通知:使用@After注解标识,在被代理的目标方法最终结束后执行(盖棺定论
  • 环绕通知:使用@Around注解标识,使用try…catch…finally结构围绕整个被代理的目标方法,包括上面四种通知对应的所有位置

各种通知的执行顺序:

  • Spring版本5.3.x以前:
  • 前置通知
  • 目标操作
  • 后置通知
  • 返回通知或异常通知
  • Spring版本5.3.x以后:
  • 前置通知
  • 目标操作
  • 返回通知或异常通知
  • 后置通知

5、切入点表达式语法

①作用

②语法细节

  • 用*号代替“权限修饰符”和“返回值”部分表示“权限修饰符”和“返回值”不限
  • 在包名的部分,一个“*”号只能代表包的层次结构中的一层,表示这一层是任意的。
  • 例如:*.Hello匹配com.Hello,不匹配com.atguigu.Hello
  • 在包名的部分,使用“*…”表示包名任意、包的层次深度任意
  • 在类名的部分,类名部分整体用*号代替,表示类名任意
  • 在类名的部分,可以使用*号代替类名的一部分
  • 例如:*Service匹配所有名称以Service结尾的类或接口
  • 在方法名部分,可以使用*号表示方法名任意
  • 在方法名部分,可以使用*号代替方法名的一部分
  • 例如:*Operation匹配所有方法名以Operation结尾的方法
  • 在方法参数列表部分,使用(…)表示参数列表任意
  • 在方法参数列表部分,使用(int,…)表示参数列表以一个int类型的参数开头
  • 在方法参数列表部分,基本数据类型和对应的包装类型是不一样的
  • 切入点表达式中使用 int 和实际方法中 Integer 是不匹配的
  • 在方法返回值部分,如果想要明确指定一个返回值类型,那么必须同时写明权限修饰符
  • 例如:execution(public int Service.(…, int)) 正确
    例如:execution(
    int *…Service.(…, int)) 错误

6、重用切入点表达式

①声明

@Pointcut("execution(* com.atguigu.aop.annotation.*.*(..))")
publicvoidpointCut(){}

②在同一个切面中使用

@Before("pointCut()")
publicvoidbeforeMethod(JoinPointjoinPoint){
StringmethodName=joinPoint.getSignature().getName();
Stringargs=Arrays.toString(joinPoint.getArgs());
System.out.println("Logger-->前置通知,方法名:"+methodName+",参数:"+args);
}

③在不同切面中使用

@Before("com.atguigu.aop.CommonPointCut.pointCut()")
publicvoidbeforeMethod(JoinPointjoinPoint){
StringmethodName=joinPoint.getSignature().getName();
Stringargs=Arrays.toString(joinPoint.getArgs());
System.out.println("Logger-->前置通知,方法名:"+methodName+",参数:"+args);
}

7、获取通知的相关信息

①获取连接点信息

获取连接点信息可以在通知方法的参数位置设置JoinPoint类型的形参

@Before("execution(public int com.atguigu.aop.annotation.CalculatorImpl.*(..))")
publicvoidbeforeMethod(JoinPointjoinPoint){
//获取连接点的签名信息StringmethodName=joinPoint.getSignature().getName();
//获取目标方法到的实参信息Stringargs=Arrays.toString(joinPoint.getArgs());
System.out.println("Logger-->前置通知,方法名:"+methodName+",参数:"+args);
}

②获取目标方法的返回值

@AfterReturning中的属性returning,用来将通知方法的某个形参,接收目标方法的返回值

@AfterReturning(value="execution(* com.atguigu.aop.annotation.CalculatorImpl.*(..))", returning="result")
publicvoidafterReturningMethod(JoinPointjoinPoint, Objectresult){
StringmethodName=joinPoint.getSignature().getName();
System.out.println("Logger-->返回通知,方法名:"+methodName+",结果:"+result);
}

③获取目标方法的异常

@AfterThrowing中的属性throwing,用来将通知方法的某个形参,接收目标方法的异常

@AfterThrowing(value="execution(* com.atguigu.aop.annotation.CalculatorImpl.*(..))", throwing="ex")
publicvoidafterThrowingMethod(JoinPointjoinPoint, Throwableex){
StringmethodName=joinPoint.getSignature().getName();
System.out.println("Logger-->异常通知,方法名:"+methodName+",异常:"+ex);
}

8、环绕通知

@Around("execution(* com.atguigu.aop.annotation.CalculatorImpl.*(..))")
publicObjectaroundMethod(ProceedingJoinPointjoinPoint){
StringmethodName=joinPoint.getSignature().getName();
Stringargs=Arrays.toString(joinPoint.getArgs());
Objectresult=null;
try {
System.out.println("环绕通知-->目标对象方法执行之前");
//目标方法的执行,目标方法的返回值一定要返回给外界调用者result=joinPoint.proceed();
System.out.println("环绕通知-->目标对象方法返回值之后");
    } catch (Throwablethrowable) {
throwable.printStackTrace();
System.out.println("环绕通知-->目标对象方法出现异常时");
    } finally {
System.out.println("环绕通知-->目标对象方法执行完毕");
    }
returnresult;
}

9、切面的优先级

相同目标方法上同时存在多个切面时,切面的优先级控制切面的内外嵌套顺序。

  • 优先级高的切面:外面
  • 优先级低的切面:里面

使用@Order注解可以控制切面的优先级:

  • @Order(较小的数):优先级高
  • @Order(较大的数):优先级低

二、基于XML的AOP

1、准备工作

参考基于注解的AOP环境

2、实现

<context:component-scanbase-package="com.atguigu.aop.xml"></context:component-scan><aop:config><!--配置切面类--><aop:aspectref="loggerAspect"><aop:pointcutid="pointCut"expression="execution(* com.atguigu.aop.xml.CalculatorImpl.*(..))"/><aop:beforemethod="beforeMethod"pointcut-ref="pointCut"></aop:before><aop:aftermethod="afterMethod"pointcut-ref="pointCut"></aop:after><aop:after-returningmethod="afterReturningMethod"returning="result"pointcut-ref="pointCut"></aop:after-returning><aop:after-throwingmethod="afterThrowingMethod"throwing="ex"pointcut-ref="pointCut"></aop:after-throwing><aop:aroundmethod="aroundMethod"pointcut-ref="pointCut"></aop:around></aop:aspect></aop:config>

总结

以上就是spring之面向切面:AOP(2)的相关知识点,希望对你有所帮助。

积跬步以至千里,积怠惰以至深渊。时代在这跟着你一起努力哦!

相关文章
|
1月前
|
XML Java 数据安全/隐私保护
Spring Aop该如何使用
本文介绍了AOP(面向切面编程)的基本概念和术语,并通过具体业务场景演示了如何在Spring框架中使用Spring AOP。文章详细解释了切面、连接点、通知、切点等关键术语,并提供了完整的示例代码,帮助读者轻松理解和应用Spring AOP。
Spring Aop该如何使用
|
20天前
|
监控 安全 Java
什么是AOP?如何与Spring Boot一起使用?
什么是AOP?如何与Spring Boot一起使用?
46 5
|
24天前
|
Java 开发者 Spring
深入解析:Spring AOP的底层实现机制
在现代软件开发中,Spring框架的AOP(面向切面编程)功能因其能够有效分离横切关注点(如日志记录、事务管理等)而备受青睐。本文将深入探讨Spring AOP的底层原理,揭示其如何通过动态代理技术实现方法的增强。
52 8
|
24天前
|
Java 开发者 Spring
Spring AOP 底层原理技术分享
Spring AOP(面向切面编程)是Spring框架中一个强大的功能,它允许开发者在不修改业务逻辑代码的情况下,增加额外的功能,如日志记录、事务管理等。本文将深入探讨Spring AOP的底层原理,包括其核心概念、实现方式以及如何与Spring框架协同工作。
|
24天前
|
XML 监控 安全
深入调查研究Spring AOP
【11月更文挑战第15天】
36 5
|
24天前
|
Java 开发者 Spring
Spring AOP深度解析:探秘动态代理与增强逻辑
Spring框架中的AOP(Aspect-Oriented Programming,面向切面编程)功能为开发者提供了一种强大的工具,用以将横切关注点(如日志、事务管理等)与业务逻辑分离。本文将深入探讨Spring AOP的底层原理,包括动态代理机制和增强逻辑的实现。
35 4
|
1月前
|
安全 Java 编译器
什么是AOP面向切面编程?怎么简单理解?
本文介绍了面向切面编程(AOP)的基本概念和原理,解释了如何通过分离横切关注点(如日志、事务管理等)来增强代码的模块化和可维护性。AOP的核心概念包括切面、连接点、切入点、通知和织入。文章还提供了一个使用Spring AOP的简单示例,展示了如何定义和应用切面。
123 1
什么是AOP面向切面编程?怎么简单理解?
|
2月前
|
存储 缓存 Java
Spring高手之路23——AOP触发机制与代理逻辑的执行
本篇文章深入解析了Spring AOP代理的触发机制和执行流程,从源码角度详细讲解了Bean如何被AOP代理,包括代理对象的创建、配置与执行逻辑,帮助读者全面掌握Spring AOP的核心技术。
48 3
Spring高手之路23——AOP触发机制与代理逻辑的执行
|
1月前
|
Java Spring
[Spring]aop的配置与使用
本文介绍了AOP(面向切面编程)的基本概念和核心思想。AOP是Spring框架的核心功能之一,通过动态代理在不修改原代码的情况下注入新功能。文章详细解释了连接点、切入点、通知、切面等关键概念,并列举了前置通知、后置通知、最终通知、异常通知和环绕通知五种通知类型。
37 1
|
3月前
|
设计模式 Java 测试技术
spring复习04,静态代理动态代理,AOP
这篇文章讲解了Java代理模式的相关知识,包括静态代理和动态代理(JDK动态代理和CGLIB),以及AOP(面向切面编程)的概念和在Spring框架中的应用。文章还提供了详细的示例代码,演示了如何使用Spring AOP进行方法增强和代理对象的创建。
spring复习04,静态代理动态代理,AOP