版权声明:本文为博主武伟峰原创文章,转载请注明地址http://blog.csdn.net/tianyaleixiaowu。

aop是spring的两大功能模块之一,功能非常强大,为解耦提供了非常优秀的解决方案。

现在就以springboot中aop的使用来了解一下aop。

一:使用aop来完成全局请求日志处理

pom文件如下:


  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  4. <modelVersion>4.0.0</modelVersion>
  5. <groupId>com.example</groupId>
  6. <artifactId>testaop</artifactId>
  7. <version>0.0.1-SNAPSHOT</version>
  8. <packaging>jar</packaging>
  9. <name>testaop</name>
  10. <description>Demo project for Spring Boot</description>
  11. <parent>
  12. <groupId>org.springframework.boot</groupId>
  13. <artifactId>spring-boot-starter-parent</artifactId>
  14. <version>1.5.9.RELEASE</version>
  15. <relativePath/> <!-- lookup parent from repository -->
  16. </parent>
  17. <properties>
  18. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  19. <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
  20. <java.version>1.8</java.version>
  21. </properties>
  22. <dependencies>
  23. <dependency>
  24. <groupId>org.springframework.boot</groupId>
  25. <artifactId>spring-boot-starter-aop</artifactId>
  26. </dependency>
  27. <dependency>
  28. <groupId>org.springframework.boot</groupId>
  29. <artifactId>spring-boot-starter-web</artifactId>
  30. </dependency>
  31. <dependency>
  32. <groupId>org.springframework.boot</groupId>
  33. <artifactId>spring-boot-starter-test</artifactId>
  34. <scope>test</scope>
  35. </dependency>
  36. </dependencies>
  37. <build>
  38. <plugins>
  39. <plugin>
  40. <groupId>org.springframework.boot</groupId>
  41. <artifactId>spring-boot-maven-plugin</artifactId>
  42. </plugin>
  43. </plugins>
  44. </build>
  45. </project>

创建个controller


  1. package com.example.controller;
  2. import org.springframework.web.bind.annotation.RequestMapping;
  3. import org.springframework.web.bind.annotation.RestController;
  4. /**
  5. * Created by wuwf on 17/4/27.
  6. *
  7. */
  8. @RestController
  9. public class FirstController {
  10. @RequestMapping("/first")
  11. public Object first() {
  12. return "first controller";
  13. }
  14. @RequestMapping("/doError")
  15. public Object error() {
  16. return 1 / 0;
  17. }
  18. }

创建一个aspect切面类


  1. package com.example.aop;
  2. import org.aspectj.lang.JoinPoint;
  3. import org.aspectj.lang.ProceedingJoinPoint;
  4. import org.aspectj.lang.annotation.*;
  5. import org.springframework.stereotype.Component;
  6. import org.springframework.web.context.request.RequestContextHolder;
  7. import org.springframework.web.context.request.ServletRequestAttributes;
  8. import javax.servlet.http.HttpServletRequest;
  9. import java.util.Arrays;
  10. /**
  11. * Created by wuwf on 17/4/27.
  12. * 日志切面
  13. */
  14. @Aspect
  15. @Component
  16. public class LogAspect {
  17. @Pointcut("execution(public * com.example.controller.*.*(..))")
  18. public void webLog(){}
  19. @Before("webLog()")
  20. public void deBefore(JoinPoint joinPoint) throws Throwable {
  21. // 接收到请求,记录请求内容
  22. ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
  23. HttpServletRequest request = attributes.getRequest();
  24. // 记录下请求内容
  25. System.out.println("URL : " + request.getRequestURL().toString());
  26. System.out.println("HTTP_METHOD : " + request.getMethod());
  27. System.out.println("IP : " + request.getRemoteAddr());
  28. System.out.println("CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
  29. System.out.println("ARGS : " + Arrays.toString(joinPoint.getArgs()));
  30. }
  31. @AfterReturning(returning = "ret", pointcut = "webLog()")
  32. public void doAfterReturning(Object ret) throws Throwable {
  33. // 处理完请求,返回内容
  34. System.out.println("方法的返回值 : " + ret);
  35. }
  36. //后置异常通知
  37. @AfterThrowing("webLog()")
  38. public void throwss(JoinPoint jp){
  39. System.out.println("方法异常时执行.....");
  40. }
  41. //后置最终通知,final增强,不管是抛出异常或者正常退出都会执行
  42. @After("webLog()")
  43. public void after(JoinPoint jp){
  44. System.out.println("方法最后执行.....");
  45. }
  46. //环绕通知,环绕增强,相当于MethodInterceptor
  47. @Around("webLog()")
  48. public Object arround(ProceedingJoinPoint pjp) {
  49. System.out.println("方法环绕start.....");
  50. try {
  51. Object o = pjp.proceed();
  52. System.out.println("方法环绕proceed,结果是 :" + o);
  53. return o;
  54. } catch (Throwable e) {
  55. e.printStackTrace();
  56. return null;
  57. }
  58. }
  59. }

启动项目

模拟正常执行的情况,访问http://localhost:8080/first,看控制台结果:

方法环绕start.....

URL : http://localhost:8080/first

HTTP_METHOD : GET

IP : 0:0:0:0:0:0:0:1

CLASS_METHOD : com.example.controller.FirstController.first

ARGS : []

方法环绕proceed,结果是 :first controller

方法最后执行.....

方法的返回值 : first controller

/****************************分割线****************************/

模拟出现异常时的情况,访问http://localhost:8080/doError,看控制台结果:

方法环绕start.....

URL : http://localhost:8080/doError

HTTP_METHOD : GET

IP : 0:0:0:0:0:0:0:1

CLASS_METHOD : com.example.controller.FirstController.error

ARGS : []

java.lang.ArithmeticException: / by zero

......

方法最后执行.....

方法的返回值 : null

/****************************分割线****************************/

通过上面的简单的例子,可以看到aop的执行顺序。知道了顺序后,就可以在相应的位置做切面处理了。

二: 切面方法说明

@Aspect

作用是把当前类标识为一个切面供容器读取

@Before

标识一个前置增强方法,相当于BeforeAdvice的功能

@AfterReturning

后置增强,相当于AfterReturningAdvice,方法退出时执行

@AfterThrowing

异常抛出增强,相当于ThrowsAdvice

@After

final增强,不管是抛出异常或者正常退出都会执行

@Around

环绕增强,相当于MethodInterceptor

/****************************分割线****************************/

各方法参数说明:

除了@Around外,每个方法里都可以加或者不加参数JoinPoint,如果有用JoinPoint的地方就加,不加也可以,JoinPoint里包含了类名、被切面的方法名,参数等属性,可供读取使用。@Around参数必须为ProceedingJoinPoint,pjp.proceed相应于执行被切面的方法。@AfterReturning方法里,可以加returning = “XXX”,XXX即为在controller里方法的返回值,本例中的返回值是“first controller”。@AfterThrowing方法里,可以加throwing = "XXX",供读取异常信息,如本例中可以改为:


  1. //后置异常通知
  2. @AfterThrowing(throwing = "ex", pointcut = "webLog()")
  3. public void throwss(JoinPoint jp, Exception ex){
  4. System.out.println("方法异常时执行.....");
  5. }

一般常用的有before和afterReturn组合,或者单独使用Around,即可获取方法开始前和结束后的切面。

三:关于切面PointCut的切入点

转载请注明来源-作者@loongshawn:http://blog.csdn.net/loongshawn/article/details/72303040

1.execution表达式


  1. <aop:config>
  2. <aop:pointcut id="pointcut" expression="execution(* com.loongshawn.method.ces..*.*
  3. (..))" />
  4. <aop:aspect ref="logAspect">
  5. <aop:before pointcut-ref="pointcut" method="beforeAdvice"/>
  6. <aop:after-returning pointcut-ref="pointcut" arg-names="joinPoint,retValue"
  7. returning="retValue" method="afterAdvice"/>
  8. </aop:aspect>
  9. </aop:config>
  • 上述配置为AOP配置代码片段,其中expression部分为定义切点的表达式部分,如下:

execution(* com.loongshawn.method.ces..*.*(..))

  注意:markdown中符号“*”是加粗,因此输出“*”符号需要进行转义“*”。

  表达式结构解释如下:

标识符 含义
execution() 表达式的主体
第一个“*”符号 表示返回值的类型任意
com.loongshawn.method.ces AOP所切的服务的包名,即,需要进行横切的业务类
包名后面的“..” 表示当前包及子包
第二个“*” 表示类名,*即所有类
.*(..) 表示任何方法名,括号表示参数,两个点表示任何参数类型

2.官方文档介绍

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) 

throws-pattern?)

  上述表达式结构是Spring官方文档说明,翻译为中文如下,其中除了返回类型模式、方法名模式和参数模式外,其它项都是可选的。

execution(<修饰符模式>?<返回类型模式><方法名模式>(<参数模式>)<异常模式>?)

  接下来,结合官方文档中的例子从不同的维度来了解execution表达式。表达式上方的中文说明为对该表达式的解释,请注意阅读方式。

1.通过方法修饰符定义切点

  匹配所有的public修饰符的方法:

execution(public * *(..))

2.通过方法名定义切点

  匹配所有”set”开头的方法:

execution(* set*(..))

3.通过类定义切点

  匹配AccountService 接口的所有方法:

execution(* com.xyz.service.AccountService.*(..))

4.通过包定义切点

  匹配service包中的所有方法:

execution(* com.xyz.service..(..))

  匹配service包及其子包中的所有方法:

execution(* com.xyz.service...(..))

5.通过方法入参定义切点

  切点表达式中方法入参部分比较复杂,可以使用”*”和“..”通配符,其中“*”表示任意类型的参数,而“..”表示任意类型参数且参数个数不限。

  匹 配joke(String,int)方法,且joke()方法的第一个入参是String,第二个入参是int。如果方法中的入参类型是Java.lang包下的类,可以直接使用类名,否则必须使用全限定类名,如joke(java.util.List,int);

execution(* joke(String,int))

  匹 配目标类中的joke()方法,该方法第一个入参为String,第二个入参可以是任意类型,如joke(Strings1,String s2)和joke(String s1,double d2)都匹配,但joke(String s1,doubled2,String s3)则不匹配;

execution(* joke(String,*))

  匹配目标类中的joke()方法,该方法第 一个入参为String,后面可以有任意个入参且入参类型不限,如joke(Strings1)、joke(String s1,String s2)和joke(String s1,double d2,Strings3)都匹配。

execution(* joke(String,..))

  匹 配目标类中的joke()方法,方法拥有一个入参,且入参是Object类型或该类的子类。它匹配joke(Strings1)和joke(Client c)。如果我们定义的切点是execution(*joke(Object)),则只匹配joke(Object object)而不匹配joke(Stringcc)或joke(Client c)。

execution(* joke(Object+))

3.官方文档截图

Aspect Oriented Programming with Spring–>11.2.3 Declaring a pointcut 
https://docs.spring.io/spring/docs/current/spring-framework-reference/html/aop.html

本文仅对官方文档做了翻译及简要说明,需要在实际使用过程中加深印象及理解。

四:自定义注解

一般多用于某些特定的功能,比较零散的切面,譬如特定的某些方法需要处理,就可以单独在方法上加注解切面。

我们来自定义一个注解:


  1. package com.example.aop;
  2. import java.lang.annotation.ElementType;
  3. import java.lang.annotation.Retention;
  4. import java.lang.annotation.RetentionPolicy;
  5. import java.lang.annotation.Target;
  6. /**
  7. * Created by wuwf on 17/4/27.
  8. */
  9. @Target({ElementType.METHOD, ElementType.TYPE})
  10. @Retention(RetentionPolicy.RUNTIME)
  11. public @interface UserAccess {
  12. String desc() default "无信息";
  13. }

注解里提供了一个desc的方法,供被切面的地方传参,如果不需要传参可以不写。

在Controller里加个方法


  1. @RequestMapping("/second")
  2. @UserAccess(desc = "second")
  3. public Object second() {
  4. return "second controller";
  5. }

切面类:


  1. package com.example.aop;
  2. import org.aspectj.lang.JoinPoint;
  3. import org.aspectj.lang.ProceedingJoinPoint;
  4. import org.aspectj.lang.annotation.*;
  5. import org.springframework.stereotype.Component;
  6. /**
  7. * Created by wuwf on 17/4/27.
  8. */
  9. @Component
  10. @Aspect
  11. public class UserAccessAspect {
  12. @Pointcut(value = "@annotation(com.example.aop.UserAccess)")
  13. public void access() {
  14. }
  15. @Before("access()")
  16. public void deBefore(JoinPoint joinPoint) throws Throwable {
  17. System.out.println("second before");
  18. }
  19. @Around("@annotation(userAccess)")
  20. public Object around(ProceedingJoinPoint pjp, UserAccess userAccess) {
  21. //获取注解里的值
  22. System.out.println("second around:" + userAccess.desc());
  23. try {
  24. return pjp.proceed();
  25. } catch (Throwable throwable) {
  26. throwable.printStackTrace();
  27. return null;
  28. }
  29. }
  30. }

主要看一下@Around注解这里,如果需要获取在controller注解中赋给UserAccess的desc里的值,就需要这种写法,这样UserAccess参数就有值了。

/****************************分割线****************************/

启动项目,访问http://localhost:8080/second,看控制台:

方法环绕start.....

URL : http://localhost:8080/second

HTTP_METHOD : GET

IP : 0:0:0:0:0:0:0:1

CLASS_METHOD : com.example.controller.FirstController.second

ARGS : []

second around:second

second before

方法环绕proceed,结果是 :second controller

方法最后执行.....

方法的返回值 : second controller

/****************************分割线****************************/

通知结果可以看到,两个aop切面类都工作了,顺序呢就是下面的

spring aop就是一个同心圆,要执行的方法为圆心,最外层的order最小。从最外层按照AOP1、AOP2的顺序依次执行doAround方法,doBefore方法。然后执行method方法,最后按照AOP2、AOP1的顺序依次执行doAfter、doAfterReturn方法。也就是说对多个AOP来说,先before的,一定后after。

对于上面的例子就是,先外层的就是对所有controller的切面,内层就是自定义注解的。

那不同的切面,顺序怎么决定呢,尤其是同格式的切面处理,譬如两个execution的情况,那spring就是随机决定哪个在外哪个在内了。

所以大部分情况下,我们需要指定顺序,最简单的方式就是在Aspect切面类上加上@Order(1)注解即可,order越小最先执行,也就是位于最外层。像一些全局处理的就可以把order设小一点,具体到某个细节的就设大一点。

springboot aop的使用 学习总结的更多相关文章

  1. springBoot AOP学习(一)

    AOP学习(一) 1.简介 AOp:面向切面编程,相对于OOP面向对象编程. Spring的AOP的存在目的是为了解耦.AOP可以让一切类共享相同的行为.在OOP中只能通过继承类或者实现接口,使代码的 ...

  2. 快速体验Spring Boot了解使用、运行和打包 | SpringBoot 2.7.2学习系列

    SpringBoot 2.7.2 学习系列,本节内容快速体验Spring Boot,带大家了解它的基本使用.运行和打包. Spring Boot 基于 Spring 框架,底层离不开 IoC.AoP ...

  3. springboot+aop切点记录请求和响应信息

    本篇主要分享的是springboot中结合aop方式来记录请求参数和响应的数据信息:这里主要讲解两种切入点方式,一种方法切入,一种注解切入:首先创建个springboot测试工程并通过maven添加如 ...

  4. SpringBoot+AOP整合

    SpringBoot+AOP整合 https://blog.csdn.net/lmb55/article/details/82470388 https://www.cnblogs.com/onlyma ...

  5. 框架源码系列十:Spring AOP(AOP的核心概念回顾、Spring中AOP的用法、Spring AOP 源码学习)

    一.AOP的核心概念回顾 https://docs.spring.io/spring/docs/5.1.3.RELEASE/spring-framework-reference/core.html#a ...

  6. springboot aop 不生效原因解决

    最近参照资料创建Springboot AOP ,结果运行后aop死活不生效. 查明原因: 是我在创建AOP类时选择了Aspect类型,创建后才把这个文件改为Class类型,导致一直不生效, 代码配置这 ...

  7. springboot aop 自定义注解方式实现完善日志记录(完整源码)

    版权声明:本文为博主原创文章,欢迎转载,转载请注明作者.原文超链接 一:功能简介 本文主要记录如何使用aop切面的方式来实现日志记录功能. 主要记录的信息有: 操作人,方法名,参数,运行时间,操作类型 ...

  8. springboot aop 自定义注解方式实现一套完善的日志记录(完整源码)

    https://www.cnblogs.com/wenjunwei/p/9639909.html https://blog.csdn.net/tyrant_800/article/details/78 ...

  9. 【原创】SpringBoot & SpringCloud 快速入门学习笔记(完整示例)

    [原创]SpringBoot & SpringCloud 快速入门学习笔记(完整示例) 1月前在系统的学习SpringBoot和SpringCloud,同时整理了快速入门示例,方便能针对每个知 ...

随机推荐

  1. day38 20-Spring与Junit整合

    package cn.itcast.test; import org.junit.Test; import org.junit.runner.RunWith; import org.springfra ...

  2. web服务器与tomcat

    web服务器与tomcat 服务器分类: 硬件服务器和软件服务器 web服务器: 提供资源供别人访问 web: 网页的意思,资源. web资源分类: 动态的web资源:内容有可能发生改变的 静态的we ...

  3. 洛谷 P3950 部落冲突 树链剖分

    目录 题面 题目链接 题目描述 输入输出格式 输入格式 输出格式 输入输出样例 输入样例1 输出样例1 输入样例2 输出样例2 输入样例3 输出样例3 说明 思路 AC代码 总结 题面 题目链接 P3 ...

  4. 【水滴石穿】rn_antd_dva_reactnavigation

    这个项目好像就是记录了一个数据的流向,大体思想好像是这个 项目地址:https://github.com/Yangzhuren/rn_antd_dva_reactnavigation 先看效果 第一个 ...

  5. 加快liferay 7的启动速度

    在启动Liferay的过程中,你会发现在某个时刻,会特别慢,停留了很久,它是停在validate LPKGs,检验LPKG files是否被篡改,这个过程在开发的过程中十分令人头疼. 现在Lifera ...

  6. Directx11 教程(1) 基本的windows应用程序框架(1)

    原文:Directx11 教程(1) 基本的windows应用程序框架(1)        在vs2010中,建立一个新的win32工程,名字是: myTutorialD3D11, 注意:同时勾选Cr ...

  7. 中国境内PE\VC\投资公司名单

    中国境内PE\VC\投资公司名单 1.青云创投 2.高盛 3.红杉资本 4.鼎晖创投 5.枫丹国际 6.派杰投资银行 7.凯雷投资 8.长安私人资本 9.格林雷斯 10.汉能资本 11.启明创投 12 ...

  8. swiper踩过的哪些坑

    最近,苦恼于各种轮播的需求,每个自己都要自己写,写的挺烦的.终于,在网上发现了swiper插件,发现还是挺实用的,但其中还是踩过了不少的坑,其中有不少都是很简单的问题,但到发现的时候都是花了不少时间, ...

  9. Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第十五章:第一人称摄像机和动态索引

    原文:Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第十五章:第一人称摄像机和动态索引 代码工程地址: https://g ...

  10. OpenCV在各版本上的安装教程

    目录 使用pip安装OpenCV 安装Python版的OpenCV 4 安装Python版的OpenCV 3 在OSX和 macOS上安装OpenCV 3 在Ubuntu上安装Python版的Open ...