关注微信公众号:CodingTechWork,一起学习进步。

AOP介绍

AOP概述

  AOP是Aspect-Oriented Programming,即为面向(切面)方面编程。在维基百科中的解释:Aspect是一种新的模块化机制,用来描述分散在对象、类或函数中的横切关注点。从关注点中分离出横切关注点是面向切面的程序设计核心概念。分离关注点使得解决特定领域问题的代码从业务逻辑中独立出来,业务逻辑代码不需要再包含针对特定领域问题代码的调用,比如一些公用模块的日志、安全等代码。
  代码通过切面抽离,更加整齐和清晰,将重复的代码抽取出来单独的进行维护,在需要使用的时候,统一调用这些公共模块的代码,这样一个类就是一个基本的模块,方便统一维护和扩展更新。
  AOP就是为业务实现提供了切面注入的一种机制,将定义好的切面通过切入点(pointcut)在业务逻辑中进行绑定。比如SpringBoot微服务中的所有controller层需要对http请求进行一些常规日志的打印,如果每次在controller进行打印,代码就会冗余,如果说将这些公共代码进行封装,也需要每一个controller类进行调用,所以AOP出现的恰到好处,这时候引入AOP对http相关的日志逻辑进行统一管理编写代码,不需要controller层进行调用,只需要创建一个切面,并通过切入点绑定controller即可,下面的示例会讲到。

AOP相关术语

Spring AOP

  • 切面(Aspect):是指横切多个对象的关注点的一个模块化,事务管理就是J2EE应用中横切关注点的很好示例。在Spring AOP中,切面通过常规类(基本模式方法)或者通过使用了注解@Aspect的常规类来实现。
  • 连接点(Joint point):是指在程序执行期间的一个点,比如某个方法的执行或者是某个异常的处理。在Spring AOP中,一个连接点往往代表的是一个方法执行
  • 通知(Advice):是指切面在某个特殊连接点上执行的动作。通知有不同类型,包括"around","before""after"通知。许多AOP框架包括Spring,将通知建模成一个拦截器,并且围绕连接点维持一个拦截器链。
  • 切入点(Pointcut):是指匹配连接点的一个断言。通知是和一个切入点表达式关联的,并且在任何被切入点匹配的连接点上运行(举例,使用特定的名字执行某个方法)。AOP的核心就是切入点表达式匹配连接点的思想。Spring默认使用AspectJ切入点表达式语言
  • 引入(Introduction):代表了对一个类型额外的方法或者属性的声明。Spring AOP允许引入新接口到任何被通知对象(以及一个对应实现)。比如,可以使用一个引入去使一个bean实现IsModified接口,从而简化缓存机制。(在AspectJ社区中,一个引入也称为一个inter-type declaration类型间声明)
  • 目标对象(Target object):是指被一个或多个切面通知的那个对象。也指被通知对象("advised object"),由于Spring AOP是通过运行时代理事项的,这个目标对象往往是一个代理对象
  • AOP 代理(AOP proxy):是指通过AOP框架创建的对象,用来实现切面合约的(执行通知方法等等)。在Spring框架中,一个AOP代理是一个JDK动态代理或者是一个CGLIB代理
  • 织入(Weaving):将切面和其他应用类型或者对象连接起来,创骗一个被通知对象。这些可以在编译时(如使用AspectJ编译器)、加载时或者运行时完成。Spring AOP,比如其他纯Java AOP框架一般是在运行时完成织入。

AOP Advice相关术语

  • 前置通知(Before advice):在一个连接点之前执行的通知。但这种通知不能阻止连接点的执行流程(除非它抛出一个异常)
  • 后置返回通知(After returning advice):在一个连接点正常完成后执行的通知(如,如果一个方法没有抛出异常的返回)
  • 后置异常通知(After throwing advice):在一个方法抛出一个异常退出时执行的通知。
  • 后置(最终)通知(After(finally) advice):在一个连接点退出时(不管是正常还是异常返回)执行的通知。
  • 环绕通知(Around advice):环绕一个连接点的通知,比如方法的调用。这是一个最强大的通知类型。环绕通知可以在方法调用之前和之后完成自定义的行为。也负责通过返回自己的返回值或者抛出异常这些方式,选择是否继续执行连接点或者简化被通知方法的执行。

SpringBoot中集成AOP

1)pom.xml引入
    <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.1</version></dependency><dependency><groupId>org.aspectj</groupId><artifactId>aspectjrt</artifactId><version>1.9.1</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.14</version></dependency></dependencies>

其中,关于aop相关的主要引入了支持切面编程的依赖:org.aspectj.aspectjweaverorg.aspectj.aspectjrt的依赖。aspectjweaver是aspectj的织入包,aspectjrt是aspectj的运行时包。

2)配置文件
server:context-path: /demo/v1port: 9000
3)controller类
package com.example.andya.demo.controller;import org.springframework.web.bind.annotation.*;/*** @author Andya* @create 2020-04-12 10:36*/
@RestController
@RequestMapping("/aopTest")
public class AopController {@RequestMapping(value = "/sayHi/{name}", method = RequestMethod.GET)public String sayHi(@PathVariable(value = "name") String name) {return "hi, " + name;}
}
4)AOP层类
package com.example.andya.demo.aop;import com.alibaba.fastjson.JSON;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;/*** @author Andya* @create 2020-04-12 10:39*/
@Aspect
@Component
public class WebLogAspect {private Logger LOG = LoggerFactory.getLogger(WebLogAspect.class);ThreadLocal<Long> startTime = new ThreadLocal<>();/*** 定义切入点,以controller下所有包的请求为切入点*/@Pointcut("execution(public * com.example.andya.demo.controller..*.*(..))*")public void webLog(){}/***前置通知:在切入点之前执行的通知* @param joinPoint* @throws Throwable*/@Before("webLog()")public void doBefore(JoinPoint joinPoint) throws Throwable {startTime.set(System.currentTimeMillis());ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();HttpServletRequest request = servletRequestAttributes.getRequest();//打印请求相关参数LOG.info("========================================== Start ==========================================");LOG.info("URL:" + request.getRequestURL().toString());LOG.info("HTTP_METHOD:" + request.getMethod());//header第一种格式展示Enumeration<String> enumeration = request.getHeaderNames();Map<String, String> headerMap = new HashMap<>();while (enumeration.hasMoreElements()) {String headerName = enumeration.nextElement();headerMap.put(headerName, request.getHeader(headerName));}String headerJsonStr = JSON.toJSONString(headerMap);if (headerJsonStr.length() > 0) {LOG.info("HTTP_HEADERS INFO IS: {}", headerJsonStr);}//header第二种格式展示LOG.info("HTTP_HEADERS: ");Enumeration<?> enumeration1 = request.getHeaderNames();while (enumeration1.hasMoreElements()) {String key = (String) enumeration1.nextElement();String value = request.getHeader(key);LOG.info("     {}: {}", key, value);}LOG.info("IP:" + request.getRemoteAddr());LOG.info("CLASS_METHOD:" + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());try {LOG.info("REQUEST BODY : [{}]", JSON.toJSONString(joinPoint.getArgs()[0]));
//            LOG.info("ARGS:{}", Arrays.toString(joinPoint.getArgs()));} catch (Exception e) {LOG.error("REQUEST BODY PARSE ERROR!");}HttpSession session = (HttpSession) servletRequestAttributes.resolveReference(RequestAttributes.REFERENCE_SESSION);LOG.info("SESSION ID:" + session.getId());}//    /**
//     * 后置通知
//     * @param ret
//     * @throws Throwable
//     */
//    @AfterReturning(returning = "ret", pointcut = "webLog()")
//    public void doAfterReturning(Object ret) throws Throwable {
//        // 处理完请求,返回内容
//        LOG.info("RESPONSE : " + ret);
//        LOG.info("SPEND TIME : " + (System.currentTimeMillis() - startTime.get()));
//
//    }/*** 后置最终通知* @throws Throwable*/@After("webLog()")public void doAfter() throws Throwable {LOG.info("=========================================== End ===========================================");// 每个请求之间空一行LOG.info("");}/*** 环绕通知* 环绕通知第一个参数必须是org.aspectj.lang.ProceedingJoinPoint类型* @param proceedingJoinPoint* @return* @throws Throwable*/@Around("webLog()")public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {long startTime = System.currentTimeMillis();Object result = proceedingJoinPoint.proceed();String resultStr = JSON.toJSONString(result);// 打印出参LOG.info("RESPONSE ARGS  : {}", resultStr);// 执行耗时LOG.info("TIME-CONSUMING : {} ms", System.currentTimeMillis() - startTime);return result;}}

其中:

  • @Aspect注解是表示该类为切面类,@Component注解是将切面类加入到Ioc容器中。
  • @Pointcut定义切入点为整个controller包下的所有函数。
  • joinPoint.getArgs()获取目标方法的参数信息。
  • joinPoint.getSignature()获取通知的签名,并且通过joinPoint.getSignature().getDeclaringTypeName()获取代理类的名字,joinPoint.getSignature().getName()获取代理方法的名字。
5)运行类
package com.example.andya.demo;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
public class DemoApplication {public static void main(String[] args) {SpringApplication.run(DemoApplication.class, args);}}
6)访问url

http://127.0.0.1:9000/demo/v1/aopTest/sayHi/andya

7)运行结果

... ...
... ...
... ...
2020-04-12 15:21:31.737  INFO 18548 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
2020-04-12 15:21:31.782  INFO 18548 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 9000 (http)
2020-04-12 15:21:31.786  INFO 18548 --- [           main] com.example.andya.demo.DemoApplication   : Started DemoApplication in 2.928 seconds (JVM running for 4.26)
2020-04-12 15:21:41.084  INFO 18548 --- [nio-9000-exec-2] o.a.c.c.C.[.[localhost].[/demo/v1]       : Initializing Spring FrameworkServlet 'dispatcherServlet'
2020-04-12 15:21:41.085  INFO 18548 --- [nio-9000-exec-2] o.s.web.servlet.DispatcherServlet        : FrameworkServlet 'dispatcherServlet': initialization started
2020-04-12 15:21:41.102  INFO 18548 --- [nio-9000-exec-2] o.s.web.servlet.DispatcherServlet        : FrameworkServlet 'dispatcherServlet': initialization completed in 17 ms
2020-04-12 15:21:41.126  INFO 18548 --- [nio-9000-exec-2] com.example.andya.demo.aop.WebLogAspect  : ========================================== Start ==========================================
2020-04-12 15:21:41.126  INFO 18548 --- [nio-9000-exec-2] com.example.andya.demo.aop.WebLogAspect  : URL:http://127.0.0.1:9000/demo/v1/aopTest/sayHi/andya
2020-04-12 15:21:41.126  INFO 18548 --- [nio-9000-exec-2] com.example.andya.demo.aop.WebLogAspect  : HTTP_METHOD:GET
2020-04-12 15:21:41.155  INFO 18548 --- [nio-9000-exec-2] com.example.andya.demo.aop.WebLogAspect  : HTTP_HEADERS INFO IS: {"accept-language":"zh-CN","cookie":"JSESSIONID=1014BD34FFE9D2660CB47B282C63FA7D","host":"127.0.0.1:9000","connection":"Keep-Alive","accept-encoding":"gzip, deflate","accept":"text/html, application/xhtml+xml, image/jxr, */*","user-agent":"Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko"}
2020-04-12 15:21:41.156  INFO 18548 --- [nio-9000-exec-2] com.example.andya.demo.aop.WebLogAspect  : HTTP_HEADERS:
2020-04-12 15:21:41.156  INFO 18548 --- [nio-9000-exec-2] com.example.andya.demo.aop.WebLogAspect  :      accept: text/html, application/xhtml+xml, image/jxr, */*
2020-04-12 15:21:41.156  INFO 18548 --- [nio-9000-exec-2] com.example.andya.demo.aop.WebLogAspect  :      accept-language: zh-CN
2020-04-12 15:21:41.156  INFO 18548 --- [nio-9000-exec-2] com.example.andya.demo.aop.WebLogAspect  :      user-agent: Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko
2020-04-12 15:21:41.156  INFO 18548 --- [nio-9000-exec-2] com.example.andya.demo.aop.WebLogAspect  :      accept-encoding: gzip, deflate
2020-04-12 15:21:41.156  INFO 18548 --- [nio-9000-exec-2] com.example.andya.demo.aop.WebLogAspect  :      host: 127.0.0.1:9000
2020-04-12 15:21:41.156  INFO 18548 --- [nio-9000-exec-2] com.example.andya.demo.aop.WebLogAspect  :      connection: Keep-Alive
2020-04-12 15:21:41.156  INFO 18548 --- [nio-9000-exec-2] com.example.andya.demo.aop.WebLogAspect  :      cookie: JSESSIONID=1014BD34FFE9D2660CB47B282C63FA7D
2020-04-12 15:21:41.156  INFO 18548 --- [nio-9000-exec-2] com.example.andya.demo.aop.WebLogAspect  : IP:127.0.0.1
2020-04-12 15:21:41.157  INFO 18548 --- [nio-9000-exec-2] com.example.andya.demo.aop.WebLogAspect  : CLASS_METHOD:com.example.andya.demo.controller.AopController.sayHi
2020-04-12 15:21:41.157  INFO 18548 --- [nio-9000-exec-2] com.example.andya.demo.aop.WebLogAspect  : REQUEST BODY : ["andya"]
2020-04-12 15:21:41.160  INFO 18548 --- [nio-9000-exec-2] com.example.andya.demo.aop.WebLogAspect  : SESSION ID:0357B2624C73C5F79BC977AD628DB45F
2020-04-12 15:21:41.162  INFO 18548 --- [nio-9000-exec-2] com.example.andya.demo.aop.WebLogAspect  : RESPONSE ARGS  : "hi, andya"
2020-04-12 15:21:41.163  INFO 18548 --- [nio-9000-exec-2] com.example.andya.demo.aop.WebLogAspect  : TIME-CONSUMING : 37 ms
2020-04-12 15:21:41.163  INFO 18548 --- [nio-9000-exec-2] com.example.andya.demo.aop.WebLogAspect  : =========================================== End ===========================================
2020-04-12 15:21:41.163  INFO 18548 --- [nio-9000-exec-2] com.example.andya.demo.aop.WebLogAspect  :

参考书籍
《SPING技术内幕 深入解析SPRING架构与设计原理》
参考官网
Spring AOP官网

SpringBoot—集成AOP详解(面向切面编程Aspect)相关推荐

  1. MVC过滤器详解 面向切面编程(AOP)

    面向切面编程:Aspect Oriented Programming(AOP),面向切面编程,是一个比较热门的话题.AOP主要实现的目的是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个 ...

  2. 理解AOP思想(面向切面编程)

    本文旨在帮助还没有理解AOP的童鞋看透弄懂AOP,也欢迎高手批评指正. 先说一个Spring是什么吧,大家都是它是一个框架,但框架这个词对新手有点抽象,以致于越解释越模糊,不过它确实是个框架的,但那是 ...

  3. Spring AOP——Spring 中面向切面编程

    前面两篇文章记录了 Spring IOC 的相关知识,本文记录 Spring 中的另一特性 AOP 相关知识. 部分参考资料: <Spring实战(第4版)> <轻量级 JavaEE ...

  4. SpringBoot之AOP详解

    面向方面编程(AOP)通过提供另一种思考程序结构的方式来补充面向对象编程(OOP). OOP中模块化的关键单元是类,而在AOP中,模块化单元是方面. 文章目录 准备工作 1. @Pointcut 切入 ...

  5. SpringBoot集成MyBatis详解

    本文将使用mybatis官方starter来配置Mybatis 首先加入mybatis-spring-boot-stater的Maven依赖 <dependency><groupId ...

  6. Spring面向切面编程(AOP)详解

    Spring面向切面编程(AOP)详解 面向切面编程(AOP)是Spring框架的另外一个重要的核心内容. 而在讲AOP之前,先来了解一下动态代理这个概念,因为AOP基于动态代理. 动态代理概念:在程 ...

  7. 动态代理——拦截器——责任链——AOP面向切面编程底层原理详解(迪丽热巴版)

    目录 动态代理模式详解 前言 什么是代理模式 如何进行代理 静态代理 动态代理 JDK动态代理 CGLIB动态代理 拦截器 责任链模式 博客文章版权申明 动态代理模式详解 前言 代理模式是设计模式中非 ...

  8. Spring-05 -AOP [面向切面编程] -Schema-based 实现aop的步骤

    一.AOP [知识点详解] AOP:中文名称面向切面编程 英文名称:(Aspect Oriented Programming) 正常程序执行流程都是纵向执行流程 3.1 又叫面向切面编程,在原有纵向执 ...

  9. Javascript aop(面向切面编程)之around(环绕)

    Aop又叫面向切面编程,其中"通知"是切面的具体实现,分为before(前置通知).after(后置通知).around(环绕通知),用过spring的同学肯定对它非常熟悉,而在j ...

最新文章

  1. PandaRSS 自助服务系统安装配置
  2. fatal error C1010: 在查找预编译头时遇到意外的文件结尾。是否忘记了向源中添加“#include stdafx.h”?
  3. 初烧盲狙一条铁三角e40
  4. JPA关系映射系列一:one-to-one外键关联
  5. win10格式化linux分区,直接删除linux分区再重装linux可以恢復启动么,我是直接在win10里把linux mint...
  6. Python开发常用工具库
  7. Veeam创建复制任务Replication Job
  8. CentOS 6.4 安装 media wiki 1.23.6(转)
  9. CMO全国第十,保送清华姚班,别人家的牛娃是这样学习数学竞赛的!
  10. 一种针对图数据超级节点的数据建模优化解决方案
  11. C#+ AE实现地图注记功能
  12. 小程序:canvas绘制网络图片
  13. h5 和 微信小程序添加emoji表情处理
  14. damon ps2 android,DamonPS2模拟器
  15. 事务是什么?干什么用?
  16. 余弦相似度计算的实现方式
  17. 肯德基店里的广告 We Do Chicken Right
  18. 电商总结-日志监控系统的解决方案
  19. 系统学习NLP(七)--词语相似度
  20. 常用正则表达式-数字、字母、金额等

热门文章

  1. RabbitMQ集群并处理失败
  2. Pudb调试python
  3. 剖析Caffe源码之Net(上)---NetParameter参数
  4. POI处理超过65536条记录
  5. html5 getchildren,jquery children() find()用法
  6. java 指针 地址压缩_JVM优化之压缩普通对象指针(CompressedOops)
  7. java 反射 成员变量_Java 反射:成员变量
  8. 一句话了解 v-show 和 v-if 的区别
  9. 如何使用router-link对象方式传递参数?
  10. 独角兽导航带音乐带后台带客服