AOP的基本概念
(1)Aspect(切面):通常是一个类,里面可以定义切入点和通知。
(2)JointPoint(连接点):程序执行过程中明确的点,一般是方法的调用(就是要拦截的方法,在这个方法前后织入对应的通知)。
(3)Advice(通知):AOP在特定的切入点上执行的增强处理,有before,after,afterReturning,afterThrowing,around。
(4)Pointcut(切入点):就是带有通知的连接点,在程序中主要体现为书写切入点表达式。
接口
public interface UserService { void printUser(User user); }接口的实现类(被代理对象)
//被代理的对象 @Component public class UserServiceImpl implements UserService { //连接点(从动态代理角度看,就是被拦截的方法,在这个方法前后织入对应的AOP通知) @Override public void printUser(User user) { System.out.println("name:"+user.getName()+" age:"+user.getAge()); }
切面类
//使用@Aspect注解一个类Spring ioc容器就会认为这是一个切面 //切面 @Aspect public class UserAspect { //前置通知(在被代理对象的方法前调用) *代表任意返回类型 printUser被拦截的方法 (..)任意参数 @Before("execution( * aop.UserServiceImpl.printUser(..))") public void before(){ System.out.println("before..."); } //后置通知(在被代理对象的方法后调用) @After("execution( * aop.UserServiceImpl.printUser(..))") public void after(){ System.out.println("after..."); } //返回通知(在被代理对象的方法正常返回后调用) @AfterReturning("execution( * aop.UserServiceImpl.printUser(..))") public void afterReturning(){ System.out.println("afterReturning..."); } //异常通知(在被代理对象的方法抛出异常后调用) @AfterThrowing("execution( * aop.UserServiceImpl.printUser(..))") public void afterThrowing(){ System.out.println("afterThrowing..."); } }
AopConfig.java
@EnableAspectJAutoProxy//开启自动代理 @ComponentScan @Configuration public class AopConfig { @Bean public UserAspect getUserAspect(){ return new UserAspect(); } }
测试
AnnotationConfigApplicationContext ioc= new AnnotationConfigApplicationContext(AopConfig.class); UserService uService=ioc.getBean(UserService.class); User user=new User(); user.setName("张三"); user.setAge(18); uService.printUser(user);
结果
扫描二维码关注公众号,回复:
1457955 查看本文章
before... name:张三 age:18 after... afterReturning...
如果认为每次都写"execution( * aop.UserServiceImpl.printUser(..))"比较麻烦可以用@Pointcut注解来定义切点
//使用@Aspect注解一个类Spring ioc容器就会认为这是一个切面 //切面 @Aspect public class UserAspect { //@Pointcut注解定义一个切点 *代表任意返回类型 printUser被拦截的方法 (..)任意参数 @Pointcut("execution( * aop.UserServiceImpl.printUser(..))") public void print(){ } //前置通知(在被代理对象的方法前调用) @Before("print()") public void before(){ System.out.println("before..."); } //后置通知(在被代理对象的方法后调用) @After("print()") public void after(){ System.out.println("after..."); } //返回通知(在被代理对象的方法正常返回后调用) @AfterReturning("print()") public void afterReturning(){ System.out.println("afterReturning..."); } //异常通知(在被代理对象的方法抛出异常后调用) @AfterThrowing("print()") public void afterThrowing(){ System.out.println("afterThrowing..."); } }
给通知传递参数(以前置通知为例)
//前置通知(在被代理对象的方法前调用) @Before("execution( * aop.UserServiceImpl.printUser(..))&&args(user)") public void before(User user){ System.out.println("before..."+user.getName()); }
需要注意的是被代理对象的接口存在并不是必须的,如果有接口Spring会采用JDK动态代理,并织入对应的通知,如果不存在则使用CGLIB动态代理。
二、多切面
Spring不仅支持单切面,同样对多切面有着很好的支持。我们将接口去掉修改UserService代码,并定义多个切面。
//被代理的对象 @Component public class UserService{ //连接点(被拦截的方法) public void printUser(User user) { System.out.println("name:"+user.getName()+" age:"+user.getAge()); } }
共有三个切面分别是UserAspect1,UserAspect2,UserAspect3。切面一代码如下
//切面1 @Aspect @Order(1) public class UserAspect1 { @Before("execution( * aop.UserService.printUser(..))") public void before(){ System.out.println("before1..."); } @After("execution( * aop.UserService.printUser(..))") public void after(){ System.out.println("after1..."); } @AfterReturning("execution( * aop.UserService.printUser(..))") public void afterReturning(){ System.out.println("afterReturning1..."); } @AfterThrowing("execution( * aop.UserService.printUser(..))") public void afterThrowing(){ System.out.println("afterThrowing1..."); } }
@Order(1)注解用来保证切面在织入时的执行顺序,这里传入1代表第一个执行
AopConfig.java
@EnableAspectJAutoProxy//开启自动代理 @ComponentScan @Configuration public class AopConfig { @Bean public UserAspect1 getUserAspect1(){ return new UserAspect1(); } @Bean public UserAspect2 getUserAspect2(){ return new UserAspect2(); } @Bean public UserAspect3 getUserAspect3(){ return new UserAspect3(); } }
测试
AnnotationConfigApplicationContext ioc=new AnnotationConfigApplicationContext(AopConfig.class); UserService userService =ioc.getBean(UserService.class); User user=new User(); user.setName("李四"); user.setAge(20); userService.printUser(user);
测试结果
before1... before2... before3... name:李四 age:20 after3... afterReturning3... after2... afterReturning2... after1... afterReturning1...
执行顺序图解