Spring AOP 注解概述

1、Spring 的 AOP 功能除了在配置文件中配置一大堆的配置,比如切入点、表达式、通知等等以外,使用注解的方式更为方便快捷,特别是 Spring boot 出现以后,基本不再使用原先的 beans.xml 等配置文件了,而都推荐注解编程。

@Aspect

切面声明,标注在类、接口(包括注解类型)或枚举上。

@Pointcut

切入点声明,即切入到哪些目标类的目标方法。

value 属性指定切入点表达式,默认为 “”,用于被通知注解引用,这样通知注解只需要关联此切入点声明即可,无需再重复写切入点表达式

@Before

前置通知, 在目标方法(切入点)执行之前执行。

value 属性绑定通知的切入点表达式,可以关联切入点声明,也可以直接设置切入点表达式

注意:如果在此回调方法中抛出异常,则目标方法不会再执行,会继续执行后置通知 -> 异常通知。

@After

后置通知, 在目标方法(切入点)执行之后执行

@AfterRunning

返回通知, 在目标方法(切入点)返回结果之后执行,在 @After 的后面执行

pointcut 属性绑定通知的切入点表达式,优先级高于 value,默认为 “”

@AfterThrowing

异常通知, 在方法抛出异常之后执行, 意味着跳过返回通知

pointcut 属性绑定通知的切入点表达式,优先级高于 value,默认为 “”

注意:如果目标方法自己 try-catch 了异常,而没有继续往外抛,则不会进入此回调函数

@Around

环绕通知:目标方法执行前后分别执行一些代码,发生异常的时候执行另外一些代码

2、上面这些 AOP 注解都是位于如下所示的 aspectjweaver依赖中:

12f0db221afbb9e2afd7cc36182d318b.png

3、对于习惯了 Spring 全家桶编程的人来说,并不是需要直接引入 aspectjweaver 依赖,因为 spring-boot-starter-aop 组件默认已经引用了 aspectjweaver 来实现  AOP 功能。换句话说 Spring 的 AOP 功能就是依赖的 aspectjweaver !

XHTML

1

2

3

4

5

6

org.springframework.boot

spring-boot-starter-aop

2.1.4.RELEASE

@Aspect 快速入门

1、@Aspect 常见用于记录日志、异常集中处理、权限验证、Web 参数校验、事务处理等等

2、要想把一个类变成切面类,只需3步:

1)在类上使用 @Aspect 注解使之成为切面类

2)切面类需要交由 Sprign 容器管理,所以类上还需要有 @Service、@Repository、@Controller、@Component  等注解

2)在切面类中自定义方法接收通知

3、AOP 的含义就不再累述了,下面直接上示例:

Java

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

importorg.aspectj.lang.JoinPoint;

importorg.aspectj.lang.Signature;

importorg.aspectj.lang.annotation.*;

importorg.aspectj.lang.reflect.SourceLocation;

importorg.springframework.core.annotation.Order;

importorg.springframework.stereotype.Component;

importjava.util.Arrays;

/**

* 切面注解 Aspect 使用入门

* 1、@Aspect:声明本类为切面类

* 2、@Component:将本类交由 Spring 容器管理

* 3、@Order:指定切入执行顺序,数值越小,切面执行顺序越靠前,默认为 Integer.MAX_VALUE

*

* @author wangMaoXiong

* @version 1.0

* @date 2020/8/20 19:22

*/

@Aspect

@Order(value=999)

@Component

publicclassAspectHelloWorld{

/**

* @Pointcut :切入点声明,即切入到哪些目标方法。value 属性指定切入点表达式,默认为 ""。

* 用于被下面的通知注解引用,这样通知注解只需要关联此切入点声明即可,无需再重复写切入点表达式

*

* 切入点表达式常用格式举例如下:

* - * com.wmx.aspect.EmpService.*(..)):表示 com.wmx.aspect.EmpService 类中的任意方法

* - * com.wmx.aspect.*.*(..)):表示 com.wmx.aspect 包(不含子包)下任意类中的任意方法

* - * com.wmx.aspect..*.*(..)):表示 com.wmx.aspect 包及其子包下任意类中的任意方法

*

*/

@Pointcut(value="execution(* com.wmx.aspect.EmpServiceImpl.*(..))")

privatevoidaspectPointcut(){

}

/**

* 前置通知:目标方法执行之前执行以下方法体的内容。

* value:绑定通知的切入点表达式。可以关联切入点声明,也可以直接设置切入点表达式

*

*

* @param joinPoint:提供对连接点处可用状态和有关它的静态信息的反射访问

*                Object[] getArgs():返回此连接点处(目标方法)的参数

*                Signature getSignature():返回连接点处的签名。

*                Object getTarget():返回目标对象

*                Object getThis():返回当前正在执行的对象

*                StaticPart getStaticPart():返回一个封装此连接点的静态部分的对象。

*                SourceLocation getSourceLocation():返回与连接点对应的源位置

*                String toLongString():返回连接点的扩展字符串表示形式。

*                String toShortString():返回连接点的缩写字符串表示形式。

*                String getKind():返回表示连接点类型的字符串

*                

*/

@Before(value="aspectPointcut()")

publicvoidaspectBefore(JoinPointjoinPoint){

Object[]args=joinPoint.getArgs();

Signaturesignature=joinPoint.getSignature();

Objecttarget=joinPoint.getTarget();

ObjectaThis=joinPoint.getThis();

JoinPoint.StaticPartstaticPart=joinPoint.getStaticPart();

SourceLocationsourceLocation=joinPoint.getSourceLocation();

StringlongString=joinPoint.toLongString();

StringshortString=joinPoint.toShortString();

System.out.println("【前置通知】");

System.out.println("\targs="+Arrays.asList(args));

System.out.println("\tsignature="+signature);

System.out.println("\ttarget="+target);

System.out.println("\taThis="+aThis);

System.out.println("\tstaticPart="+staticPart);

System.out.println("\tsourceLocation="+sourceLocation);

System.out.println("\tlongString="+longString);

System.out.println("\tshortString="+shortString);

}

/**

* 后置通知:目标方法执行之后执行以下方法体的内容,不管目标方法是否发生异常。

* value:绑定通知的切入点表达式。可以关联切入点声明,也可以直接设置切入点表达式

*/

@After(value="aspectPointcut()")

publicvoidaspectAfter(JoinPointjoinPoint){

System.out.println("【后置通知】");

System.out.println("\tkind="+joinPoint.getKind());

}

/**

* 返回通知:目标方法返回后执行以下代码

* value 属性:绑定通知的切入点表达式。可以关联切入点声明,也可以直接设置切入点表达式

* pointcut 属性:绑定通知的切入点表达式,优先级高于 value,默认为 ""

* returning 属性:通知签名中要将返回值绑定到的参数的名称,默认为 ""

*

* @param joinPoint :提供对连接点处可用状态和有关它的静态信息的反射访问

* @param result    :目标方法返回的值,参数名称与 returning 属性值一致。无返回值时,这里 result 会为 null.

*/

@AfterReturning(pointcut="aspectPointcut()",returning="result")

publicvoidaspectAfterReturning(JoinPointjoinPoint,Objectresult){

System.out.println("【返回通知】");

System.out.println("\t目标方法返回值="+result);

}

/**

* 异常通知:目标方法发生异常的时候执行以下代码,此时返回通知不会再触发

* value 属性:绑定通知的切入点表达式。可以关联切入点声明,也可以直接设置切入点表达式

* pointcut 属性:绑定通知的切入点表达式,优先级高于 value,默认为 ""

* throwing 属性:与方法中的异常参数名称一致,

*

* @param ex:捕获的异常对象,名称与 throwing 属性值一致

*/

@AfterThrowing(pointcut="aspectPointcut()",throwing="ex")

publicvoidaspectAfterThrowing(JoinPointjp,Exceptionex){

StringmethodName=jp.getSignature().getName();

System.out.println("【异常通知】");

if(exinstanceofArithmeticException){

System.out.println("\t【"+methodName+"】方法算术异常(ArithmeticException):"+ex.getMessage());

}else{

System.out.println("\t【"+methodName+"】方法异常:"+ex.getMessage());

}

}

}

07dbbdd8d62971a17d2033aa585858b2.gif

如上所示在不修改原来业务层代码的基础上,就可以使用 AOP 功能,在目标方法执行前后或者异常时都能捕获然后执行。

execution 切点表达式

1、@Pointcut 切入点声明注解,以及所有的通知注解都可以通过 value 属性或者 pointcut 属性指定切入点表达式。

2、切入点表达式通过 execution 函数匹配连接点,语法:execution([方法修饰符]  返回类型  方法路径(参数类型) [异常类型])

3、切入点表达式的写法比较灵活,比如:* 号表示任意一个,.. 表示任意多个,还可以使用 &&、||、! 进行逻辑运算,不过实际开发中通常用不到那么多花里胡哨的,掌握以下几种就基本够用了。

切入点表达式常用举例

execution(* com.wmx.aspect.EmpServiceImpl.findEmpById(Integer))

匹配 com.wmx.aspect.EmpService 类中的 findEmpById 方法,且带有一个 Integer 类型参数。

execution(* com.wmx.aspect.EmpServiceImpl.findEmpById(*))

匹配 com.wmx.aspect.EmpService 类中的 findEmpById 方法,且带有一个任意类型参数。

execution(* com.wmx.aspect.EmpServiceImpl.findEmpById(..))

匹配 com.wmx.aspect.EmpService 类中的 findEmpById 方法,参数不限。

execution(* com.wmx.aspect.EmpService.*(..))

匹配 com.wmx.aspect.EmpService 类中的任意方法

execution(* com.wmx.aspect.*.*(..))

匹配 com.wmx.aspect 包(不含子包)下任意类中的任意方法

execution(* com.wmx.aspect..*.*(..))

匹配 com.wmx.aspect 包及其子包下任意类中的任意方法

转自:https://blog.csdn.net/wangmx1993328/article/details/108164388

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐