SpringBoot 使用 Aop 自定义注解展示日志信息,mysqlsql 性能调优的方法
<includes>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
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;
}
}
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 {};
}
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;
}
}
}
<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();
}
}
yml 文件必须注意缩进
端口号
server:
port: 9091
数据源
spring:
数据源配置
datasource:
type: com.alibaba.druid.pool.DruidDataSource
url: jdbc:mysql://localhost:3306/xxx?useUni
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
评论