Spring中文开发手册(2)[AOP]

 第 6 章 使用Spring进行面向切面编程(AOP)
[@AspectJ支持方式(注解方式)]

1、@AspectJ支持
     启用该支持:
通过在你的Spring的配置中引入下列元素来启用Spring对@AspectJ的支持:
<aop:aspectj-autoproxy/>
或通过在你的application context中添加如下定义来启用@AspectJ支持:
ean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator" />
classpath中引入两个AspectJ库:aspectjweaver.jar 和 aspectjrt.jar。

 2、过程
      声明aspect-->声明pointCut-->声明advice

eg:声明aspect、pointcut

package  com.xyz.someapp;

import  org.aspectj.lang.annotation.Aspect;
import  org.aspectj.lang.annotation.Pointcut;

@Aspect
public   class  SystemArchitecture  {

  
/**
   * A join point is in the web layer if the method is defined
   * in a type in the com.xyz.someapp.web package or any sub-package
   * under that.
   
*/

  @Pointcut(
"within(com.xyz.someapp.web..*)")
  
public void inWebLayer() {}

  
/**
   * A join point is in the service layer if the method is defined
   * in a type in the com.xyz.someapp.service package or any sub-package
   * under that.
   
*/

  @Pointcut(
"within(com.xyz.someapp.service..*)")
  
public void inServiceLayer() {}

  
/**
   * A join point is in the data access layer if the method is defined
   * in a type in the com.xyz.someapp.dao package or any sub-package
   * under that.
   
*/

  @Pointcut(
"within(com.xyz.someapp.dao..*)")
  
public void inDataAccessLayer() {}

  
/**
   * A business service is the execution of any method defined on a service
   * interface. This definition assumes that interfaces are placed in the
   * "service" package, and that implementation types are in sub-packages.
   * 
   * If you group service interfaces by functional area (for example, 
   * in packages com.xyz.someapp.abc.service and com.xyz.def.service) then
   * the pointcut expression "execution(* com.xyz.someapp..service.*.*(..))"
   * could be used instead.
   
*/

  @Pointcut(
"execution(* com.xyz.someapp.service.*.*(..))")
  
public void businessService() {}
  
  
/**
   * A data access operation is the execution of any method defined on a 
   * dao interface. This definition assumes that interfaces are placed in the
   * "dao" package, and that implementation types are in sub-packages.
   
*/

  @Pointcut(
"execution(* com.xyz.someapp.dao.*.*(..))")
  
public void dataAccessOperation() {}

}

切入点指定者的支持 (具体写法可能需要参考更多文档)

Spring AOP 支持在切入点表达式中使用如下的AspectJ切入点指定者:

  • within - 限定匹配特定类型的连接点(在使用Spring AOP的时候,在匹配的类型中定义的方法的执行)。

  • this - 限定匹配特定的连接点(使用Spring AOP的时候方法的执行),其中bean reference(Spring AOP 代理)是指定类型的实例。

  • target - 限定匹配特定的连接点(使用Spring AOP的时候方法的执行),其中目标对象(被代理的appolication object)是指定类型的实例。

  • args - 限定匹配特定的连接点(使用Spring AOP的时候方法的执行),其中参数是指定类型的实例。

  • @target - 限定匹配特定的连接点(使用Spring AOP的时候方法的执行),其中执行的对象的类已经有指定类型的注解。

  • @args - 限定匹配特定的连接点(使用Spring AOP的时候方法的执行),其中实际传入参数的运行时类型有指定类型的注解。

  • @within - 限定匹配特定的连接点,其中连接点所在类型已指定注解(在使用Spring AOP的时候,所执行的方法所在类型已指定注解)。

@annotation - 限定匹配特定的连接点(使用Spring AOP的时候方法的执行),其中连接点的主题有某种给定的注解。

eg:声明adivce
2.1 前置通知(Before advice)

 

import  org.aspectj.lang.annotation.Aspect;
import  org.aspectj.lang.annotation.Before;

@Aspect
public   class  BeforeExample  {

  @Before(
"com.xyz.myapp.SystemArchitecture.dataAccessOperation()")
  
public void doAccessCheck() {
    
// ...
  }


}

 

如果使用一个in-place 的切入点表达式,我们可以把上面的例子换个写法:

 

import  org.aspectj.lang.annotation.Aspect;
import  org.aspectj.lang.annotation.Before;

@Aspect
public   class  BeforeExample  {

  @Before(
"execution(* com.xyz.myapp.dao.*.*(..))")
  
public void doAccessCheck() {
    
// ...
  }


}

 

2.2 返回后通知(After returning advice)
@AfterReturning

2.3  抛出后通知(After throwing advice)
@AfterThrowing

2.4 后通知(After (finally) advice)
不论一个方法是如何结束的,在它结束后(finally)后通知(After (finally) advice)都会运行。
@After

2.5 环绕通知(Around Advice)
环绕通知在一个方法执行之前和之后执行。 它使得通知有机会既在一个方法执行之前又在执行之后运行。
环绕通知使用 @Around 注解来声明。通知的第一个参数必须是 ProceedingJoinPoint 类型。 在通知体内,调用 ProceedingJoinPointproceed() 方法将会导致潜在的连接点方法执行。 proceed 方法也可能会被调用并且传入一个 Object[] 对象-该数组将作为方法执行时候的参数。

 

import  org.aspectj.lang.annotation.Aspect;
import  org.aspectj.lang.annotation.Around;
import  org.aspectj.lang.ProceedingJoinPoint;

@Aspect
public   class  AroundExample  {

  @Around(
"com.xyz.myapp.SystemArchitecture.businessService()")
  
public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
    
// start stopwatch
    Object retVal = pjp.proceed();
    
// stop stopwatch
    return retVal;
  }


}

 

3、通知参数(Advice parameters)
      你可以在通知签名中声明所需的参数,当通知生效时可以获取所需的参数!

      3.1 访问当前的连接点(即该advice生效时所处理的方法)
      任何通知方法可以将第一个参数定义为 org.aspectj.lang.JoinPoint 类型 (环绕通知需要定义为 ProceedingJoinPoint 类型的, 它是 JoinPoint 的一个子类。) JoinPoint 接口提供了一系列有用的方法, 比如 getArgs()(返回方法参数)、getThis()(返回代理对象)、getTarget()(返回目标)、getSignature()(返回正在被通知的方法相关信息)和 toString()(打印出正在被通知的方法的有用信息)。

      3.2 传递参数给通知(Advice)
      如果在一个参数表达式中应该使用类型名字的地方使用一个参数名字,那么当通知执行的时候对应的参数值将会被传递进来。 可能给出一个例子会更好理解。假使你想要通知(advise)接受某个Account对象作为第一个参数的DAO操作的执行,你想要在通知体内也能访问到account对象,你可以写如下的代码: 

@Before( " com.xyz.myapp.SystemArchitecture.dataAccessOperation() && "   +  
        
" args(account,..) " )
public   void  validateAccount(Account account)  {
  
// ...
}

切入点表达式的 args(account,..) 部分有两个目的: 首先它保证了只会匹配那些接受至少一个参数的方法的执行,而且传入的参数必须是 Account 类型的实例, 其次它使得可以在通知体内通过 account 参数来访问那个account参数。

另外一个办法是定义一个切入点,这个切入点在匹配某个连接点的时候“提供”了一个Account对象, 然后直接从通知中访问那个命名的切入点。你可以这样写:

@Pointcut( " com.xyz.myapp.SystemArchitecture.dataAccessOperation() && "   +  
          
" args(account,..) " )
private   void  accountDataAccessOperation(Account account)  {}

@Before(
" accountDataAccessOperation(account) " )
public   void  validateAccount(Account account)  {
  
// ...
}

3.3 决定参数名
      //待学

3.4 处理参数 
      //待学
   

 实例:
Logger

package  net.qking.spring.aop2xml;

import  org.apache.commons.logging.Log;
import  org.apache.commons.logging.LogFactory;

public   class  Logger  {

    
private static Log log = LogFactory.getLog(Logger.class);

    
public void entry(String message) {
        log.info(message);
    }

}



TestBean

package  net.qking.spring.aop2xml;

public   class  TestBean  {

    
public void method1() {
        System.out.println(
"in method1");
    }


    
public void method2() {
        System.out.println(
"in method2");
    }

}


 RootTest

package  net.qking.spring.aop2xml;

import  junit.framework.TestCase;

import  org.apache.commons.logging.Log;
import  org.apache.commons.logging.LogFactory;
import  org.springframework.context.ApplicationContext;
import  org.springframework.context.support.ClassPathXmlApplicationContext;


public   abstract   class  RootTest  extends  TestCase  {

    
protected ApplicationContext  ctx;

    
protected Log log = LogFactory.getLog(getClass());

    
protected RootTest() {
        ctx 
= new ClassPathXmlApplicationContext(getBeanXml());
    }


    
protected abstract String getBeanXml();

}


LogXmlTest

package  net.qking.spring.aop2xml;

public   class  LogXmlTest  extends  RootTest  {

    @Override
    
protected String getBeanXml() {
        
return "aop2aspect.xml";
    }


    
public void testLog() {
        TestBean bean 
= (TestBean) ctx.getBean("testBean");
        bean.method1();
        bean.method2();
    }


}

LogAspect

package  net.qking.spring.aop2aspect;

import  net.qking.spring.aop2xml.Logger;

import  org.aspectj.lang.ProceedingJoinPoint;
import  org.aspectj.lang.annotation.After;
import  org.aspectj.lang.annotation.Around;
import  org.aspectj.lang.annotation.Aspect;
import  org.aspectj.lang.annotation.Before;
import  org.aspectj.lang.annotation.Pointcut;

@Aspect
public   class  LogAspect  {

    
private Logger logger = new Logger();

    @Pointcut(
"execution(public * *(..))")
    
public void publicMethods() {

    }


    @Pointcut(
"execution(* net.qking.spring.aop2xml.Logger.*(..))")
    
public void logObjectCalls() {

    }


    @Pointcut(
"publicMethods()&&!logObjectCalls()")
    
public void loggableCalls() {

    }

 
    @Around(
"loggableCalls()")
    
public Object aroundLogCalls(ProceedingJoinPoint joinPoint) throws Throwable {
        logger.entry(
"before invoke method:"
                     
+ joinPoint.getSignature().getName());
        Object object 
= joinPoint.proceed();
        logger.entry(
"after invoke method:"
                     
+ joinPoint.getSignature().getName());
        
return object;
    }

    
    @Before(
"publicMethods()")
       
public void doCheckBefore(){
        logger.entry(
"in do check before");
    }

    
    @After(
"publicMethods()")
       
public void doCheckAfter(){
        logger.entry(
"in do check after");
    }

    
}


配置文件

<? xml version="1.0" encoding="UTF-8" ?>

< beans  xmlns ="http://www.springframework.org/schema/beans"
    xmlns:xsi
="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop
="http://www.springframework.org/schema/aop"
    xsi:schemaLocation
="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"

>

    
<!--  use aspectj  -->
< aop:aspectj-autoproxy />
 
 
<!--  或者使用以下定义
 <bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator" />
  
-->
 
< bean  id ="logAspect"  class ="net.qking.spring.aop2aspect.LogAspect" />
 
< bean  id ="testBean"  class ="net.qking.spring.aop2xml.TestBean" />

</ beans >

 [Schema-based AOP support]
eg:只要将上面的代码中添加下面文件,及配置文件即可。
LogBean

package  net.qking.spring.aop2xml;

import  org.aspectj.lang.ProceedingJoinPoint;

public   class  LogBean  {

    
private Logger logger = new Logger();

    
public Object aroundLogCalls(ProceedingJoinPoint joinPoint) throws Throwable {
        logger.entry(
"before invoke method:"
                     
+ joinPoint.getSignature().getName());
        Object object 
= joinPoint.proceed();
        logger.entry(
"after invoke method:"
                     
+ joinPoint.getSignature().getName());
        
return object;
    }

}



  配置文件:

<? xml version="1.0" encoding="UTF-8" ?>

< beans  xmlns ="http://www.springframework.org/schema/beans"
    xmlns:xsi
="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop
="http://www.springframework.org/schema/aop"
    xsi:schemaLocation
="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"

>

    
    
< aop:config >
        
<!--   expression 表示要执行的匹配表达式,这里匹配所有的public方法,但是去除logger类的所有方法,防止无限调用 -->

        
< aop:pointcut  id ="loggableCalls"
            expression
="execution(public * *(..)) and !execution(* net.qking.spring.aop2xml.Logger.*(..))"   />


        
< aop:pointcut  id ="single"  expression ="execution(public void net.qking.spring.aop2xml.TestBean.method1(..))"   />

        
< aop:aspect  id ="logAspect"  ref ="logBean" >
            
< aop:around  pointcut-ref ="single"
                method
="aroundLogCalls"   />
        
</ aop:aspect >

    
</ aop:config >
    
< bean  id ="logBean"  class ="net.qking.spring.aop2xml.LogBean"   />
    
< bean  id ="testBean"  class ="net.qking.spring.aop2xml.TestBean"   />
</ beans >
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值