小曹学spring--基于@AspectJ和Schema的AOP

前言:

  • 上一章中已经介绍,Spring中定义一个切面是比较麻烦的,需要实现专门的接口,并进行一些较为复杂的配置。经过改进,如今Spring AOP已经焕然一新,用户可以使用@AspectJ注解非常容易的定义一个切面,而不需要实现任何接口。
  • 对于jdk5.0以下的项目,则可以通过基于Schema的配置定义切面

1、Spring对AOP的支持

spring2.0以后对AOP功能进行了重要的增强,主要变现在以下几个方面:

  • 新增了基于Schema的配置支持,为AOP提供了专门的aop命名空间;
  • 新增了对AspectJ切点表达式语言的支持,通过注解在POJO中定义切面;
  • 无缝集成AspectJ

2、JDK5.0注解知识

2.1、什么是注解

(1)注解是Javadoc标签和Xdoclet标签的延伸和发展,在java5.0以上版本,我们可以自定义这些标签,并通过java语言的反射机制获取类中标注的注解,完成特定的功能。

(2)注解是代码的附属信息,它遵循一个基本原则:注解不能直接干扰程序代码的运行,无论增加或删除注解,代码都能正常运行。

2.2、一个简单的注解类

(1)声明一个注解

@Retention(RetentionPolicy.RUNTIME)//①声明注解的保留期限

@Target(ElementType.METHOD)//②声明可以使用该注解的目标类型

public    @interface    TestInterface{//③定义注解

boolean    value()    default    true;//④声明注解成员

}

(2)注解的成员声明有以下几点限制:

  • 成员以无入参、无抛出异常的方式声明,如int    value(String aa),boolean    value()    throws    Exception等都是非法的;
  • 可以通过default为成员指定一个默认值;
  • 成员类型是受限的,合法的类型包括原始类型及其封装类、String、Class、枚举类、注解类型、上述类型的数组类型;
  • 如果注解只有一个成员,则成员名必须取为value(),这样在使用时,可以忽略成员名和赋值等号(=);
  • 如果注解有多个成员,只对value赋值时可以省略value和赋值等号(=),多个成员赋值用逗号隔开;
  • 所有的注解都隐式继承于Annotation,但注解不允许显式继承其他接口;
  • 如果成员是数组类型,则使用“{xxx,xxx,xxx}”将值赋给成员名。

(3)jdk5.0以后,对于Package、Class、Constructor、Method、Field等反射对象都新增了访问注解信息的方法,该方法支持通过泛型直接返回注解对象。

3、着手使用@AspectJ

3.1、使用前的准备

因为java反射机制无法获取入参名,所以需要引入spring的asm模块,asm是轻量级的字节码处理框架,spring就利用asm处理@AspectJ中所描述的方法入参名。

3.2、一个简单的例子

@Aspect--------------//①通过该注解将类标识为一个切面

public    class    TestAspectj{

@Before("execution(* greetTo(..))")-----------------------//②定义切点和增强类型

public    void    beforeGreeting(){---------------------//③增强的横切逻辑

..............................

}

}

注:②处的切点表达式的意思是,在greetTo方法处织入增强,greetTo方法可以有任意入参和返回值。

3.3、通过配置使用@Aspect切面

(1)如同spring使用proxyfactory将切面织入目标对象,AspectJ使用AspectJProxyFactory将切面织入目标对象。

(2)AspectJ的自动代理创建器是

<bean    class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator"/>

(3)抛开自动代理创建器,使用基于Schema的aop命名空间进行配置更简单:

<!-- 基于@Aspect切面的驱动器 -->

<aop:aspectj-autoproxy/>

<bean    id="target"    class="目标对象"/>

<bean    class="注解指定的切面类"/>

当然,spring内部依旧采用AnnotationAwareAspectJAutoProxyCreator进行自动代理的创建工作,但具体实现细节已经被<aop:aspectj-autoproxy/>隐藏起来了。

<aop:aspectj-autoproxy/>有一个属性proxy-target-class属性:

  • 默认为false,表示采用jdk的动态代理织入切面;
  • 值为true,表示采用cglib的动态代理织入增强;
  • 不过即使proxy-target-class属性设置为false,如果目标类没有声明接口,则spring将自动使用cglib动态代理。

3.4、@Aspect语法基础

(1)切点表达式函数

切点表达式由关键字和操作参数组成,比如execution(* greetTo(..)),exccution就是关键字,* greetTo(..)就是操作参数。

spring支持9种@AspectJ切点表达式函数,根据描述对象的不同,可以分为4种类型:

  • 方法切点函数:通过描述目标类方法信息定义连接点;
  • 方法入参切点函数:通过描述目标类方法入参的信息定义连接点;
  • 目标类切点函数:通过描述目标类的代理类的信息定义连接点;
  • 代理类切点函数:通过描述目标类的代理类的信息定义连接点。

4种类型的切点函数如图:

(2)在函数入参中使用通配符

@Aspect支持3种通配符:

  • *    匹配任意字符,但只能匹配上下文中的一个元素;
  • ..    匹配任意字符,可以匹配上下文中的多个元素,但在表示类时,必须和*联合使用,而在表示入参时,则单独使用;
  • +   表示按类型匹配指定类的所有类,必须跟在类名后面,如com.baobaotao.Car+。表示匹配Car类及其子类;

@AspectJ函数按其是否支持通配符及其支持的程度,可以分为以下3类:

  • 支持所有通配符:execution(),within();
  • 仅支持“+”通配符:args(),this(),target();
  • 不支持通配符:@args(),@within(),@target()和@annotation()。

(3)逻辑运算符

切点表达式由切点函数组成,切点函数之间还可以进行逻辑运算,组成复合切点,

spring支持以下的切点运算符:&&,||,!

由于以上在XML中是特殊字符,spring在XML中的切点表达式函数提供and,or,not作为替代。

注:当使用not时,前面得有空格,否则报解析错误。

(4)不同的增强类型

@AspectJ为各种增强类型提供了不同的注解

①@Before    相当于BeforeAdvice,它有2个成员:

  • value    该成员用于定义切点;
  • argNames    由于无法通过反射机制获取方法入参名,所以如果在Java编译时未启用调试信息或者需要在运行期解析切点,就必须通过这个成员指定注解所标注增强方法的参数名(注意两者名字必须完全相同),多个参数名用逗号分隔。

②@AfterReturning    后置增强,相当于AfterReturningAdvice,AfterReturning,它拥有4个成员:

  • value    该成员用于定义切点;
  • pointcut    表示切点的信息,如果显示指定point值,它将覆盖value的设置值,可以将pointcut成员看成是value的同义词;
  • returning    将目标对象方法的返回值绑定给增强的方法;
  • argNames   如前所述。

③@Around    环绕增强,相当于MethodInterceptor,它有2个成员:

  • value    该成员用于定义切点;
  • argNames   如前所述。

④@AfterThrowing    抛出增强,相当于ThrowsAdvice,AfterThrowing,它有4个成员:

  • value    该成员用于定义切点;
  • pointcut    表示切点的信息,如果显示指定point值,它将覆盖value的设置值,可以将pointcut成员看成是value的同义词;
  • throwing    将抛出的异常绑定到增强方法中;
  • argNames   如前所述。

⑤@After    Final增强,不管是抛出异常还是正常退出,该增强都会得到执行,该增强没有对应的增强接口,可以把它看成ThrowsAdvice和AfterReturningAdvice的混合物,一般用于释放资源,它有2个成员:

  • value    该成员用于定义切点;
  • argNames   如前所述。

⑥@DeclareParents    引介增强,相当于IntroductionInterceptor,它有2个成员:

  • value    该成员用于定义切点,它表示在哪个目标类上添加引介增强,即不需要使用函数表达式,直接指定全类名;
  • defaultImpl    默认的接口实现类。

(5)引介增强的具体用法

引介增强通过切面技术将目标类要实现接口的子类融合到目标类中,目标类持有目标接口的实现类的引用,这样目标类就相当于实现了目标接口。

3.5、切点函数详解

(1)execution()

execution()是最常用的切点函数,其语法如下所示:

execution(<访问修饰符>?<返回类型><方法名>(<方法参数>)<异常>?)

?:表示前面的内容可选,即除了返回类型,方法名和方法参数,其他都是可选的。

具体示例:

(2)args()和@args()

(3)命名切点

在前面的例子中,切点直接声明在增强方法处,这种切点声明方式称为匿名切点,匿名切点只能在声明处使用。如果希望其他地方重用一个切点,我们可以通过@Ponitcut注解以及切面类方法对切点对切点进行命名。

具体示例如下:

(4)增强织入的顺序

一个连接点可以同时匹配多个切点,切点对应的增强在连接点上的织入顺序是如何安排的呢?

这个问题要分3种情况讨论:

  • 如果增强在同一个切面类中声明,则依照增强在切面类中定义的顺序进行织入;
  • 如果增强位于不同的切面类中,且这些切面类都实现了org.springframework.core.Ordered接口,则顺序号小的先织入;
  • 如果增强位于不同的切面类中,且这些切面没有实现Ordered接口,则织入的顺序是不确定的。

(5)访问连接点信息

AspectJ使用org.aspectj.lang.JoinPonit接口表示目标类连接点对象,如果是环绕增强时,使用org.aspectj.lang.ProceedingJoinPoint表示连接点对象,该类是JoinPoint的子接口。任何一个增强方法都可以通过将第一个入参声明为JoinPoint访问到连接点上下文的信息(一定要在第一个位置)。

JoinPonit和ProceedingJoinPoint的方法签名如下:

(6)绑定连接点方法入参

(7)绑定代理对象

(8)绑定返回值

3.6、基于Schema配置切面

(1)基于Schema的配置就是将切面信息移到XML中,一个配置示例如下:

(2)配置命名切点:

在7-15②处通过pointcut属性声明的切点是匿名切点,它不能被其他增强或其他切面引用。spring提供了命名切点的配置方式,如下所示:

(3)各种增强类型的配置

(4)绑定连接点信息

(5)Advisor配置

一个配置示例:

3.7、混合切面类型

(1)配置示例:

(2)切面类型总结:

4、JVM Class文件字节码转换基础知识

在类加载期织入切面。

时间: 2024-11-06 04:05:18

小曹学spring--基于@AspectJ和Schema的AOP的相关文章

基于@AspectJ和schema的aop(一)

在前面我们使用Pointcut和Advice描述切点和增强, 并使用Advisor整合两者描述切面[email protected]使用注解来描述切点和增强.两者使用的方式不同, 但是在本质上都是一样的. 我们还是用以前的例子来举例, 学习如何使用@AspectJ来描述切点和增强.首先看一个简单的例子. package com.bao.bao.aspectj; /** * Created by xinfengyao on 16-10-23. */ public interface Waiter

基于@AspectJ和schema的aop(二)[email&#160;protected]基础语法

@AspectJ使用jdk5.0和正规的AspectJ切点表达式描述切面, 由于spring只支持方法的连接点,所以Spring只支持部分AspectJ的切点语言. 1.切点表达式函数 AspectJ 5的切点表达式有关键字和操作参数组成,如execution(* greetTo(..))的切点表达式, execution为关键字, "* greetTo(..)"为操作参数.在这里execution表示目标类执行某一方法, 而"* greetTo(..)"描述目标方

基于@AspectJ和schema的aop(四)[email&#160;protected]进阶

@AspectJ可以使用切点函数定义切点, 我们还可以使用逻辑运算对切点进行复合运算得到复合的切点. 我们还可以对切点进行命名, 从而可以复用切点.当一个连接点匹配多个切点时, 需要考虑增强织入的顺序.还有一个在前面提到过的问题就是, 如何访问在增强中访问连接点上下文信息.下面对这几方面进行学习. 1.对切点进行复合运算 使用切点进行复合运算, 使得我们能够拥有强大的表达切点的能力.例子如下: package com.bao.bao.aspectj; import org.aspectj.lan

基于@AspectJ和schema的aop(三)---切点函数详解

切点函数是AspectJ表达式语言的核心, 也是使用@AspectJ进行切面定义的难点.本小节我们通过具体的实例对切点函数进行深入学习. [email protected]() @annotation()表示标注了某个注解的所有方法,这个比较简单. 2.execution() execution()是最常使用的切点函数,其语法如下: execution(<修饰符模式>? <返回类型模式> <方法名模式> (<参数模式>) <异常模式>?) 除了返

Spring学习之旅(七)基于XML配置与基于AspectJ注解配置的AOP编程比较

本篇博文用一个稍复杂点的案例来对比一下基于XML配置与基于AspectJ注解配置的AOP编程的不同. 相关引入包等Spring  AOP编程准备,请参考小编的其他博文,这里不再赘述. 案例要求: 写一个简单的实现四则运算的计算器. 加入AOP功能:日志功能:检测参数中是否有负数的功能. 废话不多说了,直接上代码: (一)基于XML配置: 定义了一个接口类: package com.edu.aop; public interface ArithmeticCalculator { int add(i

[Spring框架]Spring AOP基础入门总结二:Spring基于AspectJ的AOP的开发.

前言: 在上一篇中: [Spring框架]Spring AOP基础入门总结一. 中 我们已经知道了一个Spring AOP程序是如何开发的, 在这里呢我们将基于AspectJ来进行AOP 的总结和学习. 一, AspectJ的概述: AspectJ是一个面向切面的框架,它扩展了Java语言.AspectJ定义了AOP语法所以它有一个专门的编译器用来生成遵守Java字节编码规范的Class文件. Spring为了简化自身的AOP的开发,将AspectJ拿过来作为Spring自身一个AOP的开发.

Spring 基于Aspectj切面表达式(6)

1 package com.proc; 2 3 import org.aspectj.lang.JoinPoint; 4 import org.aspectj.lang.ProceedingJoinPoint; 5 import org.aspectj.lang.annotation.After; 6 import org.aspectj.lang.annotation.AfterReturning; 7 import org.aspectj.lang.annotation.AfterThrow

Spring 基于xml配置方式的AOP

我们具体用代码来说明: 1.ArithmeticCalculator.java 1 package com.proc; 2 3 public interface ArithmeticCalculator { 4 int add(int i, int j); 5 int sub(int i, int j); 6 7 int mul(int i, int j); 8 int div(int i, int j); 9 } 2.ArithmeticCalculatorImpl.java 实现接口Arit

使用Spring进行面向切面编程(AOP)

转载于http://www.blogjava.net/supercrsky/articles/174368.html 文章太长,写的很好,没看完,转过来慢慢理解,品味 简介 面向切面编程(AOP)提供另外一种角度来思考程序结构,通过这种方式弥补了面向对象编程(OOP)的不足. 除了类(classes)以外,AOP提供了 切面.切面对关注点进行模块化,例如横切多个类型和对象的事务管理. (这些关注点术语通常称作 横切(crosscutting) 关注点.) Spring的一个关键的组件就是 AOP