`
阅读更多

 

 

 

在之前一系列的文章中都是提供了全部的代码,在之后的文章中就提供核心的代码进行讲解。有什么问题大家可以给我留言或者加我QQ,进行咨询。

       AOPAspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOPSpring框架中的一个重要内容,它通过对既有程序定义一个切入点,然后在其前后切入不同的执行内容,比如常见的有:打开数据库连接/关闭数据库连接、打开事务/关闭事务、记录日志等。基于AOP不会破坏原来程序逻辑,因此它可以很好的对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

       本文就是要通过AOP技术统一处理web请求的日志。

准备工作

       因为需要对web请求做切面来记录日志,所以先引入web模块,并创建一个简单的hello请求的处理。

<dependency>

      <groupId>org.springframework.boot</groupId>

      <artifactId>spring-boot-starter-web</artifactId>

</dependency>

 

实现一个简单请求处理

@RestController

publicclass HelloController {

   

    @RequestMapping("/hello")

    public String hello(String name,int state){

       return"name "+name+"---"+state;

    }

}

这时候我们编写一个启动类启动运行程序访问:

http://127.0.0.1:8080/hello?name=林峰&state=1  就能看到页面返回的信息了,但是现在还是不能进行拦截的。

 

引入AOP依赖

<dependency>

      <groupId>org.springframework.boot</groupId>

      <artifactId>spring-boot-starter-aop</artifactId>

</dependency>

 

在完成了引入AOP依赖包后,一般来说并不需要去做其他配置。也许在Spring中使用过注解配置方式的人会问是否需要在程序主类中增加@EnableAspectJAutoProxy来启用,实际并不需要。

可以看下面关于AOP的默认配置属性,其中spring.aop.auto属性默认是开启的,也就是说只要引入了AOP依赖后,默认已经增加了@EnableAspectJAutoProxy

# AOP spring.aop.auto=true # Add @EnableAspectJAutoProxy.

spring.aop.proxy-target-class=false # Whether subclass-based (CGLIB) proxies are to be created (true) as opposed to standard Java interface-based proxies (false).

我在做测试的时候,以上两个配置我都没有进行使用,请自行进行测试。

       而当我们需要使用CGLIB来实现AOP的时候,需要配置spring.aop.proxy-target-class=true,不然默认使用的是标准Java的实现。

 

实现Web层的日志切面

实现AOP的切面主要有以下几个要素:

使用@Aspect注解将一个java类定义为切面类

使用@Pointcut定义一个切入点,可以是一个规则表达式,比如下例中某个package下的所有函数,也可以是一个注解等。

根据需要在切入点不同位置的切入内容

使用@Before在切入点开始处切入内容

使用@After在切入点结尾处切入内容

使用@AfterReturning在切入点return内容之后切入内容(可以用来对处理返回值做一些加工处理)

使用@Around在切入点前后切入内容,并自己控制何时执行切入点自身的内容

使用@AfterThrowing用来处理当切入内容部分抛出异常之后的处理逻辑

package com.kfit.config.aop.log;

import java.util.Arrays;

import java.util.Enumeration;

import javax.servlet.http.HttpServletRequest;

import org.aspectj.lang.JoinPoint;

import org.aspectj.lang.annotation.AfterReturning;

import org.aspectj.lang.annotation.Aspect;

import org.aspectj.lang.annotation.Before;

import org.aspectj.lang.annotation.Pointcut;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.core.annotation.Order;

import org.springframework.stereotype.Component;

import org.springframework.web.context.request.RequestContextHolder;

import org.springframework.web.context.request.ServletRequestAttributes;

 

/**

 * 实现Web层的日志切面

 * @author Angel(QQ:412887952)

 * @version v.0.1

 */

@Aspect

@Component

@Order(-5)

public class WebLogAspect {

    private Logger logger =  LoggerFactory.getLogger(this.getClass());

   

    /**

     * 定义一个切入点.

     * 解释下:

     *

     * ~ 第一个 * 代表任意修饰符及任意返回值.

     * ~ 第二个 * 任意包名

     * ~ 第三个 * 代表任意方法.

     * ~ 第四个 * 定义在web包或者子包

     * ~ 第五个 * 任意方法

     * ~ .. 匹配任意数量的参数.

     */

     @Pointcut("execution(public * com.kfit.*.web..*.*(..))")

     publicvoid webLog(){}

     

     @Before("webLog()")

     publicvoid doBefore(JoinPoint joinPoint){

        

       // 接收到请求,记录请求内容

        logger.info("WebLogAspect.doBefore()");

        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();

         HttpServletRequest request = attributes.getRequest();

        

        

      // 记录下请求内容

        logger.info("URL : " + request.getRequestURL().toString());

        logger.info("HTTP_METHOD : " + request.getMethod());

        logger.info("IP : " + request.getRemoteAddr());

        logger.info("CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());

        logger.info("ARGS : " + Arrays.toString(joinPoint.getArgs()));

       //获取所有参数方法一:

        Enumeration<String> enu=request.getParameterNames(); 

        while(enu.hasMoreElements()){ 

            String paraName=(String)enu.nextElement(); 

            System.out.println(paraName+": "+request.getParameter(paraName)); 

        } 

     }

     

     @AfterReturning("webLog()")

     publicvoid  doAfterReturning(JoinPoint joinPoint){

       // 处理完请求,返回内容

        logger.info("WebLogAspect.doAfterReturning()");

     }

     

}

整个代码比较不好理解地方就是切点表达式,已经在注释中详细说明了,这里不再过多的介绍。编码中需要根据您自己的包命名规范进行修改下。

这时候运行程序访问 http://127.0.0.1:8080/hello?name=林峰&state=1  就可以看到控制台的打印信息了。

: WebLogAspect.doBefore()

: URL : http://127.0.0.1:8080/hello

: HTTP_METHOD : GET

: IP : 127.0.0.1

: CLASS_METHOD : com.kfit.demo.web.HelloController.hello

: ARGS : [林峰, 1]

name: 林峰

state: 1

: WebLogAspect.doAfterReturning()

: 耗时(毫秒) : 1

优化:AOP切面中的同步问题

       WebLogAspect切面中,分别通过doBeforedoAfterReturning两个独立函数实现了切点头部和切点返回后执行的内容,若我们想统计请求的处理时间,就需要在doBefore处记录时间,并在doAfterReturning处通过当前时间与开始处记录的时间计算得到请求处理的消耗时间。

 

       那么我们是否可以在WebLogAspect切面中定义一个成员变量来给doBeforedoAfterReturning一起访问呢?是否会有同步问题呢?

 

       的确,直接在这里定义基本类型会有同步问题,所以我们可以引入ThreadLocal对象,像下面这样进行记录改造代码为如下:

package com.kfit.config.aop.log;

import java.util.Arrays;

import java.util.Enumeration;

import javax.servlet.http.HttpServletRequest;

import org.aspectj.lang.JoinPoint;

import org.aspectj.lang.annotation.AfterReturning;

import org.aspectj.lang.annotation.Aspect;

import org.aspectj.lang.annotation.Before;

import org.aspectj.lang.annotation.Pointcut;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.stereotype.Component;

import org.springframework.web.context.request.RequestContextHolder;

import org.springframework.web.context.request.ServletRequestAttributes;

 

/**

 * 实现Web层的日志切面

 * @author Angel(QQ:412887952)

 * @version v.0.1

 */

@Aspect

@Component

 

public class WebLogAspect {

    private Logger logger =  LoggerFactory.getLogger(this.getClass());

   

    ThreadLocal<Long> startTime = new ThreadLocal<Long>();

   

    /**

     * 定义一个切入点.

     * 解释下:

     *

     * ~ 第一个 * 代表任意修饰符及任意返回值.

     * ~ 第二个 * 任意包名

     * ~ 第三个 * 代表任意方法.

     * ~ 第四个 * 定义在web包或者子包

     * ~ 第五个 * 任意方法

     * ~ .. 匹配任意数量的参数.

     */

     @Pointcut("execution(public * com.kfit.*.web..*.*(..))")

     publicvoid webLog(){}

     

     @Before("webLog()")

     publicvoid doBefore(JoinPoint joinPoint){

        startTime.set(System.currentTimeMillis());

        

       // 接收到请求,记录请求内容

        logger.info("WebLogAspect.doBefore()");

        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();

         HttpServletRequest request = attributes.getRequest();

        

        

      // 记录下请求内容

        logger.info("URL : " + request.getRequestURL().toString());

        logger.info("HTTP_METHOD : " + request.getMethod());

        logger.info("IP : " + request.getRemoteAddr());

        logger.info("CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());

        logger.info("ARGS : " + Arrays.toString(joinPoint.getArgs()));

       //获取所有参数方法一:

        Enumeration<String> enu=request.getParameterNames(); 

        while(enu.hasMoreElements()){ 

            String paraName=(String)enu.nextElement(); 

            System.out.println(paraName+": "+request.getParameter(paraName)); 

        } 

     }

     

     @AfterReturning("webLog()")

     publicvoid  doAfterReturning(JoinPoint joinPoint){

       // 处理完请求,返回内容

        logger.info("WebLogAspect.doAfterReturning()");

        logger.info("耗时(毫秒) : " + (System.currentTimeMillis() - startTime.get()));

     }

     

}

 

优化:AOP切面的优先级

       由于通过AOP实现,程序得到了很好的解耦,但是也会带来一些问题,比如:我们可能会对Web层做多个切面,校验用户,校验头信息等等,这个时候经常会碰到切面的处理顺序问题。

       所以,我们需要定义每个切面的优先级,我们需要@Order(i)注解来标识切面的优先级。i的值越小,优先级越高。假设我们还有一个切面是CheckNameAspect用来校验name必须为didi,我们为其设置@Order(10),而上文中WebLogAspect设置为@Order(5),所以WebLogAspect有更高的优先级,这个时候执行顺序是这样的:

@Before中优先执行@Order(5)的内容,再执行@Order(10)的内容

@After@AfterReturning中优先执行@Order(10)的内容,再执行@Order(5)的内容

所以我们可以这样子总结:

 

在切入点前的操作,按order的值由小到大执行

在切入点后的操作,按order的值由大到小执行

 

 

在实际中order值可以设置为负值,确保是第一个进行执行的。

 Spring Boot 系列博客】

à悟空学院:https://t.cn/Rg3fKJD

学院中有Spring Boot相关的课程!点击「阅读原文」进行查看!

SpringBoot视频:http://t.cn/A6ZagYTi

Spring Cloud视频:http://t.cn/A6ZagxSR

SpringBoot Shiro视频:http://t.cn/A6Zag7IV

SpringBoot交流平台:https://t.cn/R3QDhU0

SpringData和JPA视频:http://t.cn/A6Zad1OH

SpringSecurity5.0视频:http://t.cn/A6ZadMBe

Sharding-JDBC分库分表实战http://t.cn/A6ZarrqS

分布式事务解决方案「手写代码」:http://t.cn/A6ZaBnIr

 

网易云课堂视频最新更新

第十一章 Spring Boot 日志

1、spring boot日志—理论

2、Spring Boot日志-logback

3、Spring Boot日志-log4j2

第十二章 Spring Boot 知识点2

1、spring boot 服务配置和部署

2、Spring Boot 定制URL匹配规则

 

 

历史章节

 

第一章 快速开始

1、Spring Boot之Hello World

2、Spring Boot之Hello World访问404

 

第二章 Spring Boot之JSON

1、spring boot返回json数据

2、Spring Boot完美使用FastJson解析JSON数据

 

第三章 Spring Boot热部署

1、Spring Boot热部署(springloader)

2、springboot + devtools(热部署)

 

第四章 Spring Boot数据库

1、Spring Boot JPA/Hibernate/Spring Data概念

2、Spring Boot JPA-Hibernate

3、Spring Boot Spring Data JPA介绍

4、Spring Boot JdbcTemplate

5、Spring Boot集成MyBatis

 

第五章 web开发

1、全局异常捕捉

2、配置server信息

3、spring boot使用thymeleaf

4、Spring Boot 使用freemarker

5、Spring Boot添加JSP支持

 

第六章 定时任务

1、Spring Boot定时任务

2、Spring Boot 定时任务升级篇(动态修改cron参数)

3、Spring Boot 定时任务升级篇(动态添加修改删除定时任务)

4、Spring Boot 定时任务升级篇(集群/分布式下的定时任务说明)

5、Spring Boot Quartz介绍

6、Spring Boot Quartz在Java Project中使用

7、Spring Boot 集成Quartz普通使用

8、Spring Boot 集成Quartz升级版

9、Spring Boot 集成Quartz二次升级版

10、Spring Boot 集成Quartz-Job如何自动注入Spring容器托管的对象

 

第七章 Spring Boot MyBatis升级篇

1、Spring Boot MyBatis升级篇-注解

2、Spring Boot MyBatis升级篇-注解-自增ID

3、Spring Boot MyBatis升级篇-注解-增删改查

4、Spring Boot MyBatis升级篇-注解-分页查询

5、Spring Boot MyBatis升级篇-注解-分页PageHelper不生效

6、Spring Boot MyBatis升级篇-注解- mybatic insert异常:BindingException: Parameter 'name' not found

7、Spring Boot MyBatis升级篇-注解- #和$符号特别篇

8、Spring Boot MyBatis升级篇-注解-@Result

9、Spring Boot MyBatis升级篇-注解-动态SQL(if test)-方案一:<script>

10、Spring Boot MyBatis升级篇-注解-动态SQL(if test)-方案二:@Provider

11、Spring Boot MyBatis升级篇-注解-动态SQL-参数问题

12、Spring Boot MyBatis升级篇-注解-特别篇:@MapperScan和@Mapper

13、Spring Boot MyBatis升级篇-XML

14、Spring Boot MyBatis升级篇-XML-自增ID

15、Spring Boot MyBatis升级篇-XML-增删改查

16、Spring Boot MyBatis升级篇-XML-分页查询

17、Spring Boot MyBatis升级篇-XML-分页PageHelper不生效

18、Spring Boot MyBatis升级篇-XML-动态SQL(if test)

19、Spring Boot MyBatis升级篇-XML-注解-初尝试

20、Spring Boot MyBatis升级篇- pagehelper替换为pagehelper-spring-boot-starter

 

第八章 Spring Boot 知识点1

1、Spring Boot 拦截器HandlerInterceptor

2、Spring Boot启动加载数据CommandLineRunner

3、Spring Boot环境变量读取和属性对象的绑定

4、Spring Boot使用自定义的properties

5、Spring Boot使用自定义的properties

6、Spring Boot使用@SpringBootApplication

7、Spring Boot 监控和管理生产环境

 

第十章 Spring Boot 打包部署

1、Spring Boot打包部署((提供Linux的sh文件))

 

第十一章 Spring Boot 日志

1、spring boot日志—理论

2、Spring Boot日志-logback

3、Spring Boot日志-log4j2

 

更多查看博客: http://412887952-qq-com.iteye.com/

 

分享到:
评论
9 楼 W_ILU 2018-02-05  
/**

     * 定义一个切入点.

     * 解释下:

     *

     * ~ 第一个 * 代表任意修饰符及任意返回值.

     * ~ 第二个 * 任意包名

     * ~ 第三个 * 代表任意方法.

     * ~ 第四个 * 定义在web包或者子包

     * ~ 第五个 * 任意方法

     * ~ .. 匹配任意数量的参数.

     */

     @Pointcut("execution(public * com.kfit.*.web..*.*(..))")
博主这里写错了吧,只有四个星
8 楼 林祥纤 2016-12-29  
xiaoqiang01 写道
博主,你好,你的文章我都有学习,写得很通俗易懂..
可是我对这篇文章一直测不通.
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
这里返回null,于是attributes.getRequest();就报空指针异常了.不知道怎么解决.请求博主指点,谢谢啊



加群处理。
7 楼 xiaoqiang01 2016-12-27  
博主,你好,你的文章我都有学习,写得很通俗易懂..
可是我对这篇文章一直测不通.
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
这里返回null,于是attributes.getRequest();就报空指针异常了.不知道怎么解决.请求博主指点,谢谢啊
6 楼 林祥纤 2016-11-27  
k88520 写道
非常感谢博主的分享~~~

5 楼 k88520 2016-11-23  
非常感谢博主的分享~~~
4 楼 林祥纤 2016-07-27  
masuweng 写道
用普通成员变量为什么会有同步,我改为普通成员变量,没发现有同步的现象!


并发的时候才能测试出来。
3 楼 masuweng 2016-07-27  
用普通成员变量为什么会有同步,我改为普通成员变量,没发现有同步的现象!
2 楼 林祥纤 2016-07-25  
zqlovezxl 写道
博主 扣扣号 多少????


412887952
1 楼 zqlovezxl 2016-07-25  
博主 扣扣号 多少????

相关推荐

    详解Spring Boot中使用AOP统一处理Web请求日志

    Spring Boot 中使用 AOP 统一处理 Web 请求日志是指在 Spring Boot 应用程序中使用Aspect Oriented Programming(面向 Aspect 编程)技术来实现统一的 Web 请求日志处理机制。这种方法可以将日志记录功能从业务逻辑...

    spring boot如何使用AOP统一处理web请求

    本文主要介绍了如何使用Spring Boot框架结合AOP(Aspect-Oriented Programming,面向切面编程)技术来统一处理Web请求日志。通过使用AOP,我们可以在不修改原有业务逻辑的情况下,对Web请求进行统一的日志记录,从而...

    springboot基于AOP将web请求写入日志

    本教程将引导新手理解如何利用AOP来记录Web请求日志。下面将详细阐述这个过程。 首先,我们需要了解AOP的基本概念。AOP允许我们在程序执行过程中,在特定的“连接点”(例如方法调用)上插入自定义的行为,这些行为...

    spring_Aop.rar

    OP是Spring框架中的一个重要内容,它通过对既有程序定义一个切入点,然后在其前后切入不同的执行内容,这份代码提供了最基础的Spring Boot中使用AOP统一处理Web请求日志得学习

    spring-boot 自定义xml配置web请求拦截器

    标题中的"spring-boot 自定义xml配置web请求拦截器"指的是在Spring Boot项目中,通过XML配置方式实现对Web请求的拦截处理。这涉及到Spring Boot的Web层架构、AOP(面向切面编程)以及自定义拦截器的概念。Spring ...

    springboot 用aop打印web请求参数信息例子

    本教程将通过一个具体的例子来介绍如何使用Spring AOP在Spring Boot中打印Web请求的参数信息。 首先,我们需要了解Spring AOP的基本概念。AOP是一种编程范式,用于处理系统中的横切关注点,如日志、事务管理等。在...

    spring-framework-5.3.23 源码

    Spring Framework 是一个广泛使用的Java应用开发框架,由Pivotal ...通过研究源码,你可以学习如何实现DI、AOP,以及Spring如何处理数据库操作和Web请求。这对于提升你的Java开发技能和理解软件设计原则非常有帮助。

    spring_in_action-sixth-edition.pdf

    其中,Spring 提供了多种方式来处理 web 请求,例如使用 @RestController 注解或使用 Spring MVC 框架。 4. Spring Boot Spring Boot 是 Spring 框架的子项目,提供了一个快速构建生产级别应用程序的方式。Spring ...

    spring boot 全面的样例代码

    - chapter4-2-4:[使用AOP统一处理Web请求日志](http://blog.didispace.com/springbootaoplog/) - chapter4-2-5:[使用log4j记录日志到MongoDB](http://blog.didispace.com/springbootlog4jmongodb/) - chapter4-2-6...

    spring boot 使用Aop通知打印控制器请求报文和返回报文问题

    本文主要介绍了如何使用 Spring Boot 的 AOP 框架来打印控制器请求报文和返回报文,解决了开发过程中需要写许多重复代码的问题。 一、简介 在开发过程中,我们往往需要写许多重复的代码,例如打印请求参数和返回...

    spring.jar spring-webmvc-struts.jar spring-webmvc.jar

    标题中的"spring.jar"、"spring-webmvc-struts.jar"和"spring-webmvc.jar"都是Spring框架相关的Java库文件,通常以.jar结尾的文件是Java的归档文件,包含了类、资源和元数据,用于Java应用程序的运行。这些文件在...

    【BAT必备】spring面试题,spring MVC面试题,spring boot面试题,spring cloud面试题

    在Spring MVC中,控制器可以通过`@RequestMapping`注解来指定处理哪些URL请求。 ### Spring Boot入门与实践 #### 1. Spring Boot简介 Spring Boot是由Pivotal团队提供的全新框架,其设计目标是简化新Spring应用的...

    spring boot 入门开发

    - **AOP 处理请求**:面向切面编程 (AOP) 是一种编程范式,可以将横切关注点(如日志记录、安全控制等)从业务逻辑中分离出来。Spring Boot 支持 AOP,可以通过 `@Aspect` 和 `@Before`、`@After` 等注解实现。 ###...

    Manning.Spring.in.Action.5th.Edition

    3. 面向切面编程:AOP是Spring提供的一种用于处理系统中横切关注点(如日志、事务管理)的方法。它将这些关注点与业务逻辑分离,提高了代码的可重用性和可维护性。 4. Spring MVC:Spring框架中的Web层解决方案,...

    spring-boot项目示例学习demo-66套.zip

    该项目已成功集成 actuator(监控)、admin(可视化监控)、logback(日志)、aopLog(通过AOP记录web请求日志)、统一异常处理(json级别和页面级别)、freemarker(模板引擎)、thymeleaf(模板引擎)、Beetl(模板引擎)、Enjoy...

    spring boot入门.txt

    3. **基于切面 (AOP) 和惯例进行声明式编程**:AOP 是一种编程范式,允许将横切关注点(如日志记录、事务管理等)从业务逻辑中分离出来,使得业务逻辑更加清晰。 4. **通过切面和模板减少样板代码**:Spring 提供了...

    spring 常用的26个包

    12. `org.springframework.web.struts`:Spring与Struts框架的集成,方便在Struts应用中使用Spring的功能。 13. `org.springframework.web.portlet`:针对portlet应用的MVC支持。 14. `org.springframework.web....

    Spring boot通过AOP防止API重复请求代码实例

    本文主要介绍了使用 Spring Boot 和 AOP 防止 API 重复请求的代码实例。 知识点一:什么是 AOP? AOP(Aspect-Oriented Programming),即面向方面编程,是一种编程范式,它提供了一种新的编程方式,允许开发者将...

    Spring aop.docx

    通过以上内容,你将能够了解Spring AOP的基础知识,如何在Spring Boot项目中使用AOP进行日志记录,并逐步掌握面向切面编程的精髓。继续深入学习,你将能够熟练运用Spring AOP解决实际问题,提升代码质量和可维护性。

    IDEA中使用Spring的简单入门(加小案例)

    在本文中,我们将深入探讨如何在IntelliJ IDEA(IDEA)中使用Spring框架进行简单的入门。Spring是一个广泛使用的开源Java框架,它为构建企业级应用程序提供了强大的支持,包括依赖注入、面向切面编程(AOP)、事务...

Global site tag (gtag.js) - Google Analytics