四、基于@AspectJ的AOP
Spring除了支持Schema方式配置AOP,还支持注解方式:使用@AspectJ风格的切面声明。
4.1 、 启用对@AspectJ的支持
Spring默认不支持@AspectJ风格的切面声明,为了支持需要使用如下配置:
<aop:aspectj-autoproxy/>
这样Spring就能发现@AspectJ风格的切面并且将切面应用到目标对象。
4.2、 声明切面
@AspectJ风格的声明切面非常简单,使用@Aspect注解进行声明:
@Aspect()
Public class Aspect{
……
}
然后将该切面在配置文件中声明为Bean后,Spring就能自动识别并进行AOP方面的配置:
<bean id="aspect" class="……Aspect"/>
该切面就是一个POJO,可以在该切面中进行切入点及通知定义,接着往下看吧.
4.3 、声明切入点
@AspectJ风格的命名切入点使用org.aspectj.lang.annotation包下的@Pointcut+方法(方法必须是返回void类型)实现。
@Pointcut(value="切入点表达式", argNames = "参数名列表")
public void pointcutName(……) {}
- value:指定切入点表达式;
- argNames:指定命名切入点方法参数列表参数名字,可以有多个用“,”分隔,这些参数将传递给通知方法同名的参数,同时比如切入点表达式“args(param)”将匹配参数类型为命名切入点方法同名参数指定的参数类型。
- pointcutName:切入点名字,可以使用该名字进行引用该切入点表达式。
@Pointcut(value="execution(* com.nieshenkuan..*.sayAdvisorBefore(..)) && args(param)",
argNames = "param")
public void beforePointcut(String param) {}
定义了一个切入点,名字为“beforePointcut”,该切入点将匹配目标方法的第一个参数类型为通知方法实现中参数名为“param”的参数类型。
4.4 、声明通知
@AspectJ风格的声明通知也支持5种通知类型:
一、前置通知:使用org.aspectj.lang.annotation 包下的@Before注解声明;
@Before(value = "切入点表达式或命名切入点", argNames = "参数列表参数名")
- value :指定切入点表达式或命名切入点;
- argNames:与Schema方式配置中的同义。
接下来示例一下吧:
1、定义接口和实现,在此我们就使用Schema风格时的定义;
2、定义切面:
package com.nieshenkuan.aop;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
//定义切面
@Aspect
public class HelloWorldAspect2 {
}
3、定义切入点:
// 定义切入点
@Pointcut(value = "execution(* com.nieshenkuan..*.sayBefore(..)) && args(param)", argNames = "param")
public void beforePointcut(String param) {
}
4、定义通知:
// 定义通知
@Before(value = "beforePointcut(param)", argNames = "param")
public void beforeAdvice(String param) {
System.out.println("===========before advice param:" + param);
}
整个切面:
package com.nieshenkuan.aop;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
//定义切面
@Aspect
public class HelloWorldAspect2 {
// 定义切入点
@Pointcut(value = "execution(* com.nieshenkuan..*.sayBefore(..)) && args(param)", argNames = "param")
public void beforePointcut(String param) {
}
// 定义通知
@Before(value = "beforePointcut(param)", argNames = "param")
public void beforeAdvice(String param) {
System.out.println("===========before advice param:" + param);
}
}
5、在applicationContext7.xml配置文件中进行如下配置:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.2.xsd ">
<bean id="helloWorldService" class="com.nieshenkuan.aop.HelloWorldService"></bean>
<aop:aspectj-autoproxy />
<bean id="aspect" class="com.nieshenkuan.aop.HelloWorldAspect2" />
</beans>
6、测试代码:Test.java
@org.junit.Test
public void testAnnotationBeforeAdvice() {
System.out.println("======================================");
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext7.xml");
IHelloWorldService helloworldService = ctx.getBean("helloWorldService", IHelloWorldService.class);
helloworldService.sayBefore("before");
System.out.println("======================================");
}
输出:
======================================
===========before advice param:before
============say before
======================================
切面、切入点、通知全部使用注解完成:
1)使用@Aspect将POJO声明为切面;
2)使用@Pointcut进行命名切入点声明,同时指定目标方法第一个参数类型必须是java.lang.String,对于其他匹配的方法但参数类型不一致的将也是不匹配的,通过argNames = "param"指定了将把该匹配的目标方法参数传递给通知同名的参数上;
3)使用@Before进行前置通知声明,其中value用于定义切入点表达式或引用命名切入点;
4)配置文件需要使用<aop:aspectj-autoproxy/>来开启注解风格的@AspectJ支持;
5)需要将切面注册为Bean,如“aspect”Bean;
6)测试代码完全一样。
二、后置返回通知:使用org.aspectj.lang.annotation 包下的@AfterReturning注解声明;
@AfterReturning(
value="切入点表达式或命名切入点",
pointcut="切入点表达式或命名切入点",
argNames="参数列表参数名",
returning="返回值对应参数名")
- value:指定切入点表达式或命名切入点;
- pointcut:同样是指定切入点表达式或命名切入点,如果指定了将覆盖value属性指定的,pointcut具有高优先级;
- argNames:与Schema方式配置中的同义;
- returning:与Schema方式配置中的同义。
@AfterReturning(
value="execution(* com.nieshenkuan..*.sayBefore(..))",
pointcut="execution(* com.nieshenkuan..*.sayAfterReturning(..))",
argNames="retVal", returning="retVal")
public void afterReturningAdvice(Object retVal) {
System.out.println("===========after returning advice retVal:" + retVal);
}
这时候,切面就为:
package com.nieshenkuan.aop;
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 class HelloWorldAspect2 {
// 定义切入点
@Pointcut(value = "execution(* com.nieshenkuan..*.sayBefore(..)) && args(param)", argNames = "param")
public void beforePointcut(String param) {
}
// 定义通知
@Before(value = "beforePointcut(param)", argNames = "param")
public void beforeAdvice(String param) {
System.out.println("===========before advice param:" + param);
}
// 后置返回通知
@AfterReturning(value = "execution(* com.nieshenkuan..*.sayBefore(..))", pointcut = "execution(* com.nieshenkuan..*.sayAfterReturning(..))", argNames = "retVal", returning = "retVal")
public void afterReturningAdvice(Object retVal) {
System.out.println("===========after returning advice retVal:" + retVal);
}
}
测试代码:Test.java(其他代码不用改,这种方式,只是用注解的方式,代替了xml的配置,只需要在切面中定义前置,后置等等通知即可);
@org.junit.Test
public void testAnnotationAfterReturningAdvice() {
System.out.println("======================================");
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext7.xml");
IHelloWorldService helloworldService = ctx.getBean("helloWorldService", IHelloWorldService.class);
helloworldService.sayBefore("before");
System.out.println("======================================");
}
结果:
======================================
===========before advice param:before
============say before
===========after returning advice retVal:null
======================================
三、后置异常通知:使用org.aspectj.lang.annotation 包下的@AfterThrowing注解声明;
@AfterThrowing (
value="切入点表达式或命名切入点",
pointcut="切入点表达式或命名切入点",
argNames="参数列表参数名",
throwing="异常对应参数名")
- value:指定切入点表达式或命名切入点;
- pointcut:同样是指定切入点表达式或命名切入点,如果指定了将覆盖value属性指定的,pointcut具有高优先级;
- argNames:与Schema方式配置中的同义;
- throwing:与Schema方式配置中的同义。
@AfterThrowing(value = "execution(* com.nieshenkuan..*.sayAfterThrowing(..))", pointcut = "execution(* com.nieshenkuan..*.sayAfterThrowing(..))", argNames = "exception", throwing = "exception")
public void afterThrowingAdvice(Exception exception) {
System.out.println("===========after throwing advice exception:" + exception);
}
切面代码如下:
package com.nieshenkuan.aop;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
//定义切面
@Aspect
public class HelloWorldAspect2 {
// 定义切入点
@Pointcut(value = "execution(* com.nieshenkuan..*.sayBefore(..)) && args(param)", argNames = "param")
public void beforePointcut(String param) {
}
// 定义通知
@Before(value = "beforePointcut(param)", argNames = "param")
public void beforeAdvice(String param) {
System.out.println("===========before advice param:" + param);
}
// 后置返回通知
@AfterReturning(value = "execution(* com.nieshenkuan..*.sayBefore(..))", pointcut = "execution(* com.nieshenkuan..*.sayAfterReturning(..))", argNames = "retVal", returning = "retVal")
public void afterReturningAdvice(Object retVal) {
System.out.println("===========after returning advice retVal:" + retVal);
}
@AfterThrowing(value = "execution(* com.nieshenkuan..*.sayAfterThrowing(..))", pointcut = "execution(* com.nieshenkuan..*.sayAfterThrowing(..))", argNames = "exception", throwing = "exception")
public void afterThrowingAdvice(Exception exception) {
System.out.println("===========after throwing advice exception:" + exception);
}
}
测试代码:Test.java
@org.junit.Test
public void testAnnotationAfterThrowing() {
System.out.println("======================================");
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext7.xml");
IHelloWorldService helloworldService = ctx.getBean("helloWorldService", IHelloWorldService.class);
helloworldService.sayAfterThrowing();
System.out.println("======================================");
}
结果:
======================================
============before throwing
===========after throwing advice exception:java.lang.RuntimeException
四、后置最终通知:使用org.aspectj.lang.annotation 包下的@After注解声明;
@After (
value="切入点表达式或命名切入点",
argNames="参数列表参数名")
- value:指定切入点表达式或命名切入点;
- argNames:与Schema方式配置中的同义;
@After(value = "execution(* com.nieshenkuan..*.sayAfterFinally(..))")
public void afterFinallyAdvice() {
System.out.println("===========after finally advice");
}
此时切面代码:
package com.nieshenkuan.aop;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
//定义切面
@Aspect
public class HelloWorldAspect2 {
// 定义切入点
@Pointcut(value = "execution(* com.nieshenkuan..*.sayBefore(..)) && args(param)", argNames = "param")
public void beforePointcut(String param) {
}
// 定义前置通知
@Before(value = "beforePointcut(param)", argNames = "param")
public void beforeAdvice(String param) {
System.out.println("===========before advice param:" + param);
}
// 后置返回通知
@AfterReturning(value = "execution(* com.nieshenkuan..*.sayBefore(..))", pointcut = "execution(* com.nieshenkuan..*.sayAfterReturning(..))", argNames = "retVal", returning = "retVal")
public void afterReturningAdvice(Object retVal) {
System.out.println("===========after returning advice retVal:" + retVal);
}
// 后置异常通知
@AfterThrowing(value = "execution(* com.nieshenkuan..*.sayAfterThrowing(..))", pointcut = "execution(* com.nieshenkuan..*.sayAfterThrowing(..))", argNames = "exception", throwing = "exception")
public void afterThrowingAdvice(Exception exception) {
System.out.println("===========after throwing advice exception:" + exception);
}
// 后置最终通知
@After(value = "execution(* com.nieshenkuan..*.sayAfterFinally(..))")
public void afterFinallyAdvice() {
System.out.println("===========after finally advice");
}
}
测试代码:
@org.junit.Test
public void testAnnotationAfterFinally() {
System.out.println("======================================");
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext7.xml");
IHelloWorldService helloworldService = ctx.getBean("helloWorldService", IHelloWorldService.class);
helloworldService.sayAfterFinally();
System.out.println("======================================");
}
结果:
============before finally
===========after finally advice
五、环绕通知:使用org.aspectj.lang.annotation 包下的@Around注解声明;
@Around (
value="切入点表达式或命名切入点",
argNames="参数列表参数名")
- value:指定切入点表达式或命名切入点;
- argNames:与Schema方式配置中的同义;
// 环绕通知
@Around(value = "execution(* com.nieshenkuan..*.sayAround(..))")
public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("===========around before advice");
Object retVal = pjp.proceed(new Object[] { "replace" });
System.out.println("===========around after advice");
return retVal;
}
切面代码如下:
package com.nieshenkuan.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
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 HelloWorldAspect2 {
// 定义切入点
@Pointcut(value = "execution(* com.nieshenkuan..*.sayBefore(..)) && args(param)", argNames = "param")
public void beforePointcut(String param) {
}
// 定义前置通知
@Before(value = "beforePointcut(param)", argNames = "param")
public void beforeAdvice(String param) {
System.out.println("===========before advice param:" + param);
}
// 后置返回通知
@AfterReturning(value = "execution(* com.nieshenkuan..*.sayBefore(..))", pointcut = "execution(* com.nieshenkuan..*.sayAfterReturning(..))", argNames = "retVal", returning = "retVal")
public void afterReturningAdvice(Object retVal) {
System.out.println("===========after returning advice retVal:" + retVal);
}
// 后置异常通知
@AfterThrowing(value = "execution(* com.nieshenkuan..*.sayAfterThrowing(..))", pointcut = "execution(* com.nieshenkuan..*.sayAfterThrowing(..))", argNames = "exception", throwing = "exception")
public void afterThrowingAdvice(Exception exception) {
System.out.println("===========after throwing advice exception:" + exception);
}
// 后置最终通知
@After(value = "execution(* com.nieshenkuan..*.sayAfterFinally(..))")
public void afterFinallyAdvice() {
System.out.println("===========after finally advice");
}
// 环绕通知
@Around(value = "execution(* com.nieshenkuan..*.sayAround(..))")
public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("===========around before advice");
Object retVal = pjp.proceed(new Object[] { "replace" });
System.out.println("===========around after advice");
return retVal;
}
}
测试代码如下:
@org.junit.Test
public void testAnnotationAround() {
System.out.println("======================================");
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext7.xml");
IHelloWorldService helloworldService = ctx.getBean("helloWorldService", IHelloWorldService.class);
helloworldService.sayAround("haha");
System.out.println("======================================");
}
结果为:
======================================
===========around before advice
============around param:replace
===========around after advice
======================================
4.5 、引入
@AspectJ风格的引入声明在切面中使用org.aspectj.lang.annotation包下的@DeclareParents声明:
@DeclareParents(
value=" AspectJ语法类型表达式",
defaultImpl=引入接口的默认实现类)
private Interface interface;
- value:匹配需要引入接口的目标对象的AspectJ语法类型表达式;与Schema方式中的types-matching属性同义;
- private Interface interface:指定需要引入的接口;
- defaultImpl:指定引入接口的默认实现类,没有与Schema方式中的delegate-ref属性同义的定义方式;
@DeclareParents(value="com.nieshenkuan..*.IHelloWorldService+",
defaultImpl=com.nieshenkuan.aop.IntroductiondServiceImpl.class)
private IIntroductionService introductionService;
这里就不再测试了。测的手都酸了。。。
未完........
网友评论