写点什么

SpringBoot 使用 Aop 自定义注解展示日志信息,mysqlsql 性能调优的方法

作者:Java高工P7
  • 2021 年 11 月 10 日
  • 本文字数:7909 字

    阅读完需:约 26 分钟

<includes>


<include>**/*.xml</include>


</includes>


</resource>


</resources>


<plugins>


<plugin>


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


<artifactId>spring-boot-maven-plugin</artifactId>


</plugin>


</plugins>


</build>


4:枚举类




package com.qycq.server.enums;


import io.swagger.annotations.ApiModel;


import lombok.extern.slf4j.Slf4j;


/**


  • @author 七月初七

  • @version 1.0

  • @date 2021/7/19 23:11


*/


@ApiModel(value = "日志操作类型")


@Slf4j


public enum LogEnum {


LOGIN("登录操作!"),


LOGOUT("退出操作!"),


SELECT("查询操作"),


DELETE("删除操作"),


UPDATE("更新操作!"),


CALCULATION("计算操作!"),


INSERT("增加操作");


/**


  • 操作类型


*/


private final String type;


LogEnum(final String type) {


this.type = type;


}


public String getType() {


return this.type;


}


}


5:@Log 自定义注解




package com.qycq.server.annotations;


import com.qycq.server.enums.LogEnum;


import io.swagger.annotations.ApiModel;


import java.lang.annotation.*;


/**


  • @author 七月初七

  • @version 1.0

  • @date 2021/7/19 22:50


*/


@Documented


@Retention(RetentionPolicy.RUNTIME)


@Target(ElementType.METHOD)


@ApiModel(value = "日志注解")


public @interface Log {


/**


  • 日志描述

  • @return


*/


LogEnum[] type() default {};


}


6:切面类




package com.qycq.server.aspect;


import com.fasterxml.jackson.databind.ObjectMapper;


import com.qycq.server.encapsulation.ResultBean;


import com.qycq.server.enums.LogEnum;


import com.qycq.server.exception.GlobalException;


import com.qycq.server.pojo.Errorlog;


import com.qycq.server.pojo.Log;


import com.qycq.server.service.ErrorlogService;


import com.qycq.server.service.LogService;


import com.qycq.server.utils.HttpServletRequestUtil;


import com.qycq.server.utils.IpUtil;


import com.qycq.server.utils.StringUtil;


import io.swagger.annotations.ApiModel;


import io.swagger.annotations.ApiOperation;


import lombok.extern.slf4j.Slf4j;


import org.aspectj.lang.JoinPoint;


import org.aspectj.lang.ProceedingJoinPoint;


import org.aspectj.lang.Signature;


import org.aspectj.lang.annotation.*;


import org.aspectj.lang.reflect.MethodSignature;


import org.slf4j.Logger;


import org.slf4j.LoggerFactory;


import org.springframework.beans.factory.annotation.Autowired;


import org.springframework.context.annotation.Configuration;


import org.springframework.core.annotation.Order;


import org.springframework.security.authentication.AnonymousAuthenticationToken;


import org.springframework.security.core.Authentication;


import org.springframework.security.core.context.SecurityContextHolder;


import org.springframework.web.bind.annotation.RequestBody;


import org.springframework.web.bind.annotation.RequestParam;


import javax.servlet.http.HttpServletRequest;


import java.lang.reflect.Method;


import java.lang.reflect.Parameter;


import java.text.SimpleDateFormat;


import java.util.*;


/**


  • @author 七月初七

  • @version 1.0

  • @date 2021/7/19 23:17


*/


@ApiModel(value = "日志注解切面类")


@Slf4j


@Configuration


@Aspect


@Order(1)


public class LogAspectImpl {


private static final Logger LOGGER = LoggerFactory.getLogger(LogAspectImpl.class);


@Autowired


private ObjectMapper objectMapper;


@Autowired


private LogService logService;


@Autowired


private ErrorlogService errorlogService;


private final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");


/**


  • 切点


*/


@Pointcut("@annotation(com.qycq.server.annotations.Log)")


public void pointcut() {


}


/**


  • 前置增强

  • @param joinPoint


*/


@Before("pointcut()")


public void before(JoinPoint joinPoint) {


LOGGER.info("========= 前置增强 start... =========");


}


/**


  • 环绕增强

  • @param proceedingJoinPoint

  • @return


*/


@Around("pointcut()")


public Object result(ProceedingJoinPoint proceedingJoinPoint) {


LOGGER.info("========= 环绕增强 start... =========");


//获取开始时间


long startTime = System.currentTimeMillis();


HttpServletRequest httpServletRequest = HttpServletRequestUtil.getHttpServletRequest();


Log log = new Log();


Object proceed = null;


try {


//返回结果


proceed = proceedingJoinPoint.proceed();


Signature signature = proceedingJoinPoint.getSignature();


MethodSignature methodSignature = (MethodSignature) signature;


Method method = methodSignature.getMethod();


if (method.isAnnotationPresent(ApiOperation.class)) {


//获取方法描述


ApiOperation annotation = method.getAnnotation(ApiOperation.class);


log.setComment(annotation.value());


LOGGER.info(annotation.value());


}


if (method.isAnnotationPresent(com.qycq.server.annotations.Log.class)) {


//获取操作类型


com.qycq.server.annotations.Log annotation = method.getAnnotation(com.qycq.server.annotations.Log.class);


for (LogEnum logEnum : annotation.type()) {


log.setRequestType(logEnum.getType());


LOGGER.info(logEnum.getType());


}


}


//获取登录用户名


Authentication authentication = SecurityContextHolder.getContext().getAuthentication();


if (!(authentication instanceof AnonymousAuthenticationToken)) {


log.setUsername(authentication.getName());


LOGGER.info(authentication.getName());


}


//获取 JSON 数据耗时


long endDate = System.currentTimeMillis();


//获取方法类型(get,post...)


String methodType = httpServletRequest.getMethod();


String requestURL = httpServletRequest.getRequestURL().toString();


String requestURI = httpServletRequest.getRequestURI();


String className = proceedingJoinPoint.getTarget().getClass().getName() + "." + proceedingJoinPoint.getSignature().getName();


String requestIp = IpUtil.getIpAddr(httpServletRequest);


Object params = getParams(method, proceedingJoinPoint.getArgs());


log.setRequestMethod(methodType);


log.setRequestURL(requestURL);


log.setRequestURI(requestURI);


log.setClassName(className);


log.setRequestIp(IpUtil.getIpAddr(httpServletRequest));


log.setRequestParams(objectMapper.writeValueAsString(params));


log.setStartTime(simpleDateFormat.format(new Date(startTime)));


log.setEndDate(System.currentTimeMillis() - endDate);


log.setRequestMethodName(proceedingJoinPoint.getSignature().getName());


log.setResult(objectMapper.writeValueAsString(proceed));


// 打印日志信息输出到控制台


LOGGER.info("methodType:{}", methodType);


LOGGER.info("requestMethodName:{}", proceedingJoinPoint.getSignature().getName());


LOGGER.info("requestURL:{}", requestURL);


LOGGER.info("requestURI:{}", requestURI);


LOGGER.info("className:{}", className);


LOGGER.info("requestIp:{}", requestIp);


LOGGER.info("params:{}", objectMapper.writeValueAsString(params));


LOGGER.info("startDate:{}", simpleDateFormat.format(new Date(startTime)));


LOGGER.info("endDate:{}", startTime - endDate);


LOGGER.info("result:{}", objectMapper.writeValueAsString(proceed));


//保存日志


boolean save = logService.save(log);


if (save) {


LOGGER.info("日志保存成功!");


}


} catch (Throwable throwable) {


throwable.printStackTrace();


LOGGER.error("日志保存失败:{" + throwable.getMessage() + "}");


}


return proceed;


}


/**


  • 后置增强

  • @param joinPoint


*/


@AfterReturning("pointcut()")


public void afterReturning(JoinPoint joinPoint) {


LOGGER.info("========== 进入后置增强... ==========");


}


/**


  • 异常增强

  • @param joinPoint

  • @param error


*/


@AfterThrowing(value = "pointcut()", throwing = "error")


public void afterThrowing(JoinPoint joinPoint, Throwable error) {


Errorlog errorlog = new Errorlog();


HttpServletRequest httpServletRequest = HttpServletRequestUtil.getHttpServletRequest();


try {


//获取代理方法


Signature signature = joinPoint.getSignature();


MethodSignature methodSignature = (MethodSignature) signature;


//获取方法名称


Method method = methodSignature.getMethod();


if (method.isAnnotationPresent(ApiOperation.class)) {


//获取方法描述


ApiOperation annotation = method.getAnnotation(ApiOperation.class);


errorlog.setComment(annotation.value());


LOGGER.info(annotation.value());


}


if (method.isAnnotationPresent(com.qycq.server.annotations.Log.class)) {


//获取操作类型


com.qycq.server.annotations.Log annotation = method.getAnnotation(com.qycq.server.annotations.Log.class);


for (LogEnum logEnum : annotation.type()) {


errorlog.setRequestType(logEnum.getType());


LOGGER.info(logEnum.getType());


}


}


//获取完整类路径名


String classMethodName = method + "";


errorlog.setClassName(classMethodName);


//获取请求地址


String ipAddr = IpUtil.getIpAddr(httpServletRequest);


errorlog.setRequestIp(ipAddr);


//获取参数


Object args = getParams(method, joinPoint.getArgs());


errorlog.setRequestParams(objectMapper.writeValueAsString(args));


//获取异常名称


String errorName = error.getClass().getName();


errorlog.setErrorName(errorName);


//获取请求方法类型


String methodType = httpServletRequest.getMethod();


errorlog.setRequestMethod(methodType);


//获取相对方法名称


String simpleName = joinPoint.getTarget().getClass().getSimpleName();


errorlog.setRequestMethodName(simpleName);


//获取方法的 url


String requestURL = httpServletRequest.getRequestURL().toString();


errorlog.setRequestURL(requestURL);


//相对路径


String requestURI = httpServletRequest.getRequestURI();


errorlog.setRequestURI(requestURI);


//返回堆栈跟踪元素


String errorMessage = this.stackTraceToString(errorName, error.getMessage(), error.getStackTrace());


errorlog.setErrorMessage(errorMessage);


LOGGER.info("方法:{}", classMethodName);


LOGGER.info("入参:{}", args);


LOGGER.info("uri:{}", requestURI);


LOGGER.info("url:{}", requestURL);


LOGGER.info("methodType:{}", methodType);


LOGGER.info("ipAddr:{}", ipAddr);


LOGGER.error("异常信息:{}", errorMessage);


boolean save = errorlogService.save(errorlog);


if (save) {


LOGGER.info("错误日志保存成功!");


}


// LOGGER.error("方法:{}, 入参:{}, uri:{}, 请求 ip:{}, 异常信息:{}", classMethodName, args, requestURI, ipAddr, errorMessage);


} catch (Throwable throwable) {


log.error("{}", throwable.getMessage(), throwable);


}


}


/**


  • 组装异常信息

  • @param errorName

  • @param errorMessage

  • @param stackTrace

  • @return


*/


private String stackTraceToString(String errorName, String errorMessage, StackTraceElement[] stackTrace) {


StringBuilder stringBuffer = new StringBuilder();


for (StackTraceElement traceElement : stackTrace) {


stringBuffer.append(traceElement).append("\n");


}


return errorName + ":" + errorMessage + "\n\t" + stringBuffer.toString();


}


/**


  • 获取参数

  • @param method 当前方法

  • @param args 当前参数

  • @return


*/


private Object getParams(Method method, Object[] args) {


List<Object> paramsList = new ArrayList<>();


//拿到当前方法的参数数组


Parameter[] parameters = method.getParameters();


for (int i = 0; i < parameters.length; i++) {


//获取 RequestBody 注解修饰的参数


if (parameters[i].isAnnotationPresent(RequestBody.class)) {


RequestBody requestBody = parameters[i].getAnnotation(RequestBody.class);


if (requestBody != null) {


paramsList.add(args[i]);


}


}


//获取 RequestParam 注解修饰的参数


if (parameters[i].isAnnotationPresent(RequestParam.class)) {


RequestParam requestParam = parameters[i].getAnnotation(RequestParam.class);


Map<String, Object> map = new HashMap<>(16);


//获取的形参名称作为 key


String key = parameters[i].getName();


System.out.println("key = " + key);


if (StringUtil.isNotEmpty(requestParam.value())) {


key = requestParam.value();


}


map.put(key, args[i]);


paramsList.add(map);


}


}


if (paramsList.size() == 0) {


return null;


} else if (paramsList.size() == 1) {


return paramsList.get(0);


} else {


return paramsList;


}


}


}


7:mybatisPlus 代码生成器 pom




<properties>


<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>


<maven.compiler.source>1.8</maven.compiler.source>


<maven.compiler.target>1.8</maven.compiler.target>


</properties>


<dependencies>


<dependency>


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


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


</dependency>


<dependency>


<groupId>mysql</groupId>


<artifactId>mysql-connector-java</artifactId>


<scope>runtime</scope>


</dependency>


<dependency>


<groupId>com.baomidou</groupId>


<artifactId>mybatis-plus-boot-starter</artifactId>


<version>3.3.1.tmp</version>


</dependency>


<dependency>


<groupId>org.apache.velocity</groupId>


<artifactId>velocity-engine-core</artifactId>


<version>2.0</version>


</dependency>


<dependency>


<groupId>com.baomidou</groupId>


<artifactId>mybatis-plus-generator</artifactId>


<version>3.3.1.tmp</version>


<scope>compile</scope>


</dependency>


</dependencies>


8:直接运行下面代码即可生成对应的 pojo、service、mapper、mapper.xml、serviceImpl、controller,我这里就不一一去截图了。注意修改你的项目路径




package com.qycq.generator;


import com.baomidou.mybatisplus.annotation.DbType;


import com.baomidou.mybatisplus.generator.AutoGenerator;


import com.baomidou.mybatisplus.generator.config.DataSourceConfig;


import com.baomidou.mybatisplus.generator.config.GlobalConfig;


import com.baomidou.mybatisplus.generator.config.PackageConfig;


import com.baomidou.mybatisplus.generator.config.StrategyConfig;


import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;


/**


  • @author 七月初七

  • @version 1.0

  • @date 2021/7/10 15:51


*/


public class MyBatisPlusGenerator {


/**


  • 数据库链接


*/


private static final String DRIVER_NAME = "com.mysql.cj.jdbc.Driver";


/**


  • 用户名


*/


private static final String USERNAME = "root";


/**


  • 密码


*/


private static final String PASSWORD = "2829";


/**


  • 链接的 url


*/


private static final String JDBC_URL = "jdbc:mysql://localhost:3306/yeb?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8";


/**


  • 项目的绝对路径


*/


private static final String PATH = "E:\Project\qycq\yeb-server";


// private static final String PATH = "E:\Project\qycq";


/**


  • 作者


*/


private static final String AUTHOR = "七月初七";


/**


  • 包名


*/


private static final String PACKAGE = "com.qycq.server";


/**


  • 实体类


*/


private static final String ENTITY = "pojo";


/**


  • mapper


*/


private static final String MAPPER = "mapper";


/**


  • controller


*/


private static final String CONTROLLER = "controller";


/**


  • service


*/


private static final String SERVICE = "service";


/**


  • serviceImpl


*/


private static final String SERVICE_IMPL = "service.impl";


/**


  • 根据表名生成多个模块代码


*/


private static final String[] TABLE_NAME = {"t_log","t_errorLog"};


public static void main(String[] args) {


//代码生成器


AutoGenerator autoGenerator = new AutoGenerator();


//数据源配置


DataSourceConfig dataSourceConfig = new DataSourceConfig();


dataSourceConfig.setDbType(DbType.MYSQL);//指定数据库类型


dataSourceConfig.setDriverName(DRIVER_NAME);


dataSourceConfig.setUsername(USERNAME);


dataSourceConfig.setPassword(PASSWORD);


dataSourceConfig.setUrl(JDBC_URL);


autoGenerator.setDataSource(dataSourceConfig);


//全局配置


GlobalConfig globalConfig = new GlobalConfig();


globalConfig.setOpen(false);


globalConfig.setFileOverride(false); //重新生成时文件是否覆盖


globalConfig.setBaseResultMap(true);


//xml 开启 BaseColumnList


globalConfig.setBaseColumnList(true);


// 实体属性 Swagger2 注解


globalConfig.setSwagger2(true);


//输出路径


globalConfig.setOutputDir(PATH + "/src/main/java");


//设置作者名字


globalConfig.setAuthor(AUTHOR);


//去掉 service 的 I 前缀,一般只需要设置 service 就行


globalConfig.setServiceImplName("%sServiceImpl");


globalConfig.setServiceName("%sService");


autoGenerator.setGlobalConfig(globalConfig);


//包配置


PackageConfig packageConfig = new PackageConfig();


packageConfig.setParent(PACKAGE);//自定义包的路径


packageConfig.setEntity(ENTITY);


packageConfig.setMapper(MAPPER);


packageConfig.setController(CONTROLLER);


packageConfig.setService(SERVICE);


packageConfig.setServiceImpl(SERVICE_IMPL);


autoGenerator.setPackageInfo(packageConfig);


//策略配置


StrategyConfig strategyConfig = new StrategyConfig();


//是否使用 Lombok


strategyConfig.setEntityLombokModel(true);


//生成 RestController


strategyConfig.setRestControllerStyle(true);


//根据表名生成模块


strategyConfig.setInclude(TABLE_NAME);


//去掉表前缀


strategyConfig.setTablePrefix("t_");


autoGenerator.setStrategy(strategyConfig);


//数据库表映射到实体的命名策略


strategyConfig.setNaming(NamingStrategy.underline_to_camel);


//数据库表字段映射到实体的命名策略


strategyConfig.setColumnNaming(NamingStrategy.no_change);


//执行


autoGenerator.execute();


}


}


9:yml 文件



yml 文件必须注意缩进

端口号

server:


port: 9091

数据源

spring:

数据源配置

datasource:


type: com.alibaba.druid.pool.DruidDataSource


url: jdbc:mysql://localhost:3306/xxx?useUni


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


code=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8


username: root


password: 2829


hikari:

连接池名

pool-name: DateHikariCP

最小空闲连接数

minimum-idle: 5

空闲连接存活最大时间,默认 600000(10 分钟)

idle-timeout: 180000

最大连接数,默认 10

maximum-pool-size: 10

从连接池返回的连接的自动提交

auto-commit: true

连接最大存活时间,0 表示永久存活,默认 1800000(30 分钟)

max-lifetime: 1800000

连接超时时间,默认 30000(30 秒)

用户头像

Java高工P7

关注

还未添加个人签名 2021.11.08 加入

还未添加个人简介

评论

发布
暂无评论
SpringBoot使用Aop自定义注解展示日志信息,mysqlsql性能调优的方法