在掌握了可用的增强后,接下来要做的就是精确的描述切点。前面的示例都是指定一个目标类并把增强织入到所有方法中,实际开发显然会有更精细的筛选需求,比如对所有类中名称以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) - 创建切面的更多相关文章

  1. spring AOP AspectJ 定义切面实现拦截

    总结记录一下AOP常用的应用场景及使用方式,如有错误,请留言. 1.  讲AOP之前,先来总结web项目的几种拦截方式    A:  过滤器 使用过滤器可以过滤URL请求,以及请求和响应的信息,但是过 ...

  2. Spring AOP:面向切面编程,AspectJ,是基于spring 的xml文件的方法

    导包等不在赘述: 建立一个接口:ArithmeticCalculator,没有实例化的方法: package com.atguigu.spring.aop.impl.panpan; public in ...

  3. Spring AOP:面向切面编程,AspectJ,是基于注解的方法

    面向切面编程的术语: 切面(Aspect): 横切关注点(跨越应用程序多个模块的功能)被模块化的特殊对象 通知(Advice): 切面必须要完成的工作 目标(Target): 被通知的对象 代理(Pr ...

  4. Spring -- aop(面向切面编程),前置&后置&环绕&抛异常通知,引入通知,自动代理

    1.概要 aop:面向方面编程.不改变源代码,还为类增加新的功能.(代理) 切面:实现的交叉功能. 通知:切面的实际实现(通知要做什么,怎么做). 连接点:应用程序执行过程期间,可以插入切面的地点. ...

  5. Spring AOP(面向切面示例)

    什么是AOP?基本概念切面(aspect):横切关注点被模块化的特殊对象.通知(advice):切面必须要完成的工作.切面中的每个方向称之为通知.通知是在切面对象中的.目标(target):被通知的对 ...

  6. 循序渐进之Spring AOP(3) - 配置代理

    上一篇介绍了几种Advice(增强),并通过代码演示了生成代理的方式,下面来看通过配置文件配置方式把Advice织入目标类. 注意,配置文件方式仍然不是spring AOP的最好方式,学习配置方式也是 ...

  7. 吴裕雄--天生自然JAVA SPRING框架开发学习笔记:Spring AOP(面向切面编程)

    面向切面编程(AOP)和面向对象编程(OOP)类似,也是一种编程模式.Spring AOP 是基于 AOP 编程模式的一个框架,它的使用有效减少了系统间的重复代码,达到了模块间的松耦合目的. AOP ...

  8. 循序渐进之Spring AOP(1) - 原理

    AOP全称是Aspect Oriented Programing,通常译为面向切面编程.利用AOP可以对面向对象编程做很好的补充. 用生活中的改装车比喻,工厂用面向对象的方法制造好汽车后,车主往往有些 ...

  9. Spring AOP 自动创建代理

        Spring为我们提供了自动代理机制,让容器为我们自动生成代理,把我们从烦琐的配置工作中解放出来,在内部,Spring 使用BeanPostProcessor自动地完成这项工作.   1.实现 ...

随机推荐

  1. Sprint第二个冲刺(第十一天)

    看板: 燃尽图:

  2. vs2010编译出的exe“应用程序无法正常启动(0xc0150002)”

    今天编译出一个使用ogre1.6.5动态库的应用程序,启动时报"应用程序无法正常启动(0xc0150002)"的错误提示. 编译环境是Win10+VS2010.这个错误可以在Win ...

  3. SCRUM项目 5.0

    5.0--------------------------------------------------- 1.团队成员完成自己认领的任务. 2.燃尽图:理解.设计并画出本次Sprint的燃尽图的理 ...

  4. Android画图Path的使用

    /**       * Paint类介绍       *        * Paint即画笔,在绘图过程中起到了极其重要的作用,画笔主要保存了颜色,       * 样式等绘制信息,指定了如何绘制文本 ...

  5. MSSQL FOR MXL PATH 运用(转载)

    FOR XML PATH 有的人可能知道有的人可能不知道,其实它就是将查询结果集以XML形式展现,有了它我们可以简化我们的查询语句实现一些以前可能需要借助函数活存储过程来完成的工作.那么以一个实例为主 ...

  6. Win7 Update 遭遇8024200D

    可用办法如下: 1.点击开始菜单,点运行,输入cmd然后按回车.然后在命令行下输入: net stop WuAuServ 2.点击开始菜单,点运行,输入%windir%然后按回车. 3.在打开的文件夹 ...

  7. [Linux] CentOS 加入开机启动

    1.在/etc/init.d/目录下新建一个文件:autostart.sh #!/bin/sh #chkconfig: 2345 80 80 #description: auto start web ...

  8. [内核同步]Linux内核同步机制之completion

    转自:http://blog.csdn.net/bullbat/article/details/7401688 内核编程中常见的一种模式是,在当前线程之外初始化某个活动,然后等待该活动的结束.这个活动 ...

  9. 通过Java Api与HBase交互(转)

    HBase提供了Java Api的访问接口,掌握这个就跟Java应用使用RDBMS时需要JDBC一样重要,本文将继续前两篇文章中blog表的示例,介绍常用的Api. import java.io.IO ...

  10. NeHe OpenGL教程 第四十八课:轨迹球

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...