【开源项目】Easy-Trans数据翻译服务的快速入门及原理解析

news/2024/11/19 18:30:07/

项目介绍

easy-trans是一款用于做数据翻译的代码辅助插件,利用mybatis plus/jpa/beetsql 等ORM框架的能力自动查表,让开发者可以快速的把id/字典码 翻译为前端需要展示的数据。

快速入门

maven依赖

    <properties> <fhs.release.version>2.2.1-M1</fhs.release.version></properties>        <dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><dependency><groupId>com.fhs-opensource</groupId><artifactId>easy-trans-spring-boot-starter</artifactId><version>${fhs.release.version}</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><dependency><groupId>com.fhs-opensource</groupId><artifactId>easy-trans-mybatis-plus-extend</artifactId><version>${fhs.release.version}</version></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.2</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency>

application.yml

spring:#数据库配置datasource:url: jdbc:mysql://127.0.0.1:3306/charles?useUnicode=true&useSSL=false&characterEncoding=utf-8username: rootpassword: rootdriver-class-name: com.mysql.jdbc.Drivereasy-trans:#启用redis缓存 如果不用redis请设置为falseis-enable-redis: true#启用全局翻译(拦截所有responseBody进行自动翻译),如果关闭需要手动调用翻译方法或者方法加注解,具体看文档is-enable-global: true#启用平铺模式is-enable-tile: false#字典缓存放到redis 微服务模式请开启dict-use-redis: true# ruoyi相关的请开启is-enable-map-result: true

定义实体类

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class StudentVo implements TransPojo {private Long id;@Trans(type = TransType.DICTIONARY, key = "sex")private Integer sex;@Trans(type = TransType.DICTIONARY, key = "disable", ref = "disableName")private boolean disable;private String disableName;
}

测试效果

@RestController
public class TestController implements InitializingBean {@Autowiredprivate DictionaryTransService dictionaryTransService;@RequestMapping("/t1")public StudentVo t1() {return StudentVo.builder().sex(1).disable(false).build();}@Overridepublic void afterPropertiesSet() throws Exception {//字典国际化支持Map<String, String> transMap = new HashMap<>();// 如果不要国际化则是  ransMap.put("0","男");  transMap.put("1","女");transMap.put("0", "男");transMap.put("1", "女");transMap.put("2", "TS");dictionaryTransService.makeUseRedis();dictionaryTransService.refreshCache("sex", transMap);transMap = new HashMap<>();transMap.put("true", "禁用");transMap.put("false", "启用");dictionaryTransService.refreshCache("disable", transMap);transMap = new HashMap<>();// 如果不要国际化则是  ransMap.put("0","男");  transMap.put("1","女");transMap.put("610100", "西安");dictionaryTransService.refreshCache("address", transMap);}
}

不选用平铺(is-enable-tile=false):{"id":null,"sex":1,"disable":false,"disableName":"启用","transMap":{"sexName":"女"}}

选用平铺(is-enable-tile=true):{"id":null,"sex":1,"disable":false,"disableName":"启用","sexName":"女"}

原理解析

切面拦截

TransMethodResultAop拦截处理带有TransMethodResult注解的方法。

@Slf4j
@Aspect
public class TransMethodResultAop implements InitializingBean {/*** 开启平铺模式*/@Value("${easy-trans.is-enable-tile:false}")private Boolean isEnableTile;/*** 支持vo包装类是map*/@Value("${easy-trans.is-enable-map-result:false}")private Boolean isEnableMapResult;@Autowiredprivate TransService transService;@Around("@annotation(com.fhs.core.trans.anno.TransMethodResult)")public Object transResult(ProceedingJoinPoint joinPoint) throws Throwable {Object proceed = null;Set<String> includeFields = null;Set<String> excludeFields = null;try {proceed = joinPoint.proceed();//1.获取用户行为日志(ip,username,operation,method,params,time,createdTime)//获取类的字节码对象,通过字节码对象获取方法信息Class<?> targetCls = joinPoint.getTarget().getClass();//获取方法签名(通过此签名获取目标方法信息)MethodSignature ms = (MethodSignature) joinPoint.getSignature();//获取目标方法上的注解指定的操作名称Method targetMethod =targetCls.getDeclaredMethod(ms.getName(),ms.getParameterTypes());if (targetMethod.isAnnotationPresent(TransSett.class)) {TransSett transSett = targetMethod.getAnnotation(TransSett.class);if (transSett.include().length != 0) {includeFields = new HashSet<>(Arrays.asList(transSett.include()));}else{excludeFields = new HashSet<>(Arrays.asList(transSett.exclude()));}}} catch (Throwable e) {throw e;}try {return TransUtil.transOne(proceed, transService, isEnableTile, new ArrayList<>(),includeFields,excludeFields);} catch (Exception e) {log.error("翻译错误", e);}return proceed;}@Overridepublic void afterPropertiesSet() throws Exception {if (isEnableMapResult) {TransUtil.transResultMap = true;}}
}

TransService#trans,获取ClassInfo,遍历transTypeServiceMap的键,获取对应的ITransTypeService,进行翻译。

    private void trans(List<? extends VO> objList, VO obj, Set<String> includeFields, Set<String> excludeFields) {ClassInfo info = ClassManager.getClassInfoByName(obj != null ? obj.getClass() : ((VO)objList.get(0)).getClass());if (info.getTransTypes() != null) {Set<String> transTypes = new HashSet(Arrays.asList(info.getTransTypes()));List<Field> tempTransFieldList = null;List<Field> transFieldList = null;Iterator var9 = transTypeServiceMap.keySet().iterator();while(var9.hasNext()) {String type = (String)var9.next();if (transTypes.contains(type)) {tempTransFieldList = info.getTransField(type);if (includeFields != null) {transFieldList = (List)tempTransFieldList.stream().filter((field) -> {return includeFields.contains(field.getName());}).collect(Collectors.toList());} else if (excludeFields != null) {transFieldList = (List)tempTransFieldList.stream().filter((field) -> {return !excludeFields.contains(field.getName());}).collect(Collectors.toList());} else {transFieldList = tempTransFieldList;}if (transFieldList != null && transFieldList.size() != 0) {transFieldList.sort(new Comparator<Field>() {public int compare(Field o1, Field o2) {return ((Trans)o1.getAnnotation(Trans.class)).sort() - ((Trans)o2.getAnnotation(Trans.class)).sort();}});ITransTypeService transTypeService = (ITransTypeService)transTypeServiceMap.get(type);if (ObjectUtils.isEmpty(transTypeService)) {logger.warn("没有匹配的转换器:" + type);} else if (objList != null) {transTypeService.transMore(objList, transFieldList);} else {transTypeService.transOne(obj, transFieldList);}}}}}}

AutoTransService#afterPropertiesSet,实现了ITransTypeService的服务在afterPropertiesSet方法中会进行注入

    public void afterPropertiesSet() throws Exception {TransService.registerTransType("auto", this);TransMessageListener.regTransRefresher("auto", this::refreshCache);}

TransService#registerTransType,注入翻译服务。

public class TransService {private static Logger logger = LoggerFactory.getLogger(TransService.class);private static Map<String, ITransTypeService> transTypeServiceMap = new LinkedHashMap();public TransService() {}public static void registerTransType(String type, ITransTypeService transTypeService) {transTypeServiceMap.put(type, transTypeService);}
}

加载类信息

ClassManager#getClassInfoByName,根据类信息加载ClassInfo

    public static ClassInfo getClassInfoByName(Class<?> clazz) {ClassInfo temp = (ClassInfo)CACHE.get(clazz.getName());ClassInfo info = null;if (null == temp) {try {temp = new ClassInfo(clazz);} catch (IllegalAccessException | InstantiationException var5) {LOGGER.error(clazz.getName() + "生成classinfo错误", var5);throw new ParamException(clazz.getName() + "生成classinfo错误");}setClassInfoByName(clazz.getName(), temp);}try {info = new ClassInfo();BeanUtils.copyProperties(info, temp);} catch (Exception var4) {var4.printStackTrace();LOGGER.error(var4.getMessage());}return info;}

ClassInfo,获取到该类的所有属性,加载TransUnTrans属性的翻译类型到transTypeSetunTransTypeSet,以及该类型对应的字段加载到transFieldMapunTransFieldMap

    public <T> ClassInfo(Class<?> clazz) throws InstantiationException, IllegalAccessException {this.clazz = clazz;this.getClazzFieldMap();}private void getClazzFieldMap() throws InstantiationException, IllegalAccessException {List<Field> declaredFields = ReflectUtils.getAllField(this.clazz.newInstance());Set<String> transTypeSet = new HashSet();Set<String> unTransTypeSet = new HashSet();int mod = false;Iterator var5 = declaredFields.iterator();while(var5.hasNext()) {Field field = (Field)var5.next();int mod = field.getModifiers();if (!Modifier.isStatic(mod) && !Modifier.isFinal(mod) && !Modifier.isVolatile(mod)) {Trans trans = (Trans)field.getAnnotation(Trans.class);if (trans != null) {if (trans.type() == null) {LOGGER.warn("类 {} 属性 [{}] type为空。", this.clazz.getName(), field.getName());} else {transTypeSet.add(trans.type());List<Field> fieldList = (List)this.transFieldMap.get(trans.type());List<Field> fieldList = fieldList != null ? fieldList : new ArrayList();((List)fieldList).add(field);this.transFieldMap.put(trans.type(), fieldList);}}UnTrans untrans = (UnTrans)field.getAnnotation(UnTrans.class);if (untrans != null) {if (untrans.type() == null) {LOGGER.warn("类 {} 属性 [{}] type为空。", this.clazz.getName(), field.getName());} else {unTransTypeSet.add(untrans.type());List<Field> fieldList = (List)this.unTransFieldMap.get(untrans.type());List<Field> fieldList = fieldList != null ? fieldList : new ArrayList();((List)fieldList).add(field);this.unTransFieldMap.put(untrans.type(), fieldList);}}}}this.transTypes = new String[transTypeSet.size()];this.unTransTypes = new String[unTransTypeSet.size()];transTypeSet.toArray(this.transTypes);unTransTypeSet.toArray(this.unTransTypes);}

字典翻译

DictionaryTransService#transOne,根据实体数据和需要解析的字段。调用bothCacheService来获取数据。属性key是字典组的名称,dicCode是字典对应的值。

    @Overridepublic void transOne(VO obj, List<Field> toTransList) {Trans tempTrans = null;for (Field tempField : toTransList) {tempField.setAccessible(true);tempTrans = tempField.getAnnotation(Trans.class);String dicCodes = StringUtil.toString(ReflectUtils.getValue(obj, tempField.getName()));if(dicCodes.contains("[")){dicCodes = dicCodes.replace("[", "").replace("]", "");}if (dicCodes.contains(",")) {dicCodes = dicCodes.replace(" ", "");}String[] dicCodeArray = dicCodes.split(",");String key = tempTrans.key().contains("KEY_") ? StringUtil.toString(ReflectUtils.getValue(obj, tempTrans.key().replace("KEY_", ""))) : tempTrans.key();//sex_0/1  男 女List<String> dicCodeList = new ArrayList<>(1);for (String dicCode : dicCodeArray) {if (!StringUtil.isEmpty(dicCode)) {dicCodeList.add(bothCacheService.get(getMapKey(key.trim(), dicCode)));}}String transResult = dicCodeList.size() > Constant.ZERO ? StringUtil.getStrForIn(dicCodeList, false) : "";if (obj.getTransMap() != null && !setRef(tempTrans, obj, transResult)) {obj.getTransMap().put(tempField.getName() + "Name", transResult);}}}

DictionaryTransService#getMapKey,如果开启了国际化,需要添加this.localeGetter.getLanguageTag(),作为构建获取缓存数据key的一部分。

    public String getMapKey(String dictGroupCode, String dictCode) {return this.isOpenI18n ? dictGroupCode + "_" + dictCode + "_" + this.localeGetter.getLanguageTag() : dictGroupCode + "_" + dictCode;}

ITransTypeService#setRef(),如果该属性上的注解Trans,配置了ref或者refs的属性,修改实体数据该字段的属性值。

    default boolean setRef(Trans trans, VO vo, String val) {boolean isSetRef = false;if (CheckUtils.isNotEmpty(trans.ref())) {this.setValue(vo, trans.ref(), val);isSetRef = true;}if (CheckUtils.isNotEmpty(trans.refs())) {Stream.of(trans.refs()).forEach((ref) -> {this.setValue(vo, ref, val);});isSetRef = true;}return isSetRef;}

二级缓存

BothCacheService处理数据,是优先存放到ConcurrentHashMap,其次调用redisCacheService

@Data
public class BothCacheService<T> {/*** redis key前缀*/private static final String TRANS_PRE = "trans:";@Autowired(required = false)private RedisCacheService<T> redisCacheService;@Value("${easy-trans.dict-use-redis:false}")private boolean useRedis;/*** 用来放字典缓存的map*/private Map<String, T> localCacheMap = new ConcurrentHashMap<>();/*** 添加缓存** @param key       key* @param value     value* @param onlyLocal 是否只添加本地缓存*/public void put(String key, T value, boolean onlyLocal) {if (!onlyLocal && redisCacheService != null && useRedis) {redisCacheService.put(TRANS_PRE + key, value);}localCacheMap.put(key, value);}/*** 获取本地缓存** @param key key* @return value*/public T get(String key) {if (!StringUtil.isEmpty(key)) {if (localCacheMap.containsKey(key)) {T result = localCacheMap.get(key);return result;}if (redisCacheService != null && useRedis) {T result = redisCacheService.get(TRANS_PRE + key);if (Objects.nonNull(result)) {localCacheMap.put(key, result);}return result;}return null;}return null;}
}

数据库翻译

AutoTransService#getTempTransCacheMap,根据namespace和主键值,优先从缓存中获取,缓存数据获取不到,调用baseServiceMap里面的AutoTransable实现。

    private Map<String, Object> getTempTransCacheMap(String namespace, Object pkey) {AutoTrans autoTrans = (AutoTrans)this.transSettMap.get(namespace);if (this.localTransCacheMap.containsKey(namespace + "_" + pkey)) {return (Map)this.localTransCacheMap.get(namespace + "_" + pkey);} else if (((AutoTrans)this.transSettMap.get(namespace)).globalCache() && this.getFromGlobalCache(pkey, namespace, "auto") != null) {return this.getFromGlobalCache(pkey, namespace, "auto");} else if (autoTrans != null && !autoTrans.useRedis()) {if (autoTrans.useCache()) {return new HashMap();} else if (this.threadLocalCache.get() == null) {if (CheckUtils.isNullOrEmpty(pkey)) {return new HashMap();} else {VO vo = this.findById(() -> {return ((AutoTransable)this.baseServiceMap.get(namespace)).selectById(pkey);}, (String)null);return this.createTempTransCacheMap(vo, autoTrans);}} else {return (Map)((Map)this.threadLocalCache.get()).get(namespace + "_" + pkey);}} else {Map<String, Object> redisCacheResult = (Map)this.getRedisTransCache().get(namespace + "_" + pkey);return (Map)(redisCacheResult != null ? redisCacheResult : new HashMap());}}

ITransTypeService#findById

    default VO findById(Callable<VO> callable, String dataSourceName) {if (!TransConfig.MULTIPLE_DATA_SOURCES) {try {return (VO)callable.call();} catch (Exception var5) {Logger.error("", var5);return null;}} else {CompletableFuture cf = CompletableFuture.supplyAsync(() -> {try {if (!StringUtil.isEmpty(dataSourceName)) {TransConfig.dataSourceSetter.setDataSource(dataSourceName);}return (VO)callable.call();} catch (Exception var3) {Logger.error("", var3);return null;}});try {return (VO)cf.get();} catch (InterruptedException var6) {Logger.error("", var6);} catch (ExecutionException var7) {Logger.error("", var7);}return null;}}

AutoTransService#init,获取容器中带有AutoTrans注解中的类,存放AutoTrans注解上的namespace和该实例bean。

    public void init(ApplicationReadyEvent event) {ConfigurableApplicationContext context = event.getApplicationContext();Map<String, Object> beans = context.getBeansWithAnnotation(AutoTrans.class);Iterator var4 = beans.values().iterator();while(var4.hasNext()) {Object baseService = var4.next();if (baseService instanceof AutoTransable) {AutoTrans autoTransSett = (AutoTrans)baseService.getClass().getAnnotation(AutoTrans.class);this.baseServiceMap.put(autoTransSett.namespace(), (AutoTransable)baseService);this.transSettMap.put(autoTransSett.namespace(), autoTransSett);}}(new Thread(() -> {Thread.currentThread().setName("refresh auto trans cache");this.refreshCache(new HashMap());})).start();}

MybatisPlus适配

EasyTransMybatisPlusConfig,Mybaits适配配置。

@Configuration
public class EasyTransMybatisPlusConfig {/*** service的包路径*/@Value("${easy-trans.autotrans.package:com.*.*.service.impl}")private String packageNames;@Bean@ConditionalOnProperty(name = "easy-trans.is-enable-auto", havingValue = "true")public MybatisPlusTransableRegister mybatisPlusTransableRegister() {MybatisPlusTransableRegister result = new MybatisPlusTransableRegister();result.setPackageNames(packageNames);return result;}
}

MybatisPlusTransableRegister启动的时候,获取到包名下AutoTrans的所有类信息,注册到baseServiceMap

@Data
public class MybatisPlusTransableRegister implements ApplicationListener<ApplicationReadyEvent> {/*** service的包路径*/private String packageNames;@Autowiredprivate AutoTransService autoTransService;@Overridepublic void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {//spring容器初始化完成之后,就会自行此方法。Set<Class<?>> entitySet = AutoTransService.scan(AutoTrans.class, packageNames.split(";"));// 遍历所有class,获取所有用@autowareYLM注释的字段if (entitySet != null) {final List<String> namespaceList = new ArrayList<>();for (Class<?> entity : entitySet) {AutoTrans autoTransSett = entity.getAnnotation(AutoTrans.class);if (autoTransSett.ref() == VO.class || (!autoTransSett.ref().isAnnotationPresent(TableName.class))) {continue;}// 获取该类Object baseService = SpringContextUtil.getBeanByClass(entity);if ((baseService instanceof AutoTransable)) {continue;}namespaceList.add(autoTransSett.namespace());autoTransService.regTransable(new MybatisPlusTransableAdapter(autoTransSett.ref()), autoTransSett);}new Thread(() -> {Thread.currentThread().setName("refresh auto trans cache");for (String namespace : namespaceList) {autoTransService.refreshOneNamespace(namespace);}}).start();}}}

MybatisPlusTransableAdapter根据类信息获取到对应的SqlSession。

public class MybatisPlusTransableAdapter implements AutoTransable {private Class<? extends VO> voClass;public MybatisPlusTransableAdapter(Class<? extends VO> voClass) {this.voClass = voClass;}@Overridepublic VO selectById(Object primaryValue) {SqlSession sqlSession = this.sqlSession();VO result;try {result = (VO) sqlSession.selectOne(this.sqlStatement(SqlMethod.SELECT_BY_ID), primaryValue);} finally {this.closeSqlSession(sqlSession);}return result;}protected SqlSession sqlSession() {return SqlHelper.sqlSession(this.voClass);}protected String sqlStatement(SqlMethod sqlMethod) {return this.sqlStatement(sqlMethod.getMethod());}
}

RPC访问

RpcTransService#findById,访问请求。

http://easyTrans/easyTrans/proxy/com.fhs.test.pojo.School/findById/2?uniqueField=&targetFields=schoolName

    public VO findById(Object id, Trans tempTrans) {if (!this.isEnableCloud) {try {Class clazz = Class.forName(tempTrans.targetClassName());return this.findById(() -> {return this.transDiver.findById((Serializable)id, clazz, tempTrans.uniqueField(), new HashSet(Arrays.asList(tempTrans.fields())));}, tempTrans.dataSource());} catch (ClassNotFoundException var4) {throw new IllegalArgumentException("类找不到:" + tempTrans.targetClassName());}} else {try {return (VO)this.restTemplate.getForObject("http://" + tempTrans.serviceName() + this.getContextPath(tempTrans) + "/easyTrans/proxy/" + tempTrans.targetClassName() + "/findById/" + id + "?uniqueField=" + tempTrans.uniqueField() + "&targetFields=" + (String)Arrays.stream(tempTrans.fields()).collect(Collectors.joining(",")), BasicVO.class, new Object[0]);} catch (Exception var5) {log.error("trans service执行RPC Trans 远程调用错误:" + tempTrans.serviceName(), var5);return null;}}}

TransProxyController#findById,RPC服务的具体处理。

    @GetMapping("/{targetClass}/findById/{id}")public Object findById(@PathVariable("targetClass") String targetClass, @PathVariable("id") String id, @RequestParam("uniqueField")String uniqueField, @RequestParam("targetFields")String targetFields) throws ClassNotFoundException, IllegalAccessException {Assert.notNull(targetClass, "targetClass 不可为空");Assert.notNull(targetClass, "id 不可为空");Serializable sid = id;Class fieldType = getPkeyFieldType(targetClass);// 如果字段类型不是String,则转换if (fieldType == int.class || fieldType == Integer.class) {sid = Integer.valueOf(id);} else if (fieldType == long.class || fieldType == Long.class) {sid = Long.valueOf(id);}Set<String> targetFieldSet = null;if(!StringUtil.isEmpty(targetFields) && !"null".equals(targetFields)){targetFieldSet = new HashSet<>(Arrays.asList(targetFields.split(",")));}VO vo = simpleTransDiver.findById(sid, (Class<? extends VO>) Class.forName(targetClass),uniqueField,targetFieldSet);if (vo == null) {return null;}return vo2BasicVO(vo);}

页面展示

EasyTransResponseBodyAdvice,对数据进行翻译

@Slf4j
@ControllerAdvice
@ConditionalOnProperty(name = "easy-trans.is-enable-global", havingValue = "true")
public class EasyTransResponseBodyAdvice implements ResponseBodyAdvice {/*** 开启平铺模式*/@Value("${easy-trans.is-enable-tile:false}")private Boolean isEnableTile;@Autowiredprivate TransService transService;@Overridepublic boolean supports(MethodParameter methodParameter, Class aClass) {return true;}@Overridepublic Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {// 如果主动指定了忽略某个方法,则不执行翻译if (methodParameter.getExecutable().isAnnotationPresent(IgnoreTrans.class)) {return o;}Set<String> includeFields = null;Set<String> excludeFields = null;if (methodParameter.getExecutable().isAnnotationPresent(TransSett.class)) {TransSett transSett = methodParameter.getExecutable().getAnnotation(TransSett.class);if (transSett.include().length != 0) {includeFields = new HashSet<>(Arrays.asList(transSett.include()));} else {excludeFields = new HashSet<>(Arrays.asList(transSett.exclude()));}}Object result = null;try {result = TransUtil.transOne(o, transService, isEnableTile, new ArrayList<>(), includeFields, excludeFields);} catch (Exception e) {log.error("翻译错误", e);}return result == null ? o : result;}
}

TransUtil#transOne,如果选用平铺模式,会执行TransUtil#createProxyVo方法。

    public static Object transOne(Object object, TransService transService, boolean isProxy, ArrayList<Object> hasTransObjs, Set<String> includeFields, Set<String> excludeFields) throws IllegalAccessException, InstantiationException {if (object == null) {return null;} else if (contains(hasTransObjs, object)) {return object;} else {hasTransObjs.add(object);boolean isVo = false;if (transResultMap && object instanceof Map) {Map tempMap = (Map)object;Iterator var8 = tempMap.keySet().iterator();while(var8.hasNext()) {Object key = var8.next();Object mapValue = tempMap.get(key);try {tempMap.put(key, transOne(mapValue, transService, isProxy, hasTransObjs, includeFields, excludeFields));} catch (Exception var12) {}}return object;} else {if (object instanceof VO) {transService.transOne((VO)object, includeFields, excludeFields);transFields(object, transService, isProxy, hasTransObjs, includeFields, excludeFields);isVo = true;} else {if (object instanceof Collection) {return transBatch(object, transService, isProxy, hasTransObjs, includeFields, excludeFields);}if (object.getClass().getName().startsWith("java.")) {return object;}transFields(object, transService, isProxy, hasTransObjs, includeFields, excludeFields);}return isProxy && isVo ? createProxyVo((VO)object) : object;}}}

TransUtil#createProxyVo,创建代理类,通过属性赋值赋值到代理类中。

    public static Object createProxyVo(VO vo) {if (vo != null && vo.getTransMap() != null) {try {Class clazz = genNewClass(vo);Object newObject = clazz.newInstance();if (newObject == null) {return vo;} else {BeanUtils.copyProperties(vo, newObject);Iterator var3 = vo.getTransMap().keySet().iterator();while(var3.hasNext()) {String property = (String)var3.next();ReflectUtils.setValue(newObject, property, ConverterUtils.toString(vo.getTransMap().get(property)));}return newObject;}} catch (Exception var5) {log.error("easy trans 赋值错误", var5);return vo;}} else {return vo;}}

TransUtil#genNewClass,核心就是根据ASM来生成新的代理类

    public static Class genNewClass(VO vo) {try {Class targetClass = (Class)proxyClassMap.get(vo.getClass());boolean isGenNewClass = true;if (targetClass != null && validProxyClass(vo.getTransMap().keySet(), targetClass)) {isGenNewClass = false;}if (isGenNewClass) {AnnotationDescription jacksonIgnore = Builder.ofType(JsonIgnore.class).build();AnnotationDescription fastJsonIgnore = Builder.ofType(JSONField.class).define("serialize", false).build();net.bytebuddy.dynamic.DynamicType.Builder<? extends Object> builder = (new ByteBuddy()).subclass(vo.getClass()).name(vo.getClass().getSimpleName() + "DynamicTypeBuilder" + StringUtil.getUUID()).defineMethod("getTransMap", Map.class, 1).intercept(FixedValue.nullValue()).annotateMethod(new AnnotationDescription[]{jacksonIgnore, fastJsonIgnore});String property;for(Iterator var6 = vo.getTransMap().keySet().iterator(); var6.hasNext(); builder = ((net.bytebuddy.dynamic.DynamicType.Builder)builder).defineField(property, String.class, 1)) {property = (String)var6.next();}targetClass = ((net.bytebuddy.dynamic.DynamicType.Builder)builder).make().load(ClassUtils.getDefaultClassLoader(), Default.INJECTION).getLoaded();proxyClassMap.put(vo.getClass(), targetClass);proxyClassFieldMap.put(targetClass, vo.getTransMap().keySet());}return targetClass;} catch (Exception var8) {if (var8 instanceof ClassNotFoundException) {log.error("生成新class错误,目前不支持JDK17,请关闭平铺模式: easy-trans.is-enable-tile 设置为false");} else {log.error("生成新class错误", var8);}return null;}}

在这里插入图片描述


http://www.ppmy.cn/news/89159.html

相关文章

js函数的扩展

函数的扩展 函数参数的默认值 基本用法 ES6 之前&#xff0c;不能直接为函数的参数指定默认值&#xff0c;只能采用变通的方法。 function log(x, y) {y y || World;console.log(x, y); }log(Hello) // Hello World log(Hello, China) // Hello China log(Hello, ) // Hel…

【Java-Crawler】爬取动态页面(HtmlUnit、WebMagic)

爬取动态页面&#xff08;WebMagic、HtmlUnit&#xff09; 一、HtmlUnit的基本使用引入依赖一般使用步骤WebClient 的一些配置&#xff08;上述一般步骤中的第二步&#xff09; 二、案例&#xff08;爬取CSDN首页&#xff09;测试&#xff08;WebMagicHtmlUnit&#xff09;三、…

《微服务实战》 第十五章 RabbitMQ 延迟队列

前言 实际业务中&#xff0c;例如秒杀系统&#xff0c;秒杀商品成功会有截止时间&#xff0c;这时需要用到RabbitMQ延迟服务。 1、RabbitMQ延迟队列 1.1、方式1&#xff1a;RabbitMQ通过死信机制来实现延迟队列的功能 TTL &#xff0c;即 Time-To-Live&#xff0c;存活时间…

Maven 概述及下载安装

一、为什么要学习 Maven 我们构建一个项目需要用到很多第三方的类库&#xff0c;就需要引入大量的jar包&#xff0c;并且Jar包之间的关系错综复杂&#xff0c;缺少任何一个Jar包都会导致项目编译失败。Maven 能帮助我们下载及管理依赖。 本地项目代码开发完成后&#xff0c;我…

CMake初学笔记(一)

CMake初学笔记&#xff08;一&#xff09; CMake是什么CMake怎么实现跨平台CMake具体实践过程CMakeLists.txt编写快速入门常见函数例子 CMake是什么 跨平台编译工具&#xff0c;为了实现“write once, run everywhere”。 CMake怎么实现跨平台 开发者编写与平台无关的编译过…

【小沐学NLP】Python实现聊天机器人(OpenAI,模型概述笔记)

&#x1f37a;NLP开发系列相关文章编写如下&#x1f37a;&#xff1a;1&#x1f388;【小沐学NLP】Python实现词云图&#x1f388;2&#x1f388;【小沐学NLP】Python实现图片文字识别&#x1f388;3&#x1f388;【小沐学NLP】Python实现中文、英文分词&#x1f388;4&#x1…

Seata AT模式源码解析三(AT模式工作机制)

文章目录 代码示例流程源码解析开启全局事务注册分支事务一阶段提交全局事务提交分支事务二阶段提交全局事务回滚分支事务二阶段回滚 代码示例 从一个微服务示例开始&#xff0c;案例采用Seata官方提供的Demo。 用户购买商品的业务逻辑。整个业务逻辑由3个微服务提供支持&…

Bits, Bytes and Integers——二进制unsigned以及Two-complement表示,十六进制

这篇文章梳理一下Bits, Bytes and Integers——二进制unsigned以及Two-complement表示&#xff0c;十六进制这些事儿。 计算机中所有数据都是用二进制的0和1组成的&#xff0c;直接上知识点。 二进制 Unsigned以及Two-complement 同样的一串二进制数&#xff0c;按照有符号…