Spring AOP实际应用之:根据时区全局修改用户时间

背景

国际版项目,每个用户有自己的时区,数据库存储的时间固定是东八区,现在需要根据用户设置的时区显示时间

解决方案很多,这里的方案是 实体类增加时间格式化属性(赋值,前端显示),使用 Spring AOP 全局修改返回对象属性值

实体类可以不加属性,直接修改原来的Date
有个问题是Date类型的数据以时间戳的形式返回给前端,时间戳会根据浏览器客户端时区的改变而改变
所以需要后端定义一个String类型格式化时间,前端显示String

一:实体类增加格式化属性
在这里插入图片描述

二:自定义注解
package com.sendcloud.marketing.api.util;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.springframework.web.bind.annotation.Mapping;

@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface HandleDateField {
	
	String[]  dateFiled();
}
三:AOP修改时间
package com.sendcloud.marketing.api.util;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

import com.sendcloud.marketing.api.controller.BaseController;
import com.sendcloud.marketing.api.core.R;
import com.sendcloud.marketing.common.util.DateUtils;
import com.sendcloud.marketing.model.UserInfo;
import com.sendcloud.marketing.service.UserInfoService;
import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

@Aspect
@Component
public class ChangeDateForTimeZoneAspect {
	@Autowired
	private UserInfoService userInfoService;

	@Resource(name = "redisTemplateObject")
	private RedisTemplate<Object,Object> redisTemplate;

	private Object changObjectValue(Object _obj, Set<String> propertySet,
			String timeZoneId) throws Exception {
		Class<?> resultClz = _obj.getClass();
		for(String property:propertySet){
			Field field = getField(resultClz, property);
			if(field!=null){
				//权限,允许修改
				field.setAccessible(true);
				//获得数据库中的属性值
				Object fieldValue = field.get(_obj);
				if(StringUtils.isNotBlank(ObjectUtils.toString(fieldValue))){
					//根据用户时区转换后的时间
					Date date = DateUtils.timeZoneTransfer((Date)fieldValue, 8, Integer.valueOf(timeZoneId));
					String formatDate = DateUtils.format(date, "yyyy-MM-dd HH:mm:ss");
					//反射修改对象属性
					setField(resultClz,field,_obj,formatDate);
				}
			}
		}
		return _obj;
	}

	/*
		反射获取对象
		若当前对象没有field属性,向父类找,直到没有父类
	 */
	public Field getField(Class<?> clazz,String field){
		//没有父类时结束
		if (clazz == null)
			return null;
		try {
			return clazz.getDeclaredField(field);
		} catch (NoSuchFieldException e) {
			//方法重载
			return getField(clazz.getSuperclass(), field);
		}
	}

	/*
		反射修改对象属性
		set 父类只找一次
	 */
	public Field setField(Class<?> clazz,Field field,Object _obj,String formatDate) throws Exception{
		//验证参数
		if (clazz == null)
			return null;
		try {
			Field f = clazz.getDeclaredField(field.getName() + "Fmt");
			f.setAccessible(true);
			f.set(_obj,formatDate);
			return f;
		} catch (NoSuchFieldException e) {
			Field f = clazz.getSuperclass().getDeclaredField(field.getName() + "Fmt");
			f.setAccessible(true);
			f.set(_obj,formatDate);
			return f;
		}
	}

	@Around(value = "@annotation(com.sendcloud.marketing.api.util.HandleDateField)")
	public Object hanle(ProceedingJoinPoint joinPoint) throws Throwable {

		MethodSignature methodSignature = (MethodSignature) joinPoint
				.getSignature();
		Method method = methodSignature.getMethod();
		HandleDateField ananHandleDateField = method
				.getAnnotation(HandleDateField.class);

		String[] propertys = ananHandleDateField.dateFiled();
		Set<String> propertySet = new HashSet<String>();
		if (propertys != null) {
			for (String e : propertys) {

				propertySet.add(e);
			}

		}

		HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder
				.getRequestAttributes()).getRequest();
		Integer userId =(Integer) redisTemplate.opsForValue().get(BaseController.CONTACT_API_TOKEN_PREFIX + request.getHeader("token"));
		UserInfo userInfo = userInfoService.findUserInfo(userId);

		Object returnValue = joinPoint.proceed();
		if(StringUtils.isEmpty(userInfo.getTimeZone())){
			return returnValue;
		}
		
		//R为自定义的返回对象
		if (returnValue instanceof R) {
			R r = (R) returnValue;
			Map<String, ?> map = r.getData();
			for (Object obj : map.values()) {
				//若接口返回的数据类型为对象
				if(obj instanceof Object){
					changObjectValue(obj,propertySet,userInfo.getTimeZone());
				}
				//若接口返回的数据类型为集合类型
				if (obj instanceof List) {
					List<Object> objList = (List<Object>) obj;
					for (Object _obj : objList) {
						 changObjectValue(_obj,propertySet,userInfo.getTimeZone());
					}
				}
			}

		}

		return returnValue;
	}

}
时区转换方法
    /**
     * 时区转换
     * @param dateTime 日期时间
     * @param targetTimeZone 目标时区 +8,0,+9,-1 等等
     * @return
     */
    public static Date timeZoneTransfer(Date dateTime, int nowTimeZone,int targetTimeZone){
        if (dateTime == null) {
            return null;
        }
        String time = format(dateTime);
        //转换位timezone可以识别的格式
        String tTimezone = "";
        String nTimezone = "";
        tTimezone = getTimeZone(targetTimeZone, tTimezone);
        nTimezone = getTimeZone(nowTimeZone, nTimezone);
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(DATE_SEC_STYLE);
        //默认中国时区
        simpleDateFormat.setTimeZone(TimeZone.getTimeZone("GMT" + nTimezone));
        Date date;
        try {
            date = simpleDateFormat.parse(time);
        } catch (ParseException e) {
            e.printStackTrace();
            return null;
        }
        simpleDateFormat.setTimeZone(TimeZone.getTimeZone("GMT" + tTimezone));
        try {
            dateTime = format(simpleDateFormat.format(date),DATE_SEC_STYLE);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return dateTime;
    }
四:controller 层接口添加注解

在这里插入图片描述

五:测试
注释自定义注解,fmt属性为null
添加自定义注解后,成功写入值
测试通过

在这里插入图片描述

在这里插入图片描述

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值