代码齐全,我使用了jpa,当然你如果想用mybaties也行
1.导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--Spring AOP日志所需要的包-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- 用于日志切面中,以 json 格式打印出入参 -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.5</version>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.22</version>
<scope>provided</scope>
</dependency>
<!--mysql 驱动程序-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.16</version>
</dependency>
<!--springBoot JPA的起步依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
2.配置文件
server:
port: 9000
spring:
application:
name: spring-aop
#数据库配置
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: root
#jpa配置
jpa:
database: MYSQL
#是否打印执行sql语句
show-sql: true
#是否自动创建或更改表
generate-ddl: true
3.自定义注解 @log
import java.lang.annotation.*;
//表示作用于方法上
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log {
/**
* 描述信息:注解声明在方法上
* */
String description() default "";
}
4.日志实体类
@Data
@Entity//entity 标注当前是jpa的实体类
@Table(name = "sys_log")// @Table 当前实体与数据库的对应关系 name:数据库表名
public class SysLog implements Serializable{
//主键
@Id
//自增策略
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String ip;//请求ip
private String url;//请求的url
private String description;//方法的描述信息
@Column(name = "method_all_path")
private String methodAllPath;//切入方法的全路径
@Column(name = "request_params")
private String requestParams;//请求参数
@Column(name = "response_params")
private String responseParams;//响应参数
private String time;//方法执行的时间 单位毫秒
@Column(name = "create_time")
@DateTimeFormat(pattern = "yyyy-MM-dd hh:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd hh:mm:ss")
private Date createTime;//日志的什么时候存入
}
5.SysLogDao
import com.zwh.pojo.SysLog;
import org.springframework.data.jpa.repository.JpaRepository;
public interface SysLogDao extends JpaRepository<SysLog,Integer> {
}
6.自定义切面类(使用环绕通知)
import com.google.gson.Gson;
import com.zwh.dao.SysLogDao;
import com.zwh.pojo.SysLog;
import com.zwh.utils.HttpContextUtils;
import com.zwh.utils.IpUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Date;
//标注当前类是一个切面类
@Aspect
//交给spring
@Component
@Slf4j
public class LogAspect {
/**
* 换行符
*/
private static final String LINE_SEPARATOR = System.lineSeparator();
//以自定义 @Log注解 为切点
@Pointcut("@annotation(com.zwh.aop.Log)")
public void Log() {
}
//以controller下的所有控制器为切点
@Pointcut("execution(public * com.*.controller..*.*(..))")
public void controllerMethod() {
}
@Autowired
SysLogDao sysLogDao;
/**
* 环绕通知
*/
@Around("controllerMethod()")
public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
//获取http请求请求对象
HttpServletRequest request = HttpContextUtils.getHttpServletRequest();
//请求 url
String url = request.getRequestURL().toString();
//获取签名
Signature signature = proceedingJoinPoint.getSignature();
//获取方法签名
MethodSignature methodSignature = (MethodSignature)signature;
//2.获取被切入方法对象
Method method = methodSignature.getMethod();
//3.获取方法上的注解
Log annotation = method.getAnnotation(Log.class);
String description = "";
if(annotation != null){
// 获取 @Log 注解的描述信息
description = annotation.description();
}
//请求方式
String httpMethod = request.getMethod();
//获取切入方法的全路径
String declaringTypeName = signature.getDeclaringTypeName();
String methodName = signature.getName();
String fullPath = declaringTypeName+"."+methodName+"()";
//请求ip
String ipAddr = IpUtils.getIpAddr(request);
//请求参数
Object[] args = proceedingJoinPoint.getArgs();
StringBuffer sb = new StringBuffer();
for (Object arg : args) {
sb.append(arg);
}
// // 开始打印请求日志,打印请求相关参数
log.info("==============================start==============================");
log.info("URL : {}",url);
log.info("Description : {}", description);
log.info("HTTP Method : {}",httpMethod);
log.info("Class Method : {}",fullPath);
log.info("IP : {}",ipAddr);
log.info("Request Args : {}",args);
Object proceed = null;
//方法执行的开始时间
long startTime = System.currentTimeMillis();
try {
//方法执行
proceed = proceedingJoinPoint.proceed();
// 打印响应参数
log.info("Response Args : {}", new Gson().toJson(proceed));
}catch (Exception e){
log.info("异常通知!!!");
log.info(e.getMessage());
}finally {
// 执行时长(毫秒)
long time = System.currentTimeMillis() - startTime;
//打印执行时长
log.info("Time-Consuming : {} ms",time);
log.info("==============================end==============================");
//将日志信息存入数据库
SysLog sysLog = new SysLog();
sysLog.setIp(ipAddr);
sysLog.setUrl(url);
sysLog.setDescription(description);
sysLog.setMethodAllPath(fullPath);
sysLog.setRequestParams(sb.toString());
sysLog.setResponseParams(new Gson().toJson(proceed));
sysLog.setTime(String.valueOf(time));
sysLog.setCreateTime(new Date());
sysLogDao.saveAndFlush(sysLog);
}
return proceed;
}
}
7.工具类
/**
* 获取HttpServletRequest对象
*/
public class HttpContextUtils {
public static HttpServletRequest getHttpServletRequest() {
return ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
}
}
public class IpUtils {
public static String getIpAddr(HttpServletRequest request) {
if (request == null) {
return "unknown";
}
String ip = request.getHeader("x-forwarded-for");
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("X-Forwarded-For");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("X-Real-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip;
}
}
8.测试
@log注解作为切点,标在controller层和ServiceImpl层都行
@RestController
public class TestLogController {
@Autowired
TestService testService;
@Log(description = "日志测试")
@RequestMapping(value = "/test",method = RequestMethod.POST)
public String Test(@RequestBody Map map){
return testService.test(map);
}
}