Spring除了支持Schema方式配置AOP,还支持注解方式:使用@AspectJ风格的切面声明。
一. 启用对@AspectJ的支持
Spring默认不支持@AspectJ风格的切面声明,为了支持需要使用如下配置
<aop:aspectj-autoproxy/>
二. 声明切面
@AspectJ风格的声明切面非常简单,使用@Aspect注解进行声明:
@Aspect()
Public class Aspect{
……
}
然后将该切面在配置文件中声明为Bean后,Spring就能自动识别并进行AOP方面的配置:
<bean id="aspect" class="……Aspect"/>
三. 声明切入点
@AspectJ风格的命名切入点使用org.aspectj.lang.annotation包下的@Pointcut+方法(方法必须是返回void 类型)实现。
@Pointcut(value="切入点表达式", argNames = "参数名列表") public void pointcutName(……) { }
value:指定切入点表达式;
argNames:指定命名切入点方法参数列表参数名字,可以有多个用“,”分隔,这些参数将传递给通知方法同 名的参数,同时比如切入点表达式“args(param)”将匹配参数类型为命名切入点方法同名参数指定的参数类型。
pointcutName:切入点名字,可以使用该名字进行引用该切入点表达式。
@Pointcut(value="execution(* cn.javass..*.sayAdvisorBefore(..)) && args(param)", argNames = "param") public void beforePointcut(String param) {}
说明:定义了一个切入点,名字为“beforePointcut”,该切入点将匹配目标方法的第一个参数类型为通知方法实现中参数 名为“param”的参数类型。
四. 声明通知
@AspectJ风格的声明通知也支持5种通知类型:
1. 前置通知
使用org.aspectj.lang.annotation 包下的@Before注解声明;
@Before(value = "切入点表达式或命名切入点", argNames = "参数列表参数名")
value:指定切入点表达式或命名切入点;
argNames:与Schema方式配置中的同义。
举例:
1 public interface IHelloWorldService { 2 public void sayAdviserBefore(String param); 3 }
1 package com.test.spring.service.impl; 2 3 4 import com.test.spring.service.IHelloWorldService; 5 6 public class HelloWorldService implements IHelloWorldService{ 7 public void sayAdviserBefore(String param) { 8 9 } 10 }
@Aspect public class HelloWorldAspect2 { @Pointcut(value="execution(* com.test.spring.service.impl..*.sayAdviserBefore(..)) && 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); } }
<aop:aspectj-autoproxy/> <bean id="helloWorldService" class="com.test.spring.service.impl.HelloWorldService" /> <bean id="aspect" class="com.test.spring.aop.HelloWorldAspect2" />
1 public class AopTest { 2 @Test 3 public void test() { 4 ApplicationContext context=new ClassPathXmlApplicationContext("bean.xml"); 5 IHelloWorldService helloWorldService=(IHelloWorldService) context.getBean("helloWorldService"); 6 helloWorldService.sayAdviserBefore("hahah"); 7 } 8 } 9 10 11 ===========before advice param:hahah
分析:
切面、切入点、通知全部使用注解完成:
1)使用@Aspect将POJO声明为切面;
2)使用@Pointcut进行命名切入点声明,同时指定目标方法第一个参数类型必须是java.lang.String,对于其他 匹配的方法但参数类型不一致的将也是不匹配的,通过argNames = "param"指定了将把该匹配的目标方法参数传递 给通知同名的参数上;
3)使用@Before进行前置通知声明,其中value用于定义切入点表达式或引用命名切入点;
4)配置文件需要使用<aop:aspectj-autoproxy/>来开启注解风格的@AspectJ支持;
5)需要将切面注册为Bean,如“aspect”Bean;
6)测试代码完全一样。
2. 后置返回通知
使用org.aspectj.lang.annotation 包下的@AfterReturning注解声明;
@AfterReturning( value="切入点表达式或命名切入点", pointcut="切入点表达式或命名切入点", argNames="参数列表参数名", returning="返回值对应参数名")
value:指定切入点表达式或命名切入点;
pointcut:同样是指定切入点表达式或命名切入点,如果指定了将覆盖value属性指定的,pointcut具有高优先级;
argNames:与Schema方式配置中的同义;
returning:与Schema方式配置中的同义。
@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); }
3. 后置异常通知
三、后置异常通知:
使用org.aspectj.lang.annotation 包下的@AfterThrowing注解声明;
@AfterThrowing ( value="切入点表达式或命名切入点", pointcut="切入点表达式或命名切入点", argNames="参数列表参数名", throwing="异常对应参数名")
value:指定切入点表达式或命名切入点;
pointcut:同样是指定切入点表达式或命名切入点,如果指定了将覆盖value属性指定的,pointcut具有高优先级;
argNames:与Schema方式配置中的同义;
throwing:与Schema方式配置中的同义。
@AfterThrowing( value="execution(* cn.javass..*.sayAfterThrowing(..))", argNames="exception", throwing="exception") public void afterThrowingAdvice(Exception exception) { System.out.println("===========after throwing advice exception:" + exception); }
四. 后置最终通知
使用org.aspectj.lang.annotation 包下的@After注解声明
@After ( value="切入点表达式或命名切入点", argNames="参数列表参数名")
value:指定切入点表达式或命名切入点;
argNames:与Schema方式配置中的同义;
@After(value="execution(* cn.javass..*.sayAfterFinally(..))") public void afterFinallyAdvice() { System.out.println("===========after finally advice"); }
5. 环绕通知
使用org.aspectj.lang.annotation 包下的@Around注解声明
@Around ( value="切入点表达式或命名切入点", argNames="参数列表参数名")
value:指定切入点表达式或命名切入点;
argNames:与Schema方式配置中的同义;
@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; }
参考文献
https://jinnianshilongnian.iteye.com/blog/1420689