Spring framework(5):AOP


以下示例代码完整代码地址:https://gitee.com/assad/springframework-test-aop

AOP 基础

AOP (Aspect Oriented Programing)面向切面编程是通过预编译方式和运行动态代理实现程序功能同意维护的一种技术,利用AOP可以对业务逻辑的各个部分进行隔离,从而降低业务逻辑各部分之间的耦合度。

AOP 相关术语

  • 连接点(Joinpoint)
连接点是程序执行的某个特定的位置,如类的初始化前/初始化后、类的某个方法调用前/调用后、方法抛出异常后;
  • 切点(Pointcut)
AOP通过切点定位连接点,用数据库查询的概念类比:连接点相当于数据中的记录,切点相当于查询条件;
  • 增强(Advice)
增强是织入目标对象切点上的一段程序代码,在Spring中,增强用于描述一段代码和该段代码执行点的位置;
  • 引介(Introduction)
引介是一种特殊的增强,它为目标类添加了一些属性和方法;
  • 目标对象(Traget)
增强逻辑的织入目标类;
  • 编织(Weaving)
织入是将增强添加到目标类具体连接点上的过程;
AOP有3种织入方式:编译时织入、类装载织入、动态代理织入;
Spring 采用动态代理织入,AspectJ 采用编译期织入和类装载期织入;
  • 代理(Proxy)
一个类被AOP织入增强后,就产生了一个代理类,该类是融合了原类和增强逻辑类的代理;
根据不同的代理方式,代理类可能是和原类具有相同接口的类,也可能是原类的子类,所有可以采用与调用和原类相同的方式调用代理类;
  • 切面(Aspect)
切面=切点+增强(引介),它包含横切逻辑的定义和连接点的定义,Spring AOP 就是负责实施切面的框架,它将切面所定义的逻辑织入切面所指定的连接点中;

AOP的主要实现项目

  • AspectJ:语言级别的 AOP 实现; 
  • AspectWerkz:基于 Java 的简单、动态、轻量的 AOP 框架,目前已经和 AspectJ 合并;
  • JBoss AOP
  • Spring AOP:Spring AOP 使用纯 Java 实现,不需要专门的编译过程,和特殊的类装载器,它在运行时通过代理的方式向目标织入增强代码;

AOP 底层实现方式:动态代理机制

Spring AOP 使用 动态代理技术在运行期织入增强的代码,在在底层使用了以下2种基本的动态代理机制:
  • JDK 动态代理
JDK 动态代理是通过接口来实现方法拦截的,使用 java.lang.reflect 包中的2个类:Proxy,InvocationHandler;
InvocationHnadler 是一个接口,可以以通过实现该接口定义横切逻辑,并通过反射机制调用目标类的代码,动态地将横切逻辑和业务逻辑编织到一起;
Proxy 利用 InvocationHandler 动态地创建一个符合某一个接口的实例,生成目标类的代理对象;
  • CGLib 动态代理
CGLib 动态代理是通过动态生成代理子类来实现方法拦截的,CGLib 采用底层的字节码技术,可以为一个类创建子类,在子类中采用方法拦截技术拦截所有父类方法的调用,同时织入横切逻辑;

2种动态代理方式的比较
JDK 动态代理有一个限制:只能为接口创建代理实例,JDK 动态代理创建代理时速度快,但是创建的代理对象运行效率比较低;
CGLib 动态代理可以为类创建代理实例,CGLib 动态代理创建代理时速度比较慢,但是创建的代理对象运行效率高;

对于 singleton 对象的代理,推荐使用 CGLIb 代理;

2种动态代理方法的实例演示
代码模块: site.assad.DynamicProxy.*;site.assad.DynamicProxy.FooServcie;
FooService 为目标业务类的接口,FooServiceImpl为目标业务类;
PerformanceMonitor和MethodPerformance 为性能测试的类组,
PermanaceHandler 为 JDK 代理方式实现的增强类;
CglibPorxy 为Cglib 代理方式实现的增强类;
FooServiceTest 为测试类;




Spring 对于 AOP 的支持



在 Spring 3.0前,Spring 对 AOP 的支持是通过 Advice 描述增强,用 PointCut 描述切点,用 Advisor 整合二者描述切面 ,定义一个切面的过程往往要编写一系列的类或 xml 配置,比较繁琐;
在Spring3.0 之后,Spring 提供了对于 Java5.0 注解的支持,对于使用 Java 5.0+ 的项目,可以使用 @AspectJ 注解快速地进行 AOP 切面描述;
对于 J ava 5.0 以下的项目 ,Spring 提供了 Schema 方式配置定义 AOP 切面;
同时 Spring 还支持直接使用 AspectJ 语言编写切面;


基于 @Aspect 的 AOP


一个简单的前置增强示例

以下以一个简单的 前置增强的示例,来演示使用@Aspect进行切面配置和代理使用;

示例 代码模块: site.assad.common.Waiter, site.assad.common.NaiveWaiter,  site.assad.aspectj.example.PregreetingAspect ;
site/assad/aspect/example/bean1.xml ;   
site.assad.aspectj.example.ApsectProxyTest , site.assad.aspectj.example.ApsectConfigTest 

示例中 PregreetingAspect 为对于 NaiveWaiter 的前置增强切面类,如下:
 
package site.assad.aspectj.example;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
/** Description: 前置增强例子,增强 Waiter 子类的 greetTo 方法; */
@Aspect  //将 PreGreetingAspect 标记为一个切面
public class PreGreetingAspect {
    @Before("execution(* greetTo(..))")   //定义前置增强的切点
    public void beforeGreeting(){   //增强的横切逻辑
        System.out.println("Hello!");
    }
}

将 PreGreetingAspect 切面织入 NaiveWaiter 目标类中,可以使用 AspectJProxyFactory 类,有以下2种织入方式:
① 直接在代码中织入
ApsectProxyTest 中相应的代码如下
 
    @Test
    public void testBeforeAspect(){
        Waiter target = new NaiveWaiter();  //目标对象
        
        //创建组装 AspectJ 代理类工厂
        AspectJProxyFactory factory = new AspectJProxyFactory();
        factory.setTarget(target);   //设置目标对象
        factory.addAspect(PreGreetingAspect.class);  //设置切面类
        Waiter proxy = factory.getProxy();  //获取代理类
        proxy.greetTo("Al-assad");
        proxy.serveTo("Al-assad");
    }
② 使用XML文件进行配置
beans.xml 中相应的配置部分如下:
 
 <!--使用aop命名空间配置基于 @Aspect 切面的驱动器-->
    <aop:aspectj-autoproxy proxy-target-class="true" /> 
     <!--proxy-target-class=true,设置使用CGLib-->
    <!--配置前置增强切面-->
    <bean id="waiter" class="site.assad.common.NaiveWaiter" />
    <bean class="site.assad.aspectj.example.PreGreetingAspect" />
ApsectConfigTest  中相应的代码如下
 
 @Test
    public void testBeforeAspect(){
        ApplicationContext ctx = new 
            ClassPathXmlApplicationContext("site/assad/aspectj/example/bean1.xml");
        Waiter waiter = ctx.getBean("waiter",NaiveWaiter.class);
        waiter.greetTo("Al-assad");
        waiter.serveTo("Al-assad");
    }
一般在项目中,会更多使用第二种方法(通过XML配置),以减少代码冗余;


@Aspect 语法


切点表达式
在上面示例中,使用增强注解是,会指定切点表达作为切点定位的参数,类似 @Before("execution(* greetTo(..))") 中的  "execution(* greetTo(..))" 就是一个切点表达式,AspectJ 5.0 的切点表达式由关键字和操作参数组成,以上例子中,execution为关键字,* greetTo(..) 为操作参数;
切点表达式函数
Spring 中支持的切点表达式可以分为4类:
  • 方法切点函数:通过描述目标类的信息定义连接点;
  • 方法入参切点函数:通过描述目标类方法入参定义连接点;
  • 目标类切点函数:通过描述目标类类型定义连接点;
  • 代理类切点函数:通过描述目标类的代理类信息定义连接点;

类别函数入参说明
方法切点函数execution()方法匹配模式串表示满足某一个匹配模式的所有目标类方法的连接点;
如:execution(* greetTo(..)) 表示匹配所有目标类的 greetTo() 方法连接点,匹配该连接点任意入参的切点;
@annotation()方法注解类名表示标注了特定注解的目标类方法的连接点;
如:@annotation(site.assad.anno.Monitable) 表示匹配目标类的方法连接点中,任何标注了 @Monitable 的方法连接点的切点;
方法入参切点函数args()类名通过判别目标类方法运行时入参对象的类型定义的连接点;
如:args(site.assad.common.Waiter) 表示目标类所有连接点中,连接点的方法入参含有一个匹配 Waiter 及其实现类\拓展类的切点;
@args()类型注解类名通过判别目标类方法运行时入参对象的类是否标注特定的注来指定连接点;
如:@args(site.assad.common.Monitorable) 表示目标类的所有连接点中,连接点的方法入参含有一个标注了 @Monitable 注解的对象的切点;
目标类切点函数within()类名匹配串通过特定域下的所有连接点;
如:within(site.assad.service.*) 表示 site.assad.service 包下所有类的所有连接点;
within(site.assad.service.*Service) 表示 site.assad.service 包下所有命名以Servcie结尾的所有连接点;
@within()类名注解串通过目标类按类型匹配于某个类A,且类A标注了特定注解,则目标类的所有子类(或实现类)连接点匹配该切点;
如: @within(site.assad.common.Monitorable) 下,加入 Waiter 标注了 @Monitorable 注解,那么 Waiter 及其所有的实现类的所有连接点都匹配该切点;
target()类名假如目标类按类型匹配到了某指定类,这目标类的所有连接点都匹配这个切点;
如:target(site.assad.common.Waiter) 匹配 Waiter及其实现类NaiveWaiter、NaughtyWaiter 的切点;
@target()类型注解类名假如目标类标注了特定注解,这目标类及其实现类的所有连接点都匹配该切点;
如:@target(site.assad.common.Monitorable) 下,假设 Waiter 标注了 @Monitorable ,则 Waiter 及其实现类 NaiveWaiter、NaughtyWaiter 都匹配该切点;
代理类this()类名代理类按类型匹配于指定类,则被代理的目标类的所有连接点都匹配该切点;
入参通配符
在表达式函数的入参中可以使用通配符,@Aspect 支持 3 种通配符;
*匹配任意字符,但只匹配上下文的一个元素
..匹配任意字符,可以匹配上下文的多个元素;
在表示类时,必须和 * 联合使用,类似 site.assad..*
在表示入参时,可以单独使用;
+表示按类型匹配指定类的所有类,必须在类名后;
如:site.assad.common.Waiter+ 表示匹配Waiter,以及继承和拓展 Waiter 的所有类;
入参函数对于通配符的支持度:
支持所有通配符execution()、within()
仅支持+通配符args()、this()、target();但是对于这3个函数来说意义不大,以为是不是用 + 通配符表示的意义的一样的;
不支持通配符@args()、@within()、@target()、@annotation()
除了以上规则之外,args()、this()、target()、@args()、@within()、@target()、@annotation() 还支持指定变量名
逻辑运算符
切点表达式可以由一系列的切点表达式函数通过逻辑运算符连接组成,支持的逻辑表达式有以下3个:
&&  、 and与运算符,如果在 Spring XML 配置文件中使用 && ,需要使用转义字符 &amp;&amp; 来代替;
如: within(site.assad..*) && args(String) 表示 site.assad 包下所有连接点中,连接点的方法入参是一个String入参的方法切点;
||  、or或运算符;
如:within(site.assad.service..*) && args(String) || within(site.assad.dao..*)&& args(String) 表示site.assad.service  或 site.assad.dao 包下所有包含一个String方法入参的切点;
!、 not非运算
如:within(site.assad..*) args(int) 表示 site.assad 包下所有连接点中,方法入参不是一个String入参的切点;
execution() 函数的一些典型的匹配模式
相对于其他的切点表达式函数,execution() 函数更加灵活,在实际开发中用得很多,以下是一些典型的使用方式:

通过类包定义切点
  • execution(* site.assad.*(..))   匹配 site.assad 包下的所有类的所有方法(仅下一层);
  • execution(* site.assad..*(..))  匹配 site.assad 包及其所有子孙包下所有类的所有方法;
  • execution(* site..*.*Servcie.find*(..))  匹配 site 包下所有以 Servcie 为结尾的类中 以find 为前缀的方法;
通过类定义切点
  • execution(* site.assad.Waiter.*(..))   匹配Waiter接口的所有方法(greeTo(),serveTo()), 包括子类的这些方法;
  • execution(* site.assad.Waiter+.*(..))   表示除了匹配以上的方法外,还匹配 Waiter 子类中新增的方法;
通过方法签名定义切点
  • execution(* greetTo(..))     匹配目标类的greetTo() 方法;
  • execution(public * *(..) )   匹配目标类所有public方法;
  • execution(* *To(..))         匹配目标类所有以 To 结尾的方法;
通过方法入参定义切点
  • execution(* joke(String,int))  匹配目标类中的 joke()方法,且该方法两个入参类型分别是String、int,当这些类为 java.lang 包下的类可以省略类限定名,否则需要使用全限定名;
  • execution(* joke(String,*))    匹配目标类中的 joke()方法,其方法的2个入参分别是String和任意类型;
  • execution(* joke(String,..))   匹配目标类中的 joke() 方法,其方法的第一个入参必须是String类型,可以拥有随后不限定个数和类型的入参;
  • execution(* joke(List+))       匹配目标类中的 joke() 方法,该方法只含有一个入参,该入参必须是List或者其子类;



增强类型注解
Spring 支持的增强类型包括5种: 前置增强、后置增强、环绕增强、抛出增强、引介增强;
并提供了一系列的注解支持这些增强类型;
1. @Before 前置增强 
相当于 BeforeAdvice ,@Before 包含以下2个成员属性:
- value:定义切点;
- argNames:指定注解所标注的增强方法的参数名,多个参数用逗号隔开(这是由于Spring 无法通过Java 方式机制获取方法入参名造成的,一般用在运行期解析切点);

示例 代码模块:
site.assad.common.Waiter, NaiveWaiter(目标类), site.assad.aspectj.example.PregreetingAspect (切面对象);
site/assad/aspect/example/bean1.xml (切面编织配置文件); 
site.assad.aspectj.example.ApsectConfigTest(代理类获取测试) 

2. @AfterReturning 后置增强
相当于 AfterReturningAdvice ,@AfterRerurning 包含以下4个成员属性:
- value:定义切点;
- pointcut:表示切点的信息,使用该属性时需要在目标类显式指定切点(在目标类的切点使用@PointCut 标记),该属性值类似于 “site.assad.common.Waiter.greetTo()” ,当该属性指定时,会覆盖 value 属性;
- retruning:将目标对象方法的返回值绑定给增强的方法;
- argNames:同上;

示例代码模块:
site.assad.common.Waiter, NaiveWaiter(目标类), AfterGreetingAspect (切面对象);
site/assad/aspect/example/bean2.xml (切面编织配置文件); 
site.assad.aspectj.example.beans.ApsectConfigTest(代理类获取测试) 
 
@Aspect
public class AfterGreetingAspect {
    @AfterReturning(value = "execution( * greetTo(..)) && target(site.assad.common.Waiter)",
                    returning = "retVal")   //绑定返回值
    public void afterGreeting(Object retVal){
        System.out.println("Goodbye!");
        if(retVal != null){
            System.out.println(retVal);
        }
    }
}

3. @Around 环绕增强
相当于 MethodInterceptor ,@Around 包含以下属性:
-value
-argNames

示例代码模块:
site.assad.common.Waiter, NaiveWaiter(目标类), site.assad.aspectj.example.AroundGreetingAspect (切面对象);
site/assad/aspect/example/bean3.xml (切面编织配置文件); 
site.assad.aspectj.example.ApsectConfigTest(代理类获取测试) 

其中AroundGreetingAspect 切面类如下:
 
package site.assad.aspectj.example;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class AroundGreetingAspect {
    @Around("execution(* greetTo(..)) && target(site.assad.common.Waiter)")
    public Object aroundAspect(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("Hello!");      //before greetTo()
        Object retVal = pjp.proceed();     //连接点程序执行,并返回结果
        System.out.println("Goodbye!");    //after greetTo()
        return retVal;
    }
}
4. @AfterThrowing 抛出增强
相当于 ThrowsAdvice ,@AfterThrowing 包含以下属性:
- value
- pointCut
- throwing:将抛出的异常绑定到增强方法中;
-argNames

示例代码模块:
site.assad.common.ThrowExceptionTarget(目标类), site.assad.aspectj.example.AfterThrowingAspect(切面对象);
site/assad/aspect/example/bean4.xml (切面编织配置文件); 
site.assad.aspectj.example.ApsectConfigTest(代理类获取测试)

其中 AfterThrowingAspect 切面类的代码如下:
 
package site.assad.aspectj.example;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class AfterThrowingAspect {
    @AfterThrowing(pointcut = "site.assad.common.ThrowExceptionTarget.ohShit()",
                   throwing = "ex")    //绑定抛出异常对象
    public void afterThrowException(UnsupportedOperationException ex){
        System.out.println("Exception message:"+ex.getClass().getName()+":"+ex.getMessage());
    }
}
5. @After Final增强
不管抛出异常还是正常退出,该增强都会执行,可以把它看成 ThrowsAdvice 和 AfterReturningAdvice 的混合物,一般用于释放资源。相当于 try-finally 控制流,含有以下属性:
- value
- argNames

5. @DeclareParents 引介增强
相当于 IntroductionInterceptor ,拥有以下2个属性:
-value :定义切点,表示在哪个目标类上添加引介增强;
-defaultImpl:默认的接口实现类;

示例代码模块:
site.assad.common.Waiter,NaiveWaiter(目标类), Seller,NaiveSeller(引介增强对象),site.assad.aspectj.example.EnableSellerAspect(引介切面对象);
site/assad/aspect/example/bean5.xml (切面编织配置文件); 
site.assad.aspectj.example.ApsectConfigTest(代理类获取测试)

其中NaiveSeller作为引介增强对象,增加一个成员方法:
 
package site.assad.common;
public class NaiveSeller implements Seller{
    @Override
    public void sell(String goods, String clientName) {
        System.out.println("NaiveSeller: sell "+goods+" to "+clientName);
    }
}
EnableSellerAspect  引介切面代码如下:
 
package site.assad.aspectj.example;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.DeclareParents;
import site.assad.common.NaiveSeller;
import site.assad.common.Seller;
@Aspect
public class EnableSellerAspect {
    @DeclareParents(value = "site.assad.common.NaiveWaiter",  //为 NaiveWaiter 添加接口实现
                    defaultImpl = NaiveSeller.class)     //默认的接口实现类
    public Seller seller;  //引介增强要实现的接口
}
获取代理类代码如下:
 
ApplicationContext ctx = new ClassPathXmlApplicationContext("site/assad/aspectj/example/bean5.xml");
Waiter waiter = ctx.getBean("waiter",NaiveWaiter.class);
waiter.greetTo("Al-assad");
Seller seller = (Seller)waiter;   //强制转换为引介切面类型,使用引介增强的成员方法;
seller.sell("Cola","Al-assad");



@Aspect 的高级用法
切点复合运算
Spring @Aspect 支持切点的符合运算,如下:
 
package site.assad.aspectj.expand;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class ComboAspect {
    @Before("execution(* greetTo(..)) && within(site.assad.common.Waiter+)")
        public void beforeGreeting(){
        System.out.println("Hello!");
    }
    @After("execution(* greetTo(..)) && within(site.assad.common.Waiter+)")
    public void afterExecution(){
        System.out.println("Goodbye!");
    }
    @AfterReturning("execution(* greetTo(..)) && target(site.assad.common.Waiter)")
    public void afterGreetig(){
        System.out.println("end!");
    }
}
示例代码模块:
site.assad.common.Waiter,NaiveWaiter(目标类)site.assad.aspectj.expand.ComboAspect(复合切面对象);
site/assad/aspect/expand/bean1.xml (切面编织配置文件); 
site.assad.aspectj.expand.ApsectConfigTest(代理类获取测试)
增强切面的顺序
一个连接点可能匹配多个切点,切点对应的增强在连接点上的编织顺序如下:
  • 如果增强在同一个切面类中声明,则依照增强在切面类中的顺序进行编织;
  • 如果增强位于不同切面类中,如果这些切面实现了 org.framework.core.Ordered 接口,则由接口方法的顺序决定(顺序号小的先织入),如果没有实现 Ordered 接口,则织入顺序是不确定的;
访问连接点信息
@Aspect 使用 org.aspectj.lang.JoinPoint 接口表示连接节点对象,对于环绕类使用  org.aspectj.lang.ProceedingJoinPoint 来表示连接点对象,主要的接口方法如下:
① JoinPoint
  • Object[] getArgs():获取连接点方法的运行参数;
  • Singnature getSingnature():获取连接带你的方法签名对象;
  • Object getTarget():获取连接点所在的目标对象;
  • Object getThis()  :获取代理对象;
② ProceedingJoinPoint
  • Object proceed():通过反射执行目标对象连接点处的方法
  • Object proceed(Object[] args):使用新参数执行连接点方法;
 
@Aspect
public class TestAspect {
    @Before("execution(* greeTo(..))")
    public void getJoinPoint(JoinPoint jp){
        System.out.println(jp.getTarget().getClass().getName()); 
    }
}
绑定连接点方法入参
 args()、this()、target()、@args()、@within()、@target()、@annotation() 这7个函数除了指定类名之外,还可以指定参数名,将目标对象的连接点的方法入参绑定到增强的方法中;
 
@Aspect
public class TestAspect {
    @Before("target(com.smart.NaiveWaiter) && args(name,num,..)")
    public void bindJoinPointParams(int num,String name){
       System.out.println("name:"+name);
       System.out.println("num:"+num);
    }
}
绑定代理对象
使用this(),target() 函数时,可以绑定被代理对象的实例,在通过实例名绑定对象时,依然具有原来连接点匹配的功能,不过类名是用过增强方法中同名参数的类型间接决定的;
绑定代理对象的实际意义在于在切面类中获取代理对象的实例
 
package site.assad.aspectj.expand;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import site.assad.common.Waiter;
@Aspect
public class BilndProxyAspect {
    @Before("this(waiter) && execution(* greeTo(..))")
    public void bindProxObj(Waiter waiter){
        System.out.println("bindProxyObj() :"+ waiter.getClass().getName());   //通过绑定代理对象的方式在切面中获取代理对象的实例
    }
}
绑定类注解对象
@within()、@target() 函数可以将目标类的注解对象绑定到增强方法中;
 
@Aspect
public class TestAspect {
    @Before("@within(m)")   
    public void bindTypeAnnoObject(Monitorable m){   //m为 @Monitorale 注解对象
       System.out.println(m.getClass().getName());
    }
}
其他绑定对象 
除了以上的绑定对象之外,在后置增强 @AfterReturning 中,可以通过 returning 属性绑定连接点方法的返回值;
在抛出增强 @AfterThrowing 中,可以通过 throwing 属性绑定连接点抛出的异常对象;





基于 Schema 的 AOP


在项目使用 Java 5.0 以下的版本,就无法使用基于 @Aspect 注解的切面,此时 Spring 提供了 基于 Schema 的配置方式,兼容使用 AspectJ 切点表达式;

一个简单的示例

示例代码模块:
site.assad.common.Waiter,NaiveWaiter(目标类)site.assad.schema.AdviceMethods(增强方法对象);
site/assad/schema/bean1.xml (切面编织配置文件); 
site.assad.schema.SchemaAppectTest(代理类获取测试)

基于Schema 的配置方式是使用 xml 配置文件来描述切面和编织切面,该配置文件 bean1.xml 如下:
 
<beans ...>
   <!--配置切面-->
    <aop:config proxy-target-class="true" >
        <aop:aspect ref="adviceMethods">
            <!--声明增强类型,切点和切面织入方法-->
            <aop:before pointcut="target(site.assad.common.NaiveWaiter) and execution(* greetTo(..))"
                         method="beforeGreeting"/>
        </aop:aspect>
    </aop:config>
    <bean id="adviceMethods" class="site.assad.schema.AdvcieMethods" />
    <bean id="waiter" class="site.assad.common.NaiveWaiter" />
</beans>
增强逻辑代码储存在 AdvcieMethods 中的一个方法中,该处代码如下:
 
public class AdvcieMethods {
    //前置增强方法
    public void beforeGreeting(){
        System.out.println("Hello!");
    }
}
调用代理类的代码如下:
 
 @Test
    public void testBeforeGreeting(){
        ApplicationContext ctx = new ClassPathXmlApplicationContext("site/assad/schema/bean1.xml");
        Waiter waiter = ctx.getBean("waiter", NaiveWaiter.class);
        waiter.greetTo("Al-assad");
    }

各种增强类型的配置

前置增强类型

示例代码模块:
site.assad.common.Waiter,NaiveWaiter(目标类)site.assad.schema.AdviceMethods(增强方法对象);
site/assad/schema/bean1.xml (切面编织配置文件); 
site.assad.schema.SchemaAppectTest(代理类获取测试)
其中配置文件如下:
 
   <aop:config proxy-target-class="true" >
        <aop:aspect ref="adviceMethods">
            <!--声明增强类型,切点和切面织入方法-->
            <aop:before pointcut="target(site.assad.common.NaiveWaiter) and execution(* greetTo(..))"
                         method="beforeGreeting"/>
        </aop:aspect>
    </aop:config>
    <bean id="adviceMethods" class="site.assad.schema.AdvcieMethods" />
    <bean id="waiter" class="site.assad.common.NaiveWaiter" />

后置增强类型

示例代码模块:
site.assad.common.Waiter,NaiveWaiter(目标类)site.assad.schema.AdviceMethods(增强方法对象);
site/assad/schema/bean2.xml (切面编织配置文件); 
site.assad.schema.SchemaAppectTest(代理类获取测试)
其中配置文件如下:
 
    <aop:config proxy-target-class="true" >
        <aop:aspect ref="adviceMethods">
            <!--声明增强类型,切点和切面织入方法-->
            <aop:after-returning pointcut="target(site.assad.common.NaiveWaiter) and execution(* greetTo(..))"
                        method="afterGreeting"
                        returning="retVal"/>
        </aop:aspect>
    </aop:config>
    <bean id="adviceMethods" class="site.assad.schema.AdvcieMethods" />
    <bean id="waiter" class="site.assad.common.NaiveWaiter" />

环绕增强类型

示例代码模块:
site.assad.common.Waiter,NaiveWaiter(目标类)site.assad.schema.AdviceMethods(增强方法对象);
site/assad/schema/bean3.xml (切面编织配置文件); 
site.assad.schema.SchemaAppectTest(代理类获取测试)
其中配置文件如下:
 
    <aop:config proxy-target-class="true" >
        <aop:aspect ref="adviceMethods">
            <!--声明增强类型,切点和切面织入方法-->
            <aop:around pointcut="target(site.assad.common.NaiveWaiter) and execution(* greetTo(..))"
                method="aroundGreeting"/>
        </aop:aspect>
    </aop:config>
    <bean id="adviceMethods" class="site.assad.schema.AdvcieMethods" />
    <bean id="waiter" class="site.assad.common.NaiveWaiter" />

final 增强类型

 
    <aop:config proxy-target-class="true" >
        <aop:aspect ref="adviceMethods">
            <!--声明增强类型,切点和切面织入方法-->
            <aop:after pointcut="target(site.assad.common.NaiveWaiter) and execution(* greetTo(..))"
                method="afterMethod"/>
        </aop:aspect>
    </aop:config>
    <bean id="adviceMethods" class="site.assad.schema.AdvcieMethods" />
    <bean id="waiter" class="site.assad.common.NaiveWaiter" />

抛出增强类型

site.assad.common.ThrowExceptionTarget(目标类)site.assad.schema.AdviceMethods(增强方法对象);
site/assad/schema/bean4.xml (切面编织配置文件); 
site.assad.schema.SchemaAppectTest(代理类获取测试)
其中配置部分代码如下:
 
    <aop:config proxy-target-class="true" >
        <aop:aspect ref="adviceMethods">
            <!--声明增强类型,切点和切面织入方法-->
            <aop:after-throwing pointcut="target(site.assad.common.NaiveWaiter) and execution(* greetTo(..))"
                method="afterThrowing"
                throwing="ex"/>
        </aop:aspect>
    </aop:config>
    <bean id="adviceMethods" class="site.assad.schema.AdvcieMethods" />
    <bean id="target" class="site.assad.common.ThrowExceptionTarget" />











  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值