需求说明##
需求是客户想跟踪数据修改前和后数据发生了那些变化,并将其输出到日志里面
思路
- JavaBean 增加注解,监控那些字段发生变化才会记录到日志中。
- 利用内省机制动态获取JavaBean对象监控的属性值(注意这里不能BeanUtils.getProperty(bean, name)这个方法,因为其转化出来的数值都是String类型的)
- 将变更前的对象和变更后的对象分别保存到Map中,并对齐进行逐行比对,如果有不同则进行记录。
Java代码
/**
注解
*/
@Retention(RetentionPolicy.RUNTIME)
public @interface LogCompar {
/**
* 汉字全称
* @return
*/
String name();
/**
* Date 如何格式化,默认可以为空
* @return
*/
String dateFormat() default "";
}
核心Java代码如下
package bcode.capinfo.com.modules.sys.utils;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.collections.map.HashedMap;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.math.NumberUtils;
import bcode.capinfo.com.common.utils.StringUtils;
import bcode.capinfo.com.modules.enclosure.entity.CapFilesDemo;
import bcode.capinfo.com.modules.sys.anno.LogCompar;
public class Snippet {
public static void main(String[] args) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException, ParseException, IntrospectionException {
// Log log = new Log();
// log.setId("f7afefe497f1428da91c3e836e9a047b");
// deleteOutCycleLog();
CapFilesDemo c = new CapFilesDemo();
c.setId("1");
c.setFilename("我操");
c.setFilesrc("你好");
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日");
c.setSd(sdf.parse("2018年10月12日"));
CapFilesDemo c2 = new CapFilesDemo();
c2.setId("1");
c2.setFilename("我");
c2.setFilesrc("你好");
c2.setSd(sdf.parse("2018年10月15日"));
Map<String,LogValue> oldMap= getValuesToMap(c);
Map<String,LogValue> newMap= getValuesToMap(c2);
StringBuffer sb = comparatorObject(oldMap, newMap);
System.out.println(sb.toString());
}
/**
* 对象比较器
* @param oldMap 原对象参数
* @param newMap 现对象参数
* @return 返回比较之后的结果
*/
private static StringBuffer comparatorObject(
Map<String, LogValue> oldMap, Map<String, LogValue> newMap) {
StringBuffer sb = new StringBuffer();
if(oldMap!=null&&!oldMap.isEmpty()){
Set<Entry<String,LogValue>> comparSet= oldMap.entrySet();
for (Entry<String, LogValue> entry : comparSet) {
LogValue newVo = newMap.get(entry.getKey());
LogValue oldVo = entry.getValue();
Object newValue = newVo.getValue();
Object oldValue = oldVo.getValue();
Class<?> type = newValue.getClass();
String typeName = newValue.getClass().getName();
if (newVo!=null) {
if ("java.lang.String".equals(typeName)) {
String newStr = (String)newValue;
String oldStr = (String)oldValue;
if (!StringUtils.equals(newStr, oldStr)) {
sb.append(showMsg(newVo, newStr, oldStr));
}
}else if ("java.sql.Timestamp".equals(typeName)) {
DateFormat format = new SimpleDateFormat(StringUtils.isBlank(newVo.getFormat())?"yyyy-MM-dd HH:mm:ss":newVo.getFormat());
java.sql.Timestamp newTime = (java.sql.Timestamp)newValue;
java.sql.Timestamp oldTime = (java.sql.Timestamp)oldValue;
String newTempTimeStr = "";
String oldTimeTimeStr = "";
if (newTime!=null) {
newTempTimeStr = format.format(newTime);
}
if(oldTime!=null) {
oldTimeTimeStr = format.format(oldTime);
}
if (!StringUtils.equals(newTempTimeStr, oldTimeTimeStr)) {
sb.append(showMsg(newVo, format.format(newTime), format.format(oldTime)));
}
}else if ("java.lang.Long".equals(typeName) || Long.TYPE == type) {
java.lang.Long newLog = NumberUtils.createLong(NumberUtils.isNumber(newValue+"")?newValue+"":"0");
java.lang.Long oldLog = NumberUtils.createLong(NumberUtils.isNumber(oldValue+"")?oldValue+"":"0");
if(newLog.compareTo(oldLog)!=0){
sb.append(showMsg(newVo, newLog, oldLog));
}
}else if ("java.lang.Integer".equals(typeName) || Integer.TYPE == type) {
java.lang.Integer newInt = NumberUtils.createInteger(NumberUtils.isNumber(newValue+"")?newValue+"":"0");
java.lang.Integer oldInt = NumberUtils.createInteger(NumberUtils.isNumber(oldValue+"")?oldValue+"":"0");
if(newInt.compareTo(oldInt)!=0){
sb.append(showMsg(newVo, newInt, oldInt));
}
}else if ("java.lang.Boolean".equals(typeName) || Boolean.TYPE == type) {
java.lang.Boolean newbool = BooleanUtils.toBoolean(newValue+"")?true:false;
java.lang.Boolean oldbool = BooleanUtils.toBoolean(oldValue+"")?true:false;
if(newbool!=oldbool){
sb.append(showMsg(newVo, newbool, oldbool));
}
} else if ("java.lang.Character".equals(typeName)
|| Character.TYPE == type) {
// 预留
} else if ("java.lang.Byte".equals(typeName) || Byte.TYPE == type) {
//预留不处理
} else if ("java.lang.Short".equals(typeName) || Short.TYPE == type) {
//预留不处理 有需要在处理
} else if ("java.lang.Float".equals(typeName) || Float.TYPE == type) {
java.lang.Float newFloat = NumberUtils.createFloat(NumberUtils.isNumber(newValue+"")?newValue+"":"0");
java.lang.Float oldFloat = NumberUtils.createFloat(NumberUtils.isNumber(oldValue+"")?oldValue+"":"0");
if(newFloat.compareTo(oldFloat)!=0){
sb.append(showMsg(newVo, newFloat, oldFloat));
}
} else if ("java.lang.Double".equals(typeName) || Double.TYPE == type) {
java.lang.Double newDouble = NumberUtils.createDouble(NumberUtils.isNumber(newValue+"")?newValue+"":"0");
java.lang.Double oldDouble = NumberUtils.createDouble(NumberUtils.isNumber(oldValue+"")?oldValue+"":"0");
if(newDouble.compareTo(oldDouble)!=0){
sb.append(showMsg(newVo, newDouble, oldDouble));
}
} else if ("java.util.Date".equals(typeName)) {
DateFormat format = new SimpleDateFormat(StringUtils.isBlank(newVo.getFormat())?"yyyy-MM-dd":newVo.getFormat());
java.util.Date newTime = (java.util.Date)newValue;
java.util.Date oldTime = (java.util.Date)oldValue;
String newTempTimeStr = "";
String oldTimeTimeStr = "";
if (newTime!=null) {
newTempTimeStr = format.format(newTime);
}
if(oldTime!=null) {
oldTimeTimeStr = format.format(oldTime);
}
if (!StringUtils.equals(newTempTimeStr, oldTimeTimeStr)) {
sb.append(showMsg(newVo, format.format(newTime), format.format(oldTime)));
}
}
}
}
}
return sb;
}
/**
* 页面标记
* @param newVo
* @param newLog
* @param oldLog
* @return
*/
private static String showMsg(LogValue newVo, Object newLog,
Object oldLog) {
return "["+newVo.getName()+"]发生变化由原先的:"+oldLog+"改变为:"+newLog+"<br>";
}
/**
* 将要转化的对象进行拆分为Map<String,LogValue> LogValue 保存自定义标签的名称 和 数值等内容
* @param entity
* @return
* @throws IllegalAccessException
* @throws InvocationTargetException
* @throws NoSuchMethodException
* @throws IntrospectionException
*/
private static Map<String,LogValue> getValuesToMap(Object entity)
throws IllegalAccessException, InvocationTargetException,
NoSuchMethodException, IntrospectionException {
Map<String,LogValue> comparMap = new HashedMap();
java.lang.reflect.Field[] fields = entity.getClass().getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
String name = fields[i].getName();
if(PropertyUtils.isReadable(entity, name) && PropertyUtils.isWriteable(entity, name)) {
if(fields[i].isAnnotationPresent(LogCompar.class)){
LogCompar logVo = fields[i].getAnnotation(LogCompar.class);
LogValue lv = new LogValue(logVo.name(),getProperties(entity, name),logVo.dateFormat());
comparMap.put(name, lv);
}
}
}
return comparMap;
}
/**
* 利用内省机制来获取参数
* @param b
* @param name
* @return
* @throws IntrospectionException
* @throws IllegalAccessException
* @throws InvocationTargetException
*/
private static Object getProperties(Object b, String name)
throws IntrospectionException, IllegalAccessException,
InvocationTargetException {
/*
PropertyDescriptor p = new PropertyDescriptor(name, b.getClass());
//通过内省的方法来执行制定字段的get方法
Method mathGet = p.getReadMethod();
mathGet.setAccessible(true);
Object age = mathGet.invoke(b);*/
BeanInfo beanInfo = Introspector.getBeanInfo(b.getClass());
PropertyDescriptor[] mathAll = beanInfo.getPropertyDescriptors();
Object value = null;
for(PropertyDescriptor pd:mathAll){
if(pd.getName().equals(name)){
Method mathGet = pd.getReadMethod();
mathGet.setAccessible(true);
value = mathGet.invoke(b);
}
}
return value;
}
}
测试JavaBean
/**
* 测试的JavaBean
* @author litaolin
* @version 2018-04-23
*/
public class CapFilesDemo extends DataEntity<CapFilesDemo> {
private static final long serialVersionUID = 1L;
@LogCompar(name="文件名")
private String filename; // 文件名
@LogCompar(name="地址")
private String filesrc; // 文件地址
@LogCompar(name="日期验证",dateFormat="yyyy年MM月dd日")
private Date sd;
public CapFilesDemo() {
super();
}
public CapFilesDemo(String id){
super(id);
}
@Length(min=0, max=64, message="文件名长度必须介于 0 和 64 之间")
public String getFilename() {
return filename;
}
public void setFilename(String filename) {
this.filename = filename;
}
@Length(min=0, max=500, message="文件地址长度必须介于 0 和 500 之间")
public String getFilesrc() {
return filesrc;
}
public void setFilesrc(String filesrc) {
this.filesrc = filesrc;
}
public Date getSd() {
return sd;
}
public void setSd(Date sd) {
this.sd = sd;
}
}
补充一下: 这个就是一个普通的javabean类,上次写文档的时候没有加,这次补上
/**
* 用于保存自定义注解中解析后的数据
*
*/
public class LogValue {
/**
* 中文名称
*/
private String name;
/**
* 属性值
*/
private Object value;
/**
* 日期格式化
*/
private String format;
public LogValue(String name, Object value,String format) {
super();
this.name = name;
this.value = value;
this.format=format;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Object getValue() {
return value;
}
public void setValue(Object value) {
this.value = value;
}
public String getFormat() {
return format;
}
public void setFormat(String format) {
this.format = format;
}
}
执行结果
[日期验证]发生变化由原先的:2018年10月12日改变为:2018年10月15日
[文件名]发生变化由原先的:我操改变为:我
说明执行成功