博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
spring11----基于Schema的AOP
阅读量:5319 次
发布时间:2019-06-14

本文共 13984 字,大约阅读时间需要 46 分钟。

       基于Schema的AOP从Spring2.0之后通过“aop”命名空间来定义切面、切入点及声明通知。

       在Spring配置文件中,所以AOP相关定义必须放在<aop:config>标签下,该标签下可以有<aop:pointcut>、 <aop:advisor>、<aop:aspect>标签,配置顺序不可变。

一. 声明切面

切面就是包含切入点和通知的对象,在Spring容器中将被定义为一个Bean,Schema方式的切面需要一个切面支持 Bean,该支持Bean的字段和方法提供了切面的状态和行为信息,并通过配置方式来指定切入点和通知实现。

切面使用<aop:aspect>标签指定,ref属性用来引用切面支持Bean。

切面支持Bean“aspectSupportBean”跟普通Bean完全一样使用,切面使用“ref”属性引用它。

二. 声明切入点

切入点在Spring中也是一个Bean,Bean定义方式可以有以下3种:

1. 在<aop:config>标签下使用<aop:pointcut>声明一个切入点Bean,该切入点可以被多个切面使用,对于需要 共享使用的切入点最好使用该方式,该切入点使用id属性指定Bean名字,在通知定义时使用pointcut-ref属性通过该id 引用切入点,expression属性指定切入点表达式:

2. 在<aop:aspect>标签下使用<aop:pointcut>声明一个切入点Bean,该切入点可以被多个切面使用,但一般该 切入点只被该切面使用,当然也可以被其他切面使用,但最好不要那样使用,该切入点使用id属性指定Bean名字,在通 知定义时使用pointcut-ref属性通过该id引用切入点,expression属性指定切入点表达式:

3. 匿名切入点Bean,可以在声明通知时通过pointcut属性指定切入点表达式,该切入点是匿名切入点,只被该通知 使用

三. 声明通知

基于Schema方式支持前边介绍的5种通知类型:

1. 前置通知

在切入点选择的方法之前执行,通过<aop:aspect>标签下的<aop:before>标签声明:

pointcut和pointcut-ref:二者选一,指定切入点;

method:指定前置通知实现方法名,如果是多态需要加上参数类型,多个用“,”隔开,如 beforeAdvice(java.lang.String);
arg-names:指定通知实现方法的参数名字,多个用“,”分隔,可选,类似于构造器注入中的参数 名注入限制:在class文件中没生成变量调试信息是获取不到方法参数名字的,因此只有在类没生成变量调试信息时才需 要使用arg-names属性来指定参数名,如arg-names="param"表示通知实现方法的参数列表的第一个参数名字为 “param”。

举例:

1 package com.test.spring.service;2 3 public interface IHelloWorldService {4     public void sayHello(String param);5 }
View Code
1 package com.test.spring.service.impl; 2  3 import com.test.spring.service.IHelloWorldService; 4  5 public class HelloWorldService implements IHelloWorldService{ 6  7     public void sayHello(String param) { 8         System.out.println("======Hello "+param); 9     }10 }
View Code
1 package com.test.spring.aop;2 3 public class HelloWorldAspect {4     //前置通知5     public void beforeAdvice(String param) {6         System.out.println("=====before advice param "+param);7     }8 }
View Code
//这里的param指的是通知的param
1 public class AopTest {2     @Test3     public void test() {4         ApplicationContext context=new ClassPathXmlApplicationContext("bean.xml");5         IHelloWorldService helloWorldService=(IHelloWorldService) context.getBean("helloWorldService");6         helloWorldService.sayHello("hermioner");7     }8 }
View Code

说明:

1)切入点匹配:在配置中使用“execution(* cn.javass..*.sayBefore(..)) ”匹配目标方法sayHello,且使用 “args(param)”匹配目标方法只有一个参数且传入的参数类型为通知实现方法中同名的参数类型;

2)目标方法定义:使用method=" beforeAdvice(java.lang.String) "指定前置通知实现方法,且该通知有一个参数类 型为java.lang.String参数;
3)目标方法参数命名:其中使用arg-names=" param "指定通知实现方法参数名为“param”,切入点中使用 “args(param)”匹配的目标方法参数将自动传递给通知实现方法同名参数。(比如:Service种的sayHello(String param),但是通知中是beforeAdvice(String param2),那么上面的两处arg配置都应该用param2)

2. 后置返回通知

在切入点选择的方法正常返回时执行,通过<aop:aspect>标签下的<aop:after-returning>标签声 明:

pointcutpointcut-ref:同前置通知同义,二选一;

method:同前置通知同义;
arg-names:同前置通知同义;
returning:定义一个名字,该名字用于匹配通知实现方法的一个参数名,当目标方法执行正常返回后,将把目 标方法返回值传给通知方法;returning限定了只有目标方法返回值匹配与通知方法相应参数类型时才能执行后置返回 通知,否则不执行,对于returning对应的通知方法参数为Object类型将匹配任何目标返回值。

举例:

1 package com.test.spring.service;2 3 public interface IHelloWorldService {4     public void sayHello(String param);5     public boolean sayAfterReturning();6 }
View Code
1 package com.test.spring.service.impl; 2  3 import com.test.spring.service.IHelloWorldService; 4  5 public class HelloWorldService implements IHelloWorldService{ 6  7     public void sayHello(String param) { 8         System.out.println("======Hello "+param); 9     }10 11     public boolean sayAfterReturning() {12         System.out.println("--------after returning");13         return true;14     }15 }
View Code
1 package com.test.spring.aop; 2  3 public class HelloWorldAspect { 4     //前置通知 5     public void beforeAdvice(String param2) { 6         System.out.println("=====before advice param "+param2); 7     } 8      9     //后置返回通知10     public void afterReturningAdvice(Object retVal) {11         System.out.println("==========after returning advice retVal:"+retVal);12     }13 }
View Code
1 
9 10
12
13 14
15
16
19 20
24
25
26 27
View Code
1 public class AopTest {2     @Test3     public void test() {4         ApplicationContext context=new ClassPathXmlApplicationContext("bean.xml");5         IHelloWorldService helloWorldService=(IHelloWorldService) context.getBean("helloWorldService");6         helloWorldService.sayAfterReturning();7     }8 }
View Code
1 --------after returning2 ==========after returning advice retVal:true
View Code

分析:

1)切入点匹配:在配置中使用“execution(* cn.javass..*.sayAfterReturning(..)) ”匹配目标方法 sayAfterReturning,该方法返回true;

2)目标方法定义:使用method="afterReturningAdvice"指定后置返回通知实现方法;
3)目标方法参数命名:其中使用arg-names="retVal"指定通知实现方法参数名为“retVal”;
4)返回值命名:returning="retVal"用于将目标返回值赋值给通知实现方法参数名为“retVal”的参数上。

3. 后置异常通知

在切入点选择的方法抛出异常时执行,通过<aop:aspect>标签下的<aop:after-throwing>标签声 明:

throwing定义一个名字,该名字用于匹配通知实现方法的一个参数名,当目标方法抛出异常返回后,将把目标 方法抛出的异常传给通知方法;throwing限定了只有目标方法抛出的异常匹配与通知方法相应参数异常类型时才能执行 后置异常通知,否则不执行,对于throwing对应的通知方法参数为Throwable类型将匹配任何异常。

1 package com.test.spring.service;2 3 public interface IHelloWorldService {4     public void sayHello(String param);5     public boolean sayAfterReturning();6     public void sayAfterThrowing();7 }
View Code
1 package com.test.spring.service.impl; 2  3 import com.test.spring.service.IHelloWorldService; 4  5 public class HelloWorldService implements IHelloWorldService{ 6  7     public void sayHello(String param) { 8         System.out.println("======Hello "+param); 9     }10 11     public boolean sayAfterReturning() {12         System.out.println("--------after returning");13         return true;14     }15     16     public void sayAfterThrowing() {17         System.out.println("======before thrwing");18         throw new RuntimeException();19     }20 }
View Code
1 package com.test.spring.aop; 2  3 public class HelloWorldAspect { 4     //前置通知 5     public void beforeAdvice(String param2) { 6         System.out.println("=====before advice param "+param2); 7     } 8      9     //后置返回通知10     public void afterReturningAdvice(Object retVal) {11         System.out.println("==========after returning advice retVal:"+retVal);12     }13     14     //后置异常通知15     public void afterThrowingAdvice(Exception exception) {16         System.out.println("======after throwing advice exception:"+exception);17     }18 }
View Code
1 
9 10
12
13 14
15
16
19 20
24 25
29
30
31 32
View Code
1 public class AopTest {2     @Test3     public void test() {4         ApplicationContext context=new ClassPathXmlApplicationContext("bean.xml");5         IHelloWorldService helloWorldService=(IHelloWorldService) context.getBean("helloWorldService");6         helloWorldService.sayAfterThrowing();7     }8 }
View Code
1 ======before thrwing2 ======after throwing advice exception:java.lang.RuntimeException
View Code

分析:

1)切入点匹配:在配置中使用“execution(* cn.javass..*.sayAfterThrowing(..))”匹配目标方法sayAfterThrowing, 该方法将抛出RuntimeException异常;

2)目标方法定义:使用method="afterThrowingAdvice"指定后置异常通知实现方法;
3)目标方法参数命名:其中使用arg-names="exception"指定通知实现方法参数名为“exception”;
4)异常命名:returning="exception"用于将目标方法抛出的异常赋值给通知实现方法参数名为“exception”的参数 上。

4. 后置最终通知

在切入点选择的方法返回时执行,不管是正常返回还是抛出异常都执行,通过<aop:aspect>标签 下的<aop:after >标签声明:

举例:

1 public interface IHelloWorldService {2     public void sayHello(String param);3     public boolean sayAfterReturning();4     public void sayAfterThrowing();5     public boolean sayAfterFinally();6 }
View Code
1 package com.test.spring.service.impl; 2  3 import com.test.spring.service.IHelloWorldService; 4  5 public class HelloWorldService implements IHelloWorldService{ 6  7     public void sayHello(String param) { 8         System.out.println("======Hello "+param); 9     }10 11     public boolean sayAfterReturning() {12         System.out.println("--------after returning");13         return true;14     }15     16     public void sayAfterThrowing() {17         System.out.println("======before thrwing");18         throw new RuntimeException();19     }20     21     public boolean sayAfterFinally() {22         System.out.println("============before finally");23         throw new RuntimeException(); 24     }25 }
View Code
1 package com.test.spring.aop; 2  3 public class HelloWorldAspect { 4     //前置通知 5     public void beforeAdvice(String param2) { 6         System.out.println("=====before advice param "+param2); 7     } 8      9     //后置返回通知10     public void afterReturningAdvice(Object retVal) {11         System.out.println("==========after returning advice retVal:"+retVal);12     }13     14     //后置异常通知15     public void afterThrowingAdvice(Exception exception) {16         System.out.println("======after throwing advice exception:"+exception);17     }18     19     //后置最终通知20     public void afterFinallyAdvice() {21         System.out.println("========after finally advice");22     }23 }
View Code
1 
9 10
12
13 14
15
16
19 20
24 25
29
31
32
33 34
View Code
1 public class AopTest {2     @Test3     public void test() {4         ApplicationContext context=new ClassPathXmlApplicationContext("bean.xml");5         IHelloWorldService helloWorldService=(IHelloWorldService) context.getBean("helloWorldService");6         helloWorldService.sayAfterFinally();7     }8 }
View Code
1 ============before finally2 ========after finally advice
View Code

分析:

1)切入点匹配:在配置中使用“execution(* cn.javass..*.sayAfterFinally(..))”匹配目标方法sayAfterFinally,该方法 将抛出RuntimeException异常;

2)目标方法定义:使用method=" afterFinallyAdvice "指定后置最终通知实现方法。

5. 环绕通知

环绕着在切入点选择的连接点处的方法所执行的通知,环绕通知非常强大,可以决定目标方法是否执 行,什么时候执行,执行时是否需要替换方法参数,执行完毕是否需要替换返回值,可通过<aop:aspect>标签下的 <aop:around >标签声明

环绕通知第一个参数必须是org.aspectj.lang.ProceedingJoinPoint类型,在通知实现方法内部使用 ProceedingJoinPoint的proceed()方法使目标方法执行,proceed 方法可以传入可选的Object[]数组,该数组的值将 被作为目标方法执行时的参数。

举例:

1 package com.test.spring.service.impl;2 3 import com.test.spring.service.IHelloWorldService;4 5 public class HelloWorldService implements IHelloWorldService{6     public void sayAround(String param) {7         System.out.println("=======around param:"+param);8     }9 }
View Code
1 package com.test.spring.aop; 2  3 import org.aspectj.lang.ProceedingJoinPoint; 4  5 public class HelloWorldAspect { 6     public Object aroundAdvice(ProceedingJoinPoint pip) throws Throwable { 7         System.out.println("=======around before"); 8         Object retVal=pip.proceed(new Object[] {"replace"}); 9         System.out.println("======around after");10         return retVal;11     }12 }
View Code
1 
3
4 5
6
7 8
10
11
View Code
1 public class AopTest {2     @Test3     public void test() {4         ApplicationContext context=new ClassPathXmlApplicationContext("bean.xml");5         IHelloWorldService helloWorldService=(IHelloWorldService) context.getBean("helloWorldService");6         helloWorldService.sayAround("hahah");7     }8 }
View Code
1 =======around before2 =======around param:replace3 ======around after
View Code

分析:

1)切入点匹配:在配置中使用“execution(* cn.javass..*.sayAround(..))”匹配目标方法sayAround;

2)目标方法定义:使用method="aroundAdvice"指定环绕通知实现方法,在该实现中,第一个方法参数为pjp,类型 为ProceedingJoinPoint,其中“Object retVal = pip.proceed(new Object[] {"replace"});”,用于执行目标方法, 且目标方法参数被“new Object[] {"replace"}”替换,最后返回“retVal ”返回值。
3)测试:我们使用“helloworldService.sayAround("haha");”传入参数为“haha”,但最终输出为“replace”,说 明参数被替换了。

note: 如果上面的pip.proceed(),不添加任何参数,输出的将会是hello.

 四. Advisor

不推荐使用Advisor,除了在进行事务控制的情况下,其他情况一般不推荐使用该方式,该方式属于侵入式设计,必 须实现通知API。

(mynote:等需要的时候再回头学习补充)

 五. 通知的参数自动传入

比如下面结构的代码和配置:

1.接口

1 package com.a.hm.service;2 3 public interface IHelloApi {4     5     public void sayHello(String param,String a);6     7     8 9 }
View Code

2.接口实现

1 package com.a.hm.service; 2  3 public class HelloImpl implements IHelloApi{ 4  5     public void sayHello(String param,String a) { 6         System.out.println("hello world"+" "+param+","+" "+a); 7     } 8  9     10 }
View Code

3.通知类

1 package com.a.hm;2 3 public class Advice {4     public void beforeAdvice(String m,String n) {5         System.out.println("before advice"+" "+m+", "+n);6     }7 }
View Code

4.配置

1 
9 10
11
12 13
14
15 16
17
18
19
20
21
22
23 24
View Code

Note:上面配置的切入点表达式中的args(m,n)必须和通知中的方法变量名一样,否则报错

Note:上面配置的切入点表达式中的args中的参数个数和通知类中的方法的参数个数一致;可以不用和目标方法中的参数个数一致

5.测试

1 package com.a.hm; 2  3 import org.springframework.context.ApplicationContext; 4 import org.springframework.context.support.ClassPathXmlApplicationContext; 5  6 import com.a.hm.service.IHelloApi; 7  8 public class Test { 9     public static void main(String args[]) {10         ApplicationContext context=new ClassPathXmlApplicationContext("bean.xml");11         IHelloApi helloApi=context.getBean("helloId",IHelloApi.class);12         helloApi.sayHello("hello","a");13     }14 15 }
View Code
1 before advice hello, a2 hello world hello, a
View Code

 

 

 

 

 

 

 

 

 

参考文献

https://jinnianshilongnian.iteye.com/blog/1418598

转载于:https://www.cnblogs.com/Hermioner/p/10201728.html

你可能感兴趣的文章
TCL:表格(xls)中写入数据
查看>>
Oracle事务
查看>>
String类中的equals方法总结(转载)
查看>>
标识符
查看>>
一步步教你轻松学奇异值分解SVD降维算法
查看>>
objective-c overview(二)
查看>>
python查询mangodb
查看>>
内存地址对齐
查看>>
创新课程管理系统数据库设计心得
查看>>
Could not resolve view with name '***' in servlet with name 'dispatcher'
查看>>
[转载] redis 的两种持久化方式及原理
查看>>
C++ 删除字符串的两种实现方式
查看>>
MyBaits学习
查看>>
管道,数据共享,进程池
查看>>
CSS
查看>>
[Cypress] Stub a Post Request for Successful Form Submission with Cypress
查看>>
SDUTOJ3754_黑白棋(纯模拟)
查看>>
php中的isset和empty的用法区别
查看>>
Android ViewPager 动画效果
查看>>
把word文档中的所有图片导出
查看>>