From 1cb440a5f6e138c87b3e4a084f1e348185a47643 Mon Sep 17 00:00:00 2001 From: godotg Date: Mon, 1 Jul 2024 13:52:21 +0800 Subject: [PATCH] feat[version]: use javassist to enhance version interface --- .../java/com/zfoo/orm/cache/EntityCache.java | 45 +++++----- .../zfoo/orm/cache/version/EnhanceUtils.java | 83 +++++++++++++++++++ .../{ICacheVersion.java => IVersion.java} | 16 ++-- ...ersionDefault.java => VersionDefault.java} | 18 +++- ...{CacheVersion.java => VersionReflect.java} | 32 +++++-- 5 files changed, 158 insertions(+), 36 deletions(-) create mode 100644 orm/src/main/java/com/zfoo/orm/cache/version/EnhanceUtils.java rename orm/src/main/java/com/zfoo/orm/cache/version/{ICacheVersion.java => IVersion.java} (90%) rename orm/src/main/java/com/zfoo/orm/cache/version/{CacheVersionDefault.java => VersionDefault.java} (66%) rename orm/src/main/java/com/zfoo/orm/cache/version/{CacheVersion.java => VersionReflect.java} (63%) diff --git a/orm/src/main/java/com/zfoo/orm/cache/EntityCache.java b/orm/src/main/java/com/zfoo/orm/cache/EntityCache.java index 9aabc1c4..0f37cc2a 100644 --- a/orm/src/main/java/com/zfoo/orm/cache/EntityCache.java +++ b/orm/src/main/java/com/zfoo/orm/cache/EntityCache.java @@ -21,9 +21,10 @@ import com.zfoo.orm.OrmContext; import com.zfoo.orm.anno.Version; import com.zfoo.orm.cache.persister.IOrmPersister; import com.zfoo.orm.cache.persister.PNode; -import com.zfoo.orm.cache.version.CacheVersion; -import com.zfoo.orm.cache.version.CacheVersionDefault; -import com.zfoo.orm.cache.version.ICacheVersion; +import com.zfoo.orm.cache.version.VersionDefault; +import com.zfoo.orm.cache.version.VersionReflect; +import com.zfoo.orm.cache.version.EnhanceUtils; +import com.zfoo.orm.cache.version.IVersion; import com.zfoo.orm.model.EntityDef; import com.zfoo.orm.model.IEntity; import com.zfoo.orm.query.Page; @@ -31,10 +32,7 @@ import com.zfoo.protocol.collection.ArrayUtils; import com.zfoo.protocol.collection.CollectionUtils; import com.zfoo.protocol.exception.RunException; import com.zfoo.protocol.model.Pair; -import com.zfoo.protocol.util.AssertionUtils; -import com.zfoo.protocol.util.FieldUtils; -import com.zfoo.protocol.util.ReflectionUtils; -import com.zfoo.protocol.util.ThreadUtils; +import com.zfoo.protocol.util.*; import com.zfoo.scheduler.manager.SchedulerBus; import com.zfoo.scheduler.util.LazyCache; import com.zfoo.scheduler.util.TimeUtils; @@ -61,7 +59,7 @@ public class EntityCache, E extends IEntity> imple private final LazyCache> cache; - private ICacheVersion cacheVersion = new CacheVersionDefault<>(); + private IVersion version = VersionDefault.DEFAULT; public EntityCache(EntityDef entityDef) { @@ -72,7 +70,16 @@ public class EntityCache, E extends IEntity> imple var filed = versionFields[0]; var getMethod = ReflectionUtils.getMethodByNameInPOJOClass(clazz, FieldUtils.fieldToGetMethod(clazz, filed)); var setMethod = ReflectionUtils.getMethodByNameInPOJOClass(clazz, FieldUtils.fieldToSetMethod(clazz, filed), filed.getType()); - cacheVersion = new CacheVersion<>(filed.getName(), getMethod, setMethod); + var cacheVersionReflect = new VersionReflect(clazz, getMethod, setMethod, filed.getName()); + if (GraalVmUtils.isGraalVM()) { + version = cacheVersionReflect; + } else { + try { + version = EnhanceUtils.createEventReceiver(cacheVersionReflect); + } catch (Exception e) { + throw new RuntimeException(e); + } + } } var removeCallback = new BiConsumer>, LazyCache.RemovalCause>() { @@ -97,11 +104,11 @@ public class EntityCache, E extends IEntity> imple public void run() { var collection = OrmContext.getOrmManager().getCollection(entityClass); - var version = cacheVersion.gvs(entity); - cacheVersion.svs(entity, version + 1); + var currentVersion = version.gvs(entity); + version.svs(entity, currentVersion + 1); - var filter = cacheVersion.gvs(entity) > 0 - ? Filters.and(Filters.eq("_id", entity.id()), Filters.eq(cacheVersion.versionField(), version)) + var filter = version.gvs(entity) > 0 + ? Filters.and(Filters.eq("_id", entity.id()), Filters.eq(version.name(), currentVersion)) : Filters.eq("_id", entity.id()); var result = collection.replaceOne(filter, entity); if (result.getModifiedCount() <= 0) { @@ -336,11 +343,11 @@ public class EntityCache, E extends IEntity> imple var batchList = currentUpdateList.stream() .map(it -> { - var version = cacheVersion.gvs(it); - cacheVersion.svs(it, version + 1); + var currentVersion = version.gvs(it); + version.svs(it, currentVersion + 1); - var filter = cacheVersion.gvs(it) > 0 - ? Filters.and(Filters.eq("_id", it.id()), Filters.eq(cacheVersion.versionField(), version)) + var filter = version.gvs(it) > 0 + ? Filters.and(Filters.eq("_id", it.id()), Filters.eq(version.name(), currentVersion)) : Filters.eq("_id", it.id()); return new ReplaceOneModel<>(filter, it); @@ -387,8 +394,8 @@ public class EntityCache, E extends IEntity> imple } // 如果没有版本号,则直接更新数据库 - var entityVersion = cacheVersion.gvs(entity); - var dbEntityVersion = cacheVersion.gvs(dbEntity); + var entityVersion = version.gvs(entity); + var dbEntityVersion = version.gvs(dbEntity); if (entityVersion <= 0) { OrmContext.getAccessor().update(entity); continue; diff --git a/orm/src/main/java/com/zfoo/orm/cache/version/EnhanceUtils.java b/orm/src/main/java/com/zfoo/orm/cache/version/EnhanceUtils.java new file mode 100644 index 00000000..416bf926 --- /dev/null +++ b/orm/src/main/java/com/zfoo/orm/cache/version/EnhanceUtils.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2020 The zfoo Authors + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and limitations under the License. + */ + +package com.zfoo.orm.cache.version; + +import com.zfoo.orm.model.IEntity; +import com.zfoo.protocol.util.StringUtils; +import com.zfoo.protocol.util.UuidUtils; +import javassist.*; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + +/** + * @author godotg + */ +public abstract class EnhanceUtils { + + static { + // 适配Tomcat,因为Tomcat不是用的默认的类加载器,而Javassist用的是默认的加载器 + var classArray = new Class[]{ + IVersion.class + }; + + var classPool = ClassPool.getDefault(); + + for (var clazz : classArray) { + if (classPool.find(clazz.getName()) == null) { + ClassClassPath classPath = new ClassClassPath(clazz); + classPool.insertClassPath(classPath); + } + } + } + + public static IVersion createEventReceiver(VersionReflect cacheVersion) throws NotFoundException, CannotCompileException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { + var classPool = ClassPool.getDefault(); + + Class clazz = cacheVersion.getEntityClass(); + Method getVersionMethod = cacheVersion.getGetVersionMethod(); + Method setVersionMethod = cacheVersion.getSetVersionMethod(); + + // 定义类名称 + CtClass enhanceClazz = classPool.makeClass(EnhanceUtils.class.getName() + UuidUtils.getLocalIntId()); + enhanceClazz.addInterface(classPool.get(IVersion.class.getName())); + + // 定义类实现的接口方法name + CtMethod nameMethod = new CtMethod(classPool.get(String.class.getName()), "name", null, enhanceClazz); + nameMethod.setModifiers(Modifier.PUBLIC + Modifier.FINAL); + String nameMethodBody = StringUtils.format("{ return \"{}\"; }", cacheVersion.name()); + nameMethod.setBody(nameMethodBody); + enhanceClazz.addMethod(nameMethod); + + // 定义类实现的接口方法gvs + CtMethod gvsMethod = new CtMethod(classPool.get(long.class.getName()), "gvs", classPool.get(new String[]{IEntity.class.getName()}), enhanceClazz); + gvsMethod.setModifiers(Modifier.PUBLIC + Modifier.FINAL); + String gvsMethodBody = StringUtils.format("{ return (({})$1).{}(); }", clazz.getName(), getVersionMethod.getName()); + gvsMethod.setBody(gvsMethodBody); + enhanceClazz.addMethod(gvsMethod); + + // 定义类实现的接口方法svs + CtMethod svsMethod = new CtMethod(classPool.get(void.class.getName()), "svs", classPool.get(new String[]{IEntity.class.getName(), long.class.getName()}), enhanceClazz); + svsMethod.setModifiers(Modifier.PUBLIC + Modifier.FINAL); + String svsMethodBody = StringUtils.format("{ (({})$1).{}($2); }", clazz.getName(), setVersionMethod.getName()); + svsMethod.setBody(svsMethodBody); + enhanceClazz.addMethod(svsMethod); + + // 释放缓存 + enhanceClazz.detach(); + + Class resultClazz = enhanceClazz.toClass(IVersion.class); + return (IVersion) resultClazz.newInstance(); + } +} diff --git a/orm/src/main/java/com/zfoo/orm/cache/version/ICacheVersion.java b/orm/src/main/java/com/zfoo/orm/cache/version/IVersion.java similarity index 90% rename from orm/src/main/java/com/zfoo/orm/cache/version/ICacheVersion.java rename to orm/src/main/java/com/zfoo/orm/cache/version/IVersion.java index eaf3bd3e..8b2638d3 100644 --- a/orm/src/main/java/com/zfoo/orm/cache/version/ICacheVersion.java +++ b/orm/src/main/java/com/zfoo/orm/cache/version/IVersion.java @@ -18,11 +18,12 @@ import com.zfoo.orm.model.IEntity; /** * @author godotg */ -public interface ICacheVersion, E extends IEntity> { +public interface IVersion { - default String versionField() { - return "version"; - } + /** + * version field的名称 + */ + String name(); /** * 一个文档的写入到数据库的version版本,version的get和set方法 @@ -38,11 +39,8 @@ public interface ICacheVersion, E extends IEntity> *

* 分布式环境下,要想服务器性能好,就要做成有状态的,有状态就要遇到这种多服对同一条数据写入问题,这是一个千古难以解决的问题,进退两难。 */ - default long gvs(E entity) { - return 0L; - } + long gvs(IEntity entity); - default void svs(E entity, long vs) { - } + void svs(IEntity entity, long vs); } diff --git a/orm/src/main/java/com/zfoo/orm/cache/version/CacheVersionDefault.java b/orm/src/main/java/com/zfoo/orm/cache/version/VersionDefault.java similarity index 66% rename from orm/src/main/java/com/zfoo/orm/cache/version/CacheVersionDefault.java rename to orm/src/main/java/com/zfoo/orm/cache/version/VersionDefault.java index 6bd2a375..36810ac5 100644 --- a/orm/src/main/java/com/zfoo/orm/cache/version/CacheVersionDefault.java +++ b/orm/src/main/java/com/zfoo/orm/cache/version/VersionDefault.java @@ -12,12 +12,28 @@ package com.zfoo.orm.cache.version; + import com.zfoo.orm.model.IEntity; /** * @author godotg */ -public class CacheVersionDefault, E extends IEntity> implements ICacheVersion { +public class VersionDefault implements IVersion { + public static final VersionDefault DEFAULT = new VersionDefault(); + @Override + public String name() { + return "version"; + } + + @Override + public long gvs(IEntity entity) { + return 0; + } + + @Override + public void svs(IEntity entity, long vs) { + + } } diff --git a/orm/src/main/java/com/zfoo/orm/cache/version/CacheVersion.java b/orm/src/main/java/com/zfoo/orm/cache/version/VersionReflect.java similarity index 63% rename from orm/src/main/java/com/zfoo/orm/cache/version/CacheVersion.java rename to orm/src/main/java/com/zfoo/orm/cache/version/VersionReflect.java index ae928a59..47a324e0 100644 --- a/orm/src/main/java/com/zfoo/orm/cache/version/CacheVersion.java +++ b/orm/src/main/java/com/zfoo/orm/cache/version/VersionReflect.java @@ -20,31 +20,49 @@ import java.lang.reflect.Method; /** * @author godotg */ -public class CacheVersion, E extends IEntity> implements ICacheVersion { +public class VersionReflect implements IVersion { - private String versionFiled; + private Class> entityClass; private Method getVersionMethod; private Method setVersionMethod; + private String versionFiled; - public CacheVersion(String versionFiled, Method getVersionMethod, Method setVersionMethod) { - this.versionFiled = versionFiled; + public VersionReflect(Class> entityClass, Method getVersionMethod, Method setVersionMethod, String versionFiled) { + this.entityClass = entityClass; this.getVersionMethod = getVersionMethod; this.setVersionMethod = setVersionMethod; + this.versionFiled = versionFiled; } @Override - public String versionField() { + public String name() { return versionFiled; } @Override - public long gvs(E entity) { + public long gvs(IEntity entity) { return (long) ReflectionUtils.invokeMethod(entity, getVersionMethod); } @Override - public void svs(E entity, long vs) { + public void svs(IEntity entity, long vs) { ReflectionUtils.invokeMethod(entity, setVersionMethod, vs); } + + public Class> getEntityClass() { + return entityClass; + } + + public Method getGetVersionMethod() { + return getVersionMethod; + } + + public Method getSetVersionMethod() { + return setVersionMethod; + } + + public String getVersionFiled() { + return versionFiled; + } }