对于将纯POJO申明成切面的方式中,如果不使用@AspectJ,那么就需要使用使用繁琐的XML配置,因此Spring借鉴了AspectJ的切面,以提供注解驱动的AOP,但是本质上依然是使用的SpringAop的动态代理的方式,只是变成模型几乎与AspectJ完全一样。
Spring只支持AspectJ切点指示器的一个子集,因为其没有办法进行非方法级别的切点
使用切点指示器编写切点:
execution(<访问修饰符>?<返回类型><方法名>(参数)<异常>)
* execution(“* cn.itcast.spring3.demo1.dao.*(..)”) ---只检索当前包
* execution(“* cn.itcast.spring3.demo1.dao..*(..)”) ---检索包及当前包的子包.
* execution(* cn.itcast.dao.GenericDAO+.*(..)) ---检索GenericDAO及子类
* execution(* save*(..)) ---以save开头的所有的方法
AspectJ的通知类型
AspectJ增强:
@Before 前置通知,相当于BeforeAdvice
@AfterReturning 后置通知,相当于AfterReturningAdvice
@Around 环绕通知,相当于MethodInterceptor
@AfterThrowing抛出通知,相当于ThrowAdvice
@After 最终final通知,不管是否异常,该通知都会执行
例子:
public class BookDaoImpl implements IBookDao{
public int add() {
System.out.println("正在添加图书...");
return 0;
}
public int delete() {
System.out.println("正在删除图书...");
return 0;
}
}
@Aspect
public class AnnotationAdvice {
@Before("execution(* com.njust.learning.spring.service.BookDaoImpl.add())")
public void before(){
System.out.println("前置通知...");
}
}
//或者可以使用@Pointcut指定切点
@Aspect
public class AnnotationAdvice {
@Pointcut("execution(* com.njust.learning.spring.service.BookDaoImpl.add())")
public void pointcut(){
}
@Before("pointcut()")
public void before(){
System.out.println("前置通知...");
}
}
<aop:aspectj-autoproxy/>
<bean id="bookDao" class="com.njust.learning.spring.service.BookDaoImpl"/>
<bean class="com.njust.learning.spring.annotationaop.AnnotationAdvice"></bean>
切记
Spring的AspectJ自动代理只是使用@AspectJ作为创建切面的指导,切面依然是基于代理的,在本质上他依然是Spring基于代理的切面。
在通知中处理切点方法中的参数
execution(* save*(int) && args(id))
表示拦截以save开头的,并传入一个int类型的参数的方法,这个参数在切点中的名称可以不是id,但必须是int类型的,但是在通知中的参数名称就必须是id了
public class UserService {
public int add(int id){
System.out.println("已经添加了一本书,id为:"+id);
return 1;
}
}
@Aspect
public class AnnotationAdvice {
//ids参数名可以与切点中的参数名id不一样
@Pointcut(value = "execution(* com.njust.learning.spring.service.UserService.add(int)) && args(ids)")
//这里定义的时候就必须在方法里加上参数
public void pointcut(int ids){
}
//声明的时候,指定参数名
@Before("pointcut(ids)")
public void before(int ids){
System.out.println("前置通知..."+ids);
}
}