SpringBoot中AOP编程
一、AOP是什么
AOP:Aspect Oriented Programming,翻译过来就是“面相切面编程”,它是对面相对象的一种补充和完善。
AOP的使用场景:
根据其使用场景我能知道起始AOP的实现很像我们现实中需要对业务代码部分进行切入业务实现逻辑。
它有以下特点:
- 侵入性小,几乎可以不改动原来逻辑的情况下把新的逻辑加入业务。
 
- 实现方便,使用几个注解就可以实现,使系统更容易拓展。
 
- 更好的复用代码,比如事务日志打印,简单逻辑适合所有情况。
 
二、AOP中注解的含义
@Aspect:切面。表示一个横切进业务的一个对象。他里面包含切入点(Pointcut)和Advice(通知)。
@Pointcut:切入点。表示需要切入的位置,比如某些类或者某些方法,也就是先定一个范围。
@Before:Advice(通知)的一种,切入点的方法体执行之前执行。
@Around:Advice(通知)的一种,环绕切入点执行也就是把切入点包裹起来执行。
@After:Advice(通知)的一种,在切入点正常运行结束后执行。
@AfterReturning:Advice(通知)的一种,在切入点正常运行结束后执行,异常则不执行
@AfterThrowing:Advice(通知)的一种,在切入点运行异常时执行。
三、Pointcut切入点的语法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
   | 
 
 
  @Pointcut("within(com.leo.controller..*)") public void pointcutWithin(){
  }
 
 
 
  @Pointcut("this(com.leo.controller.HelloController)") public void pointcutThis(){
  }
 
 
 
  @Pointcut("target(com.leo.service.UserInfoService)") public void pointcutTarge(){
  }
 
 
 
 
  @Pointcut("bean(*ServiceImpl)") public void pointcutBean(){
  }
 
 
 
  @Pointcut("args(String, ..)") public void pointcutArgs(){
  }
 
 
 
  @Pointcut("@annotation(org.springframework.stereotype.Controller)") public void pointcutAnnocation(){
  }
 
 
  @Pointcut("@within(org.springframework.stereotype.Controller)") public void pointcutWithinAnno(){
  }
 
 
  @Pointcut("@target(org.springframework.stereotype.Controller)") public void pointcutTargetAnno(){
  }
 
 
  @Pointcut("@args(org.springframework.stereotype.Service)") public void pointcutArgsAnno(){
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
  @Pointcut(value = "execution(public * com.leo.controller.HelloController.hello*(..))") public void pointCut() {
  }
 
 
  | 
 
需要注意:上面的匹配的类型中支持或(||)与(&&)非(!)运算。
四、AOP代码实现
首先配置pom.xml进行注解jar包的支持
1 2 3 4 5 6 7 8 9 10 11 12
   |  <dependency>     <groupId>org.aspectj</groupId>     <artifactId>aspectjrt</artifactId>     <version>1.8.9</version> </dependency> <dependency>     <groupId>org.aspectj</groupId>     <artifactId>aspectjweaver</artifactId>     <version>1.8.9</version> </dependency>
 
 
  | 
 
然后在配置文件中启动注解支持,例如我的是myspringmvc-servlet.xml文件,这里需要注意的是注解要跟配置文件在一块,举个例子:如果需要切入Controller的配置就需要在myspringmvc-servlet.xml中添加,如果是切入应用的配置如Service则需要在applicationContext.xml中添加。
1 2 3
   |  <aop:aspectj-autoproxy  proxy-target-class="true"></aop:aspectj-autoproxy>
 
 
  | 
 
AOP核心代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
   | ThreadLocal<Long> startTime = new ThreadLocal<>();
 
 
 
 
 
 
 
 
 
 
 
  @Pointcut(value = "execution(public * com.leo.controller.HelloController.hello*(..))") public void pointCut() {
  }
 
 
 
 
 
  @Before(value = "pointCut()") public void beforeLog(JoinPoint joinPoint) {     System.out.println("进入LogAop的beforeLogger");     startTime.set(System.currentTimeMillis()); }
 
 
 
 
 
  @Around(value = "pointCut()") public Object aroundLog(ProceedingJoinPoint pjp) throws Throwable {     System.out.println("进入LogAop的aroundLogger");     return pjp.proceed(); }
 
 
 
  @After(value = "pointCut()") public void afterLog() {     System.out.println("进入LogAop的afterLogger");     long start = startTime.get();     System.out.println("方法体执行耗时:" + (System.currentTimeMillis() - start) + "ms");     startTime.remove(); }
 
 
 
 
  @AfterReturning(value = "pointCut()",returning = "o") public void afterRunningLog(Object o){     System.out.println("进入LogAop的afterRunningLog");     System.out.println(o.getClass()); }
 
 
 
  @AfterThrowing(value = "pointCut()") public void afterThrowingLog(){     System.out.println("进入LogAop的afterThrowingLog"); }
 
   | 
 
执行顺序:

- 执行完
pjp.proceed()之后,开始进入@Before方法 





所以,正常的执行顺序是:@Around ->@Before->主方法体->@Around中pjp.proceed()->@After->@AfterReturning
如果存下异常那执行顺序是什么呢?以下模拟了一个异常。



因此如果异常在Around中pjp.proceed()之前,则执行顺序为:@Around -> @After -> @AfterThrowing
同理,如果异常在Around中pjp.proceed()之后,则执行顺序为:@Around ->@Before->主方法体->@Around中pjp.proceed()->@After->@AfterThrowing