1.启用对@AspectJ的支持
spring默认不支持@Aspect风格的切面声明,需要添加如下配置
<aop:aspectj-autoproxy/>
这样Spring就能发现@AspectJ风格的切面并且将切面应用到目标对象。
2.声明切面
@AspectJ风格的声明切面非常简单,使用@Aspect注解进行声明:
@Aspect()
Public class Aspect{
}
然后将该切面在配置文件中声明为Bean后,Spring就能自动识别并进行AOP方面的配置
<bean id="aspect" class="……Aspect"/>
该切面就是一个POJO,可以在该切面中进行切入点及通知定义,接着往下看吧。
3.声明切入点
@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) {}
4.声明通知
@AspectJ风格的声明通知也支持5种通知类型:
- 前置通知:使用org.aspectj.lang.annotation 包下的@Before注解声明;
@Before(value = "切入点表达式或命名切入点", argNames = "参数列表参数名")
下面来一个例子
@Aspect
@Component
public class HelloWorldAspectAnno {
@Pointcut(value="execution(* com.anjunshuang..*.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);
}
}
public interface HelloWorldService {
void sayHello();
void sayBefore(String name);
}
public class HelloWorldServiceImpl implements HelloWorldService {
@Override
public void sayHello() {
System.out.println("hello world aspect!");
}
@Override
public void sayBefore(String name) {
System.out.println("hello world aspect!" + name);
}
}
<?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.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<aop:aspectj-autoproxy/>
<bean id="helloWorldService"
class="com.anjunshuang.spring.aop.service.impl.HelloWorldServiceImpl"/>
<bean id="aspect"
class="com.anjunshuang.spring.aop.aspect.HelloWorldAspectAnno"/>
</beans>
@Test
public void testAnnotationBeforeAdvice() {
System.out.println("======================================");
ApplicationContext ctx = new ClassPathXmlApplicationContext("spring/aspect/advice2.xml");
HelloWorldService helloWorldService = ctx.getBean("helloWorldService", HelloWorldService.class);
helloWorldService.sayBefore("before");
System.out.println("======================================");
}
切面、切入点、通知全部使用注解完成:
- 使用@Aspect将POJO声明为切面;
- 使用@Pointcut进行命名切入点声明,同时指定目标方法第一个参数类型必须是java.lang.String,对于其他匹配的方法但参数类型不一致的将也是不匹配的,通过argNames = "param"指定了将把该匹配的目标方法参数传递给通知同名的参数上;
- 使用@Before进行前置通知声明,其中value用于定义切入点表达式或引用命名切入点;
- 配置文件需要使用<aop:aspectj-autoproxy/>来开启注解风格的@AspectJ支持;
- 需要将切面注册为Bean,如“aspect”Bean;
- 测试代码完全一样。
- 后置返回通知:使用org.aspectj.lang.annotation 包下的@AfterReturning注解声明;
@AfterReturning(
value="切入点表达式或命名切入点",
pointcut="切入点表达式或命名切入点",
argNames="参数列表参数名",
returning="返回值对应参数名")
- 后置异常通知:使用org.aspectj.lang.annotation 包下的@AfterThrowing注解声明;
@AfterThrowing (
value="切入点表达式或命名切入点",
pointcut="切入点表达式或命名切入点",
argNames="参数列表参数名",
throwing="异常对应参数名")
- 后置最终通知:使用org.aspectj.lang.annotation 包下的@After注解声明;
@After (
value="切入点表达式或命名切入点",
argNames="参数列表参数名")
- 环绕通知:使用org.aspectj.lang.annotation 包下的@Around注解声明;
@Around (
value="切入点表达式或命名切入点",
argNames="参数列表参数名")
几个通知的例子,大体都类似
@AfterThrowing(
value="execution(* com.anjunshuang..*.sayAfterThrowing(..))",
argNames="exception", throwing="exception")
public void afterThrowingAdvice(Exception exception) {
System.out.println("===========after throwing advice exception:" + exception);
}
@After(value="execution(* com.anjunshuang..*.sayAfterFinally(..))")
public void afterFinallyAdvice() {
System.out.println("===========after finally advice");
}
@Around(value="execution(* com.anjunshuang..*.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;
}