spring中AOP3

来源:互联网 发布:fc2live软件破解版域名 编辑:程序博客网 时间:2024/04/29 21:38

 

4.6spring的AOP支持

之前讲到的都是AOP的advice都必须实现特定的接口,而配置上依赖于XML的繁琐配置。在spring2.0之后,对于AOP的实现与设置新增了两种方式:一种是基于XML schema的设置,另外一种是基于annotation的支持。两种方式对于AOP的实现都进行了简化。

Before使用xmlschema的方式:

#

package onlyfun.beforeadvice;

 

public interfaceIHello {

   public void hello(String msg);

}

#

package onlyfun.beforeadvice;

public class HelloSpeaker implementsIHello {

   @Override

   publicvoid hello(String msg) {

      //TODO Auto-generated method stub

      System.out.println(msg);

   }

 

}

#

package onlyfun.beforeadvice;

 

import java.util.logging.Level;

import java.util.logging.Logger;

import org.aspectj.lang.JoinPoint;

public classLogBeforeAdvice {

   private Loggerlogger  = Logger.getLogger(this.getClass().getName());

  

   public void before(JoinPoint joinPoint){

      logger.log(Level.INFO,"method starts ..."+

            joinPoint.getSignature().getDeclaringTypeName()+

            "."+

            joinPoint.getSignature().getName());

           

   }

}

 

#

<?xmlversion="1.0"encoding="UTF-8"?>

<beansxmlns="http://www.springframework.org/schema/beans" 

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

    xmlns:aop="http://www.springframework.org/schema/aop" 

    xmlns:tx="http://www.springframework.org/schema/tx" 

    xmlns:context="http://www.springframework.org/schema/context"  

    xsi:schemaLocation=

        http://www.springframework.org/schema/context  

        http://www.springframework.org/schema/context/spring-context-3.0.xsd   

    http://www.springframework.org/schema/beans  

     http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 

    http://www.springframework.org/schema/tx  

    http://www.springframework.org/schema/tx/spring-tx-3.0.xsd 

     http://www.springframework.org/schema/aop  

     http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> 

    

 

    <beanid="helloSpeaker"class ="onlyfun.beforeadvice.HelloSpeaker"/>

   <beanid="logBeforeAdvice"class ="onlyfun.beforeadvice.LogBeforeAdvice"/>

    <aop:config>

         <aop:aspectid="logging"ref="logBeforeAdvice">

            <aop:before 

                pointcut="execution(*onlyfun.beforeadvice.IHello.*(..))"

                method = "before"/>

         </aop:aspect>

    </aop:config> 

    </beans>

#test

package onlyfun.beforeadvice;

 

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.ClassPathXmlApplicationContext;

 

public classTestLogBefore {

 

   public static void main(String[] args) {

      // TODO Auto-generated method stub

      ApplicationContextcontext  =new ClassPathXmlApplicationContext("spring-config2.xml");

      //这里和之前advice不太一样,原先获得的是代理的id,现在直接是

//被代理对象的id

      IHellohello= (IHello)context.getBean("helloSpeaker");

      hello.hello("this adudu ");

   }

 

}

从该例子可以看出不再需要声明PRoxyFactoryBean,所有的AOP的配置实在<aop:config>标签中设置的;<aop:aspect>标签定义Aspect的实现,也就是advice的实例。

Xml配置中开头的红色部分需要注意,引入了AOP,且指定了spring的AOP的相关xsd解析的版本。

 

<aop:before>标签标示设置advice将作为before advice, pointcut属性定义Pointcut形式,execution(*onlyfun.beforeadvice.IHello.*(..)

这三部分的颜色分别代表了返回值,方法名称,和输入参数值,*返回值任意,(..)参数为任意类型。

Method表示设置advice上要调用的方法,在这里设置调用LogBeforeAdvice的before方法。

如果需要重用pointcut可以这么写:

 

    <aop:config>

         <aop:pointcutid="logHello" expression="execution(*onlyfun.beforeadvice.IHello.*(..))"/>

         <aop:aspectid="logging"ref="logBeforeAdvice">

            <aop:before 

                pointcut-ref="logHello"

                method = "before"/>

         </aop:aspect>

    </aop:config> 

这里的jar包需要考虑使用的是spring3.2.4的所有jar,还有commons-logging.jar,AspectJWeaver1.6.10.jar,还有aopalliance1.0.jar

 

4.6.2BeforeAdvice基于注解的形式

Spring2.0中可以基于Annotation来设置AOP的advice,在XML的设置上可以简化,IHello,HelloSpeaker,Test等和原来的相同,余下的是

#LogBeforeAdvice @annotation

package onlyfun.beforeadvice;

 

import java.util.logging.Level;

import java.util.logging.Logger;

 

import org.aspectj.lang.JoinPoint;

import org.aspectj.lang.annotation.Aspect;

import org.aspectj.lang.annotation.Before;

 

@Aspect

public classLogBeforeAdvice2 {

   private Loggerlogger =  Logger.getLogger(this.getClass().getName());

  

   @Before("execution(*onlyfun.beforeadvice.IHello.*(..))")

   public void before(JoinPoint joinPoint){

      logger.log(Level.INFO,"method starting ..."+joinPoint.getSignature().getDeclaringTypeName()+"."+joinPoint.getSignature().getName());

   }

}

#spring-config3.xml

<?xmlversion="1.0"encoding="UTF-8"?>

<beansxmlns="http://www.springframework.org/schema/beans" 

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

    xmlns:aop="http://www.springframework.org/schema/aop" 

    xmlns:tx="http://www.springframework.org/schema/tx" 

    xmlns:context="http://www.springframework.org/schema/context"  

    xsi:schemaLocation=

        http://www.springframework.org/schema/context  

        http://www.springframework.org/schema/context/spring-context-3.0.xsd   

    http://www.springframework.org/schema/beans  

    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 

    http://www.springframework.org/schema/tx  

    http://www.springframework.org/schema/tx/spring-tx-3.0.xsd 

     http://www.springframework.org/schema/aop  

    http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> 

    

 

    <beanid="helloSpeaker"class ="onlyfun.beforeadvice.HelloSpeaker"/>

   <beanid="logBeforeAdvice"class ="onlyfun.beforeadvice.LogBeforeAdvice2"/>

    

     <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

    </beans>

这里使用的是<aop:aspect-autoproxy/>@Aspect

可以看出基于annotation的形式,几乎不用再xml里设置什么,只需要实例化Advice与我们的目标对象。

 

这里要将一个类设置为Aspect,只要使用@Aspect在类上表示它是Aspect的实现即可,@Before表示该方法在应用为BeforeAdvice时调用,当中直接编写pointcut表达式。

 

这里实现接口的方式、xmlschema的方式、annotation的方式究竟哪种advice的实现方式好?

 

 

4.6.3pointcut的定义

Spring2.0中声明pointcut,主要包括表达式execution,签名signature

Execution(modifiers-pattern?

Ret-type-pattern

Declaring-type-pattern?

Name-pattern(param-pattern)

Throws-pattern?)

存取修饰匹配,传回类型匹配,类类型匹配,方法名称匹配(参数类型匹配),异常类型匹配,有问号的地方可以省略不声明。

 

传回类型一般用*表示所有传回值类型都符合,也可以配置完整类型名称。方法名称匹配也可以使用*。参数类型匹配()表示没有参数,(type)表示带有一个类型为type的参数;(*)表示带有一个参数,且参数类型为任意;(..)表示零个或者多个参数。

Execution(public **(..))符合任何公开的方法

Execution(*hello*(..))符合hello开头的方法

Execution* onlyfun.beforeadvice.IHello.*(..)IHello接口中声明的任何方法

Execution*onlyfun.advice.service.*.*(..))该包下声明的所有方法

Execution*onlyfun.advice.service..*.*(..)service包或者其子包下的所有的方法

Within(onlyfun.advice.service.*)符合该包下声明的任何方法

Withinonlyfun.advice.service..*)符合该报及其子包下的任何方法。

此外还有thistargetargs等。

 

spring2.0中结合@pointcutannotation定义方式,可以让pointcut在定义上更加容易,定义包括两部分:pointcut表达式和pointcut签名。如:

@Pointcut(“execution(*hello*(..))”)//pointcut表达式

Private void anyHello();//签名

 

如要使用所定义的Pointcut可以如下使用pointcut签名:

@Before(“anyHello()”)

 

这个例子相当于直接在@Before中定义一下的pointcut表示式

@Before(“execution(*hello*(..))”)

 

Pointcut的定义时可以使用&& || !,它们用在相应的签名上.

@Pointcut(“anyHello()||anyWelcome()”)//@pointcut表达式

Private void anyHelloWelcome();//pointcut签名

 

如:例子

package onlyfun.beforeadvice;

 

import org.aspectj.lang.annotation.Pointcut;

 

public classCommonPointcut {

   @Pointcut("execution(* hello*(..))")

   public void anyHello();

 

  

   @Pointcut("execution(* welcome*(..))")

   public void anyWelcome();

  

  

   @Pointcut("execution(*onlyfun.beforeadvice.IHello.*(..))")

   public void hello();

   @Pointcut("anyHello()||anyWelcome()")

   public void anyHelloWelcome();

}

 

#

package onlyfun.beforeadvice;

 

import java.util.logging.Level;

import java.util.logging.Logger;

 

import org.aspectj.lang.JoinPoint;

import org.aspectj.lang.annotation.Aspect;

import org.aspectj.lang.annotation.Before;

 

@Aspect

public classLogBeforeAdvice2 {

   private Loggerlogger =  Logger.getLogger(this.getClass().getName());

  

   @Before("execution(*onlyfun.beforeadvice.IHello.*(..))")

   public void before(JoinPoint joinPoint){

      logger.log(Level.INFO,"method starting ..."+joinPoint.getSignature().getDeclaringTypeName()+"."+joinPoint.getSignature().getName());

   }

  

   @Before("onlyfun.beforeadvice.CommonPointcut.anyHelloWelcome()")

   public void show(JoinPoint joinPoint){

      logger.log(Level.INFO,"method show().......anyhellowelcome()");

   }

}

 

在commonPointcut中定义的pointcut,使用时只需要指定完整类名称加上Pointcut的签名。如:@Before("onlyfun.beforeadvice.CommonPointcut.anyHelloWelcome()")

只要满足相应的pointcut,将会在拦击的时候先调用show方法,在执行pointcut中设置的被拦击的方法。配置文件不变。

 

   ApplicationContextcontext  =new ClassPathXmlApplicationContext("spring-config3.xml");

     

      IHellohello= (IHello)context.getBean("helloSpeaker");

      hello.hello("this adudu ");

测试文件在执行上面的代码的时候,就会有两个pointcut,2个interceptor,

先调用before方法,在调用show方法。

 

也就是单独一个类中声明pointcut,按照表达式和签名声明pointcut,使用是可以再切入的方法上使用@Before(“该单独类的包路径和pointcut的签名方法()”)即可。

 

同时也可以在aop标签中指定pointcut

<aop:before pointcut =”onlyfun.pointcutadvice.CommonPointcut.anyHelloWelcome()”

Method=”before”/>

 

4.6.4 AfterReturningAdvice

基于XMLschema实现

   相应的advice类不用实现任何类和接口,里面写的方法随便,

只要在配置文件中

<aop:config>

         <aop:pointcutid="logHello" expression="execution(*onlyfun.afteradvice.IHello.*(..))"/>

         <aop:aspectid="logging"ref="logAfterAdvice">

            <aop:before 

                pointcut-ref="logHello"

                method = "after"/>

         </aop:aspect>

    </aop:config> 

即可。

下面的例子是使用了注解annotation的,注意其中pointcut的使用和需要处理的方法服务:

package onlyfun.afteradvice;

 

import java.util.logging.Level;

import java.util.logging.Logger;

import org.aspectj.lang.JoinPoint;

import org.aspectj.lang.annotation.AfterReturning;

import org.aspectj.lang.annotation.Aspect;

import org.aspectj.lang.annotation.Before;

import org.aspectj.lang.annotation.Pointcut;

@Aspect

public classAfterReturnningAdvice2 {

private Loggerlogger  = Logger.getLogger(this.getClass().getName());

//声明一个pointcut,包含表达式和签名两部分

@Pointcut("execution(* onlyfun.afteradvice.IHello.*(..))")

private voidlogging(){}

 

//直接使用注解,里面是pointcut的签名

   @Before("logging()")

   public void before(JoinPoint joinPoint){

      logger.log(Level.INFO,"before method showing ......");

   }

//该处是带有返回值的形式

   @AfterReturning(pointcut="logging()",returning="retVal")

   public void after(JoinPoint joinPoint,ObjectretVal){

      logger.log(Level.INFO,"after method showing ......");

   }

}

 

这里是使用的<aop:aspect-autoproxy/>

 

 

此外对于aroundadvice和throwadvice,aroundadvice也是不需要继承和实现,只需要自己定义一个invoke方法,在proceed()执行前后加入要加入的东西即可。

@Around(“execution(*onlyfun.aroundadvice.IHello.*(..))”)

Public Objectinvoke(ProceedingJoinPoint joinPoint)throws Throwable{}

 

其他类似。Throwadvice,也是类似

@AfterThrowing(pointcut=”logging()”,throwing=”throwable”)

Public void afterThrowing(JoinPointjoinPoint,Throwable throwable){}

 

以上是注解形式,下面是xmlschema的形式

<aop:around …….>

<aop:after-throwing …>具体可以参考xmlschema的具体形式。

 

AOP总结一下:

主要是cross-cutting、cross-cuttingconcern 、aspect、advice、introduction、interceptor、pointcut、advisor、jointpoint这几个概念。

在业务之外的需要添加的服务如日志动作等,可以记为cross-cutting concern,若干个cross-cutting concern可以合成一个Aspects(如日志动作、事务处理、权限过滤等集合到一起,),每一个如日志动作称为一个advice(服务),而这样的服务需要在哪里加入,则需要pointcut,advisor给予明确的地点或者是时机。执行服务时可以从jointPoint中获得相应信息如getArgs(),getSignature(),getTarget等,供使用。而被代理对象的方法是根据pointcut中设置的表达式过滤的,同时由interceptor拦截器进行拦截,根据advice的方法前后等定义在方法执行前后执行相应的advice服务。Introduction则是可以给被代理的对象提供新的行为,而不用修改被代理对象的任何代码。

 主要内容摘自《spring2.0技术手册》林信良著,其中代码自己编写。

 

0 0