注意 :GMT与Etc/GMT不是一个意思
传统ssm项目增加时区功能
实现思路:
1. 项目中的时间和数据库存储的时间一致,可以为任一指定时区;
2. mybatis实现对返回实体类中的属性拦截和修改;
方式选型:
1. aop拦截,常用于拦截到方法,对于不会注入spring容器的返回entity(返回实体一般不会注入到spring容器里面)无法拦截;
2. 自定义注解拦截,自定义注解用的是aop的机制拦截的注解,所以没办法修改;
3. mybatis拦截器可以拦截所有关于数据库的操作,然后利用反射机制获取返回数据,并对对应字段值修改;
实现步骤:
1. 写一个mybatis拦截器
package xxxx;
import com.dfocus.gateway.server.common.utils.TimeZoneUtils;
import com.dfocus.gateway.server.enums.TimeTransType;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
@Intercepts(
@Signature(method = "query",
type = Executor.class,
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}
)
)
@Slf4j
public class TimeZoneInterceptor implements Interceptor {
@Autowired
RedisTemplate redisTemplate;
/**
* 拦截器拦截操作
* @param invocation
* @return
*/
@Override
public Object intercept(Invocation invocation) {
Object proceed = null;
try {
//获取操作对象
proceed = invocation.proceed();
MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
/**
* 下面这些值(返回值类型、返回实力类...)可以在上一行打个断点看一下具体代表什么,
* 然后根据自己的需要获取到方法名或者返回实体类进行拦截和修改
*/
//获取返回值类型
String returnType = invocation.getMethod().getReturnType().getName();
//获取返回实体类
String resultMap = mappedStatement.getResultMaps().get(0).getType().getName();
//方法路径和名称
String methodPathName = mappedStatement.getId();
//注解中method的值
String methodName = invocation.getMethod().getName();
//sql类型
SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();
TimeTransType timeTransType = TimeTransType.find(returnType,resultMap,methodPathName);
if (timeTransType != null){
if ("query".equals(methodName)) {
//对有要求的字段填值
if (SqlCommandType.SELECT.equals(sqlCommandType)) {
switch (timeTransType){
case TYPE1:
List<Object> list = (List<Object>) proceed;
for (Object obj : list) {
//获取gatewaysn
String gatewaySn;
try {
gatewaySn = (String) getFieldValue(obj,"gatewaySN");
}catch (Exception e){
log.info("[时区时间转换获取gatewaySn数据出错]");
break;
}
if (StringUtils.isEmpty(gatewaySn)){
throw new NullPointerException();
}
String timeZoneId = getGatewayTimeZoneId(gatewaySn);
Map<String,Object> l = new HashMap<>();
l.put("createTime",String.class);
l.put("updateTime",String.class);
transTime(obj,timeZoneId,l);
}
break;
}
log.info("[时区时间转换成功]timezone transform success!");
}
}
}
} catch (Exception e) {
log.error("[时区实践转换失败]timezone transform error",e);
e.printStackTrace();
}
return proceed;
}
/**
* @param target 表示被拦截的对象,此处为 Executor 的实例对象
* 作用:如果被拦截对象所在的类有实现接口,就为当前拦截对象生成一个代理对象
* 如果被拦截对象所在的类没有指定接口,这个对象之后的行为就不会被代理操作
* @return
*/
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
}
/**
* 查询所属时区
* @param gatewaySn
* @return
*/
public String getGatewayTimeZoneId(String gatewaySn) {
String tzId = "UTC";
try {
//此处实现获取timeZoneId
}
} catch (Exception ex) {
log.error("获取时区timeZoneId错误,默认返回UTC时间,ex:{}", ex);
}
return tzId;
}
/**
* 反射取值
* @param obj
* @param field
* @return
* @throws Exception
*/
public Object getFieldValue(Object obj,String field) throws Exception{
Field fieldName = obj.getClass().getDeclaredField(field);
fieldName.setAccessible(true);
return fieldName.get(obj);
}
/**
* 反射设值
* @param obj
* @param field
* @param value
* @throws Exception
*/
public void setFieldValue(Object obj,String field,Object value) throws Exception{
Field[] fields = obj.getClass().getDeclaredFields();
for (Field fld : fields) {
if (field.equals(fld.getName())) {
fld.setAccessible(true);
fld.set(obj, value);
}
}
}
public void transTime(Object obj,String timeZoneId,Map<String,Object> map) throws Exception{
for (Map.Entry<String, Object> entry : map.entrySet()) {
if (entry.getValue().equals(String.class) ){
String originTime = (String) getFieldValue(obj,entry.getKey());
if (StringUtils.isEmpty(originTime)){
setFieldValue(obj,entry.getKey(),"");
continue;
}
String transTime = TimeZoneUtils.string2TimezoneDefault(originTime,timeZoneId);
setFieldValue(obj,entry.getKey(),transTime);
}
}
}
}
2.写一个enum,来自定义拦截的方法或者对象
package com.dfocus.gateway.server.enums;
public enum TimeTransType {
/**
* 返回时间拦截
*/
TYPE1("java.util.List","com.dfocus.gateway.server.module.server.base.pojo","*");
private String returnType;
private String resultMap;
private String methodPathName;
TimeTransType(String returnType, String resultMap, String methodPathName){
this.returnType = returnType;
this.resultMap = resultMap;
this.methodPathName = methodPathName;
}
public String getReturnType() {
return returnType;
}
public void setReturnType(String returnType) {
this.returnType = returnType;
}
public String getResultMap() {
return resultMap;
}
public void setResultMap(String resultMap) {
this.resultMap = resultMap;
}
public String getMethodPathName() {
return methodPathName;
}
public void setMethodPathName(String methodPathName) {
this.methodPathName = methodPathName;
}
public static TimeTransType find(String returnType,String resultMap,String methodPathName) {
for (TimeTransType timeTransType : TimeTransType.values()) {
if (timeTransType.returnType.equals(returnType)
&& timeTransType.resultMap.equals(resultMap)){
if ("*".equals(timeTransType.methodPathName)
|| timeTransType.methodPathName.equals(methodPathName)){
return timeTransType;
}
}
}
return null;
}
}