当前位置: 首页 > news >正文

循序渐进之Spring AOP(5) - 创建切面

在掌握了可用的增强后,接下来要做的就是精确的描述切点。前面的示例都是指定一个目标类并把增强织入到所有方法中,实际开发显然会有更精细的筛选需求,比如对所有类中名称以test结尾的方法加入监控执行时间,或者指定某些方法仅在输入参数是指定值时做某些特殊处理以解决临时性需求。

spring中用Pointcut接口表示一个切点,其下设有多个实现类,按使用场景分主要有静态切点、动态切点、流程切点和复合切点等。

1 静态方法切面

使用静态方法切点,通常是继承StaticMethodMatcherPointcut,通过覆盖getClassFilter()方法用于确定匹配的类,覆盖matches方法用于确定匹配的方法,Spring对目标类及其上面的方法调用两个方法以确定是否织入增强,检查结果被缓存以提高性能。来看示例:

首先定义两个类用于演示类和方法的筛选, 两个类各有一个同名方法和一个不同名方法

 

[java]  view plain  copy
 
 print?在CODE上查看代码片派生到我的代码片
  1. public class Horseman {  
  2.     public void rush(String enemy) {  
  3.         System.out.println(this.getClass().getSimpleName() + "冲刺攻击" + enemy);  
  4.     }  
  5.       
  6.     public void chop(String enemy) {  
  7.         System.out.println(this.getClass().getSimpleName() + "砍劈攻击" + enemy);  
  8.     }  
  9. }  
[java]  view plain  copy
 
 print?在CODE上查看代码片派生到我的代码片
  1. public class Swordman {  
  2.     public void block(String enemy) {  
  3.         System.out.println(this.getClass().getSimpleName() + "格挡" + enemy);  
  4.     }  
  5.       
  6.     public void chop(String enemy) {  
  7.         System.out.println(this.getClass().getSimpleName() + "砍劈攻击" + enemy);  
  8.     }  
  9. }  

 

设定一个增强,这里选了一个方法前增强

 

 

[java]  view plain  copy
 
 print?在CODE上查看代码片派生到我的代码片
  1. public class BeforeAdvice implements MethodBeforeAdvice {  
  2.     @Override  
  3.     public void before(Method method, Object[] args, Object obj)  
  4.             throws Throwable {  
  5.         System.out.println("Advice:" + obj.getClass().getSimpleName() + "蓄力");  
  6.     }  
  7. }  

下面是切点类,定义了两个选项参数methodOption和classOption,以方便察看不同切点定义下的执行效果

 

 

[java]  view plain  copy
 
 print?在CODE上查看代码片派生到我的代码片
  1. public class StoragePointcut extends StaticMethodMatcherPointcut {  
  2.   
  3.     @Override  
  4.     public boolean matches(Method method, Class<?> cls) {  
  5.         switch(methodOption) {  
  6.         case 1:  
  7.             return "chop".equals(method.getName());  
  8.         case 2:  
  9.             return "rush".equals(method.getName());  
  10.         default:  
  11.             return true;  
  12.         }  
  13.     }  
  14.   
  15.     public ClassFilter getClassFilter() {  
  16.   
  17.         return new ClassFilter() {  
  18.             public boolean matches(Class<?> cls) {  
  19.                 switch(classOption) {  
  20.                 case 1:  
  21.                     return (Horseman.class.isAssignableFrom(cls));  
  22.                 case 2:  
  23.                     return (Swordman.class.isAssignableFrom(cls));  
  24.                 default:  
  25.                     return true;  
  26.                 }  
  27.             }  
  28.         };  
  29.     }  
  30.   
  31.     private int methodOption;  
  32.     private int classOption;  
  33.   
  34.     public void setMethodOption(int methodOption) {  
  35.         this.methodOption = methodOption;  
  36.     }  
  37.   
  38.     public void setClassOption(int classOption) {  
  39.         this.classOption = classOption;  
  40.     }  
  41. }  

配置文件applicationContext.xml

 

[java]  view plain  copy
 
 print?在CODE上查看代码片派生到我的代码片
  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" xmlns:p="http://www.springframework.org/schema/p"  
  4.     xmlns:context="http://www.springframework.org/schema/context"  
  5.     xsi:schemaLocation="http://www.springframework.org/schema/beans  
  6.   http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
  7.   http://www.springframework.org/schema/context  
  8.   http://www.springframework.org/schema/context/spring-context-3.0.xsd">  
  9.   
  10.     <bean id="beforeAdvice" class="examples.chap01.BeforeAdvice" />  
  11.     <bean id="horsemanTarget" class="examples.chap01.Horseman" />  
  12.     <bean id="swordmanTarget" class="examples.chap01.Swordman" />  
  13.     <bean id="pointcut" class="examples.chap01.StoragePointcut"  
  14.         p:methodOption="0"  
  15.         p:classOption="0" />  
  16.     <bean id="advisor" class="org.springframework.aop.support.DefaultPointcutAdvisor"  
  17.         p:advice-ref="beforeAdvice"  
  18.         p:pointcut-ref="pointcut" />  
  19.       
  20.     <bean id="horseman" class="org.springframework.aop.framework.ProxyFactoryBean"  
  21.         p:target-ref="horsemanTarget"  
  22.         p:interceptorNames="advisor"  
  23.         p:proxyTargetClass="true" />  
  24.     <bean id="swordman" class="org.springframework.aop.framework.ProxyFactoryBean"  
  25.         p:target-ref="swordmanTarget"  
  26.         p:interceptorNames="advisor"  
  27.         p:proxyTargetClass="true" />  
  28. </beans>  

测试代码

 

 

[java]  view plain  copy
 
 print?在CODE上查看代码片派生到我的代码片
  1. public static void main(String[] args) {  
  2.     ApplicationContext context = new ClassPathXmlApplicationContext("examples/chap01/applicationContext.xml");  
  3.     Horseman hm = (Horseman)context.getBean("horseman");  
  4.     Swordman sm = (Swordman)context.getBean("swordman");  
  5.     hm.rush("Ghoul");  
  6.     hm.chop("Ghoul");  
  7.     sm.block("Ghoul");  
  8.     sm.chop("Ghoul");  
  9. }  

 

可以修改pointcut配置中的methodOption和classOption值类观察切点的匹配效果。

 

注意配置文件中,使用了DefaultPointcutAdvisor类作为ProxyFactoryBean的interceptorNames属性。ProxyFactoryBean必须同时取得切点和增强才能创建代理,所以不能把Pointcut直接注入interceptorNames。Spring中接口Advisor表示切面,一个切面既包含了增强,也包含了切点信息。

在前面章节的示例中,可以直接用Advice作为interceptor,这是因为Advice包含了默认的切点信息,即目标类的所有方法。

实际上,上面的示例可以直接使用静态方法切面StaticMethodMatcherPointcutAdvisor来简化:

删除StoragePointcut类并增加一个新类StoragePointcutAdvisor

 

[java]  view plain  copy
 
 print?在CODE上查看代码片派生到我的代码片
  1. public class StoragePointcutAdvisor extends StaticMethodMatcherPointcutAdvisor {  
  2.   
  3.     @Override  
  4.     public boolean matches(Method method, Class<?> cls) {  
  5.         switch(methodOption) {  
  6.         case 1:  
  7.             return "chop".equals(method.getName());  
  8.         case 2:  
  9.             return "rush".equals(method.getName());  
  10.         default:  
  11.             return true;  
  12.         }  
  13.     }  
  14.   
  15.     public ClassFilter getClassFilter() {  
  16.   
  17.         return new ClassFilter() {  
  18.             public boolean matches(Class cls) {  
  19.                 switch(classOption) {  
  20.                 case 1:  
  21.                     return (Horseman.class.isAssignableFrom(cls));  
  22.                 case 2:  
  23.                     return (Swordman.class.isAssignableFrom(cls));  
  24.                 default:  
  25.                     return true;  
  26.                 }  
  27.             }  
  28.         };  
  29.     }  
  30.   
  31.     private int methodOption;  
  32.     private int classOption;  
  33.   
  34.     public void setMethodOption(int methodOption) {  
  35.         this.methodOption = methodOption;  
  36.     }  
  37.   
  38.     public void setClassOption(int classOption) {  
  39.         this.classOption = classOption;  
  40.     }  
  41. }  

简化配置文件

 

 

[java]  view plain  copy
 
 print?在CODE上查看代码片派生到我的代码片
  1. <bean id="beforeAdvice" class="examples.chap01.BeforeAdvice" />  
  2. <bean id="horsemanTarget" class="examples.chap01.Horseman" />  
  3. <bean id="swordmanTarget" class="examples.chap01.Swordman" />  
  4. <bean id="advisor" class="examples.chap01.StoragePointcutAdvisor"  
  5.     p:advice-ref="beforeAdvice" />  
  6.   
  7. <bean id="horseman" class="org.springframework.aop.framework.ProxyFactoryBean"  
  8.     p:target-ref="horsemanTarget"  
  9.     p:interceptorNames="advisor"  
  10.     p:proxyTargetClass="true" />  
  11. <bean id="swordman" class="org.springframework.aop.framework.ProxyFactoryBean"  
  12.     p:target-ref="swordmanTarget"  
  13.     p:interceptorNames="advisor"  
  14.     p:proxyTargetClass="true" />  

 

即使简化后,代码仍然很臃肿。下面来看更简单直接的办法,使用正则表达式来定义切点

2 静态正则表达式方法切面

修改配置文件applicationContext.xml

 

[java]  view plain  copy
 
 print?在CODE上查看代码片派生到我的代码片
  1. <bean id="beforeAdvice" class="examples.chap01.BeforeAdvice" />  
  2. <bean id="horsemanTarget" class="examples.chap01.Horseman" />  
  3. <bean id="swordmanTarget" class="examples.chap01.Swordman" />  
  4. <bean id="regexpAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"  
  5.     p:advice-ref="beforeAdvice"  
  6.     p:patterns=".*chop" />  
  7.   
  8. <bean id="horseman1" class="org.springframework.aop.framework.ProxyFactoryBean"  
  9.     p:target-ref="horsemanTarget"  
  10.     p:interceptorNames="regexpAdvisor"  
  11.     p:proxyTargetClass="true" />  
  12. <bean id="swordman1" class="org.springframework.aop.framework.ProxyFactoryBean"  
  13.     p:target-ref="swordmanTarget"  
  14.     p:interceptorNames="regexpAdvisor"  
  15.     p:proxyTargetClass="true" />  

测试代码

 

 

[java]  view plain  copy
 
 print?在CODE上查看代码片派生到我的代码片
  1. ApplicationContext context = new ClassPathXmlApplicationContext("examples/chap01/applicationContext.xml");  
  2. Horseman hm1 = (Horseman)context.getBean("horseman1");  
  3. Swordman sm1 = (Swordman)context.getBean("swordman1");  
  4. hm1.rush("Ghoul");  
  5. hm1.chop("Ghoul");  
  6. sm1.block("Ghoul");  
  7. sm1.chop("Ghoul");  

正则表达式 .*chop 表示所有目标类中的chop方法,如果对正则表达式语法有兴趣可以参考更专业的书籍。

上面两个切面都是在运行前指定匹配的类和方法,现在我们接到一个奇怪的需求,因为攻击前的"蓄力"会花费时间,所以仅当敌人是Abomination(憎恶,游戏中的大型兵种)时,才进行“蓄力”,这时,可以使用动态切面。

3 动态切面

动态切点DynamicMethodMatcherPointcut相对于StaticMethodMatcherPointcut增加了一个重载的matches方法用于检查运行时的输入参数

 

[java]  view plain  copy
 
 print?在CODE上查看代码片派生到我的代码片
  1. public class DynamicStoragePointcut extends DynamicMethodMatcherPointcut {  
  2.   
  3.     //对方法做动态的输入参数匹配检查  
  4.     @Override  
  5.     public boolean matches(Method method, Class<?> cls, Object... args) {  
  6.         return args[0].equals("Abomination");  
  7.     }  
  8.       
  9.     @Override  
  10.     public boolean matches(Method method, Class<?> cls) {  
  11.         switch(methodOption) {  
  12.         case 1:  
  13.             return "chop".equals(method.getName());  
  14.         case 2:  
  15.             return "rush".equals(method.getName());  
  16.         default:  
  17.             return true;  
  18.         }  
  19.     }  
  20.   
  21.     public ClassFilter getClassFilter() {  
  22.   
  23.         return new ClassFilter() {  
  24.             public boolean matches(Class<?> cls) {  
  25.                 switch(classOption) {  
  26.                 case 1:  
  27.                     return (Horseman.class.isAssignableFrom(cls));  
  28.                 case 2:  
  29.                     return (Swordman.class.isAssignableFrom(cls));  
  30.                 default:  
  31.                     return true;  
  32.                 }  
  33.             }  
  34.         };  
  35.     }  
  36.   
  37.     private int methodOption;  
  38.     private int classOption;  
  39.   
  40.     public void setMethodOption(int methodOption) {  
  41.         this.methodOption = methodOption;  
  42.     }  
  43.   
  44.     public void setClassOption(int classOption) {  
  45.         this.classOption = classOption;  
  46.     }  
  47. }  

修改配置文件

 

 

[java]  view plain  copy
 
 print?在CODE上查看代码片派生到我的代码片
  1. <bean id="beforeAdvice" class="examples.chap01.BeforeAdvice" />  
  2. <bean id="horsemanTarget" class="examples.chap01.Horseman" />  
  3. <bean id="swordmanTarget" class="examples.chap01.Swordman" />  
  4. <bean id="pointcut" class="examples.chap02.DynamicStoragePointcut"  
  5.     p:methodOption="1"  
  6.     p:classOption="0" />  
  7. <bean id="advisor" class="org.springframework.aop.support.DefaultPointcutAdvisor"  
  8.     p:advice-ref="beforeAdvice"  
  9.     p:pointcut-ref="pointcut" />  
  10.   
  11. <bean id="horseman" class="org.springframework.aop.framework.ProxyFactoryBean"  
  12.     p:target-ref="horsemanTarget"  
  13.     p:interceptorNames="advisor"  
  14.     p:proxyTargetClass="true" />  
  15. <bean id="swordman" class="org.springframework.aop.framework.ProxyFactoryBean"  
  16.     p:target-ref="swordmanTarget"  
  17.     p:interceptorNames="advisor"  
  18.     p:proxyTargetClass="true" />  

测试代码

 

 

[java]  view plain  copy
 
 print?在CODE上查看代码片派生到我的代码片
  1. public static void main(String[] args) {  
  2.     ApplicationContext context = new ClassPathXmlApplicationContext("examples/chap02/applicationContext.xml");  
  3.     Horseman hm = (Horseman)context.getBean("horseman");  
  4.     Swordman sm = (Swordman)context.getBean("swordman");  
  5.     hm.rush("Ghoul");  
  6.     hm.chop("Ghoul");  
  7.     sm.block("Ghoul");  
  8.     sm.chop("Ghoul");  
  9.     hm.rush("Abomination");  
  10.     hm.chop("Abomination");  
  11.     sm.block("Abomination");  
  12.     sm.chop("Abomination");  
  13. }  

注意,使用动态切面检查必须在执行期间根据输入参数值来确定,无法缓存结果,每次调用目标方法都会执行该检查,可能会影响性能,应慎重使用。同时开发时应覆盖matches(Method method, Class<?> cls)和getClassFilter()方法以通过静态检查排除掉大部分方法。

转载于:https://www.cnblogs.com/sa-dan/p/6837202.html

相关文章:

  • 牛逼!阿里推出国产开源的jdk! 快来试试吧!
  • ES6 中的let 声明变量
  • 原来Java类的加载过程是这样的?
  • 淘宝数据库OceanBase SQL编译器部分 源代码阅读--生成物理查询计划
  • 聊聊jvm几种垃圾收集器
  • 搭建 webpack + React 开发环境
  • jvm垃圾回收的过程
  • 到底什么是分布式锁,进程锁,线程锁
  • 晶振參数校定
  • 这样做能让nginx新能提升10倍
  • 查看项目错误信息
  • 简单说明String类为什么是final的
  • RocketMQ启动broker提示 错误:找不到或无法加载主类
  • CSS盒子模型
  • 总结HashMap和TreeMap的区别
  • JavaScript 如何正确处理 Unicode 编码问题!
  • CSS选择器——伪元素选择器之处理父元素高度及外边距溢出
  • golang 发送GET和POST示例
  • Java超时控制的实现
  • jdbc就是这么简单
  • linux安装openssl、swoole等扩展的具体步骤
  • mysql 数据库四种事务隔离级别
  • Netty 框架总结「ChannelHandler 及 EventLoop」
  • 安卓应用性能调试和优化经验分享
  • 经典排序算法及其 Java 实现
  • 如何邀请好友注册您的网站(模拟百度网盘)
  • 算法---两个栈实现一个队列
  • 移动端解决方案学习记录
  • 译米田引理
  • 用jquery写贪吃蛇
  • 栈实现走出迷宫(C++)
  • HanLP分词命名实体提取详解
  • TPG领衔财团投资轻奢珠宝品牌APM Monaco
  • 教程:使用iPhone相机和openCV来完成3D重建(第一部分) ...
  • 新海诚画集[秒速5センチメートル:樱花抄·春]
  • (2020)Java后端开发----(面试题和笔试题)
  • (52)只出现一次的数字III
  • (阿里巴巴 dubbo,有数据库,可执行 )dubbo zookeeper spring demo
  • (三维重建学习)已有位姿放入colmap和3D Gaussian Splatting训练
  • (四)Android布局类型(线性布局LinearLayout)
  • (转载)深入super,看Python如何解决钻石继承难题
  • .net core MVC 通过 Filters 过滤器拦截请求及响应内容
  • .Net MVC4 上传大文件,并保存表单
  • .vollhavhelp-V-XXXXXXXX勒索病毒的最新威胁:如何恢复您的数据?
  • [ C++ ] STL---string类的使用指南
  • [Android Pro] android 混淆文件project.properties和proguard-project.txt
  • [Android]Android P(9) WIFI学习笔记 - 扫描 (1)
  • [CC2642R1][VSCODE+Embedded IDE+IAR Build+Cortex-Debug] TI CC2642R1基于VsCode的开发环境
  • [Java] 图说 注解
  • [JS入门到进阶] 哎,被vite小坑了一波,大家记得配置build.cssTarget为‘chrome61‘
  • [LeetCode]: 145: Binary Tree Postorder Traversal
  • [Linux_IMX6ULL应用开发]-Makefile
  • [office] 怎么在Excel2003菜单栏自定义一个选项卡 #其他#微信#知识分享
  • [Paper]Application of deep convolutional neural network for automated detection of myocardial...
  • [PAT] 1041 Be Unique (20 分)Java