循序渐进之Spring AOP(5) - 创建切面
在掌握了可用的增强后,接下来要做的就是精确的描述切点。前面的示例都是指定一个目标类并把增强织入到所有方法中,实际开发显然会有更精细的筛选需求,比如对所有类中名称以test结尾的方法加入监控执行时间,或者指定某些方法仅在输入参数是指定值时做某些特殊处理以解决临时性需求。
Spring中用Pointcut接口表示一个切点,其下设有多个实现类,按使用场景分主要有静态切点、动态切点、流程切点和复合切点等。
1 静态方法切面
使用静态方法切点,通常是继承StaticMethodMatcherPointcut,通过覆盖getClassFilter()方法用于确定匹配的类,覆盖matches方法用于确定匹配的方法,Spring对目标类及其上面的方法调用两个方法以确定是否织入增强,检查结果被缓存以提高性能。来看示例:
首先定义两个类用于演示类和方法的筛选, 两个类各有一个同名方法和一个不同名方法
- public class Horseman {
- public void rush(String enemy) {
- System.out.println(this.getClass().getSimpleName() + "冲刺攻击" + enemy);
- }
- public void chop(String enemy) {
- System.out.println(this.getClass().getSimpleName() + "砍劈攻击" + enemy);
- }
- }
- public class Swordman {
- public void block(String enemy) {
- System.out.println(this.getClass().getSimpleName() + "格挡" + enemy);
- }
- public void chop(String enemy) {
- System.out.println(this.getClass().getSimpleName() + "砍劈攻击" + enemy);
- }
- }
设定一个增强,这里选了一个方法前增强
- public class BeforeAdvice implements MethodBeforeAdvice {
- @Override
- public void before(Method method, Object[] args, Object obj)
- throws Throwable {
- System.out.println("Advice:" + obj.getClass().getSimpleName() + "蓄力");
- }
- }
下面是切点类,定义了两个选项参数methodOption和classOption,以方便察看不同切点定义下的执行效果
- public class StoragePointcut extends StaticMethodMatcherPointcut {
- @Override
- public boolean matches(Method method, Class<?> cls) {
- switch(methodOption) {
- case 1:
- return "chop".equals(method.getName());
- case 2:
- return "rush".equals(method.getName());
- default:
- return true;
- }
- }
- public ClassFilter getClassFilter() {
- return new ClassFilter() {
- public boolean matches(Class<?> cls) {
- switch(classOption) {
- case 1:
- return (Horseman.class.isAssignableFrom(cls));
- case 2:
- return (Swordman.class.isAssignableFrom(cls));
- default:
- return true;
- }
- }
- };
- }
- private int methodOption;
- private int classOption;
- public void setMethodOption(int methodOption) {
- this.methodOption = methodOption;
- }
- public void setClassOption(int classOption) {
- this.classOption = classOption;
- }
- }
配置文件applicationContext.xml
- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
- xmlns:context="http://www.springframework.org/schema/context"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
- http://www.springframework.org/schema/context
- http://www.springframework.org/schema/context/spring-context-3.0.xsd">
- <bean id="beforeAdvice" class="examples.chap01.BeforeAdvice" />
- <bean id="horsemanTarget" class="examples.chap01.Horseman" />
- <bean id="swordmanTarget" class="examples.chap01.Swordman" />
- <bean id="pointcut" class="examples.chap01.StoragePointcut"
- p:methodOption="0"
- p:classOption="0" />
- <bean id="advisor" class="org.springframework.aop.support.DefaultPointcutAdvisor"
- p:advice-ref="beforeAdvice"
- p:pointcut-ref="pointcut" />
- <bean id="horseman" class="org.springframework.aop.framework.ProxyFactoryBean"
- p:target-ref="horsemanTarget"
- p:interceptorNames="advisor"
- p:proxyTargetClass="true" />
- <bean id="swordman" class="org.springframework.aop.framework.ProxyFactoryBean"
- p:target-ref="swordmanTarget"
- p:interceptorNames="advisor"
- p:proxyTargetClass="true" />
- </beans>
测试代码
- public static void main(String[] args) {
- ApplicationContext context = new ClassPathXmlApplicationContext("examples/chap01/applicationContext.xml");
- Horseman hm = (Horseman)context.getBean("horseman");
- Swordman sm = (Swordman)context.getBean("swordman");
- hm.rush("Ghoul");
- hm.chop("Ghoul");
- sm.block("Ghoul");
- sm.chop("Ghoul");
- }
可以修改pointcut配置中的methodOption和classOption值类观察切点的匹配效果。
注意配置文件中,使用了DefaultPointcutAdvisor类作为ProxyFactoryBean的interceptorNames属性。ProxyFactoryBean必须同时取得切点和增强才能创建代理,所以不能把Pointcut直接注入interceptorNames。Spring中接口Advisor表示切面,一个切面既包含了增强,也包含了切点信息。
在前面章节的示例中,可以直接用Advice作为interceptor,这是因为Advice包含了默认的切点信息,即目标类的所有方法。
实际上,上面的示例可以直接使用静态方法切面StaticMethodMatcherPointcutAdvisor来简化:
删除StoragePointcut类并增加一个新类StoragePointcutAdvisor
- public class StoragePointcutAdvisor extends StaticMethodMatcherPointcutAdvisor {
- @Override
- public boolean matches(Method method, Class<?> cls) {
- switch(methodOption) {
- case 1:
- return "chop".equals(method.getName());
- case 2:
- return "rush".equals(method.getName());
- default:
- return true;
- }
- }
- public ClassFilter getClassFilter() {
- return new ClassFilter() {
- public boolean matches(Class cls) {
- switch(classOption) {
- case 1:
- return (Horseman.class.isAssignableFrom(cls));
- case 2:
- return (Swordman.class.isAssignableFrom(cls));
- default:
- return true;
- }
- }
- };
- }
- private int methodOption;
- private int classOption;
- public void setMethodOption(int methodOption) {
- this.methodOption = methodOption;
- }
- public void setClassOption(int classOption) {
- this.classOption = classOption;
- }
- }
简化配置文件
- <bean id="beforeAdvice" class="examples.chap01.BeforeAdvice" />
- <bean id="horsemanTarget" class="examples.chap01.Horseman" />
- <bean id="swordmanTarget" class="examples.chap01.Swordman" />
- <bean id="advisor" class="examples.chap01.StoragePointcutAdvisor"
- p:advice-ref="beforeAdvice" />
- <bean id="horseman" class="org.springframework.aop.framework.ProxyFactoryBean"
- p:target-ref="horsemanTarget"
- p:interceptorNames="advisor"
- p:proxyTargetClass="true" />
- <bean id="swordman" class="org.springframework.aop.framework.ProxyFactoryBean"
- p:target-ref="swordmanTarget"
- p:interceptorNames="advisor"
- p:proxyTargetClass="true" />
即使简化后,代码仍然很臃肿。下面来看更简单直接的办法,使用正则表达式来定义切点
2 静态正则表达式方法切面
修改配置文件applicationContext.xml
- <bean id="beforeAdvice" class="examples.chap01.BeforeAdvice" />
- <bean id="horsemanTarget" class="examples.chap01.Horseman" />
- <bean id="swordmanTarget" class="examples.chap01.Swordman" />
- <bean id="regexpAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"
- p:advice-ref="beforeAdvice"
- p:patterns=".*chop" />
- <bean id="horseman1" class="org.springframework.aop.framework.ProxyFactoryBean"
- p:target-ref="horsemanTarget"
- p:interceptorNames="regexpAdvisor"
- p:proxyTargetClass="true" />
- <bean id="swordman1" class="org.springframework.aop.framework.ProxyFactoryBean"
- p:target-ref="swordmanTarget"
- p:interceptorNames="regexpAdvisor"
- p:proxyTargetClass="true" />
测试代码
- ApplicationContext context = new ClassPathXmlApplicationContext("examples/chap01/applicationContext.xml");
- Horseman hm1 = (Horseman)context.getBean("horseman1");
- Swordman sm1 = (Swordman)context.getBean("swordman1");
- hm1.rush("Ghoul");
- hm1.chop("Ghoul");
- sm1.block("Ghoul");
- sm1.chop("Ghoul");
正则表达式 .*chop 表示所有目标类中的chop方法,如果对正则表达式语法有兴趣可以参考更专业的书籍。
上面两个切面都是在运行前指定匹配的类和方法,现在我们接到一个奇怪的需求,因为攻击前的"蓄力"会花费时间,所以仅当敌人是Abomination(憎恶,游戏中的大型兵种)时,才进行“蓄力”,这时,可以使用动态切面。
3 动态切面
动态切点DynamicMethodMatcherPointcut相对于StaticMethodMatcherPointcut增加了一个重载的matches方法用于检查运行时的输入参数
- public class DynamicStoragePointcut extends DynamicMethodMatcherPointcut {
- //对方法做动态的输入参数匹配检查
- @Override
- public boolean matches(Method method, Class<?> cls, Object... args) {
- return args[0].equals("Abomination");
- }
- @Override
- public boolean matches(Method method, Class<?> cls) {
- switch(methodOption) {
- case 1:
- return "chop".equals(method.getName());
- case 2:
- return "rush".equals(method.getName());
- default:
- return true;
- }
- }
- public ClassFilter getClassFilter() {
- return new ClassFilter() {
- public boolean matches(Class<?> cls) {
- switch(classOption) {
- case 1:
- return (Horseman.class.isAssignableFrom(cls));
- case 2:
- return (Swordman.class.isAssignableFrom(cls));
- default:
- return true;
- }
- }
- };
- }
- private int methodOption;
- private int classOption;
- public void setMethodOption(int methodOption) {
- this.methodOption = methodOption;
- }
- public void setClassOption(int classOption) {
- this.classOption = classOption;
- }
- }
修改配置文件
- <bean id="beforeAdvice" class="examples.chap01.BeforeAdvice" />
- <bean id="horsemanTarget" class="examples.chap01.Horseman" />
- <bean id="swordmanTarget" class="examples.chap01.Swordman" />
- <bean id="pointcut" class="examples.chap02.DynamicStoragePointcut"
- p:methodOption="1"
- p:classOption="0" />
- <bean id="advisor" class="org.springframework.aop.support.DefaultPointcutAdvisor"
- p:advice-ref="beforeAdvice"
- p:pointcut-ref="pointcut" />
- <bean id="horseman" class="org.springframework.aop.framework.ProxyFactoryBean"
- p:target-ref="horsemanTarget"
- p:interceptorNames="advisor"
- p:proxyTargetClass="true" />
- <bean id="swordman" class="org.springframework.aop.framework.ProxyFactoryBean"
- p:target-ref="swordmanTarget"
- p:interceptorNames="advisor"
- p:proxyTargetClass="true" />
测试代码
- public static void main(String[] args) {
- ApplicationContext context = new ClassPathXmlApplicationContext("examples/chap02/applicationContext.xml");
- Horseman hm = (Horseman)context.getBean("horseman");
- Swordman sm = (Swordman)context.getBean("swordman");
- hm.rush("Ghoul");
- hm.chop("Ghoul");
- sm.block("Ghoul");
- sm.chop("Ghoul");
- hm.rush("Abomination");
- hm.chop("Abomination");
- sm.block("Abomination");
- sm.chop("Abomination");
- }
注意,使用动态切面检查必须在执行期间根据输入参数值来确定,无法缓存结果,每次调用目标方法都会执行该检查,可能会影响性能,应慎重使用。同时开发时应覆盖matches(Method method, Class<?> cls)和getClassFilter()方法以通过静态检查排除掉大部分方法。
循序渐进之Spring AOP(5) - 创建切面相关推荐
- Spring AOP自动创建代理 和 ProxyFactoryBean创建代理
Advice 通知类型 AOP联盟为通知Advice定义了org.aopalliance.aop.Interface.Advice,Spring按照通知Advice在目标方法的连接点位置,可以分为5种 ...
- SpringBoot 框架中 使用Spring Aop 、创建注解、创建枚举类 使用过程记录
1.开始 在Springboot框架中引入AOP <dependency><groupId>org.springframework.boot</groupId>&l ...
- 【JavaEE】Spring AOP (面向切面)详解
目录: 1. 什么是 Spring AOP? 1.1 AOP 1.2 使用 AOP 的场景 2. AOP 组成 2.1 切面(Aspect) 2.2 连接点(Join Point) 2.3 切点(Po ...
- spring aop如何在切面类中获取切入点相关方法的参数、方法名、返回值、异常等信息
aop思想可以很好的帮助我们实现代码的解耦,比如我们之前提到的,将日志代码与业务层代码完全独立,通过spring aop的代理类进行整合.在切面类中,我们也能够通过spring提供的接口,很好的获取原 ...
- spring aop 申明了切面类之后,如何申明切入点呢?
2019独角兽企业重金招聘Python工程师标准>>> 8.2.3 Declaring a pointcut Recall that pointcuts determine join ...
- Spring AOP动态代理-切面
2019独角兽企业重金招聘Python工程师标准>>> 在上一节中,我们通过Advice可以对目标类进行增强,使得目标类在调用的时候可以执行增强类中的代码,但是,增强类适配了目标类中 ...
- Spring AOP(通知、连接点、切点、切面)
一.AOP术语 通知(Advice) 切面的工作被称为通知.通知定义了切面是什么以及何时使用.除了描述切面要完成的工作,通知还解决了何时执行这个工作的问题. 5种通知类型: 前置通知(Before ...
- JavaEE——Spring AOP(面向切面编程)
目录 1.面向切面编程(AOP) 2.AOP术语 3.AOP类型 4.AOP 的优势 5.Spring AOP 的代理机制 6.Spring AOP 连接点 7.Spring AOP 通知类型 8.基 ...
- Spring AOP的一些概念
切面(Aspect): 一个关注点的模块化,这个关注点可能会横切多个对象.事务管理是J2EE应用中一个关于横切关注点的很好的例子. 在Spring AOP中,切面可以使用通用类(基于模 ...
- 比较Spring AOP与AspectJ
本文翻译自博客Comparing Spring AOP and AspectJ 介绍 如今有多个可用的AOP库,这些组件需要回答一系列的问题: 是否与我现有的应用兼容? 我在哪实现AOP? 集成到我的 ...
最新文章
- 南洋理工75页最新「深度学习对话系统」大综述论文,最全面概述深度学习对话技术进展...
- SQL获取最新版本Version记录
- Linux目录结构FHS
- HDU Problem 2062 Bone Collector【01背包】
- 从nginx-rtmp中提取一帧h264帧
- java一般方法有哪些方法有哪些方法_Java代码优化有哪些方法?
- keepalived+LVS实现高可用的Web负载均衡
- 利用ajax,巧妙的sql语句组合,轻松做出不错的树型菜单
- SYBASE数据导入技巧
- html垂直线性渐变,html5线性渐变
- js判断对象为空_在 JavaScript 中如何检查对象为空
- 戴尔服务器哪1顶型号好,戴尔PowerEdge R730xd新一代服务器评测
- Anders Hejlsberg谈C#、Java和C++中的泛型
- 鱼C论坛_VIP二号光盘
- 手机能识别sim卡但是没信号_一篇文章扫盲手机SIM卡相关知识
- 安卓基于图像识别和CNN做出一个通用的斗地主记牌器(一)
- 计算机用户名显示TEMP,Windows 下Temp帐号处理
- Python 骚操作:微信远程控制电脑(转载)
- css只设置背景图片半透明,css3实现背景图片半透明内容不透明的方法示例
- Android TabLayout设置选中状态标题字体大小,粗细