为了防止每次写代码到处拷代码的鱼唇行为,将操作日志相关的类写成spring-boot-stater形式进行引入。
首先为了将配置简化,需自定义配置文件进行注入,在这咱使用实现EnvironmentPostProcessor的方式进行自定义写入配置。
import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import java.io.IOException;
import java.util.Properties;
/**
* 加载配置文件
*/
public class LogEnvironmentPostProcessor implements EnvironmentPostProcessor {
private final Properties properties = new Properties();
/**
* 存放properties文件名
*/
private String[] profiles = {
"Log.properties",
};
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
//遍历profiles,加载配置文件
for (String profile : profiles) {
Resource resource = new ClassPathResource(profile);
environment.getPropertySources().addLast(loadProfiles(resource));
}
}
//加载配置文件
private PropertySource<?> loadProfiles(Resource resource) {
if (!resource.exists()) {
throw new IllegalArgumentException("file" + resource + "not exist");
}
System.out.println("--------------------------------加载");
try {
properties.load(resource.getInputStream());
return new PropertiesPropertySource(resource.getFilename(), properties);
} catch (IOException ex) {
throw new IllegalStateException("load resource exception" + resource, ex);
}
}
}
resouces下的Log.properties
actable.table.auto=update
actable.model.pack=org.zlf.log.starter.entity
actable.database.type=mysql
#mybatis-plus.mapperLocations=classpath*:com/gitee/sunchenbin/mybatis/actable/mapping/*/*.xml
这样,当项目启动时,自定义配置就注入啦~
SysLog类
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.gitee.sunchenbin.mybatis.actable.annotation.Column;
import com.gitee.sunchenbin.mybatis.actable.annotation.Table;
import com.gitee.sunchenbin.mybatis.actable.annotation.TableCharset;
import com.gitee.sunchenbin.mybatis.actable.annotation.TableEngine;
import com.gitee.sunchenbin.mybatis.actable.constants.MySqlCharsetConstant;
import com.gitee.sunchenbin.mybatis.actable.constants.MySqlEngineConstant;
import com.gitee.sunchenbin.mybatis.actable.constants.MySqlTypeConstant;
import java.io.Serializable;
import java.util.Date;
/**
* <p>
*
* </p>
*
* @author zlf
* @since 2022-01-12
*/
@Table(value = "sys_log",comment = "操作日志类")
@TableCharset(MySqlCharsetConstant.UTF8)
@TableEngine(MySqlEngineConstant.InnoDB)
public class SysLog implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 日志id
*/
@TableId(value = "log_id", type = IdType.AUTO)
@Column(name = "log_id",type = MySqlTypeConstant.BIGINT,comment = "日志id",isKey = true,isAutoIncrement = true)
private Long logId;
/**
* 模块名称
*/
@Column(name = "log_module",type = MySqlTypeConstant.VARCHAR,comment = "模块名称")
private String logModule;
/**
* 日志内容
*/
@Column(name = "log_content",type = MySqlTypeConstant.LONGTEXT,comment = "日志内容")
private String logContent;
/**
* 日志描述
*/
@Column(name = "log_description",type = MySqlTypeConstant.VARCHAR,comment = "日志描述")
private String logDescription;
/**
* 操作用户ip
*/
@Column(name = "log_operation_ip",type = MySqlTypeConstant.VARCHAR,comment = "操作用户ip")
private String logOperationIp;
/**
* 操作用户名
*/
@Column(name = "log_username",type = MySqlTypeConstant.VARCHAR,comment = "操作用户名")
private String logUsername;
/**
* 操作时间
*/
@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
@Column(name = "log_date",type = MySqlTypeConstant.DATETIME,comment = "操作时间")
private Date logDate;
public static long getSerialVersionUID() {
return serialVersionUID;
}
public Long getLogId() {
return logId;
}
public void setLogId(Long logId) {
this.logId = logId;
}
public String getLogModule() {
return logModule;
}
public void setLogModule(String logModule) {
this.logModule = logModule;
}
public String getLogContent() {
return logContent;
}
public void setLogContent(String logContent) {
this.logContent = logContent;
}
public String getLogDescription() {
return logDescription;
}
public void setLogDescription(String logDescription) {
this.logDescription = logDescription;
}
public String getLogOperationIp() {
return logOperationIp;
}
public void setLogOperationIp(String logOperationIp) {
this.logOperationIp = logOperationIp;
}
public String getLogUsername() {
return logUsername;
}
public void setLogUsername(String logUsername) {
this.logUsername = logUsername;
}
public Date getLogDate() {
return logDate;
}
public void setLogDate(Date logDate) {
this.logDate = logDate;
}
}
其中诸如@Column、@Table等注解主要是在自动生成数据库表时使用的。
@Log
import java.lang.annotation.*;
/**
* Created with IntelliJ IDEA.
*
* @Auther: zlf
* @Date: 2022/01/12/22:31
* @Description:
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log {
/**模块*/
String module() default "";
/**描述*/
String description() default "";
}
LogAspect
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.zlf.log.starter.annotation.Log;
import org.zlf.log.starter.service.SysLogService;
import java.lang.reflect.Method;
/**
* Created with IntelliJ IDEA.
*
* @Auther: zlf
* @Date: 2022/01/12/22:32
* @Description:
*/
@Aspect
@Component
public class LogAspect {
@Autowired
SysLogService sysLogService;
@Pointcut("@annotation(org.zlf.log.starter.annotation.Log)")
public void logPointCut(){}
@AfterReturning(pointcut = "logPointCut()")
public void doAfter(JoinPoint joinPoint){
/**
* 解析Log注解
*/
String methodName = joinPoint.getSignature().getName();
Method method = currentMethod(joinPoint,methodName);
Log log = method.getAnnotation(Log.class);
sysLogService.put(joinPoint,methodName,log.module(),log.description());
}
/**
* 获取当前执行的方法
*
* @param joinPoint 连接点
* @param methodName 方法名称
* @return 方法
*/
private Method currentMethod(JoinPoint joinPoint, String methodName) {
/**
* 获取目标类的所有方法,找到当前要执行的方法
*/
Method[] methods = joinPoint.getTarget().getClass().getMethods();
Method resultMethod = null;
for (Method method : methods) {
if (method.getName().equals(methodName)) {
resultMethod = method;
break;
}
}
return resultMethod;
}
}
SysLogServiceImpl
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.ibatis.javassist.*;
import org.apache.ibatis.javassist.bytecode.CodeAttribute;
import org.apache.ibatis.javassist.bytecode.LocalVariableAttribute;
import org.apache.ibatis.javassist.bytecode.MethodInfo;
import org.aspectj.lang.JoinPoint;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.zlf.log.starter.entity.SysLog;
import org.zlf.log.starter.mapper.SysLogMapper;
import org.zlf.log.starter.service.SysLogService;
import org.zlf.log.starter.utils.IpUtils;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/**
* <p>
* 服务实现类
* </p>
*
* @author zlf
* @since 2022-01-12
*/
@Service
public class SysLogServiceImpl extends ServiceImpl<SysLogMapper, SysLog> implements SysLogService {
private static final String LOG_CONTENT = "[类名]:%s,[方法]:%s,[参数]:%s,[IP]:%s";
@Override
public void put(JoinPoint joinPoint, String methodName, String module, String description) {
try {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
UserDetails userDetails = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
String username = userDetails.getUsername();
if (StringUtils.isEmpty(username)) {
username = "未知用户";
}
String ip = IpUtils.getIpAddr(request);
SysLog sysLog = new SysLog();
sysLog.setLogUsername(username);
sysLog.setLogModule(module);
sysLog.setLogContent(operateContent(joinPoint, methodName, ip, request));
sysLog.setLogDate(new Date());
sysLog.setLogDescription(description);
sysLog.setLogOperationIp(ip);
baseMapper.insert(sysLog);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NotFoundException e) {
e.printStackTrace();
}
}
public String operateContent(JoinPoint joinPoint, String methodName, String ip, HttpServletRequest request) throws ClassNotFoundException, NotFoundException {
String className = joinPoint.getTarget().getClass().getName();
Object[] params = joinPoint.getArgs();
String classType = joinPoint.getTarget().getClass().getName();
Class<?> clazz = Class.forName(classType);
String clazzName = clazz.getName();
Map<String, Object> nameAndArgs = getFieldsName(this.getClass(), clazzName, methodName, params);
StringBuffer bf = new StringBuffer();
if (!CollectionUtils.isEmpty(nameAndArgs)) {
Iterator it = nameAndArgs.entrySet().iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
String key = (String) entry.getKey();
String value = JSONObject.toJSONString(entry.getValue());
bf.append(key).append("=");
bf.append(value).append("&");
}
}
if (StringUtils.isEmpty(bf.toString())) {
bf.append(request.getQueryString());
}
return String.format(LOG_CONTENT, className, methodName, bf.toString(), ip);
}
private Map<String, Object> getFieldsName(Class cls, String clazzName, String methodName, Object[] args) throws NotFoundException {
Map<String, Object> map = new HashMap<String, Object>();
ClassPool pool = ClassPool.getDefault();
ClassClassPath classPath = new ClassClassPath(cls);
pool.insertClassPath(classPath);
CtClass cc = pool.get(clazzName);
CtMethod cm = cc.getDeclaredMethod(methodName);
MethodInfo methodInfo = cm.getMethodInfo();
CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);
if (attr == null) {
// exception
return map;
}
int pos = Modifier.isStatic(cm.getModifiers()) ? 0 : 1;
for (int i = 0; i < cm.getParameterTypes().length; i++) {
map.put(attr.variableName(i + pos), args[i]);//paramNames即参数名
}
return map;
}
}
注入Spring
以上都是正常的贴代码,大家会发现这些类都使用了@Component等这些注解 这些注解在咱第三方组件中并不会注入到Spring容器中! 所以需要进行注解扫描。
LogAutoConfig
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
@Configuration
// 扫描 mybatis.actable
@ComponentScan(basePackages = {"org.zlf.log.starter","com.gitee.sunchenbin.mybatis.actable.manager.*"})
// 配置mybatis扫描actable的mapper以及本模块的mapper
@MapperScan(basePackages = {"com.gitee.sunchenbin.mybatis.actable.dao.*","org.zlf.log.starter.mapper"} )
public class LogAutoConfig {
}
咱需要将LogAutoConfig加入到spring中,所以我们需要使用Spring.facrories .
Spring.facrories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.zlf.log.starter.config.LogAutoConfig
# 加载自定义配置文件
org.springframework.boot.env.EnvironmentPostProcessor=\
org.zlf.log.starter.config.LogEnvironmentPostProcessor
这样在引入在springboot项目中时,spring就会扫描到这些类并管理啦~