Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.fhs.trans.service.impl.AutoTransService Maven / Gradle / Ivy
package com.fhs.trans.service.impl;
import com.fhs.cache.service.RedisCacheService;
import com.fhs.common.spring.AnnotationTypeFilterBuilder;
import com.fhs.common.spring.SpringClassScanner;
import com.fhs.common.utils.CheckUtils;
import com.fhs.common.utils.ConverterUtils;
import com.fhs.core.trans.anno.AutoTrans;
import com.fhs.core.trans.anno.Trans;
import com.fhs.core.trans.constant.TransType;
import com.fhs.core.trans.util.ReflectUtils;
import com.fhs.core.trans.vo.VO;
import com.fhs.trans.listener.TransMessageListener;
import com.fhs.trans.service.AutoTransable;
import lombok.Data;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.util.StringUtils;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/**
* 本接类使用需要配合Autotrans 注解和autoTransAble的实现类
*
* @Description: 自动翻译服务
* @Author: Wanglei
* @Date: Created in 10:14 2019/10/15
*/
public class AutoTransService implements ITransTypeService, InitializingBean, ApplicationListener {
public static final Logger LOGGER = LoggerFactory.getLogger(AutoTransService.class);
/**
* 翻译数据缓存map
*/
private Map> localTransCacheMap = new HashMap<>();
/**
* 缓存 默认时间:半个小时
*/
private RedisCacheService> redisTransCache;
/**
* 基础服务
*/
private Map baseServiceMap = new HashMap<>();
/**
* 配置
*/
private Map transSettMap = new ConcurrentHashMap<>();
/**
* 如果直接去表里查询,放到这个cache中
*/
private ThreadLocal>> threadLocalCache = new ThreadLocal<>();
/**
* 翻译字段配置map
*/
private Map transFieldSettMap = new HashMap<>();
@Override
public void transOne(VO obj, List toTransList) {
Trans tempTrans = null;
for (Field tempField : toTransList) {
TransFieldSett transFieldSett = transFieldSettMap.containsKey(tempField) ? transFieldSettMap.get(tempField) : new TransFieldSett(tempField);
tempTrans = transFieldSett.getTrans();
String namespace = transFieldSett.getNamespace();
String alias = transFieldSett.getAlias();
if (transSettMap.containsKey(namespace) && CheckUtils.isNullOrEmpty(alias)) {
alias = transSettMap.get(namespace).defaultAlias();
}
String pkey = ConverterUtils.toString(ReflectUtils.getValue(obj, tempField.getName()));
if (StringUtils.isEmpty(pkey)) {
continue;
}
Map transCache = null;
// 主键可能是数组
pkey = pkey.replace("[", "").replace("]", "");
if (pkey.contains(",")) {
String[] pkeys = pkey.split(",");
transCache = new LinkedHashMap<>();
Map tempTransCache = null;
for (String tempPkey : pkeys) {
tempTransCache = getTempTransCacheMap(namespace, tempPkey);
if (tempTransCache == null || tempTransCache.isEmpty()) {
LOGGER.warn("auto trans缓存未命中:" + namespace + "_" + tempPkey);
continue;
}
// 比如学生表 可能有name和age 2个字段
for (String key : tempTransCache.keySet()) {
transCache.put(key, transCache.containsKey(key) ? transCache.get(key) + "," + tempTransCache.get(key) : tempTransCache.get(key));
}
}
} else {
//如果不是多个保证原汁原味 的原来的什么类型就是什么类型 而不是string
transCache = getTempTransCacheMap(namespace, ReflectUtils.getValue(obj, tempField.getName()));
if (transCache == null || transCache.isEmpty()) {
LOGGER.warn("auto trans缓存未命中:" + namespace + "_" + pkey);
continue;
}
}
setRef(tempTrans, obj, transCache);
Map transMap = obj.getTransMap();
if (transMap == null) {
continue;
}
if (!CheckUtils.isNullOrEmpty(alias)) {
Map tempMap = new HashMap<>();
Set keys = transCache.keySet();
for (String key : keys) {
tempMap.put(alias + key.substring(0, 1).toUpperCase() + key.substring(1), transCache.get(key));
}
transCache = tempMap;
}
Set keys = transCache.keySet();
for (String key : keys) {
if (CheckUtils.isNullOrEmpty(transMap.get(key))) {
transMap.put(key, ConverterUtils.toString(transCache.get(key)));
}
}
}
}
@Override
public void transMore(List extends VO> objList, List toTransList) {
threadLocalCache.set(new HashMap<>());
// 根据namespace 把字段分组
Map> namespaceFieldsGroupMap = new HashMap<>();
for (Field tempField : toTransList) {
tempField.setAccessible(true);
Trans tempTrans = tempField.getAnnotation(Trans.class);
String namespace = tempTrans.key();
// 如果是 good#student 翻译出来应该是 goodStuName goodStuAge customer#customer customerName
if (namespace.contains("#")) {
namespace = namespace.substring(0, namespace.indexOf("#"));
}
if (!this.baseServiceMap.containsKey(namespace)) {
LOGGER.warn("namesapce对应的service没有标记autotrans:" + namespace);
continue;
}
AutoTrans autoTransSett = this.transSettMap.get(namespace);
if (autoTransSett.useCache()) {
continue;
}
List fields = namespaceFieldsGroupMap.containsKey(namespace) ? namespaceFieldsGroupMap.get(namespace) : new ArrayList<>();
fields.add(tempField);
namespaceFieldsGroupMap.put(namespace, fields);
}
// 由于一些表数据比较多,所以部分数据不是从缓存取的,是从db先放入缓存的,翻译完了释放掉本次缓存的数据
for (String namespace : namespaceFieldsGroupMap.keySet()) {
final Set ids = new HashSet<>();
final List fields = namespaceFieldsGroupMap.get(namespace);
objList.forEach(obj -> {
for (Field field : fields) {
try {
Object tempId = field.get(obj);
if (CheckUtils.isNotEmpty(tempId)) {
String pkey = ConverterUtils.toString(tempId).replace("[", "").replace("]", "");
if (pkey.contains(",")) {
String[] pkeys = pkey.split(",");
for (String id : pkeys) {
ids.add(id);
}
} else {
ids.add(tempId);
}
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
});
if (!ids.isEmpty()) {
if (transSettMap.get(namespace).globalCache()) {
Set newIds = initLocalFromGlobalCache(threadLocalCache, ids, namespace, TransType.AUTO_TRANS);
ids.clear();
ids.addAll(newIds);
}
if(!ids.isEmpty()){
AutoTrans autoTransSett = this.transSettMap.get(namespace);
if (autoTransSett.useCache()) {
continue;
}
List extends VO> dbDatas = findByIds(() -> {
return baseServiceMap.get(namespace).selectByIds(new ArrayList<>(ids));
}, null);
for (VO vo : dbDatas) {
threadLocalCache.get().put(namespace + "_" + vo.getPkey(), createTempTransCacheMap(vo, autoTransSett));
}
}
}
}
objList.forEach(obj -> {
this.transOne(obj, toTransList);
});
threadLocalCache.set(null);
}
@Override
public void afterPropertiesSet() throws Exception {
TransService.registerTransType(TransType.AUTO_TRANS, this);
TransMessageListener.regTransRefresher(TransType.AUTO_TRANS, this::refreshCache);
}
public void init(ApplicationReadyEvent event) {
//spring容器初始化完成之后,就会自行此方法。
ConfigurableApplicationContext context = event.getApplicationContext();
Map beans = context.getBeansWithAnnotation(AutoTrans.class);
for (Object baseService : beans.values()) {
if (!(baseService instanceof AutoTransable)) {
continue;
}
Class> baseServiceClass = baseService.getClass();
AutoTrans autoTransSett = AnnotationUtils.findAnnotation(baseServiceClass, AutoTrans.class);
this.baseServiceMap.put(autoTransSett.namespace(), (AutoTransable) baseService);
this.transSettMap.put(autoTransSett.namespace(), autoTransSett);
}
new Thread(() -> {
Thread.currentThread().setName("refresh auto trans cache");
refreshCache(new HashMap<>());
}).start();
}
/**
* 刷新缓存
*
* @param messageMap 消息
*/
public void refreshCache(Map messageMap) {
//这里必须能拿到namespace 拿不到,就当作全部刷新
String namespace = messageMap.get("namespace") != null ?
messageMap.get("namespace").toString() : null;
if (namespace == null) {
Set namespaceSet = this.transSettMap.keySet();
namespaceSet.forEach(temp -> {
refreshOneNamespace(temp);
});
} else {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
LOGGER.error("刷新缓存错误:", e);
}
refreshOneNamespace(namespace);
}
}
/**
* 刷新一个namespace下的所有的缓存
*
* @param namespace namespace
*/
public void refreshOneNamespace(String namespace) {
if (!this.transSettMap.containsKey(namespace)) {
LOGGER.info("本系统无需刷新此缓存namespace:" + namespace);
return;
}
AutoTrans autoTrans = this.transSettMap.get(namespace);
//不适用缓存的不做缓存
if (!autoTrans.useCache()) {
return;
}
LOGGER.info("开始刷新auto-trans缓存:" + namespace);
List vos = this.baseServiceMap.get(namespace).select();
if (vos == null || vos.isEmpty()) {
return;
}
Object pkeyVal = null;
String fielVal = null;
Map tempCacheTransMap = null;
VO po = null;
for (int i = 0; i < vos.size(); i++) {
po = vos.get(i);
pkeyVal = po.getPkey();
//如果使用redis则给redis放
if (autoTrans.useRedis()) {
this.getRedisTransCache().put(namespace + "_" + pkeyVal, createTempTransCacheMap(po, autoTrans));
} else {
//否则给本地缓存放
localTransCacheMap.put(namespace + "_" + pkeyVal, createTempTransCacheMap(po, autoTrans));
}
}
LOGGER.info("刷新auto-trans缓存完成:" + namespace);
}
public RedisCacheService> getRedisTransCache() {
if (this.redisTransCache == null) {
throw new IllegalArgumentException("请确定开启了easy-tran.is-enable-redis 为true,springBoot启动已经完成");
}
return this.redisTransCache;
}
/**
* 创建一个临时缓存map
*
* @param po po
* @param autoTrans 配置
* @return
*/
private Map createTempTransCacheMap(VO po, AutoTrans autoTrans) {
Object fielVal = null;
Map tempCacheTransMap = new LinkedHashMap<>();
if (po == null) {
return tempCacheTransMap;
}
for (String field : autoTrans.fields()) {
fielVal = ReflectUtils.getValue(po, field);
tempCacheTransMap.put(field, fielVal);
}
if (autoTrans.globalCache()) {
put2GlobalCache(tempCacheTransMap, autoTrans.isAccess(), autoTrans.cacheSeconds(), autoTrans.maxCache(), po.getPkey(),
autoTrans.namespace(), TransType.AUTO_TRANS);
}
return tempCacheTransMap;
}
private Map getTempTransCacheMapSource(String namespace, Object pkey) {
AutoTrans autoTrans = this.transSettMap.get(namespace);
//如果内存缓存中有,则优先用内存缓存
if (localTransCacheMap.containsKey(namespace + "_" + pkey)) {
return localTransCacheMap.get(namespace + "_" + pkey);
} else if (this.transSettMap.get(namespace).globalCache() && getFromGlobalCache(pkey, namespace, TransType.AUTO_TRANS) != null) {
return getFromGlobalCache(pkey, namespace, TransType.AUTO_TRANS);
}
//如果注解为空,代表可能是其他的服务提供的翻译,尝试去redis获取缓存
else if (autoTrans == null || autoTrans.useRedis()) {
Map redisCacheResult = this.getRedisTransCache().get(namespace + "_" + pkey);
//如果获取到了返回
if (redisCacheResult != null) {
return redisCacheResult;
}
//redis获取不到返回空map
return new HashMap<>();
} else {
//如果强调使用缓存,则可能是还没刷新进来,直接返回空map,前端在刷新一下就好了
if (autoTrans.useCache()) {
return new HashMap<>();
}
if (this.threadLocalCache.get() == null) {
if (CheckUtils.isNullOrEmpty(pkey)) {
return new HashMap<>();
}
VO vo = findById(() -> {
return this.baseServiceMap.get(namespace).selectById(pkey);
}, null);
return createTempTransCacheMap(vo, autoTrans);
}
return this.threadLocalCache.get().get(namespace + "_" + pkey);
}
}
/**
* 获取用于翻译的缓存
*
* @param namespace namespace
* @param pkey 主键
* @return 缓存
*/
private Map getTempTransCacheMap(String namespace, Object pkey) {
Map transCache = getTempTransCacheMapSource( namespace, pkey);
if(transCache == null || transCache.isEmpty()){
return transCache;
}
Map transCacheResult = new LinkedHashMap<>(transCache);
return transCacheResult;
}
/**
* 翻译单个的key
*
* @param namespace namespace
* @param pkeyVal 主键
* @return
*/
public String transKey(String namespace, String pkeyVal) {
Map tempCacheTransMap = localTransCacheMap.get(namespace + "_" + pkeyVal);
if (tempCacheTransMap == null) {
LOGGER.error("auto trans缓存未命中:" + namespace + "_" + pkeyVal);
} else {
for (String key : tempCacheTransMap.keySet()) {
return ConverterUtils.toString(tempCacheTransMap.get(key));
}
}
return null;
}
/**
* 类扫描器
*
* @param annotationClass 注解
* @param packageNames 包
* @return 符合条件的类
*/
public static Set> scan(Class extends Annotation> annotationClass, String[] packageNames) {
TypeFilter entityFilter = AnnotationTypeFilterBuilder.build(annotationClass);
SpringClassScanner entityScanner = new SpringClassScanner.Builder().typeFilter(entityFilter).build();
for (String packageName : packageNames) {
entityScanner.getScanPackages().add(packageName);
}
Set> entitySet = null;
try {
entitySet = entityScanner.scan();
} catch (ClassNotFoundException | IOException e) {
LOGGER.error("包扫描错误", e);
// log or throw runTimeExp
throw new RuntimeException(e);
}
return entitySet;
}
@Override
public void onApplicationEvent(ApplicationReadyEvent event) {
init(event);
}
public void setRedisTransCache(RedisCacheService> redisTransCache) {
this.redisTransCache = redisTransCache;
}
public void regTransable(AutoTransable transAble, AutoTrans autoTransSett) {
this.baseServiceMap.put(autoTransSett.namespace(), transAble);
this.transSettMap.put(autoTransSett.namespace(), autoTransSett);
}
@Override
public void reset() {
threadLocalCache.set(null);
}
}
/**
* 被翻译的字段的实体
*/
@Data
class TransFieldSett {
/**
* trans注解
*/
private Trans trans;
/**
* 命名空间
*/
private String namespace;
/**
* 别名
*/
String alias;
public TransFieldSett(Field transField) {
transField.setAccessible(true);
trans = transField.getAnnotation(Trans.class);
namespace = trans.key();
// 如果是 good#student 翻译出来应该是 goodStuName goodStuAge customer#customer customerName
if (namespace.contains("#")) {
alias = namespace.substring(namespace.indexOf("#") + 1);
namespace = namespace.substring(0, namespace.indexOf("#"));
}
//如果别名不等于空不等于null的话则使用此别名
if (trans.alias() != null && (!"".equals(trans.alias()))) {
alias = trans.alias();
}
}
}