建日志表的SQL语句:
DROP TABLE IF EXISTS `t_log`;
CREATE TABLE `t_log` (
`logID` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '日志ID 主键自增长',
`operator` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '操作人',
`opDescribe` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '操作描述',
`operMethod` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '操作方法',
`opAddress` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '操作地址',
`operTime` datetime(0) DEFAULT NULL COMMENT '操作时间',
`delFlag` int(11) NOT NULL DEFAULT 0 COMMENT '置废标识',
UNIQUE INDEX `logID`(`logID`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 337176 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
日志表的截图:
该表t_log各个字段的含义见上述sql语句中的备注字段
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.7.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.xgp</groupId>
<artifactId>mylog</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>mylog</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
其中注意,与一般的SpringBoot项目中,此处多引入了一个依赖(Spring框架的apo依赖),因为本次演示需要使用到Spring框架的切面。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
spring:
datasource:
# 数据源基本配置
username: ****
password: ****
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3307/smartclassroom?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai&zeroDateTimeBehavior=round
server:
port: 80
#开启mybatis的sql语句显示功能
logging:
level:
com:
xgp:
mylog:
mapper: debug
package com.xgp.mylog.model;
import java.util.Date;
public class Log {
private Long logid;
private String operator;
private String opdescribe;
private String opermethod;
private String opaddress;
private Date opertime;
private Integer delflag;
public Log() {
}
public Log(String operator, String opdescribe, String opermethod, String opaddress, Date opertime) {
this.operator = operator;
this.opdescribe = opdescribe;
this.opermethod = opermethod;
this.opaddress = opaddress;
this.opertime = opertime;
}
public Long getLogid() {
return logid;
}
public void setLogid(Long logid) {
this.logid = logid;
}
public String getOperator() {
return operator;
}
public void setOperator(String operator) {
this.operator = operator == null ? null : operator.trim();
}
public String getOpdescribe() {
return opdescribe;
}
public void setOpdescribe(String opdescribe) {
this.opdescribe = opdescribe == null ? null : opdescribe.trim();
}
public String getOpermethod() {
return opermethod;
}
public void setOpermethod(String opermethod) {
this.opermethod = opermethod == null ? null : opermethod.trim();
}
public String getOpaddress() {
return opaddress;
}
public void setOpaddress(String opaddress) {
this.opaddress = opaddress == null ? null : opaddress.trim();
}
public Date getOpertime() {
return opertime;
}
public void setOpertime(Date opertime) {
this.opertime = opertime;
}
public Integer getDelflag() {
return delflag;
}
public void setDelflag(Integer delflag) {
this.delflag = delflag;
}
@Override
public String toString() {
return "Log{" +
"logid=" + logid +
", operator='" + operator + '\'' +
", opdescribe='" + opdescribe + '\'' +
", opermethod='" + opermethod + '\'' +
", opaddress='" + opaddress + '\'' +
", opertime=" + opertime +
", delflag=" + delflag +
'}';
}
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
package com.xgp.mylog.annotation;
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyLog {
String value();
}
如果要使用Spring框架的Aop切面,需要在类级别上标注@Aspect注解
如果该注解标红,是你前面的依赖没导
切点注解:
@Pointcut("@annotation(com.xgp.mylog.annotation.MyLog)")
表示在切点前进行解析自定义注解的注解:
@Before("annotationPointcut()")
表示在切点后进行解析自定义注解的注解:
@After("annotationPointcut()")
本次的日志因为需要写入数据库,而与数据库的交互的方法较慢,为了不影响用户的体验速度,因此是在操作完成后进行日志的写入。
@Aspect
@Component
public class MyLogAspect {
@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
@Autowired
LogMapper logMapper;
@Pointcut("@annotation(com.xgp.mylog.annotation.MyLog)")
public void annotationPointcut() {
}
@After("annotationPointcut()")
public void afterPointcut(JoinPoint joinPoint) throws IOException {
其中该方法的JoinPoint参数为切点对象类,可以通过该类的实例获得被切方法的一些信息。
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Method method = methodSignature.getMethod();
MyLog annotation = method.getAnnotation(MyLog.class);
String opdescribe = annotation.value();
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
String[] uri = request.getRequestURI().split("/");
String requestName = uri[uri.length - 1];
String operator = null;
if(request.getSession().getAttribute("token") == null) {
operator = (String) joinPoint.getArgs()[0];
}
String ip = getIpAddr(request);
该函数如下:
public String getIpAddr(HttpServletRequest request) {
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("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
if ("0:0:0:0:0:0:0:1".equals(ip)) {
ip = "127.0.0.1";
}
if (ip.split(",").length > 1) {
ip = ip.split(",")[0];
}
return ip;
}
Date requestTime = new Date();
Log log = new Log();
log.setOperator(operator);
log.setOpdescribe(opdescribe);
log.setOpermethod(requestName);
log.setOpaddress(ip);
log.setOpertime(requestTime);
//获取返回结果
int result = (int) request.getAttribute("flag");
if(result == 1) {
//登陆成功写入数据库
logMapper.insert(log);
}
package com.xgp.mylog.annotation.aspect;
import com.xgp.mylog.annotation.MyLog;
import com.xgp.mylog.mapper.LogMapper;
import com.xgp.mylog.model.Log;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Date;
@Aspect
@Component
public class MyLogAspect {
@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
@Autowired
LogMapper logMapper;
@Pointcut("@annotation(com.xgp.mylog.annotation.MyLog)")
public void annotationPointcut() {
}
@After("annotationPointcut()")
public void afterPointcut(JoinPoint joinPoint) throws IOException {
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Method method = methodSignature.getMethod();
MyLog annotation = method.getAnnotation(MyLog.class);
String opdescribe = annotation.value();
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
String[] uri = request.getRequestURI().split("/");
String requestName = uri[uri.length - 1];
String operator = null;
if(request.getSession().getAttribute("token") == null) {
operator = (String) joinPoint.getArgs()[0];
}
String ip = getIpAddr(request);
Date requestTime = new Date();
Log log = new Log();
log.setOperator(operator);
log.setOpdescribe(opdescribe);
log.setOpermethod(requestName);
log.setOpaddress(ip);
log.setOpertime(requestTime);
//获取返回结果
int result = (int) request.getAttribute("flag");
if(result == 1) {
//登陆成功写入数据库
logMapper.insert(log);
}
}
public String getIpAddr(HttpServletRequest request) {
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("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
if ("0:0:0:0:0:0:0:1".equals(ip)) {
ip = "127.0.0.1";
}
if (ip.split(",").length > 1) {
ip = ip.split(",")[0];
}
return ip;
}
}
注解解析器中设计到了日志与数据库的交互,因此在该类中编写与数据库交互的方法:
package com.xgp.mylog.mapper;
import com.xgp.mylog.model.Log;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface LogMapper {
@Insert("insert into t_log (operator,opDescribe,operMethod,opAddress,operTime) values (#{operator,jdbcType=VARCHAR}, #{opdescribe,jdbcType=VARCHAR}, #{opermethod,jdbcType=VARCHAR}, #{opaddress,jdbcType=VARCHAR}, #{opertime,jdbcType=TIMESTAMP})")
void insert(Log log);
}
这里需注意一点,mybatis在取方法的参数值时,最好以#{}d的方式进行取值,可以有效的防治SQL注入的问题。
package com.xgp.mylog.Controller;
import com.xgp.mylog.annotation.MyLog;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
@RestController
public class UserController {
/**
* 登陆方法
*/
@MyLog("登陆方法")
@GetMapping("/login")
public String login(@RequestParam String username, @RequestParam String password, HttpServletRequest request) {
if("xgp".equals(username) && "123".equals(password)) {
request.setAttribute("flag",1);
return "登陆成功";
}
request.setAttribute("flag",0);
return "登陆失败";
}
}
这里使用自己的自定义注解上的参数为"登陆方法",并且如果登陆成功,则像request域中写入1,反之写入0。
因为方便测试,这里的登陆方法采用的是GET请求,测试者可以在浏览器中的地址栏中方便的进行测试:
看到前端返回了登陆成功,表示我们的数据已经成功的插如到了数据库中,打开数据库中进行验证,也确实插入成功了。
之前使用拦截器的做法:
package com.c611.smartclassroom.component;
import com.c611.smartclassroom.mapper.LogMapper;
import com.c611.smartclassroom.model.Log;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
public class LogHandlerInterceptor implements HandlerInterceptor {
@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
@Autowired
LogMapper logMapper;
private static Map<String,String> requestMap = new HashMap<>();
public LogHandlerInterceptor() {
// requestMap.put("querySchByClassSeme","查询课表");
requestMap.put("exportSch","导出课表");
requestMap.put("importSch","导入课表");
requestMap.put("addSchool","添加学校");
requestMap.put("addZone","添加区域");
requestMap.put("addBuild","添加教学楼");
requestMap.put("addClassRoom","添加教室");
requestMap.put("setCourseTime","设置课时");
requestMap.put("setSemesterDate","设置学期时间");
requestMap.put("editSemesterDate","编辑学期时间");
requestMap.put("addRole","增加角色");
requestMap.put("delRole","删除角色");
requestMap.put("editAuth","编辑授权码");
//工单管理模块
requestMap.put("queryWorkOrder","按时间排序分页查询所有工单");
requestMap.put("queryWorkOrderResult","按工单编号查询处理结果");
requestMap.put("saveWorkOrderResult","填写处理结果");
requestMap.put("delWorkOrder","删除工单");
requestMap.put("addWorkOrder","增加工单");
//网关管理
requestMap.put("addGateWay","添加网关");
requestMap.put("saveGateWay","编辑网关");
requestMap.put("delGateWay","删除网关");
//设备管理
requestMap.put("addDevice","添加设备");
requestMap.put("saveDevice","编辑设备信息");
requestMap.put("delDevice","删除设备");
}
/* private static final String USER_AGENT = "user-agent";
private Logger logger = LoggerFactory.getLogger(this.getClass());*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
/* HttpSession sesson = request.getSession();
String userId = (String) sesson.getAttribute("userId");
//说明未登陆,不能放行
if(userId == null) return false;
//查数据库,根据userId查找用户,或者从session中取出*/
String userName = "薛国鹏";
String[] uri = request.getRequestURI().split("/");
String requestName = uri[uri.length - 1];
// System.out.println(requestName);
String chaineseName = requestMap.get(requestName);
// System.out.println(chaineseName);
if(chaineseName != null) {
String ip = getIpAddr(request);
Date requestTime = new Date();
Log log = new Log();
log.setOperator(userName);
log.setOpdescribe(chaineseName);
log.setOpermethod(requestName);
log.setOpaddress(ip);
log.setOpertime(requestTime);
logMapper.insertSelective(log);
return true;
}
return false;
}
public String getIpAddr(HttpServletRequest request) {
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("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
if ("0:0:0:0:0:0:0:1".equals(ip)) {
ip = "127.0.0.1";
}
if (ip.split(",").length > 1) {
ip = ip.split(",")[0];
}
return ip;
}
}
分析代码可以知道,有如下弊端:
其中有一个Map集合数据量可能会较大,占用较大内存
拦截器的编写者需要手动的将其他开发者编写的Controller层的方法一一翻译放入map中,工作量大且繁琐。
java基础复习-自定义注解3(自定义注解在SpringBoot中的使用)
原文:https://www.cnblogs.com/xgp123/p/12261566.html