• 概述
  • 实例
  • 结论

概述

低版本中,Spring提供了用于创建动态切面的DynamicMethodMatcherPointcutAdvisor抽象类,这个抽象类在2.0已过时,现在可以使用DefaultPointcutAdvisorDynamicMethodMatcherPointcut来完成相同的功能。

DynamicMethodMatcherPointcut是一个抽象类,它将 isRuntime()标识位final并返回true,这样其子类就一定是一个动态切点。 该抽象类默认匹配所有的类和方法,因此需要扩展该类编写符合要求的动态切点


实例

代码已托管到Github—> https://github.com/yangshangwei/SpringMaster

目标:我们使用动态切面对特定的客户进行织入前置增强的横切代码。

我们先看下动态切点

package com.xgj.aop.spring.advisor.DynamicAdvisor;import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;import org.springframework.aop.ClassFilter;
import org.springframework.aop.support.DynamicMethodMatcherPointcut;public class GreetingDynamicPointcut extends DynamicMethodMatcherPointcut {public static List<String> specialClientList = new ArrayList<String>();static {specialClientList.add("XiaoGongJiangOne");specialClientList.add("XiaoGongJiangTwo");}/*** (1)对类进行静态切点检查*/public ClassFilter getClassFilter() {return new ClassFilter() {@Overridepublic boolean matches(Class<?> clazz) {System.out.println("调用getClassFilter()对 类【 " + clazz.getName()+ "】做静态检查\n");return Waiter.class.isAssignableFrom(clazz);}};}/*** (2)对方法进行静态切点检查*/@Overridepublic boolean matches(Method method, Class<?> targetClass) {System.out.println("调用matches(Method method, Class<?> targetClass),对方法【"+ targetClass.getName() + "." + method.getName()+ "】做静态检查\n");return "greetTo".equals(method.getName());}/*** (3)对方法进行动态切点检查*/@Overridepublic boolean matches(Method method, Class<?> targetClass, Object... args) {System.out.println("调用matches(Method method, Class<?> targetClass, Object... args)对方法【"+ targetClass.getName()+ "."+ method.getName()+ "】做动态检查\n");String clientName = (String) args[0];return specialClientList.contains(clientName);}
}

我们可以看到GreetingDynamicPointcut 类既有用于静态切点检查的方法,又有动态切点检查的方法。

由于动态切点检查会对性能造成很大的影响,所以应当尽量避免在运行时每次都对目标类的各个方法进行动态检查。

Spring采用的机制如下: 在创建代理时对目标类的每个连接点使用静态切点检查,如果仅通过静态切点检查就知可以知道连接点是不匹配的,这在运行时就会进行动态检查。 反之,则进行动态切点检查

在动态切点类中定义静态切点检查的方法可以避免不必要的动态检查操作,从而极大地提高运行效率。

我们在(3)处通过 matches(Method method, Class<?> targetClass, Object... args)定义了动态切点检查的方法,结合(2)处只对目标方法为greetTo(clientName)且clientName为特殊客户的方法启用增强,通过specialClientList模拟特殊的客户名单。


前置增强同之前的一样,我们来看下

package com.xgj.aop.spring.advisor.DynamicAdvisor;import java.lang.reflect.Method;import org.springframework.aop.MethodBeforeAdvice;public class GreetingBeforeAdvice implements MethodBeforeAdvice {@Overridepublic void before(Method method, Object[] args, Object target)throws Throwable {// 输出切点System.out.println("Pointcut:" + target.getClass().getName() + "."+ method.getName());String clientName = (String) args[0];System.out.println("How are you " + clientName + " ?");}}

编写好动态切点和增强后, 我们通过Spring配置文件中装配出一个动态切面

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:p="http://www.springframework.org/schema/p"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><!-- 目标对象 --><bean id="waiterTarget" class="com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter"/><!-- 第一种写法 <bean id="dynamicAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor"><property name="pointcut"><bean class="com.xgj.aop.spring.advisor.DynamicAdvisor.GreetingDynamicPointcut"/></property><property name="advice"><bean class="com.xgj.aop.spring.advisor.DynamicAdvisor.GreetingBeforeAdvice"/></property></bean>  -->    <!-- 前置增强 --><bean id="greetBeforeAdvice" class="com.xgj.aop.spring.advisor.DynamicAdvisor.GreetingBeforeAdvice"/><!-- 切点 --><bean id="greetingDynamicPointcut" class="com.xgj.aop.spring.advisor.DynamicAdvisor.GreetingDynamicPointcut"/><!-- 切面 --> <bean id="dynamicAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor"p:pointcut-ref="greetingDynamicPointcut"p:advice-ref="greetBeforeAdvice"/> <!-- 代理类 --><bean id="waiter" class="org.springframework.aop.framework.ProxyFactoryBean"p:interceptorNames="dynamicAdvisor"p:target-ref="waiterTarget"p:proxyTargetClass="true"/>
</beans>

我们可以看到动态切面的配置和静态切面的配置没有什么区别。 静态切面的配置可以参考 http://blog.csdn.net/yangshangwei/article/details/77393421 比对下,一样的。

使用org.springframework.aop.support.DefaultPointcutAdvisor定义切面,注入动态切点greetingDynamicPointcut,织入增强greetBeforeAdvice。 当然了DefaultPointcutAdvisor还有个order属性,用于定义切面的织入顺序。


现在我们写个测试类,运行下

package com.xgj.aop.spring.advisor.DynamicAdvisor;import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class DynamicAdvisorTest {@Testpublic void test() {ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:com/xgj/aop/spring/advisor/DynamicAdvisor/conf-dynamicAdvisor.xml");Waiter waiter = ctx.getBean("waiter", Waiter.class);// list中的特殊客户waiter.greetTo("XiaoGongJiangOne");waiter.serverTo("XiaoGongJiangOne");// list中的特殊客户waiter.greetTo("XiaoGongJiangTwo");waiter.serverTo("XiaoGongJiangTwo");// 不在list中的客户waiter.greetTo("XiaoGongJiang1");waiter.serverTo("XiaoGongJiang1");}
}

运行结果:

2017-08-20 01:41:20,872  INFO [main] (AbstractApplicationContext.java:583) - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@3b293677: startup date [Sun Aug 20 01:41:20 BOT 2017]; root of context hierarchy
2017-08-20 01:41:20,989  INFO [main] (XmlBeanDefinitionReader.java:317) - Loading XML bean definitions from class path resource [com/xgj/aop/spring/advisor/DynamicAdvisor/conf-dynamicAdvisor.xml]
调用getClassFilter()对 类【 com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter】做静态检查调用matches(Method method, Class<?> targetClass),对方法【com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter.greetTo】做静态检查调用getClassFilter()对 类【 com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter】做静态检查调用matches(Method method, Class<?> targetClass),对方法【com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter.serverTo】做静态检查调用getClassFilter()对 类【 com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter】做静态检查调用matches(Method method, Class<?> targetClass),对方法【com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter.toString】做静态检查调用getClassFilter()对 类【 com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter】做静态检查调用matches(Method method, Class<?> targetClass),对方法【com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter.clone】做静态检查调用getClassFilter()对 类【 com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter】做静态检查调用matches(Method method, Class<?> targetClass),对方法【com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter.greetTo】做静态检查调用matches(Method method, Class<?> targetClass, Object... args)对方法【com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter.greetTo】做动态检查Pointcut:com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter.greetTo
How are you XiaoGongJiangOne ?
Waiter Greet To XiaoGongJiangOne
调用getClassFilter()对 类【 com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter】做静态检查调用matches(Method method, Class<?> targetClass),对方法【com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter.serverTo】做静态检查Waiter Server To XiaoGongJiangOne
调用matches(Method method, Class<?> targetClass, Object... args)对方法【com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter.greetTo】做动态检查Pointcut:com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter.greetTo
How are you XiaoGongJiangTwo ?
Waiter Greet To XiaoGongJiangTwo
Waiter Server To XiaoGongJiangTwo
调用matches(Method method, Class<?> targetClass, Object... args)对方法【com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter.greetTo】做动态检查Waiter Greet To XiaoGongJiang1
Waiter Server To XiaoGongJiang1

我们来分析下:

System.out日志打印的前8行输出信息反应了在织入切面Spring对目标类中所有的方法进行的静态切点检查。

接下来的几行日志

调用getClassFilter()对 类【 com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter】做静态检查调用matches(Method method, Class<?> targetClass),对方法【com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter.greetTo】做静态检查调用matches(Method method, Class<?> targetClass, Object... args)对方法【com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter.greetTo】做动态检查Pointcut:com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter.greetTo
How are you XiaoGongJiangOne ?
Waiter Greet To XiaoGongJiangOne

对Waiter.greetTo(“XiaoGongJiangOne”)第一次调用greetTo方法时,执行静态、动态切点检查

接下来的日志

调用getClassFilter()对 类【 com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter】做静态检查调用matches(Method method, Class<?> targetClass),对方法【com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter.serverTo】做静态检查Waiter Server To XiaoGongJiangOne

对Waiter.serverTo(“XiaoGongJiangOne”)第一次调用serverTo方法时,执行静态切点检查.

接下来的日志

调用matches(Method method, Class<?> targetClass, Object... args)对方法【com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter.greetTo】做动态检查Pointcut:com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter.greetTo
How are you XiaoGongJiangTwo ?
Waiter Greet To XiaoGongJiangTwo

对Waiter.greetTo(“XiaoGongJiangOne”)第二次调用greetTo方法时,仅仅执行动态切点检查。

接下的日志

Waiter Server To XiaoGongJiangTwo

对Waiter.serverTo(“XiaoGongJiangOne”)第二次调用serverTo方法时,不再执行静态切点检查.

接下来的日志

调用matches(Method method, Class<?> targetClass, Object... args)对方法【com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter.greetTo】做动态检查Waiter Greet To XiaoGongJiang1

对waiter.greetTo(“XiaoGongJiang1”),第三次调greetTo方法时只执行动态切点检查

最后一行日志

Waiter Server To XiaoGongJiang1

第三次调用 serverTo,不再执行静态切点检查


通过上述的分析,结合GreetingDynamicPointcut切点类,可以很容易的发现,

  • Spring会在创建代理织入切面时,对目标类中的所有方法进行静态切点检查

  • 在生成织入切面的代理对象后,第一次调用代理类的每一个方法都会进行一次静态切点检查,如果本次检查就能够从候选者列表中排除改方法,则以后对该方法就不会再执行静态切点检查

  • 对于那些在静态切点检查时匹配的方法,在后续调用该方法时,将执行动态切点检查

在我们的案例中,切点匹配的规则是:
1.目标类为com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter或者其子类,
2.方法名为greetTo,
3.动态入参clientName必须是特殊名单中的客户。

基于这条规则,serverTo()以及Object继承而来的 toString和clone()方法通过静态切点检查就可以排除在候选者之外,只有greetTo方法是动态切点检查的候选者,每次调用都会进行动态切点检查。


如果我们将GreetingDynamicPointcut类中 对类和方法的静态切点注释掉,重新运行日志如下:

2017-08-20 02:20:53,908  INFO [main] (AbstractApplicationContext.java:583) - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@3b293677: startup date [Sun Aug 20 02:20:53 BOT 2017]; root of context hierarchy
2017-08-20 02:20:54,004  INFO [main] (XmlBeanDefinitionReader.java:317) - Loading XML bean definitions from class path resource [com/xgj/aop/spring/advisor/DynamicAdvisor/conf-dynamicAdvisor.xml]
调用matches(Method method, Class<?> targetClass, Object... args)对方法【com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter.greetTo】做动态检查Pointcut:com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter.greetTo
How are you XiaoGongJiangOne ?
Waiter Greet To XiaoGongJiangOne
调用matches(Method method, Class<?> targetClass, Object... args)对方法【com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter.serverTo】做动态检查Pointcut:com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter.serverTo
How are you XiaoGongJiangOne ?
Waiter Server To XiaoGongJiangOne
调用matches(Method method, Class<?> targetClass, Object... args)对方法【com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter.greetTo】做动态检查Pointcut:com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter.greetTo
How are you XiaoGongJiangTwo ?
Waiter Greet To XiaoGongJiangTwo
调用matches(Method method, Class<?> targetClass, Object... args)对方法【com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter.serverTo】做动态检查Pointcut:com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter.serverTo
How are you XiaoGongJiangTwo ?
Waiter Server To XiaoGongJiangTwo
调用matches(Method method, Class<?> targetClass, Object... args)对方法【com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter.greetTo】做动态检查Waiter Greet To XiaoGongJiang1
调用matches(Method method, Class<?> targetClass, Object... args)对方法【com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter.serverTo】做动态检查Waiter Server To XiaoGongJiang1

可以发现,每次调用代理对象的任何一个方法,都会执行动态切点检查,这将导致很大的性能问题。


结论

在定义动态切点时,切勿忘记同时覆盖getClassFilter()boolean matches(Method method, Class<?> targetClass)方法,通过静态切点检查排除大部分方法,从而提高程序运行效率

Spring-AOP 动态切面相关推荐

  1. Java绝地求生—Spring AOP面向切面编程

    Java绝地求生-Spring AOP面向切面编程 背景 动态代理 构建被代理对象 自动生成代理 调用动态代理 Spring方法 方式一:使用Spring的API接口 方式二:使用自定义类 方式三:使 ...

  2. Spring AOP动态代理实现,解决Spring Boot中无法正常启用JDK动态代理的问题

    Spring AOP动态代理实现,解决Spring Boot中无法正常启用JDK动态代理的问题 参考文章: (1)Spring AOP动态代理实现,解决Spring Boot中无法正常启用JDK动态代 ...

  3. java切面不需要接口了吗_详解Spring AOP 实现“切面式”valid校验

    why: 为什么要用aop实现校验? answer: spring mvc 默认自带的校验机制 @Valid + BindingResult, 但这种默认实现都得在Controller方法的中去接收B ...

  4. Spring AOP 面向切面编程

    AOP 在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是OOP的延续,是软件 ...

  5. Spring AOP(面向切面编程)

    AOP(Aspect Oriented Programming),也就是面向切面编程,作为面向对象编程的一种补充,AOP已经成为一种比较成熟的编程方式.可以这样理解:OOP是从静态角度考虑程序结构,而 ...

  6. java框架013——Spring AOP面向切面编程

    一.Spring AOP简介 AOP的全称是Aspect-Oriented Programming,即面向切面编程(也称面向方面编程).它是面向对象编程(OOP)的一种补充,目前已成为一种比较成熟的编 ...

  7. Spring AOP动态代理

    导入依赖: <dependency><groupId>org.springframework</groupId><artifactId>spring-a ...

  8. JavaEE——Spring AOP(面向切面编程)

    目录 1.面向切面编程(AOP) 2.AOP术语 3.AOP类型 4.AOP 的优势 5.Spring AOP 的代理机制 6.Spring AOP 连接点 7.Spring AOP 通知类型 8.基 ...

  9. spring aop(面向切面编程)

    aop分为两种,一种是静态代理(不常用),另一种是动态代理  . 静态代理的优点,及缺点: 优点:代理是客户端不需要知道实现类是什么,怎么做的,客户只需要知道代理就可以了.缺点:代理类和委托类都实现了 ...

  10. Spring: Spring AOP 方面/切面(Aspect)

    1.美图 2.概述 概念参考:Spring :Spring AOP 中的一些术语 由于增强包括横切代码,又包含部分连接点信息(方法前.方法后主方位信息),所以可以仅通过增强类生成一个切面. 但切点仅仅 ...

最新文章

  1. linux fork 目录,linux fork()理解
  2. elasticsearch的父子_elasticsearch父子关系(官方)实际使用中的一些建议
  3. Postman 调试技巧
  4. Hbase 技术细节笔记(上)
  5. UART的FIFO功能
  6. golang 切片 接口_一日看尽golang高级语法之slice
  7. What every programmer needs to know about game networking
  8. 每周「Paper + Code」清单:句子嵌入,文本表示,图像风格转换
  9. DML,DDL,DCL,DQL的区别
  10. resnet18 结构
  11. [CTO札记]给新助理的3句话
  12. AMP (LAMP/WAMP)
  13. 我的2016(16行走,17前行)
  14. 2022天勤数据结构
  15. sangerbox使用教程_SangerBox:一款好用的生物信息分析可视化工具
  16. PCBA可靠性测试有哪些?
  17. Collecting stars
  18. iOS开发--微信和支付宝网页支付(过审, 支付宝支付成功可回跳)
  19. QQ快速登录的实现原理
  20. 【逗老师带你学IT】阿里云监控报警回调+转发企业微信+转发SnmpTrap+PRTG

热门文章

  1. socket 非阻塞 linux,linux 非阻塞式socket编程求助。。
  2. 修改PostgreSQL数据库默认用户postgres的密码
  3. Python判断 子集
  4. cacti监控java,Cacti for Oracle监控
  5. NLP-基础知识-007(机器学习-朴素贝叶斯)
  6. 3-spark学习笔记-SparkAPI
  7. 415. Add Strings
  8. python 库整理:Timm(1)
  9. pytorch学习笔记 torchnn.ModuleList
  10. R语言实战应用精讲50篇(二十九)-R语言算法应用案例:路径路网轨迹绘图分析(英国自行车数据库)