Spring学习笔记--Spring AOP(基于@AspectJ)

Spring AOP默认是使用j2se的动态代理为AOP代理这一点可以应用于任何实现接口的类但是在一个没有实现任何接口的类情况下由于j2se的动态代理是不支持没有实现接口的类的所以Spring AOP在这种情况下会使用第三方类库CgLib来为AOP代理

 

Spring AOP使用AspectJ5提供的类库来解释和匹配注释因此需要保证AspectJaspectjweaver.jar库包含在项目中

 

以xml的方式启用@AspectJ支持

首先在xml配置文件中需要添加导入AOP命名空间的标签元素, 所以需要在xml添加如下的配置:

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

xsi:schemaLocation="http://www.springframework.org/schema/aop 

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

配置需要用到aop:aspectj-autoproxy元素:

<aop:aspectj-autoproxy/>

这样就开启了AspectJ支持

声明切面

在开启了@AspectJ的支持后任何带有@Aspect注释的Bean将被Spring自动检测并用于配置Spring AOP

下面是声明切面简例

1:

 

package com.wenj.aop.aspect;

import org.aspectj.lang.annotation.Aspect;

@Aspect
public class TestAOPAspect {
//...
}

 


这样就声明了一个简单的切面, 切面就像其他普通的类一样, 可以有任何方法和字段, 切面也可以包含切入点, 事件通知等

注意: 不可以通过另外一个切面给一个切面添加事件通知

 

声明切入点

Spring AOP 只为Spring Bean对象提供方法执行联接点所以可以认为Spring AOP的切入点就是匹配拦截Spring Bean的执行方法切入点的声明有两个部分识别标记和任意参数我们可以任意的定义切入点所要匹配拦截的执行方法切入点是使用@Pointcut注释来声明的

下面是一个小示例

1:

@Pointcut("execution(* com.wenj.servicesimpl..*.*(..))")

public void anyMethod(){ }

此切入点的功能就是拦截com.wenj.servicesimpl包下和子包下的所有执行方法

注意:Spring中并不是支持所有的AspectJ的切入点类型只是支持一部分 这一部分包括: execution, within, this, target, args, @target, @args,

@within, @annotation, 但是这些也是有限制的由于Spring AOP限制了只

能是匹配执行方法的拦截所以以上的切入点的定义比真正AspectJ的定义类型

组件的要少

 

结合切入点表达

切入点可以通过使用&&, || 和 !来进行结合可以直接通过切入点的名称来进行对切入点的引用结合

下面是一个示例

1:

@Pointcut("execution(* com.wenj.servicesimpl..add(..))")
public void addMethod(){ }
@Pointcut("execution(* com.wenj.servicesimpl..delete(..))")
public void deleteMethod(){ }
//这里结合了add与delete切入点
@Pointcut("addMethod() || deleteMethod()")
public void orMethod(){}

以上先分别定义addMethod()deleteMethod()的切入点然后orMethod()切入点是通过 ||来结合addMethod()deleteMethod()切入点这样我们就可以使用orMethod()这个切入点来拦截来自addMethod()deleteMethod()Spring Bean的执行方法

共享切入点定义

对于一个项目来说我们可以通过管理系统的模块来管理系统所以对于不同的模块我们可以定义不同的切入点来进行管理下面是典型的切面管理模块定义

 

1:

 

package com.wenj.aop.aspect;

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

@Aspect
public class TestAOPAspect {
/* web层的联接点
 * 对于一切定义在com.wenj.web包及其子包下的执行方法进行拦截
    */
   @Pointcut("within(com.wenj.web..*)")
   public void inWebLayer() {}
   /*
    * service层的联接点
    * 对已一切定义在com.wenj.service包及其子包下的执行方法进行拦截
    */
   @Pointcut("within(com.wenj.services..*)")
   public void inServiceLayer() {}
   /*
    * DAO层
    * 对已一切定义在com.wenj.dao包及其子包下的执行方法进行拦截
    */
   @Pointcut("within(com.wenj.dao..*)")
   public void inDataAccessLayer() {}
   /*
    * 业务服务层
    * 在此就会拦截任何定义在services包包及其子包的执行方法
    */
   @Pointcut("execution(* com.wenj.services..*.*(..))")
   public void businessService() {}
   /*
    * DAO层
    * 在此就会拦截任何定义在dao包及其子包的执行方法
    */
   @Pointcut("execution(* com.wenj.dao..*.*(..))")
   public void dataAccessOperation() {}

}

 

注意:

通过类匹配模式串声明切入点,within()函数定义的连接点是针对目标类而言,而非针

对运行期对象的类型而言,这一点和execetion()是相同 的。但和execution()函数不

同的是,within()所指定的连接点最小范围只能是类,而execution()所指定的连接点,

可以大到包, 小到方法入参。所以从某种意义上说,execution()函数的功能涵盖了

within()函数的功能。

所以一般会用execution的比较多    

匹配表达式样例

匹配表达式的格式为:

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)

其中returning type pattern, name pattern 和 parameters pattern 可以任意指定, 返回类型需要与联接点相匹配, 常用*作为返回类型, 意在匹配所有的返回类型, 名称匹配类型匹配的是执行方法的名称, 也可以用*来表示, 意在匹配所有名称的执行方法, 参数类型匹配稍微复杂点, 0--表示匹配的执行方法没有参数, (..)--表示匹配的执行方法, 参数任意, 参数个数可以是0 或更多, (*)--表示匹配的执行方法其中一个参数为任意类型, 例如(*, String)表示第一个参数为任意类型的, 第二个参数为String类型的

下面是一些常见的匹配表达式样例:

· 匹配任何域为public的执行方法:
execution(public * *(..))
· 匹配任何以”start”开头的执行方法:
execution(* start*(..))
· 匹配任何被定义在TestBean接口的执行方法:
execution(* com.wenj.services.TestBean.*(..))
· 匹配任何被定义在services包下的执行方法:
execution(* com.wenj.services..*(..))
· 匹配任何被定义在services包及其子包下的执行方法:
execution(* com.wenj.services..*.*(..))
· 匹配任何在services包下Bean的执行方法:
within(com.wenj.services.*)
· 匹配任何在services包及其子包下Bean的执行方法:
within(com.wenj.services..*)
· 匹配任意实现TestBean接口Bean的执行方法:
this(com.wenj.services.TestBean)
· 匹配目标对象实现TestBean接口Bean的执行方法:
target(com.wenj.services.TestBean)
· 匹配一个以传入参数类型为java.io.Serializable开头, 后面参数类型任意的执行方法, args参数是运行时动态匹配的:
args(java.io.Serializable,..)
· 匹配一个单一参数且传入参数类型为java.io.Serializable的执行方法, args参数是运行时动态匹配的:
args(java.io.Serializable)
注意: 这里不同于在切入点声明为execution(* *(java.io.Serializable)). 参数匹配是在运行时动态的匹配传入参数为Serializable, 而execution匹配时仅仅匹配一个单一参数的方法且参数为Serializable类型的;
· 匹配任何目标对象持有Transactional注解的Bean方法, 必须在目标对象声明这个注释,在接口上声明不起作用:
@target(org.springframework.transaction.annotation.Transactional)
· 匹配任何目标对象的类型持有Transactional注解的Bean方法, 必须在目标对象声明这个注释,在接口上声明不起作用:
@within(org.springframework.transaction.annotation.Transactional)
    . 匹配当前持有注解Transactional的执行方法:
@annotation(org.springframework.transaction.annotation.Transactional)
· 匹配任何一个只接受一个参数的方法, 且方法运行时传入的参数持有注解Classified, 动态切入点, 类似于arg指示符:
@args(com.wenj.security.Classified)
· 匹配以tradeService命名的Bean的执行方法:
bean(tradeService)
· 匹配命名结尾为Service的Bean的执行方法:
bean(*Service)


声明事件通知

事件通知与之前的切入点是有联系的下面是Advice的一些介绍

Before Advice

Before Advice是以注释@Before声明在切面的事件通知

:

@Pointcut("execution(public * *(..))")
public void publicMethod(){}
@Before("publicMethod()")
public void printMsg(){
  // ...
}


 

其中publicMethod()为被声明为@Pointcut的拦截方法

作用是被拦截且匹配的执行方法前先执行注释为@Before的事件通知方法

 

After returning advice

After returning advice是以注释@AfterReturning声明的事件通知

如:

@Pointcut("execution(public * *(..))")
public void publicMethod(){}
@AfterReturning("publicMethod()")
public void printMsg2(){
// ...
}


作用是: 在被拦截且匹配的执行方法成功返回后, 执行@AfterReturning的事件通知方法

有时候我们需要在事件通知的方法体内访问被拦截的执行方法返回的类型, 这时我们可以指定返回参数

如:

@Pointcut("execution(* com.wenj.services..getInt(..))")
public void getIntegerProxy(){}
@AfterReturning(
pointcut = "getIntegerProxy()", 
returning = "retVal")
public void printMsg3(Object retVal) {
// ...
Integer i = (Integer)retVal;
System.out.println("@AfterReturning i: " + i);
}

returning为被拦截执行方法的返回值

After (finally) advice

After (finally) advice是以@After注释声明的事件通知

如:

@Pointcut("execution(* com.wenj.services..*(..))")
public void testAfterAdvice(){}
@After("testAfterAdvice()")
public void afterAdvice(){
System.out.println("@After");
}

Around advice

Around advice是Spring AOP的最后一个事件通知,是以注释@Around声明的事件, 在匹配拦截执行方法后可以执行类似Before和After的事件通知, 并且它是可以决定什么时候怎么样甚至是执行方法已经在执行的时候进行切入, Around Advice 一般用于的执行方法之前与之后需要共享数据, 当然也可以用Before Advice和After Advice实现(但是对于线程安全来说这种做法是不安全的)

@Pointcut("execution(* com.wenj.services..*(..))")

public void testAroundAdvice(){}
@Around("testAroundAdvice()")
    public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
        // start stopwatch/*相当于Before*/
        Object retVal = pjp.proceed();
        // stop stopwatch  /*相当于After*/
        System.out.println("@Around");
        return retVal;
}


通知事件参数设置

传递参数给Advice

之前的@AfterReturning的示例中, 已经知道怎么绑定被拦截执行方法的返回值或抛出异常现在来实现通过绑定被拦截执行方法的参数使匹配更加严谨

 

@Pointcut("execution(* com.wenj.services..*(..))")
public void testArgs(){ }
@Before("testArgs() && args(id,..)")
public void passArgs2Advice(int id){
  if(id != 0){
    System.out.println("@Before && Args: id != 0");
  }
}


这里的@Before添加了参数匹配,只有被拦截执行方法的参数第一个参数为id的才会被匹配拦截

上述例子也可以改成下面这样:

@Pointcut("execution(* com.wenj.services..*(..)) && args(id,..)")
public void testArgs(int id){ }
@Before("testArgs(id)")
public void passArgs2Advice(int id){
  if(id != 0){
    System.out.println("@Before && Args: id != 0");
  }
}


 

......

参数传递到此告一个段落了感兴趣的朋友可以参考AspectJ 编程指导获取更多的细节

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值