您的位置:首页 > 编程语言 > Java开发

Spring Boot入门(6)-使用AOP统一处理Web请求日志

2018-03-02 23:10 1276 查看
本文介绍 SpringBoot 使用 AOP 统一处理 Web 请求日志。

一、导入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
 

二、代码示例

这里通过一个拦截器的示例介绍。主要看 HttpAspect.java 的代码1、UserController.javapackage com.liuyanzhao.blog.controller;

import com.liuyanzhao.blog.domain.User;
import com.liuyanzhao.blog.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

/**
 * @Author 言曌
 * @DATE 2017/10/29 上午10:33
 * @URL http://liuyanzhao.com
 */

@RestController
public class UserController {

    @Autowired
    private UserRepository userRepository;

    /**
     * 查询所有用户列表
     * @return
     */
    @GetMapping(value = "/users")
    public List<User> listUser() {
        System.out.println("List all user!");
        return userRepository.findAll();
    }

    /**
     * 删除一个用户
     * @param id
     */
    @DeleteMapping(value = "/users/{id}")
    public void deleteUser(@PathVariable("id") Integer id) {
        System.out.println("Delete a user!");
        userRepository.delete(id);
    }

}
 2、UserRepository.javapackage com.liuyanzhao.blog.repository;

import com.liuyanzhao.blog.domain.User;
import org.springframework.data.jpa.repository.JpaRepository;

/**
 * @Author 言曌
 * @DATE 2017/10/29 上午10:35
 * @URL http://liuyanzhao.com
 */

public interface UserRepository  extends JpaRepository<User,Integer>{//表名和Id类型

}
 3、User.javapackage com.liuyanzhao.blog.domain;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.validation.constraints.Min;

@Entity
public class User {

    @Id
    @GeneratedValue
    private Integer id;

    private String name;

    private String gender;

    private Integer age;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}
 4、HttpAspect.java 拦截器① 写法一、package com.liuyanzhao.blog.aspect;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

/**
 * @Author 言曌
 * @DATE 2017/10/30 下午7:03
 * @URL http://liuyanzhao.com
 */

@Aspect
@Component
public class HttpAspect {

    //在UserController所有方法调用之前执行一次
    @Before("execution(public * com.liuyanzhao.blog.controller.UserController.*(..))")
    public void doBefore() {
        System.out.println("11111111");
    }

    //在UserController所有方法调用之后执行一次
    @After("execution(public * com.liuyanzhao.blog.controller.UserController.*(..))")
    public void doAfter() {
        System.out.println("222222222");
    }

}
注意:@Aspect  表示该类是一个面向切面编程的类@Component  表示将该类加入 Spring 容器@Before("execution(public * com.liuyanzhao.blog.controller.UserController.*(..))")  表示在 UserController 类下的所有方法执行前执行。@After与@Before相反 然后我们可以发起一个查询所有用户的请求和删除一个用户的请求,观察控制台

可以证明拦截器是有效的 ② 写法二、我们发现上面的 @Before 和 @After 后面的代码是一样的,重复了,本着不写重复代码的原则,我们要把它整理一下,所以 HttpAspect.java 可以改成这样package com.liuyanzhao.blog.aspect;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

/**
 * @Author 言曌
 * @DATE 2017/10/30 下午7:03
 * @URL http://liuyanzhao.com
 */

@Aspect
@Component
public class HttpAspect {

    @Pointcut("execution(public * com.liuyanzhao.blog.controller.UserController.*(..))")
    public void log() { }

    //在UserController所有方法调用之前执行一次
    @Before("log()")
    public void doBefore() {
        System.out.println("11111111");
    }

    //在UserController所有方法调用之后执行一次
    @After("log()")
    public void doAfter() {
        System.out.println("222222222");
    }
}
 ③ 写法三 (推荐)package com.liuyanzhao.blog.aspect;

import org.aspectj.lang.annotation.After;
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;

/**
 * @Author 言曌
 * @DATE 2017/10/30 下午7:03
 * @URL http://liuyanzhao.com
 */

@Aspect
@Component
public class HttpAspect {

    private final static Logger logger = LoggerFactory.getLogger(HttpAspect.class);

    @Pointcut("execution(public * com.liuyanzhao.blog.controller.UserController.*(..))")
    public void log() { }

    //在UserController所有方法调用之前执行一次
    @Before("log()")
    public void doBefore() {
        logger.info("11111111111");
    }

    //在UserController所有方法调用之后执行一次
    @After("log()")
    public void doAfter() {
        logger.info("22222222222");
    }
}
注意 Logger 的包不要导错了,要导 org.slf4j.Logger; 下的和方法一执行同样的操作,控制台效果图如下

我们发现这次的日志里会有时间,进程号,端口号,哪个类输出的日志等信息,更加强大,所以更为推荐使用。 

三、将 http 请求写入日志

将上面的 HttpAspect.java 修改一下,用于获取 Http 请求,并写入日志package com.liuyanzhao.blog.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
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;

import javax.servlet.http.HttpServletRequest;

/**
 * @Author 言曌
 * @DATE 2017/10/30 下午7:03
 * @URL http://liuyanzhao.com
 */

@Aspect
@Component
public class HttpAspect {

    private final static Logger logger = LoggerFactory.getLogger(HttpAspect.class);

    @Pointcut("execution(public * com.liuyanzhao.blog.controller.UserController.*(..))")
    public void log() { }

    //在UserController所有方法调用之前执行一次
    @Before("log()")
    public void doBefore(JoinPoint joinPoint) {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();

        //url
        logger.info("url={}",request.getRequestURL());

        //method
        logger.info("method={}",request.getMethod());

        //ip
        logger.info("ip={}",request.getRemoteAddr());

        //类方法
        logger.info("class_method={}",joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());

        //参数
        logger.info("args={}",joinPoint.getArgs());

    }

    //在UserController所有方法调用之后执行一次
    @After("log()")
    public void doAfter() {
        logger.info("22222222222");
    }

    //返回结果
    @AfterReturning(returning = "object",pointcut = "log()")
    public void doAfterReturning(Object object) {
        logger.info("response={}",object);
    }
}
发起一个请求,控制台效果图
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: