Spring除了支持Schema方式配置AOP,还支持注解方式:使用@AspectJ风格的切面声明。
1 启用对@AspectJ的支持
Spring默认不支持@AspectJ风格的切面声明,为了支持需要使用如下配置:
-
- <aop:aspectj-autoproxy/></span>
这样Spring就能发现@AspectJ风格的切面并且将切面应用到目标对象。
2 声明切面
@AspectJ风格的声明切面非常简单,使用@Aspect注解进行声明:
- package zmx.spring.aop.test;
-
- import org.aspectj.lang.annotation.After;
- import org.aspectj.lang.annotation.Aspect;
- import org.aspectj.lang.annotation.Before;
- import org.aspectj.lang.annotation.Pointcut;
- //定义切面
- @Aspect()
- public class LoggerAspect {
-
- //定义切入点
- @Pointcut(value="execution(* zmx.spring.aop.test..*.*(..)) && args(param)", argNames = "param")
- public void beforePointcut(String param) {}
-
-
- @Pointcut(value="execution(* zmx.spring.aop.test..*.*(..))")
- public void afterPointcut(){}
-
-
- //定义通知
-
- //前置日志通知
- @Before(value = "beforePointcut(param)", argNames = "param")
- public void beforeAdvice(String param) {
- System.out.println("===========before advice param:" + param);
- }
-
- //后置日志通知
- @After(value = "afterPointcut()")
- public void afterAdvice() {
- System.out.println("===========after advice param");
- }
-
-
- }
然后将该切面在配置文件中声明为Bean后,Spring就能自动识别并进行AOP方面的配置:
-
- <bean id="loggerAspect" class="zmx.spring.aop.test.LoggerAspect" />
- <span style="font-size:12px;"> 该切面就是一个POJO,可以在该切面中进行切入点及通知定义,接着往下看吧。</span>
3 声明切入点
@AspectJ风格的命名切入点使用org.aspectj.lang.annotation包下的@Pointcut+方法(方法必须是返回void类型)实现。
java代码:
- @Pointcut(value="切入点表达式", argNames = "参数名列表")
- public void pointcutName(……) {}
-
value:指定切入点表达式;
argNames:指定命名切入点方法参数列表参数名字,可以有多个用“,”分隔,这些参数将传递给通知方法同名的参数,同时比如切入点表达式“args(param)”将匹配参数类型为命名切入点方法同名参数指定的参数类型。
pointcutName:切入点名字,可以使用该名字进行引用该切入点表达式。
java代码:
- @Pointcut(value="execution(* cn.javass..*.sayAdvisorBefore(..)) && args(param)", argNames = "param")
- public void beforePointcut(String param) {}
定义了一个切入点,名字为“beforePointcut”,该切入点将匹配目标方法的第一个参数类型为通知方法实现中参数名为“param”的参数类型。
4 声明通知
@AspectJ风格的声明通知也支持5种通知类型:
一、前置通知:使用org.aspectj.lang.annotation 包下的@Before注解声明;
java代码:
- @Before(value = "切入点表达式或命名切入点", argNames = "参数列表参数名")
value:指定切入点表达式或命名切入点;
argNames:与Schema方式配置中的同义。
5 实例
接下来示例一下吧:
1、定义接口和实现,在此我们就使用Schema风格时的定义;
接口:
- package zmx.spring.aop.test;
-
- public interface IHelloWorldService {
-
- public void sayHello();
-
- public void recordLog(String log);
-
- }
实现:
- package zmx.spring.aop.test;
-
- public class HelloWorldService implements IHelloWorldService {
-
- @Override
- public void sayHello() {
- System.out.println("============Hello World!================");
- }
-
- @Override
- public void recordLog(String log) {
-
- System.out.println("============record Log================"+log);
- }
-
-
-
- }
2、定义切面:
3、定义切入点:
4、定义通知:
- package zmx.spring.aop.test;
-
- import org.aspectj.lang.annotation.After;
- import org.aspectj.lang.annotation.Aspect;
- import org.aspectj.lang.annotation.Before;
- import org.aspectj.lang.annotation.Pointcut;
- //2、定义切面
- @Aspect()
- public class LoggerAspect {
-
- //3、定义切入点
- @Pointcut(value="execution(* zmx.spring.aop.test..*.*(..)) && args(param)", argNames = "param")
- public void beforePointcut(String param) {}
-
-
- @Pointcut(value="execution(* zmx.spring.aop.test..*.*(..))")
- public void afterPointcut(){}
-
-
- //4、定义通知
-
- //前置日志通知
- @Before(value = "beforePointcut(param)", argNames = "param")
- public void beforeAdvice(String param) {
- System.out.println("===========before advice param:" + param);
- }
-
- //后置日志通知
- @After(value = "afterPointcut()")
- public void afterAdvice() {
- System.out.println("===========after advice param");
- }
-
-
- }
- <?xml version="1.0" encoding="UTF-8" standalone="no"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:aop="http://www.springframework.org/schema/aop"
- xmlns:context="http://www.springframework.org/schema/context"
- xmlns:jee="http://www.springframework.org/schema/jee"
- xmlns:tx="http://www.springframework.org/schema/tx"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-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/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
- http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd
- http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
-
-
- <aop:aspectj-autoproxy/>
-
-
-
-
- <bean id="helloWorldService" class="zmx.spring.aop.test.HelloWorldService" />
-
-
- <bean id="loggerAspect" class="zmx.spring.aop.test.LoggerAspect" />
-
-
-
-
-
-
-
- </beans>
6、测试代码:
- package zmx.spring.aop.test;
-
- import org.junit.Test;
- import org.springframework.context.ApplicationContext;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
-
- public class MainTest {
- @Test
- public void testHelloWorld(){
-
- ApplicationContext ctx = new ClassPathXmlApplicationContext("zmx/spring/applicationContext.xml");
- IHelloWorldService helloworldService = ctx.getBean("helloWorldService", IHelloWorldService.class);
- helloworldService.recordLog("日志");
-
- }
-
-
-
- }
将输出:
===========before advice param:日志
============recordLog================日志
===========after advice param</span>
切面、切入点、通知全部使用注解完成:
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注解声明;
java代码:
- @AfterReturning(
- value="切入点表达式或命名切入点",
- pointcut="切入点表达式或命名切入点",
- argNames="参数列表参数名",
- returning="返回值对应参数名")
value:指定切入点表达式或命名切入点;
pointcut:同样是指定切入点表达式或命名切入点,如果指定了将覆盖value属性指定的,pointcut具有高优先级;
argNames:与Schema方式配置中的同义;
returning:与Schema方式配置中的同义。
java代码:
- @AfterReturning(
- value="execution(* cn.javass..*.sayBefore(..))",
- pointcut="execution(* cn.javass..*.sayAfterReturning(..))",
- argNames="retVal", returning="retVal")
- public void afterReturningAdvice(Object retVal) {
- System.out.println("===========after returning advice retVal:" + retVal);
- }
其中测试代码与Schema方式几乎一样,在此就不演示了,如果需要请参考AopTest.java中的testAnnotationAfterReturningAdvice测试方法。
三、后置异常通知:使用org.aspectj.lang.annotation 包下的@AfterThrowing注解声明;
java代码:
- @AfterThrowing (
- value="切入点表达式或命名切入点",
- pointcut="切入点表达式或命名切入点",
- argNames="参数列表参数名",
- throwing="异常对应参数名")
-
value:指定切入点表达式或命名切入点;
pointcut:同样是指定切入点表达式或命名切入点,如果指定了将覆盖value属性指定的,pointcut具有高优先级;
argNames:与Schema方式配置中的同义;
throwing:与Schema方式配置中的同义。
java代码:
- @AfterThrowing(
- value="execution(* cn.javass..*.sayAfterThrowing(..))",
- argNames="exception", throwing="exception")
- public void afterThrowingAdvice(Exception exception) {
- System.out.println("===========after throwing advice exception:" + exception);
- }
其中测试代码与Schema方式几乎一样,在此就不演示了,如果需要请参考AopTest.java中的testAnnotationAfterThrowingAdvice测试方法。
四、后置最终通知:使用org.aspectj.lang.annotation 包下的@After注解声明;
java代码:
- @After (
- value="切入点表达式或命名切入点",
- argNames="参数列表参数名")
value:指定切入点表达式或命名切入点;
argNames:与Schema方式配置中的同义;
java代码:
- @After(value="execution(* cn.javass..*.sayAfterFinally(..))")
- public void afterFinallyAdvice() {
- System.out.println("===========after finally advice");
- }
其中测试代码与Schema方式几乎一样,在此就不演示了,如果需要请参考AopTest.java中的testAnnotationAfterFinallyAdvice测试方法。
五、环绕通知:使用org.aspectj.lang.annotation 包下的@Around注解声明;
java代码:
- @Around (
- value="切入点表达式或命名切入点",
- argNames="参数列表参数名")
value:指定切入点表达式或命名切入点;
argNames:与Schema方式配置中的同义;
java代码:
- @Around(value="execution(* cn.javass..*.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;
- }
其中测试代码与Schema方式几乎一样,在此就不演示了,如果需要请参考AopTest.java中的annotationAroundAdviceTest测试方法。
6.4.5 引入
@AspectJ风格的引入声明在切面中使用org.aspectj.lang.annotation包下的@DeclareParents声明:
java代码:
- @DeclareParents(
- value=" AspectJ语法类型表达式",
- defaultImpl=引入接口的默认实现类)
- private Interface interface;
value:匹配需要引入接口的目标对象的AspectJ语法类型表达式;与Schema方式中的types-matching属性同义;
private Interface interface:指定需要引入的接口;
defaultImpl:指定引入接口的默认实现类,没有与Schema方式中的delegate-ref属性同义的定义方式;
java代码:
- @DeclareParents(
- value="cn.javass..*.IHelloWorldService+", defaultImpl=cn.javass.spring.chapter6.service.impl.IntroductiondService.class)
- private IIntroductionService introductionService;
-
其中测试代码与Schema方式几乎一样,在此就不演示了,如果需要请参考AopTest.java中的testAnnotationIntroduction测试方法。