三(二)、AOP配置

一、AOP的配置(注解)

步骤一、导入jar包:

处理那5个jar包之外,还需要导入:

  • aopalliance
  • aspectjweaver
  • spring-aop
  • spring-aspects

步骤二、在配置文件中加入aop、context的命名空间

步骤三分为基于注解方式配置AOP和xml方式配置aop;

基于注解方式(本篇)

①在配置文件中加入如下配置;

1 <!-- 使AspjectJ注释起作用,自动匹配的类生成代理对象 -->
2  <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

②把横切关注点的代码都加入到切面的类中,

③切面首先是一个IOC中的bean,即加@Conponent注释

④切面需要加入@Aspect注释

⑤在类中声明各种通知:

  • @Before:前置通知,在方法执行前执行;
  • @After:后置通知,在方法执行后执行
  • @AfterRunning:返回通知,在方法返回结果后执行

  • @Afterthrowing:异常通知之后

  • @Around:环绕通知

二、AOP常用通知:

  • @Before:前置通知,在方法执行前执行;
    @Before("execution(public int lixiuming.spring.aop.impl.ArithmeticCaculator.*(int, int) )") // 作用于接口中的所有方法
    public void beforeMethod(JoinPoint joinPoint) {
        String method = joinPoint.getSignature().getName();// 方法的签名
        List<Object> args = Arrays.asList(joinPoint.getArgs());// 方法的参数
        System.out.println("the method " + method + " begins with" + args);
    }
  • @After:后置通知,在方法执行后执行,在后置通知中,不能访问目标方法执行的结果,如果有异常也执行,通知在异常之前;
1     @After("execution(public int lixiuming.spring.aop.impl.ArithmeticCaculator.*(int, int) )")
2     public void afterMethod(JoinPoint joinPoint) {
3         String method = joinPoint.getSignature().getName();
4         List<Object> args = Arrays.asList(joinPoint.getArgs());
5         System.out.println("the method " + method + " is end to " + args);
6     }
  • @AfterRunning:返回通知,在方法返回结果后执行,可以访问到返回值;

1     @AfterReturning(value = "execution(public int lixiuming.spring.aop.impl.ArithmeticCaculator.add(int, int) )", returning = "result")
2     public void afterReturn(JoinPoint joinPoint, Object result) {
3         String method = joinPoint.getSignature().getName();
4         System.out.println("the method " + method + " is end with " + result);
5     }
  • @Afterthrowing:异常通知之后,可以访问到异常,并且可以指定异常类型,只有符合该异常类型时才被执行

1     @AfterThrowing(value = "execution(public int lixiuming.spring.aop.impl.ArithmeticCaculator.add(int, int) )", throwing = "ex")
2     public void afterThrowing(JoinPoint joinPoint, Object ex) {
3         String method = joinPoint.getSignature().getName();
4         System.out.println("the method " + method + " occured exception: " + ex);
5     }

 

  • @Around:环绕通知;

    环绕通知类似动态代理的全过程:ProceedingJoinPoint类型的参数可以决定是否执行日志方法 且必须有返回值,返回值是目标方法的返回值

 1     @Around("execution(public int lixiuming.spring.aop.impl.ArithmeticCaculator.add(int, int) )")
 2     public Object aroundMethod(ProceedingJoinPoint point) {
 3         Object result = null;
 4         String method = point.getSignature().getName();
 5         // 执行目标方法
 6         try {
 7             // 前置通知
 8             System.out.println("the method " + method + " is begin with " + Arrays.asList(point.getArgs()));
 9             result = point.proceed();
10             // 返回通知
11             System.out.println("the method " + method + " is end to " + result);
12         } catch (Throwable e) {
13             // TODO Auto-generated catch block
14             System.out.println("the method " + method + " occured exception: " + e);
15             throw new RuntimeException(e);
16         }
17         System.out.println("the method " + method + " ends");
18 
19         return 100;
20     }

 

三、切点表达式

表达式

1 execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)

这里问号表示当前项可以有也可以没有,其中各项的语义如下:

  • modifiers-pattern:方法的可见性,如public,protected;
  • ret-type-pattern:方法的返回值类型,如int,void等;
  • declaring-type-pattern:方法所在类的全路径名,如com.spring.Aspect;
  • name-pattern:方法名类型,如buisinessService();
  • param-pattern:方法的参数类型,如java.lang.String;
  • throws-pattern:方法抛出的异常类型,如java.lang.Exception;

举例说明:

//    @Before("execution(public int lixiuming.spring.aop.impl.ArithmeticCaculator.add(int, int) )")// 作用于接口中的add方法
//     @Before("execution(public int lixiuming.spring.aop.impl.ArithmeticCaculator.*(int, int) )") // 作用于接口中的所有方法 (所有方法是指public int类型的)
//    @Before("execution(* ixiuming.spring.aop.impl.ArithmeticCaculator.*(int, int) )") // 第一个* 表示:任意修饰符和任意返回值,第二个*代码任意参数为(int,int)方法
//    @Before("execution( public * ixiuming.spring.aop.impl.ArithmeticCaculator.*(..) )") // 第二个*代表任意方法;..代表任意个数的参数,即所有公有方法
//    @Before("execution( public double ixiuming.spring.aop.impl.ArithmeticCaculator.*(double.) )") // 返回所有double的第一个参数为double的public的方法

 @Pointcut

使用@Pointcut 来声明切入点表达式,后面的其他通知直接使用方法名来引用当前的切入点表达式;如下代码,前置通知使用了方法名为declareJoinPointExpress来引用切点表达式;

这样做的好处是,可以统一管理切点表达式;

 1     @Pointcut("execution(public int lixiuming.spring.aop.impl.ArithmeticCaculator.*(..))")
 2     public void declareJoinPointExpress() {
 3 
 4     }
 5 
 6 //声明该方法是一个前置通知:在目标方法之前执行
 7     @Before("declareJoinPointExpress()")
 8     public void beforeMethod(JoinPoint joinPoint) {
 9         String method = joinPoint.getSignature().getName();
10         List<Object> args = Arrays.asList(joinPoint.getArgs());
11         System.out.println("the method " + method + " begins with" + args);
12     }

 

四、实例说明AOP配置:

以实现三(一)中的 为ArithmeticCaculator添加 各方法 执行前 和计算结果后的日志的AOP方案为实例;

步骤一、为ArithmeticCaculatorImpl类添加@Component 注解 来表示 该组件需要被sping容器管理

 1 package lixiuming.spring.aop.impl;
 2 
 3 import org.springframework.stereotype.Component;
 4 
 5 @Component
 6 public class ArithmeticCaculatorImpl2 implements ArithmeticCaculator {
 7 
 8     @Override
 9     public int add(int i, int j) {
10         int result = i+j;
11         return result;
12     }
13 
14     @Override
15     public int sub(int i, int j) {
16         int result = i-j;
17         return result;
18     }
19 
20     @Override
21     public int mul(int i, int j) {
22         int result = i*j;
23         return result;
24     }
25 
26     @Override
27     public int div(int i, int j) {
28         int result = i/j;
29         return result;
30     }
31 
32 }
View Code

步骤二、需要添加一个切面:

关于切面声明的说明:

  • 切面需要放置在spring 容器中;所以首先需要一个@Component注解
  • 声明一个切面用注解 @Aspect;

为实现上述实例,需要添加一个前置通知和后置通知;前置通知即,在目标方法执行之前执行;后置通知,即在目标方法执行后执行,无论是否发生异常。

 1 package lixiuming.spring.aop.impl;
 2 
 3 import java.util.Arrays;
 4 import java.util.List;
 5 
 6 import org.aspectj.lang.JoinPoint;
 7 import org.aspectj.lang.annotation.After;
 8 import org.aspectj.lang.annotation.Aspect;
 9 import org.aspectj.lang.annotation.Before;
10 import org.springframework.stereotype.Component;
11 
12 //把这个类声明为一个切面,需要把该类放入到IOC容器中,再声明为一个切面
13 @Aspect
14 @Component
15 public class LoggingAspect {
16     // 声明该方法是一个前置通知:在目标方法之前执行
18     @Before("execution(public int lixiuming.spring.aop.impl.ArithmeticCaculator.*(int, int) )") // 作用于接口中的所有方法
22     public void beforeMethod(JoinPoint joinPoint) {
23         String method = joinPoint.getSignature().getName();// 方法的签名
24         List<Object> args = Arrays.asList(joinPoint.getArgs());// 方法的参数
25         System.out.println("the method " + method + " begins with" + args);
26     }
27 
28     // 声明后置通知:在目标方法执行后(无论是否发生异常)执行的通知
29     // 在后置通知中,不能访问目标方法执行的结果,需要在返回通知里面访问
30     @After("execution(public int lixiuming.spring.aop.impl.ArithmeticCaculator.*(int, int) )")
31     public void afterMethod(JoinPoint joinPoint) {
32         String method = joinPoint.getSignature().getName();
33         List<Object> args = Arrays.asList(joinPoint.getArgs());
34         System.out.println("the method " + method + " is end to " + args);
35     }
36 
37 }

 步骤三、配置文件:引入了context和aop的命名空间

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4     xmlns:aop="http://www.springframework.org/schema/aop"
 5     xmlns:context="http://www.springframework.org/schema/context"
 6     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
 7         http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
 8         http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
 9 
10 <context:component-scan base-package="lixiuming.spring.aop.impl"/>
11 <!-- 使AspjectJ注释起作用,自动匹配的类生成代理对象 -->
12 <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
13 </beans>

使用main方法测试:

 1 public static void main(String[] args) {
 2         ApplicationContext cxt = new ClassPathXmlApplicationContext("ApplicationContext.xml");
 3         ArithmeticCaculator arithmeticCaculator = cxt.getBean(ArithmeticCaculator.class);
 4 
 5         int result = arithmeticCaculator.add(1, 2);
 6         System.out.println("-->" + result);
 7 
 8         int result1 = arithmeticCaculator.div(4, 2);
 9         System.out.println("-->" + result1);
10 
11         int result2 = arithmeticCaculator.mul(4, 2);
12         System.out.println("-->" + result2);
13 
14         int result3 = arithmeticCaculator.sub(4, 2);
15         System.out.println("-->" + result3);
16 
17     }

测试运行结果:

the method add begins with[1, 2]

the method add is end to [1, 2]

-->3

the method div begins with[4, 2]

the method div is end to [4, 2]

-->2

the method mul begins with[4, 2]

the method mul is end to [4, 2]

-->8

the method sub begins with[4, 2]

the method sub is end to [4, 2]

-->2

步骤二中除了使用前置和后置通知,还可以使用环绕通知来实现上述功能;

代码如下:

 1 package lixiuming.spring.aop.impl;
 2 
 3 import java.util.Arrays;
 4 
 5 import org.aspectj.lang.ProceedingJoinPoint;
 6 import org.aspectj.lang.annotation.Around;
 7 import org.aspectj.lang.annotation.Aspect;
 8 import org.aspectj.lang.annotation.Pointcut;
 9 import org.springframework.stereotype.Component;
10 
11 //把这个类声明为一个切面,需要把该类放入到IOC容器中
12 @Aspect
13 @Component
14 public class LoggingAspect {
15 
16     // 定义一个方法,用于声明切入点表达式,一般该方法中不需要其他的代码
17     // 使用@Pointcut 来声明切入点表达式,
18     // 后面的其他通知直接使用方法名来引用当前的切入点表达式
19     @Pointcut("execution(public int lixiuming.spring.aop.impl.ArithmeticCaculator.*(..))")
20     public void declareJoinPointExpress() {
21 
22     }
23 
24     /**
25      * 环绕通知需要携带ProceedingJoinPoint类型的参数
26      * 环绕通知类似动态代理的全过程:ProceedingJoinPoint类型的参数可以决定是否执行日志方法 且必须有返回值,返回值是目标方法的返回值
27      */
28     @Around("declareJoinPointExpress()")
29     public Object aroundMethod(ProceedingJoinPoint point) {
30         Object result = null;
31         String method = point.getSignature().getName();
32         // 执行目标方法
33         try {
34             // 前置通知
35             System.out.println("the method " + method + " is begin with " + Arrays.asList(point.getArgs()));
36             result = point.proceed();
37             // 后置通知
38             System.out.println("the method " + method + " is end to " + Arrays.asList(point.getArgs()));
39         } catch (Throwable e) {
40             // TODO Auto-generated catch block
41             System.out.println("the method " + method + " occured exception: " + e);
42             throw new RuntimeException(e);
43         }
44 
45         return result;
46     }
47 }

 五、切面的优先级

使用@Order(index)指定执行顺序的优先级,index为数字,index越小,优先级越高;@Order位置为放置在@Aspect前面;代码如下:

@Order(1)//执行顺序的优先级
@Aspect
@Component
//验证通知
public class VlidationAspect {
    @Before("LoggingAspect.declareJoinPointExpress()")
    public void validationArgs(JoinPoint jointPoint){
        System.out.println("-->validation:"+Arrays.asList(jointPoint.getArgs()));
    }
    

}

六、xml方式配置AOP

ArithmeticCaculator 不变;ArithmeticCaculatorImpl移除@Component;

LoggingAspect:

 1 package lixiuming.spring.aop.impl2;
 2 
 3 import java.util.Arrays;
 4 import java.util.List;
 5 
 6 import org.aspectj.lang.JoinPoint;
 7 import org.aspectj.lang.ProceedingJoinPoint;
 8 import org.aspectj.lang.annotation.After;
 9 import org.aspectj.lang.annotation.AfterReturning;
10 import org.aspectj.lang.annotation.AfterThrowing;
11 import org.aspectj.lang.annotation.Around;
12 import org.aspectj.lang.annotation.Aspect;
13 import org.aspectj.lang.annotation.Before;
14 import org.aspectj.lang.annotation.Pointcut;
15 import org.springframework.stereotype.Component;
16 
17 public class LoggingAspect {
18 
19     public void beforeMethod(JoinPoint joinPoint){
20         String method = joinPoint.getSignature().getName();
21         List<Object> args = Arrays.asList(joinPoint.getArgs());    
22         System.out.println("the method "+method+" begins with"+args);
23     }
24 
25     public void afterMethod(JoinPoint joinPoint){
26         String method =  joinPoint.getSignature().getName();
27         List<Object> args = Arrays.asList(joinPoint.getArgs());
28         System.out.println("the method "+method+" is end to "+args);
29     }
30     
31     /**
32      *在方法正常结束后执行的代码
33      *返回通知是可以访问到方法的返回值
34      */
35     public void afterReturn(JoinPoint joinPoint,Object result){
36         String method = joinPoint.getSignature().getName();
37         System.out.println("the method "+method+" is end with " +result);
38     }
39 
40     public void afterThrowing(JoinPoint joinPoint,Object ex){
41         String method = joinPoint.getSignature().getName();
42         System.out.println("the method "+method+" occured exception: " + ex);
43     }
44     
45     public Object aroundMethod(ProceedingJoinPoint point){
46         Object result = null;
47         String method = point.getSignature().getName();
48         //执行目标方法
49         try {
50             //前置通知
51             System.out.println("the method "+method+" is begin with "+Arrays.asList(point.getArgs()));
52             result = point.proceed();
53             //返回通知
54             System.out.println("the method "+method+" is end to "+ result);
55         } catch (Throwable e) {
56             // TODO Auto-generated catch block
57             System.out.println("the method "+method+" occured exception: " + e);
58             throw new RuntimeException(e);
59         }
60         System.out.println("the method "+method+" ends");
61 
62         return 100;
63     }
64 }
VlidationAspect:
 1 package lixiuming.spring.aop.impl2;
 2 
 3 import java.util.Arrays;
 4 
 5 import org.aspectj.lang.JoinPoint;
 6 import org.aspectj.lang.annotation.Aspect;
 7 import org.aspectj.lang.annotation.Before;
 8 import org.springframework.core.annotation.Order;
 9 import org.springframework.stereotype.Component;
10 //验证通知
11 public class VlidationAspect {
12     public void validationArgs(JoinPoint jointPoint){
13         System.out.println("-->validation:"+Arrays.asList(jointPoint.getArgs()));
14     }
15     
16 
17 }

配置文件:

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4     xmlns:aop="http://www.springframework.org/schema/aop"
 5     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
 6         http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
 7 
 8 <!-- 配置bean -->
 9 <bean id="aop" class="lixiuming.spring.aop.impl2.ArithmeticCaculatorImpl2"></bean>
10 <!-- 配置切面的bean -->
11 <bean id="LoggingAspect" class="lixiuming.spring.aop.impl2.LoggingAspect"></bean>
12 <bean id="VlidationAspect" class="lixiuming.spring.aop.impl2.VlidationAspect"></bean>
13 
14 <!-- 配置AOP -->
15 <aop:config>
16     <!-- 配置切面表达式 -->
17     <aop:pointcut expression="execution(* lixiuming.spring.aop.impl2.ArithmeticCaculator.*(int,int))" id="pointcut"/>
18     <!-- 配置切面及通知 -->
19     <aop:aspect ref="LoggingAspect" order="2">
20         <aop:before method="beforeMethod" pointcut-ref="pointcut"/>
21         <aop:after method="afterMethod" pointcut-ref="pointcut"/>
22         <aop:after-throwing method="afterThrowing" pointcut-ref="pointcut" throwing="ex"/>
23         <aop:after-returning method="afterReturn" pointcut-ref="pointcut" returning="result"/>
24         <aop:around method="aroundMethod" pointcut-ref="pointcut"/>
25      </aop:aspect>
26      
27      <aop:aspect ref="VlidationAspect" order="1">
28          <aop:before method="validationArgs" pointcut-ref="pointcut"/>
29       </aop:aspect>
30 </aop:config>
31 </beans>

 

posted @ 2021-10-17 01:04  啄木鸟伍迪  阅读(258)  评论(0编辑  收藏  举报
//火箭 GenerateContentList();