Spring AOP 和 AspectJ
来源:互联网 发布:大数据产业规模 编辑:程序博客网 时间:2024/04/30 12:05
在本节将学习Spring AOP的用法和一些高级的AOP主题,如通知优先权(Advice precedence)和引入(Introduction)
使用Spring AOP的核心实现技术在所有版本中都一样:动态代理;因为AspectJ已经成长为一个完整流行的AOP框架,Spring在其AOP框架中支持使用AspectJ注解编写POJO aspect。但是在Spring AOP中使用AspectJ aspect有一些限制,因为Spring只允许aspect应用到IOC容器中申明的Bean。如果你希望这个范围之外的应用aspect,就必须使用AspectJ款那个价。
启用Spring的AspectJ注解支持
在Bean配置文件中定义<aop:aspectj-autoproxy/>
他讲自动为匹配AspectJ aspect的Bean创建代理。
用Aspect注解编写的aspect只是一个带有@Aspect注解的Java类。通知(Advice)是带有一个通知注解的简单简单Java方法。AspectJ支持5种通知注解:@Before、@After、@AfterReturning、@AfterThrowing和@Around。
前置通知
切入点匹配的执行点称为连接点(JoinPoint),切入点是匹配一组连接点的表达式,而通知是特定连接点采取的行动。
最终通知
后置通知(after advice)在连接点结束之后执行,不管返回结果还是抛出异常。一个aspect可以包含一个或多个通知。
后置通知
最终通知不管连接点正常返回还是抛出异常都执行,如果希望仅当连接点返回时记录,应该用后置通知(after returning advice)替换最终通知。在后置通知(@AfterReturning)注解中添加一个returning属性,访问连接点返回值。然后,你必须用这个名称通知方法的签名中添加一个参数,在运行时,Spring AOP通过这个参数传入返回值,注意切入点表达式要在pointcut属性中表现。
@AfterReturning(pointcut = "execution(* Arithmeticalculator.add(..))", returning = "result") public void logAfterReturning(JoinPoint joinPoint,Object result){ log.info(joinPoint.getSignature().getName()+"() ends with "+result); }
异常通知
@AfterThrowing,当且仅当连接点抛出异常时执行。可通过throwing属性来访问。Throwable类型是Java语言只能怪所有错误和异常的超类。
环绕通知
around advice,是所有通知类型中最强大的。它获得连接点的完全控制,这样可以在一个通知总组合的使用前面的所有行为,甚至可以控制合适以及是否继续原来的连接点执行。注意,对于环绕通知,连接点参数类型必须是ProceedingJoinPoint。这是JoinPoint的一个子接口,允许你控制何时继续原始连接点。
@Around("execution(* *.*(..))") public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable { //do something Object result = joinPoint.proceed(); log.info("The method "+joinPoint.getSignature().getName()+"() ends with "+result); return result; }
环绕通知类型非常强大和灵活,甚至可以修改原始参数值和最后返回值。你必须非常小心的使用这一通知,因为很容易忘记继续原始的连接点
访问连接点信息
/** * Created by leon on 2017/5/19. */@Aspect@Componentpublic class CalculatorLoggingAspect { private Log log = LogFactory.getLog(this.getClass()); @Before("execution(* Arithmeticalculator.add(..))") public void logBefore(JoinPoint joinPoint){ log.info("The method add() begins"); log.info("Join point kind:"+ joinPoint.getKind()); log.info("Signature declaring type:"+ joinPoint.getSignature().getDeclaringTypeName()); log.info("Signature name:"+joinPoint.getSignature().getName()); log.info("Arguments:"+ Arrays.toString(joinPoint.getArgs())); log.info("Target class:"+joinPoint.getTarget().getClass().getName()); log.info("This class:"+joinPoint.getThis().getClass().getName()); }}
代理封装的原始Bean称作目标(Target)对象,而代理对象称作this对象。这两个对象可以有连接点 getTarget( ) 和 getThis( ) 访问,从输出结果可以看出,二者的不同:
Join point kind:method-executionSignture declaring type: com.zy.AOP.calulator.ArithmeticCalculatorSignature name: addArguments : [1.0, 2.0]Target class: com.zy.AOP.calculator.ArithmeticCalculatorImplThis class: $Proxy6
指定aspect优先级
当多于一个aspect适用于相同的连接点时,方面的优先级不是明确的,除非显式的指定。可以通过实现Ordered接口,或者是哟好难过Order注解实现
public class CalculatorLoggingAspect implements Ordered{ private Log log = LogFactory.getLog(this.getClass()); public int getOrder() { return 0; }
编写AspectJ切入点表达式
AspectJ切入点是一种强大的表达式语言,能够匹配各种类型的连接点。但是,Spring AOP用AspectJ切入点语言定义其切入点。实际上,Spring AOP在运行时说那个AspectJ提供的库解释切入点表达式。注意只支持IOC容器内Bean的方法执行连接点。
最典型的切入点表达式按照签名匹配许多方法。
匹配 Arithmeticalculator的任意方法,‘..’表示任意参数。同一个包下可省略包名
execution(* com.zy.aop.Arithmeticalculator.*(..))
匹配所有公共方法
execution(public * com.zy.aop.Arithmeticalculator.*(..))
指定返回类型
execution(public double com.zy.aop.Arithmeticalculator.*(..))
对参数列表进行限制
execution(public double com.zy.aop.Arithmeticalculator.*(double, ..))
尽管AspectJ切入点语言在匹配各种连接点方面很强大,但是有些时候可能无法找到想要匹配的方法的任何共同的特征(例如修饰符、返回类型、方法名称模式或参数),在这种情况下可以考虑为他们提供一个自定义注解。
@Target({ElementType.METHOD,ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface RequestLimit { String whiteList() default "";}
接下来,我们可以用这个注解为所需要记录的方法加上注解。注意这些注解必须加载实现类上而不是接口,因为接口不能被继承。
现在,可以使用如下切点表达式匹配具有@RequestLimit
注解的方法。
@annotation(com.zy.AOP.annotation.RequestLimit)
类型签名模式
另一种切点表达式匹配某种类型中的所有连接点。应用到Spring AOP时,这些切入点的作用域将被缩小为匹配类型中的所有方法执行。例如:
匹配com.zy.AOP.calculator包中的方法执行连接点
within(com.zy.AOP.calculator.*)
为匹配一个包及其子包中的连接点,必须在通配符前多加一个点
within(com.zy.AOP.calculator..*)
匹配特定类中的方法执行连接点
within(com.zy.AOP.calculator.ArithmeticCalculatorImpl)
可以添加一个加号,匹配所有实现ArithmeticCalculator接口的类中的方法执行连接点
with(ArithmeticCalculator**+**)
Bean名称模式
有一种切入点类型用于匹配Bean名称。例如,下面的切入点表达式匹配一Calculator结尾的bean(注意,仅支持XML配置,在AspectJ注解中不支持):
bean(*Calculator)
合并切入点表达式
可以使用&& || ! 操作符合并,例如
@Aspectpublic class CalculatorPointCuts{ @Pointcut("arithemticOperation() || unitOperation()") public void loggingOperaion(){}}
切入点参数申明
申明一个暴露参数的切入点时,必须将他们包含在切入点方法的参数列表中。
@Aspectpublic class CalculatorPointCuts{ @Pointcut("execution(* *.*(..)) && target(target) && args(a,b)") public void logParameter(Object target,double a,double b){ log.info("Target class:"+target.getClass().getName()); log.info("Arguments : "+a+","+b); }}
引用这个参数化切入点的任何通知,可以通过同名的方法参数访问切入点参数。
@Before("com.zy.AOP.calculator.CalculatorPointcuts.logParameter(target,a,b)") public void paramterPointcut(Object target,double a,double b){ log.info("Target class:"+target.getClass().getName()); log.info("Arguments:{}, {}",a,b);}
在你的Bean中引入行为
问题
有时候,可能有一组共享公共行为的类。在OOP中,它们必须扩展相同的基类或者实现相同的接口。这个问题确实是可以用AOP模块化的一个横切关注点。此外,java的单继承机制。所以不能同时从多个实现类中继承行为。
解决方案
引入(Introduction)是AOP中的一种特殊通知,它允许为一个接口提供实现类,使对象动态的实现接口。这看上去像使对象在运行时扩展了实现类。而且,你可以用多个实现类将多个接口同时引入对象。这可以实现与多重继承相同的效果。
现定义两个接口及实现
public interface MinCalculator { double min(double a,double b);}public class MinCalculatorImpl implements MinCalculator { public double min(double a, double b) { double result = (a >= b) ? a : b; System.out.println("max("+a+","+b+")= "+result); return result; }}public interface MaxCalculator { double max(double a,double b);}public class MaxCalculatorImpl implements MaxCalculator { public double max(double a, double b) { return 0; }}
现在假定ArithmeticCalculatorImpl也执行max() 和 min() 计算,由于Java单继承机制唯一可能的办法就是复制实现或者处理委派的实现类。这两种情况,都必须重复方法申明。
而使用引入,可以实现MaxCalculatorImpl 和 MinCalculatorImpl动态地实现MaxCalculator和MinCalculator接口,这和从MaxCalculatorImpl 和 MinCalculatorImpl多重继承有相同的效果。引入这一想法的非凡之处在于,你不需要修改ArithmeticCalculatorImpl类引入新方法。这意味着,你可以在没有源代码可用的情况下,将方法引入现有的类中。
然而,引入怎么能在Spring AOP中做到这一切?答案是:动态代理。引入就是通过向动态代理添加一个接口(如MaxCalculator)来工作,当这个接口中声明的方法在代理对象调用时,代理将把调用委派给后端实现类(如MaxCalculatorImpl)。
引入和通知类似,必须在一个aspect中声明。在这个aspect中,你可以用@DeclareParents
注解任意一个字段来声明引入。
@Aspectpublic class CalculatorIntroduction { @DeclareParents( value = "com.zy.AOP.calculator.ArithmeticaCalculatorImpl", defaultImpl = MaxCalculatorImpl.class) public MaxCalculator maxCalculator; @DeclareParents( value = "com.zy.AOP.calculator.ArithmeticaCalculatorImpl", defaultImpl = MinCalculatorImpl.class) public MinCalculator minCalculator;}
@DeclareParents
注解类型的value属性表示引入的目标类。引入的接口由注解字段的类型确定。最后,用语实现这个新街口的实现类在defaultImpl属性中指定。通过这两个引入,可以动态的为ArithmeticCalculatorImpl类引入两个接口,实际上DeclareParents注解的value属性可以使用aspectJ类型匹配表达式,将一个接口引入多个类。
为你的Bean引入状态
类似引入行为
Spring中AspectJ加载时织入aspect
问题
Spring AOP框架只支持有限的AspectJ切入点类型,并允许aspect应用到IOC容器中申明的Bean。如果希望使用更多类型的切入点,或者将aspect应用到spring IOC容器之外创建的对象,就必须在Spring应用程序总使用AspectJ框架。
解决方案
织入(Weaving)是aspect应用到你的目标对象的过程。使用Spring AOP,织入在运行时动态代理发生。
未完待续······
- Spring AOP 和 AspectJ
- Spring AOP 和 AspectJ
- spring-AOP和AspectJ区别
- 比较 Spring AOP 和 AspectJ
- AOP框架之:Spring AOP和AspectJ
- AOP框架之:Spring AOP和AspectJ
- AOP框架之:Spring AOP和AspectJ
- 使用Spring AOP和AspectJ编排工作流
- Spring AOP 和 AspectJ 之间的差别
- AOP框架之:Spring 2.x AOP和AspectJ
- Spring AOP + AspectJ framework
- Spring AOP与Aspectj
- spring AspectJ aop学习
- spring aop AspectJ
- Spring AOP + AspectJ annotation
- Spring AOP之AspectJ
- spring (四) aop/aspectj
- Spring AspectJ AOP 示例
- 【备忘】Flume日志采集视频教程
- Tcp编程常见问题及解决方法总结(粘包,拆包)
- ios 去除警告
- mvc中重写url
- TabLayout+ViewPager+Fragment
- Spring AOP 和 AspectJ
- ROS之CMakeLists
- HDU 1164 JAVA
- 【模板】Dijkstra+前向星+堆优化 (模板题:洛谷P3371)
- sql中查找两个表中不同的数据
- SpringMVC interceptor有时候配置的时候path="/**" 两个星号什么意思,与path="/"以及path="/*"什么区别
- Spring 校验(validator,JSR-303)简单实现
- Wine:我可以让比特币病毒感染Linux系统
- Win7下Mybatis Plugin插件安装破解及使用