From 6d613f715f25773e877d6c869e8a199c9d43f157 Mon Sep 17 00:00:00 2001 From: redkale Date: Mon, 7 Aug 2023 11:43:17 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9ECopier?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/org/redkale/boot/Application.java | 4 +- .../redkale/source/AbstractDataSource.java | 10 +- .../redkale/source/AbstractDataSqlSource.java | 7 - .../org/redkale/source/DataMemorySource.java | 304 ++++++-- .../org/redkale/source/DataSqlSource.java | 16 +- .../java/org/redkale/source/EntityCache.java | 30 +- src/main/java/org/redkale/util/Copier.java | 718 ++++++++++++++++++ .../org/redkale/util/ResourceFactory.java | 7 +- ...produceBeanMap.java => CopierBeanMap.java} | 8 +- ...produceMapBean.java => CopierMapBean.java} | 4 +- .../{ReproduceTest.java => CopierTest.java} | 22 +- .../org/redkale/test/util/UntilTestMain.java | 14 +- 12 files changed, 1041 insertions(+), 103 deletions(-) create mode 100644 src/main/java/org/redkale/util/Copier.java rename src/test/java/org/redkale/test/util/{ReproduceBeanMap.java => CopierBeanMap.java} (72%) rename src/test/java/org/redkale/test/util/{ReproduceMapBean.java => CopierMapBean.java} (84%) rename src/test/java/org/redkale/test/util/{ReproduceTest.java => CopierTest.java} (82%) diff --git a/src/main/java/org/redkale/boot/Application.java b/src/main/java/org/redkale/boot/Application.java index 373ed9247..3c8150761 100644 --- a/src/main/java/org/redkale/boot/Application.java +++ b/src/main/java/org/redkale/boot/Application.java @@ -1257,7 +1257,7 @@ public final class Application { if (!sourceConf.getValue(AbstractDataSource.DATA_SOURCE_RESOURCE, "").isEmpty()) { DataSource source = loadDataSource(sourceConf.getValue(AbstractDataSource.DATA_SOURCE_RESOURCE), autoMemory); if (source != null) { - if (source instanceof DataMemorySource && DataMemorySource.isSearchType(sourceConf)) { + if (source instanceof DataMemorySource && source instanceof SearchSource) { resourceFactory.register(sourceName, SearchSource.class, source); } else { resourceFactory.register(sourceName, DataSource.class, source); @@ -1271,7 +1271,7 @@ public final class Application { try { DataSource source = AbstractDataSource.createDataSource(serverClassLoader, resourceFactory, sourceConf, sourceName, compileMode); dataSources.add(source); - if (source instanceof DataMemorySource && DataMemorySource.isSearchType(sourceConf)) { + if (source instanceof DataMemorySource && source instanceof SearchSource) { resourceFactory.register(sourceName, SearchSource.class, source); } else { resourceFactory.register(sourceName, DataSource.class, source); diff --git a/src/main/java/org/redkale/source/AbstractDataSource.java b/src/main/java/org/redkale/source/AbstractDataSource.java index 8e42df59b..b7d452fd2 100644 --- a/src/main/java/org/redkale/source/AbstractDataSource.java +++ b/src/main/java/org/redkale/source/AbstractDataSource.java @@ -23,6 +23,7 @@ import org.redkale.net.WorkThread; import org.redkale.persistence.Entity; import org.redkale.service.*; import org.redkale.util.*; +import static org.redkale.util.Utility.isEmpty; /** * DataSource的S抽象实现类
@@ -121,6 +122,8 @@ public abstract class AbstractDataSource extends AbstractService implements Data @Resource(name = RESNAME_APP_EXECUTOR, required = false) private ExecutorService sourceExecutor; + protected String name; + @Override public void init(AnyValue conf) { super.init(conf); @@ -131,6 +134,11 @@ public abstract class AbstractDataSource extends AbstractService implements Data } } + @Override + public final String resourceName() { + return name; + } + @ResourceListener public abstract void onResourceChange(ResourceEvent[] events); @@ -148,7 +156,7 @@ public abstract class AbstractDataSource extends AbstractService implements Data serverClassLoader = Thread.currentThread().getContextClassLoader(); } String classVal = sourceConf.getValue("type"); - if (classVal == null || classVal.isEmpty()) { + if (isEmpty(classVal)) { if (DataJdbcSource.acceptsConf(sourceConf)) { source = new DataJdbcSource(); } else { diff --git a/src/main/java/org/redkale/source/AbstractDataSqlSource.java b/src/main/java/org/redkale/source/AbstractDataSqlSource.java index 7dfedcca3..820c612fc 100644 --- a/src/main/java/org/redkale/source/AbstractDataSqlSource.java +++ b/src/main/java/org/redkale/source/AbstractDataSqlSource.java @@ -48,8 +48,6 @@ public abstract class AbstractDataSqlSource extends AbstractDataSource implement protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName()); - protected String name; - protected boolean cacheForbidden; protected String dbtype; @@ -802,11 +800,6 @@ public abstract class AbstractDataSqlSource extends AbstractDataSource implement return "sql"; } - @Override - public final String resourceName() { - return name; - } - @Override public EntityInfo apply(Class t) { return loadEntityInfo(t); diff --git a/src/main/java/org/redkale/source/DataMemorySource.java b/src/main/java/org/redkale/source/DataMemorySource.java index 41a21786e..28d2f31d3 100644 --- a/src/main/java/org/redkale/source/DataMemorySource.java +++ b/src/main/java/org/redkale/source/DataMemorySource.java @@ -8,7 +8,7 @@ package org.redkale.source; import java.io.Serializable; import java.util.*; import java.util.concurrent.CompletableFuture; -import java.util.function.*; +import java.util.stream.Stream; import org.redkale.annotation.AutoLoad; import org.redkale.annotation.ResourceListener; import org.redkale.annotation.ResourceType; @@ -33,21 +33,10 @@ import org.redkale.util.*; @AutoLoad(false) @SuppressWarnings("unchecked") @ResourceType(DataSource.class) -public class DataMemorySource extends AbstractDataSqlSource implements SearchSource { +public class DataMemorySource extends AbstractDataSource { public DataMemorySource(String name) { this.name = name; - this.cacheForbidden = false; - } - - @Override - protected int readMaxConns() { - return -1; - } - - @Override - protected int writeMaxConns() { - return -1; } @Local @@ -64,10 +53,10 @@ public class DataMemorySource extends AbstractDataSqlSource implements SearchSou public static boolean acceptsConf(AnyValue config) { return config.getValue(DATA_SOURCE_URL).startsWith("memory:"); } - - public static boolean isSearchType(AnyValue config) { - return config.getValue(DATA_SOURCE_URL).startsWith("memory:search"); - } +// +// public static boolean isSearchType(AnyValue config) { +// return config.getValue(DATA_SOURCE_URL).startsWith("memory:search"); +// } @Local @Override @@ -83,128 +72,353 @@ public class DataMemorySource extends AbstractDataSqlSource implements SearchSou return true; } - @Override - protected final boolean isAsync() { - return false; - } - @Override public String toString() { return getClass().getSimpleName() + "{type=memory, name='" + resourceName() + "'}"; } @Override - public int nativeUpdate(String sql) { + public int insert(T... entitys) { throw new UnsupportedOperationException("Not supported yet."); } @Override - public int[] nativeUpdates(String... sqls) { + public CompletableFuture insertAsync(T... entitys) { throw new UnsupportedOperationException("Not supported yet."); } @Override - public V nativeQuery(String sql, BiConsumer consumer, Function handler) { + public int delete(T... entitys) { throw new UnsupportedOperationException("Not supported yet."); } @Override - public int nativeUpdate(String sql, Map params) { + public CompletableFuture deleteAsync(T... entitys) { throw new UnsupportedOperationException("Not supported yet."); } @Override - public V nativeQuery(String sql, BiConsumer consumer, Function handler, Map params) { + public int delete(Class clazz, Serializable... pks) { throw new UnsupportedOperationException("Not supported yet."); } @Override - protected String prepareParamSign(int index) { + public CompletableFuture deleteAsync(Class clazz, Serializable... pks) { throw new UnsupportedOperationException("Not supported yet."); } @Override - protected CompletableFuture insertDBAsync(EntityInfo info, T... entitys) { + public int delete(Class clazz, Flipper flipper, FilterNode node) { throw new UnsupportedOperationException("Not supported yet."); } @Override - protected CompletableFuture deleteDBAsync(EntityInfo info, String[] tables, Flipper flipper, FilterNode node, Map> pkmap, String... sqls) { + public CompletableFuture deleteAsync(Class clazz, Flipper flipper, FilterNode node) { throw new UnsupportedOperationException("Not supported yet."); } @Override - protected CompletableFuture clearTableDBAsync(EntityInfo info, String[] tables, FilterNode node, String... sqls) { + public int clearTable(Class clazz, FilterNode node) { throw new UnsupportedOperationException("Not supported yet."); } @Override - protected CompletableFuture createTableDBAsync(EntityInfo info, String copyTableSql, Serializable pk, String... sqls) { + public CompletableFuture clearTableAsync(Class clazz, FilterNode node) { throw new UnsupportedOperationException("Not supported yet."); } @Override - protected CompletableFuture dropTableDBAsync(EntityInfo info, String[] tables, FilterNode node, String... sqls) { + public int createTable(Class clazz, Serializable pk) { throw new UnsupportedOperationException("Not supported yet."); } @Override - protected CompletableFuture updateEntityDBAsync(EntityInfo info, T... entitys) { + public CompletableFuture createTableAsync(Class clazz, Serializable pk) { throw new UnsupportedOperationException("Not supported yet."); } @Override - protected CompletableFuture updateColumnDBAsync(EntityInfo info, Flipper flipper, UpdateSqlInfo sql) { + public int dropTable(Class clazz, FilterNode node) { throw new UnsupportedOperationException("Not supported yet."); } @Override - protected CompletableFuture> getNumberMapDBAsync(EntityInfo info, String[] tables, String sql, FilterNode node, FilterFuncColumn... columns) { + public CompletableFuture dropTableAsync(Class clazz, FilterNode node) { throw new UnsupportedOperationException("Not supported yet."); } @Override - protected CompletableFuture getNumberResultDBAsync(EntityInfo info, String[] tables, String sql, FilterFunc func, Number defVal, String column, FilterNode node) { + public int update(T... entitys) { throw new UnsupportedOperationException("Not supported yet."); } @Override - protected CompletableFuture> queryColumnMapDBAsync(EntityInfo info, String[] tables, String sql, String keyColumn, FilterFunc func, String funcColumn, FilterNode node) { + public CompletableFuture updateAsync(T... entitys) { throw new UnsupportedOperationException("Not supported yet."); } @Override - protected CompletableFuture> queryColumnMapDBAsync(EntityInfo info, String[] tables, String sql, ColumnNode[] funcNodes, String[] groupByColumns, FilterNode node) { + public int updateColumn(Class clazz, Serializable pk, String column, Serializable value) { throw new UnsupportedOperationException("Not supported yet."); } @Override - protected CompletableFuture findDBAsync(EntityInfo info, String[] tables, String sql, boolean onlypk, SelectColumn selects, Serializable pk, FilterNode node) { + public CompletableFuture updateColumnAsync(Class clazz, Serializable pk, String column, Serializable value) { throw new UnsupportedOperationException("Not supported yet."); } @Override - protected CompletableFuture findColumnDBAsync(EntityInfo info, String[] tables, String sql, boolean onlypk, String column, Serializable defValue, Serializable pk, FilterNode node) { + public int updateColumn(Class clazz, String column, Serializable value, FilterNode node) { throw new UnsupportedOperationException("Not supported yet."); } @Override - protected CompletableFuture existsDBAsync(EntityInfo info, String[] tables, String sql, boolean onlypk, Serializable pk, FilterNode node) { + public CompletableFuture updateColumnAsync(Class clazz, String column, Serializable value, FilterNode node) { throw new UnsupportedOperationException("Not supported yet."); } @Override - protected CompletableFuture> querySheetDBAsync(EntityInfo info, boolean readcache, boolean needtotal, boolean distinct, SelectColumn selects, Flipper flipper, FilterNode node) { + public int updateColumn(Class clazz, Serializable pk, ColumnValue... values) { throw new UnsupportedOperationException("Not supported yet."); } @Override - public int updateMapping(Class clazz, String table) { + public CompletableFuture updateColumnAsync(Class clazz, Serializable pk, ColumnValue... values) { throw new UnsupportedOperationException("Not supported yet."); } @Override - public CompletableFuture updateMappingAsync(Class clazz, String table) { + public int updateColumn(Class clazz, FilterNode node, Flipper flipper, ColumnValue... values) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public CompletableFuture updateColumnAsync(Class clazz, FilterNode node, Flipper flipper, ColumnValue... values) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public int updateColumn(T entity, FilterNode node, SelectColumn selects) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public CompletableFuture updateColumnAsync(T entity, FilterNode node, SelectColumn selects) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public Number getNumberResult(Class entityClass, FilterFunc func, Number defVal, String column, FilterNode node) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public CompletableFuture getNumberResultAsync(Class entityClass, FilterFunc func, Number defVal, String column, FilterNode node) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public Map getNumberMap(Class entityClass, FilterNode node, FilterFuncColumn... columns) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public CompletableFuture> getNumberMapAsync(Class entityClass, FilterNode node, FilterFuncColumn... columns) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public Map queryColumnMap(Class entityClass, String keyColumn, FilterFunc func, String funcColumn, FilterNode node) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public CompletableFuture> queryColumnMapAsync(Class entityClass, String keyColumn, FilterFunc func, String funcColumn, FilterNode node) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public Map queryColumnMap(Class entityClass, ColumnNode[] funcNodes, String groupByColumn, FilterNode node) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public CompletableFuture> queryColumnMapAsync(Class entityClass, ColumnNode[] funcNodes, String groupByColumn, FilterNode node) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public Map queryColumnMap(Class entityClass, ColumnNode[] funcNodes, String[] groupByColumns, FilterNode node) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public CompletableFuture> queryColumnMapAsync(Class entityClass, ColumnNode[] funcNodes, String[] groupByColumns, FilterNode node) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public T find(Class clazz, SelectColumn selects, Serializable pk) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public CompletableFuture findAsync(Class clazz, SelectColumn selects, Serializable pk) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public T[] finds(Class clazz, SelectColumn selects, Serializable... pks) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public CompletableFuture findsAsync(Class clazz, SelectColumn selects, Serializable... pks) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public List findsList(Class clazz, Stream pks) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public CompletableFuture> findsListAsync(Class clazz, Stream pks) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public T find(Class clazz, SelectColumn selects, FilterNode node) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public CompletableFuture findAsync(Class clazz, SelectColumn selects, FilterNode node) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public Serializable findColumn(Class clazz, String column, Serializable defValue, Serializable pk) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public CompletableFuture findColumnAsync(Class clazz, String column, Serializable defValue, Serializable pk) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public Serializable findColumn(Class clazz, String column, Serializable defValue, FilterNode node) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public CompletableFuture findColumnAsync(Class clazz, String column, Serializable defValue, FilterNode node) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public boolean exists(Class clazz, Serializable pk) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public CompletableFuture existsAsync(Class clazz, Serializable pk) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public boolean exists(Class clazz, FilterNode node) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public CompletableFuture existsAsync(Class clazz, FilterNode node) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public Set queryColumnSet(String selectedColumn, Class clazz, Flipper flipper, FilterNode node) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public CompletableFuture> queryColumnSetAsync(String selectedColumn, Class clazz, Flipper flipper, FilterNode node) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public List queryColumnList(String selectedColumn, Class clazz, Flipper flipper, FilterNode node) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public CompletableFuture> queryColumnListAsync(String selectedColumn, Class clazz, Flipper flipper, FilterNode node) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public Sheet queryColumnSheet(String selectedColumn, Class clazz, Flipper flipper, FilterNode node) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public CompletableFuture> queryColumnSheetAsync(String selectedColumn, Class clazz, Flipper flipper, FilterNode node) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public Map queryMap(Class clazz, SelectColumn selects, Stream keyStream) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public CompletableFuture> queryMapAsync(Class clazz, SelectColumn selects, Stream keyStream) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public Map queryMap(Class clazz, SelectColumn selects, FilterNode node) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public CompletableFuture> queryMapAsync(Class clazz, SelectColumn selects, FilterNode node) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public Set querySet(Class clazz, SelectColumn selects, Flipper flipper, FilterNode node) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public CompletableFuture> querySetAsync(Class clazz, SelectColumn selects, Flipper flipper, FilterNode node) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public List queryList(Class clazz, SelectColumn selects, Flipper flipper, FilterNode node) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public CompletableFuture> queryListAsync(Class clazz, SelectColumn selects, Flipper flipper, FilterNode node) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public Sheet querySheet(Class clazz, SelectColumn selects, Flipper flipper, FilterNode node) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public CompletableFuture> querySheetAsync(Class clazz, SelectColumn selects, Flipper flipper, FilterNode node) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public void close() throws Exception { throw new UnsupportedOperationException("Not supported yet."); } diff --git a/src/main/java/org/redkale/source/DataSqlSource.java b/src/main/java/org/redkale/source/DataSqlSource.java index 0be24c4fe..db0db554c 100644 --- a/src/main/java/org/redkale/source/DataSqlSource.java +++ b/src/main/java/org/redkale/source/DataSqlSource.java @@ -7,7 +7,7 @@ import java.io.Serializable; import java.util.*; import java.util.function.*; import static org.redkale.source.DataResultSet.formatColumnValue; -import org.redkale.util.Reproduce; +import org.redkale.util.Copier; /** * @@ -139,30 +139,30 @@ public interface DataSqlSource extends DataSource { //----------------------------- JavaBean ----------------------------- default int nativeUpdate(String sql, Serializable bean) { - return nativeUpdate(sql, (Map) Reproduce.copy(HashMap.class, bean)); + return nativeUpdate(sql, (Map) Copier.copyToMap(bean, false)); } default V nativeQuery(String sql, Function handler, Serializable bean) { - return nativeQuery(sql, null, handler, (Map) Reproduce.copy(HashMap.class, bean)); + return nativeQuery(sql, null, handler, (Map) Copier.copyToMap(bean, false)); } default V nativeQueryOne(Class type, String sql, Serializable bean) { - return nativeQueryOne(type, sql, (Map) Reproduce.copy(HashMap.class, bean)); + return nativeQueryOne(type, sql, (Map) Copier.copyToMap(bean, false)); } default List nativeQueryList(Class type, String sql, Serializable bean) { - return nativeQueryList(type, sql, (Map) Reproduce.copy(HashMap.class, bean)); + return nativeQueryList(type, sql, (Map) Copier.copyToMap(bean, false)); } default Map nativeQueryMap(Class keyType, Class valType, String sql, Serializable bean) { - return nativeQueryMap(keyType, valType, sql, (Map) Reproduce.copy(HashMap.class, bean)); + return nativeQueryMap(keyType, valType, sql, (Map) Copier.copyToMap(bean, false)); } default Map nativeQueryStrStrMap(String sql, Serializable bean) { - return nativeQueryMap(String.class, String.class, sql, (Map) Reproduce.copy(HashMap.class, bean)); + return nativeQueryMap(String.class, String.class, sql, (Map) Copier.copyToMap(bean, false)); } default Map nativeQueryIntStrMap(String sql, Serializable bean) { - return nativeQueryMap(Integer.class, String.class, sql, (Map) Reproduce.copy(HashMap.class, bean)); + return nativeQueryMap(Integer.class, String.class, sql, (Map) Copier.copyToMap(bean, false)); } } diff --git a/src/main/java/org/redkale/source/EntityCache.java b/src/main/java/org/redkale/source/EntityCache.java index 98526ef74..532a8ea63 100644 --- a/src/main/java/org/redkale/source/EntityCache.java +++ b/src/main/java/org/redkale/source/EntityCache.java @@ -62,10 +62,10 @@ public final class EntityCache { private final Attribute primary; //新增时的复制器, 排除了标记为@Transient的字段 - private final Reproduce newReproduce; + private final Copier newCopier; //修改时的复制器, 排除了标记为@Transient或@Column(updatable=false)的字段 - private final Reproduce chgReproduce; + private final Copier chgCopier; //是否已经全量加载过 private volatile boolean fullloaded; @@ -110,7 +110,7 @@ public final class EntityCache { } } this.needCopy = !direct; - this.newReproduce = Reproduce.create(type, type, (m) -> { + this.newCopier = Copier.create(type, type, (m) -> { try { java.lang.reflect.Field field = type.getDeclaredField(m); return field.getAnnotation(Transient.class) == null && field.getAnnotation(javax.persistence.Transient.class) == null; @@ -118,7 +118,7 @@ public final class EntityCache { return true; } }); - this.chgReproduce = Reproduce.create(type, type, (m) -> { + this.chgCopier = Copier.create(type, type, (m) -> { try { java.lang.reflect.Field field = type.getDeclaredField(m); if (field.getAnnotation(Transient.class) != null) { @@ -259,7 +259,7 @@ public final class EntityCache { return null; } T rs = map.get(pk); - return rs == null ? null : (needCopy ? newReproduce.apply(this.creator.create(), rs) : rs); + return rs == null ? null : (needCopy ? newCopier.apply(this.creator.create(), rs) : rs); } public T[] finds(Serializable... pks) { @@ -273,7 +273,7 @@ public final class EntityCache { T[] result = arrayer.apply(ids.length); for (int i = 0; i < result.length; i++) { T rs = map.get(ids[i]); - result[i] = rs == null ? null : (needCopy ? newReproduce.apply(this.creator.create(), rs) : rs); + result[i] = rs == null ? null : (needCopy ? newCopier.apply(this.creator.create(), rs) : rs); } return result; } else if (t == long[].class) { @@ -281,7 +281,7 @@ public final class EntityCache { T[] result = arrayer.apply(ids.length); for (int i = 0; i < result.length; i++) { T rs = map.get(ids[i]); - result[i] = rs == null ? null : (needCopy ? newReproduce.apply(this.creator.create(), rs) : rs); + result[i] = rs == null ? null : (needCopy ? newCopier.apply(this.creator.create(), rs) : rs); } return result; } @@ -292,7 +292,7 @@ public final class EntityCache { if (needCopy) { for (int i = 0; i < result.length; i++) { T rs = array0[(Integer) pks[i]]; - result[i] = rs == null ? null : newReproduce.apply(this.creator.create(), rs); + result[i] = rs == null ? null : newCopier.apply(this.creator.create(), rs); } } else { for (int i = 0; i < result.length; i++) { @@ -305,7 +305,7 @@ public final class EntityCache { T[] result = arrayer.apply(pks.length); for (int i = 0; i < result.length; i++) { T rs = map.get(pks[i]); - result[i] = rs == null ? null : (needCopy ? newReproduce.apply(this.creator.create(), rs) : rs); + result[i] = rs == null ? null : (needCopy ? newCopier.apply(this.creator.create(), rs) : rs); } return result; } @@ -319,7 +319,7 @@ public final class EntityCache { return null; } if (selects == null) { - return (needCopy ? newReproduce.apply(this.creator.create(), rs) : rs); + return (needCopy ? newCopier.apply(this.creator.create(), rs) : rs); } T t = this.creator.create(); for (Attribute attr : this.info.attributes) { @@ -358,7 +358,7 @@ public final class EntityCache { } if (selects == null) { if (needCopy) { - rs = newReproduce.apply(ctr.create(), rs); + rs = newCopier.apply(ctr.create(), rs); } } else { T t = ctr.create(); @@ -385,7 +385,7 @@ public final class EntityCache { return null; } if (selects == null) { - return (needCopy ? newReproduce.apply(this.creator.create(), opt.get()) : opt.get()); + return (needCopy ? newCopier.apply(this.creator.create(), opt.get()) : opt.get()); } T rs = opt.get(); T t = this.creator.create(); @@ -765,7 +765,7 @@ public final class EntityCache { } final List rs = new ArrayList<>(); if (selects == null) { - Consumer action = x -> rs.add(needCopy ? newReproduce.apply(creator.create(), x) : x); + Consumer action = x -> rs.add(needCopy ? newCopier.apply(creator.create(), x) : x); if (comparator != null) { stream.forEachOrdered(action); } else { @@ -801,7 +801,7 @@ public final class EntityCache { if (entity == null) { return 0; } - final T rs = newReproduce.apply(this.creator.create(), entity); //确保同一主键值的map与list中的对象必须共用。 + final T rs = newCopier.apply(this.creator.create(), entity); //确保同一主键值的map与list中的对象必须共用。 T old = this.map.putIfAbsent(this.primary.get(rs), rs); if (old == null) { this.list.add(rs); @@ -874,7 +874,7 @@ public final class EntityCache { } tableLock.lock(); //表锁, 可优化成行锁 try { - this.chgReproduce.apply(rs, entity); + this.chgCopier.apply(rs, entity); } finally { tableLock.unlock(); } diff --git a/src/main/java/org/redkale/util/Copier.java b/src/main/java/org/redkale/util/Copier.java new file mode 100644 index 000000000..e696f45fe --- /dev/null +++ b/src/main/java/org/redkale/util/Copier.java @@ -0,0 +1,718 @@ +/* + * + */ +package org.redkale.util; + +import java.lang.reflect.*; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.*; +import static org.redkale.asm.ClassWriter.COMPUTE_FRAMES; +import org.redkale.asm.*; +import static org.redkale.asm.Opcodes.*; +import org.redkale.asm.Type; + +/** + * JavaBean类对象的拷贝,相同的字段名会被拷贝
+ * + *

+ * 详情见: https://redkale.org + * + * @author zhangjx + * @param 目标对象的数据类型 + * @param 源对象的数据类型 + */ +public interface Copier extends BiFunction { + + /** + * 将源对象字段复制到目标对象 + * + * @param dest 目标对象 + * @param src 源对象 + * + * @return 目标对象 + */ + @Override + public D apply(S src, D dest); + + /** + * 将源对象字段复制到目标对象 + * + * @param 目标类泛型 + * @param 源类泛型 + * @param dest 目标对象 + * @param src 源对象 + * + * @return 目标对象 + */ + public static D copy(final S src, final D dest) { + if (src == null || dest == null) { + return null; + } + Class destClass = (Class) dest.getClass(); + Creator creator = Creator.load(destClass); + return load((Class) src.getClass(), destClass).apply(src, creator.create()); + } + + /** + * 将源对象字段复制到目标对象 + * + * @param 目标类泛型 + * @param 源类泛型 + * @param destClass 目标类名 + * @param src 源对象 + * + * @return 目标对象 + */ + public static D copy(final S src, final Class destClass) { + if (src == null) { + return null; + } + Creator creator = Creator.load(destClass); + return load((Class) src.getClass(), destClass).apply(src, creator.create()); + } + + /** + * 将源对象字段复制到目标对象 + * + * @param 源类泛型 + * @param src 源对象 + * @param allowMapNullVal 是否允许复制value为null的键值对 + * + * @return 目标对象 + */ + public static Map copyToMap(final S src, final boolean allowMapNullVal) { + if (src == null) { + return null; + } + HashMap dest = new HashMap(); + return load((Class) src.getClass(), HashMap.class, allowMapNullVal).apply(src, dest); + } + + /** + * 创建源类到目标类的复制器并缓存 + * + * @param 目标类泛型 + * @param 源类泛型 + * @param destClass 目标类名 + * @param srcClass 源类名 + * + * @return 复制器 + */ + public static Copier load(final Class srcClass, final Class destClass) { + if (destClass == srcClass) { + return CopierInner.copierOneCaches + .computeIfAbsent(srcClass, v -> create(srcClass, destClass)); + } else { + return CopierInner.copierTwoCaches + .computeIfAbsent(srcClass, t -> new ConcurrentHashMap<>()) + .computeIfAbsent(destClass, v -> create(srcClass, destClass)); + } + } + + /** + * 创建源类到目标类的复制器并缓存 + * + * @param 目标类泛型 + * @param 源类泛型 + * @param destClass 目标类名 + * @param srcClass 源类名 + * @param allowMapNullVal 是否允许复制value为null的键值对 + * + * @return 复制器 + */ + public static Copier load(final Class srcClass, final Class destClass, final boolean allowMapNullVal) { + if (destClass == srcClass) { + return CopierInner.copierOneCaches + .computeIfAbsent(srcClass, v -> create(srcClass, destClass, allowMapNullVal, (BiPredicate) null, (Map) null)); + } else { + return CopierInner.copierTwoCaches + .computeIfAbsent(srcClass, t -> new ConcurrentHashMap<>()) + .computeIfAbsent(destClass, v -> create(srcClass, destClass, allowMapNullVal, (BiPredicate) null, (Map) null)); + } + } + + /** + * 创建源类到目标类的复制器 + * + * @param 目标类泛型 + * @param 源类泛型 + * @param destClass 目标类名 + * @param srcClass 源类名 + * + * @return 复制器 + */ + public static Copier create(final Class srcClass, final Class destClass) { + return create(srcClass, destClass, (BiPredicate) null, (Map) null); + } + + /** + * 创建源类到目标类的复制器 + * + * @param 目标类泛型 + * @param 源类泛型 + * @param destClass 目标类名 + * @param srcClass 源类名 + * @param names 源字段名与目标字段名的映射关系 + * + * @return 复制器 + */ + public static Copier create(final Class srcClass, final Class destClass, final Map names) { + return create(srcClass, destClass, (BiPredicate) null, names); + } + + /** + * 创建源类到目标类的复制器 + * + * @param 目标类泛型 + * @param 源类泛型 + * @param destClass 目标类名 + * @param srcClass 源类名 + * @param srcColumnPredicate 需复制的字段名判断期 + * + * @return 复制器 + */ + @SuppressWarnings("unchecked") + public static Copier create(final Class srcClass, final Class destClass, final Predicate srcColumnPredicate) { + return create(srcClass, destClass, (sc, m) -> srcColumnPredicate.test(m), (Map) null); + } + + /** + * 创建源类到目标类的复制器 + * + * @param 目标类泛型 + * @param 源类泛型 + * @param destClass 目标类名 + * @param srcClass 源类名 + * @param srcColumnPredicate 需复制的字段名判断期 + * @param names 源字段名与目标字段名的映射关系 + * + * @return 复制器 + */ + @SuppressWarnings("unchecked") + public static Copier create(final Class srcClass, final Class destClass, final Predicate srcColumnPredicate, final Map names) { + return create(srcClass, destClass, (sc, m) -> srcColumnPredicate.test(m), names); + } + + /** + * 创建源类到目标类的复制器 + * + * @param 目标类泛型 + * @param 源类泛型 + * @param destClass 目标类名 + * @param srcClass 源类名 + * @param srcColumnPredicate 需复制的字段名判断期 + * + * @return 复制器 + */ + @SuppressWarnings("unchecked") + public static Copier create(final Class srcClass, final Class destClass, final BiPredicate srcColumnPredicate) { + return create(srcClass, destClass, srcColumnPredicate, (Map) null); + } + + /** + * 创建源类到目标类的复制器 + * + * @param 目标类泛型 + * @param 源类泛型 + * @param destClass 目标类名 + * @param srcClass 源类名 + * @param srcColumnPredicate 需复制的字段名判断期 + * @param names 源字段名与目标字段名的映射关系 + * + * @return 复制器 + */ + @SuppressWarnings("unchecked") + public static Copier create(final Class srcClass, final Class destClass, final BiPredicate srcColumnPredicate, final Map names) { + return create(srcClass, destClass, false, srcColumnPredicate, names); + } + + /** + * 创建源类到目标类的复制器 + * + * @param 目标类泛型 + * @param 源类泛型 + * @param destClass 目标类名 + * @param srcClass 源类名 + * @param allowMapNullVal 目标类是Map子类时是否允许复制value为null的键值对 + * @param srcColumnPredicate 需复制的字段名判断期 + * @param names 源字段名与目标字段名的映射关系 + * + * @return 复制器 + */ + @SuppressWarnings("unchecked") + public static Copier create(final Class srcClass, final Class destClass, final boolean allowMapNullVal, final BiPredicate srcColumnPredicate, final Map names) { + final boolean allowMapNull = allowMapNullVal && !ConcurrentHashMap.class.isAssignableFrom(destClass); + if (Map.class.isAssignableFrom(destClass) && Map.class.isAssignableFrom(srcClass)) { + final Map names0 = names; + if (srcColumnPredicate != null) { + if (names != null) { + return (S src, D dest) -> { + Map d = (Map) dest; + ((Map) src).forEach((k, v) -> { + if (srcColumnPredicate.test(null, k.toString()) && (allowMapNull || v != null)) { + d.put(names0.getOrDefault(k, k), v); + } + }); + return dest; + }; + } else { + return (S src, D dest) -> { + Map d = (Map) dest; + ((Map) src).forEach((k, v) -> { + if (srcColumnPredicate.test(null, k.toString()) && (allowMapNull || v != null)) { + d.put(k, v); + } + }); + return dest; + }; + } + } else if (names != null) { + return (S src, D dest) -> { + Map d = (Map) dest; + ((Map) src).forEach((k, v) -> { + if (allowMapNull || v != null) { + d.put(names0.getOrDefault(k, k), v); + } + }); + return dest; + }; + } + return new Copier() { + @Override + public D apply(S src, D dest) { + if (allowMapNull) { + ((Map) dest).putAll((Map) src); + } else { + Map d = (Map) dest; + ((Map) src).forEach((k, v) -> { + if (v != null) { + d.put(k, v); + } + }); + } + return dest; + } + }; + } + // ------------------------------------------------------------------------------ + final boolean destIsMap = Map.class.isAssignableFrom(destClass); + final boolean srcIsMap = Map.class.isAssignableFrom(srcClass); + final String supDynName = Copier.class.getName().replace('.', '/'); + final String destClassName = destClass.getName().replace('.', '/'); + final String srcClassName = srcClass.getName().replace('.', '/'); + final String destDesc = Type.getDescriptor(destClass); + final String srcDesc = Type.getDescriptor(srcClass); + final ClassLoader loader = Thread.currentThread().getContextClassLoader(); + final String utilClassName = Utility.class.getName().replace('.', '/'); + final String newDynName = "org/redkaledyn/copier/_Dyn" + Copier.class.getSimpleName() + + "__" + srcClass.getName().replace('.', '_').replace('$', '_') + + "__" + destClass.getName().replace('.', '_').replace('$', '_'); + try { + Class clz = RedkaleClassLoader.findDynClass(newDynName.replace('/', '.')); + return (Copier) (clz == null ? loader.loadClass(newDynName.replace('/', '.')) : clz).getDeclaredConstructor().newInstance(); + } catch (Throwable ex) { + } + final Predicate> throwPredicate = e -> !RuntimeException.class.isAssignableFrom(e); + // ------------------------------------------------------------------------------ + ClassWriter cw = new ClassWriter(COMPUTE_FRAMES); + FieldVisitor fv; + MethodVisitor mv; + AnnotationVisitor av0; + + cw.visit(V11, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynName, "Ljava/lang/Object;L" + supDynName + "<" + srcDesc + destDesc + ">;", "java/lang/Object", new String[]{supDynName}); + + { // 构造函数 + mv = (cw.visitMethod(ACC_PUBLIC, "", "()V", null, null)); + //mv.setDebug(true); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V", false); + mv.visitInsn(RETURN); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + if (srcIsMap) { //destClass不是Map + { + mv = (cw.visitMethod(ACC_PUBLIC, "apply", "(" + srcDesc + destDesc + ")" + destDesc, null, null)); + //mv.setDebug(true); + mv.visitVarInsn(ALOAD, 1); + mv.visitVarInsn(ALOAD, 2); + mv.visitInvokeDynamicInsn("accept", + "(" + destDesc + ")Ljava/util/function/BiConsumer;", + new Handle(Opcodes.H_INVOKESTATIC, "java/lang/invoke/LambdaMetafactory", "metafactory", "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;", false), + new Object[]{Type.getType("(Ljava/lang/Object;Ljava/lang/Object;)V"), new Handle(Opcodes.H_INVOKESTATIC, newDynName, "lambda$0", "(" + destDesc + "Ljava/lang/Object;Ljava/lang/Object;)V", false), Type.getType("(Ljava/lang/Object;Ljava/lang/Object;)V")}); + mv.visitMethodInsn(srcClass.isInterface() ? INVOKEINTERFACE : INVOKEVIRTUAL, srcClassName, "forEach", "(Ljava/util/function/BiConsumer;)V", srcClass.isInterface()); + mv.visitVarInsn(ALOAD, 2); + mv.visitInsn(ARETURN); + mv.visitMaxs(2, 3); + mv.visitEnd(); + } + { + final Map elements = new LinkedHashMap<>(); + for (java.lang.reflect.Field field : destClass.getFields()) { + if (Modifier.isStatic(field.getModifiers())) { + continue; + } + if (Modifier.isFinal(field.getModifiers())) { + continue; + } + if (!Modifier.isPublic(field.getModifiers())) { + continue; + } + final String sfname = field.getName(); + if (srcColumnPredicate != null && !srcColumnPredicate.test(field, sfname)) { + continue; + } + final String dfname = names == null ? sfname : names.getOrDefault(sfname, sfname); + elements.put(dfname, field); + } + + for (java.lang.reflect.Method setter : destClass.getMethods()) { + if (Modifier.isStatic(setter.getModifiers())) { + continue; + } + if (setter.getParameterTypes().length != 1) { + continue; + } + if (Utility.contains(setter.getExceptionTypes(), throwPredicate)) { + continue; //setter方法带有非RuntimeException异常 + } + if (!setter.getName().startsWith("set")) { + continue; + } + String sfname = Utility.readFieldName(setter.getName()); + if (sfname.isEmpty()) { + continue; + } + if (srcColumnPredicate != null && !srcColumnPredicate.test(setter, sfname)) { + continue; + } + final String dfname = names == null ? sfname : names.getOrDefault(sfname, sfname); + elements.put(dfname, setter); + } + + mv = cw.visitMethod(ACC_PRIVATE + ACC_STATIC + ACC_SYNTHETIC, "lambda$0", "(" + destDesc + "Ljava/lang/Object;Ljava/lang/Object;)V", null, null); + Label goLabel = new Label(); + int i = 0; + for (Map.Entry en : elements.entrySet()) { + final int index = ++i; + final java.lang.reflect.Type fieldType = en.getValue() instanceof Field + ? ((Field) en.getValue()).getGenericType() + : ((Method) en.getValue()).getGenericParameterTypes()[0]; + final Class fieldClass = en.getValue() instanceof Field + ? ((Field) en.getValue()).getType() + : ((Method) en.getValue()).getParameterTypes()[0]; + final boolean primitive = fieldClass.isPrimitive(); + + mv.visitLdcInsn(en.getKey()); + mv.visitVarInsn(ALOAD, 1); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "equals", "(Ljava/lang/Object;)Z", false); + Label ifeq = index == elements.size() ? goLabel : new Label(); + mv.visitJumpInsn(IFEQ, ifeq); + if (primitive) { + mv.visitVarInsn(ALOAD, 2); + mv.visitJumpInsn(IFNULL, ifeq); + } + mv.visitVarInsn(ALOAD, 0); + + if (fieldClass == boolean.class) { + mv.visitFieldInsn(GETSTATIC, "java/lang/Boolean", "TYPE", "Ljava/lang/Class;"); + } else if (fieldClass == byte.class) { + mv.visitFieldInsn(GETSTATIC, "java/lang/Byte", "TYPE", "Ljava/lang/Class;"); + } else if (fieldClass == char.class) { + mv.visitFieldInsn(GETSTATIC, "java/lang/Character", "TYPE", "Ljava/lang/Class;"); + } else if (fieldClass == short.class) { + mv.visitFieldInsn(GETSTATIC, "java/lang/Short", "TYPE", "Ljava/lang/Class;"); + } else if (fieldClass == int.class) { + mv.visitFieldInsn(GETSTATIC, "java/lang/Integer", "TYPE", "Ljava/lang/Class;"); + } else if (fieldClass == float.class) { + mv.visitFieldInsn(GETSTATIC, "java/lang/Float", "TYPE", "Ljava/lang/Class;"); + } else if (fieldClass == long.class) { + mv.visitFieldInsn(GETSTATIC, "java/lang/Long", "TYPE", "Ljava/lang/Class;"); + } else if (fieldClass == double.class) { + mv.visitFieldInsn(GETSTATIC, "java/lang/Double", "TYPE", "Ljava/lang/Class;"); + } else { + mv.visitLdcInsn(Type.getType(Type.getDescriptor(fieldClass))); + } + + mv.visitVarInsn(ALOAD, 2); + mv.visitMethodInsn(INVOKESTATIC, utilClassName, "convertValue", "(Ljava/lang/reflect/Type;Ljava/lang/Object;)Ljava/lang/Object;", false); + if (fieldClass == boolean.class) { + mv.visitTypeInsn(CHECKCAST, "java/lang/Boolean"); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Boolean", "booleanValue", "()Z", false); + } else if (fieldClass == byte.class) { + mv.visitTypeInsn(CHECKCAST, "java/lang/Byte"); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Byte", "byteValue", "()B", false); + } else if (fieldClass == short.class) { + mv.visitTypeInsn(CHECKCAST, "java/lang/Short"); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Short", "shortValue", "()S", false); + } else if (fieldClass == char.class) { + mv.visitTypeInsn(CHECKCAST, "java/lang/Character"); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Character", "charValue", "()C", false); + } else if (fieldClass == int.class) { + mv.visitTypeInsn(CHECKCAST, "java/lang/Integer"); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I", false); + } else if (fieldClass == float.class) { + mv.visitTypeInsn(CHECKCAST, "java/lang/Float"); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Float", "floatValue", "()F", false); + } else if (fieldClass == long.class) { + mv.visitTypeInsn(CHECKCAST, "java/lang/Long"); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Long", "longValue", "()J", false); + } else if (fieldClass == double.class) { + mv.visitTypeInsn(CHECKCAST, "java/lang/Double"); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Double", "doubleValue", "()D", false); + } else { + mv.visitTypeInsn(CHECKCAST, fieldClass.getName().replace('.', '/')); + } + + if (en.getValue() instanceof Field) { + mv.visitFieldInsn(PUTFIELD, destClassName, en.getKey(), Type.getDescriptor(fieldClass)); + } else { + Method setter = (Method) en.getValue(); + mv.visitMethodInsn(destClass.isInterface() ? INVOKEINTERFACE : INVOKEVIRTUAL, destClassName, setter.getName(), Type.getMethodDescriptor(setter), destClass.isInterface()); + } + if (index == elements.size()) { + mv.visitLabel(goLabel); + } else { + mv.visitJumpInsn(GOTO, goLabel); + mv.visitLabel(ifeq); + } + mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); + } + + mv.visitInsn(RETURN); + mv.visitMaxs(3, 3); + mv.visitEnd(); + } + } else { + mv = (cw.visitMethod(ACC_PUBLIC, "apply", "(" + srcDesc + destDesc + ")" + destDesc, null, null)); + //mv.setDebug(true); + + for (java.lang.reflect.Field field : srcClass.getFields()) { + if (Modifier.isStatic(field.getModifiers())) { + continue; + } + if (Modifier.isFinal(field.getModifiers())) { + continue; + } + if (!Modifier.isPublic(field.getModifiers())) { + continue; + } + final String sfname = field.getName(); + if (srcColumnPredicate != null && !srcColumnPredicate.test(field, sfname)) { + continue; + } + + final String dfname = names == null ? sfname : names.getOrDefault(sfname, sfname); + if (destIsMap) { + Class st = field.getType(); + String td = Type.getDescriptor(st); + if (allowMapNull || st.isPrimitive()) { + mv.visitVarInsn(ALOAD, 2); + mv.visitLdcInsn(dfname); + mv.visitVarInsn(ALOAD, 1); + mv.visitFieldInsn(GETFIELD, srcClassName, sfname, td); + if (st == boolean.class) { + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;", false); + } else if (st == byte.class) { + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Byte", "valueOf", "(B)Ljava/lang/Byte;", false); + } else if (st == short.class) { + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Short", "valueOf", "(S)Ljava/lang/Short;", false); + } else if (st == char.class) { + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Character", "valueOf", "(C)Ljava/lang/Character;", false); + } else if (st == int.class) { + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", false); + } else if (st == float.class) { + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;", false); + } else if (st == long.class) { + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;", false); + } else if (st == double.class) { + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;", false); + } + mv.visitMethodInsn(destClass.isInterface() ? INVOKEINTERFACE : INVOKEVIRTUAL, destClassName, "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", destClass.isInterface()); + mv.visitInsn(POP); + } else { + mv.visitVarInsn(ALOAD, 1); + mv.visitFieldInsn(GETFIELD, srcClassName, sfname, td); + mv.visitVarInsn(ASTORE, 3); + mv.visitVarInsn(ALOAD, 3); + Label ifLabel = new Label(); + mv.visitJumpInsn(IFNULL, ifLabel); + mv.visitVarInsn(ALOAD, 2); + mv.visitLdcInsn(dfname); + mv.visitVarInsn(ALOAD, 3); + mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Map", "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", true); + mv.visitInsn(POP); + mv.visitLabel(ifLabel); + mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); + } + } else { + java.lang.reflect.Method setter = null; + try { + if (!field.getType().equals(destClass.getField(dfname).getType())) { + continue; + } + } catch (Exception e) { + try { + char[] cs = dfname.toCharArray(); + cs[0] = Character.toUpperCase(cs[0]); + String dfname2 = new String(cs); + setter = destClass.getMethod("set" + dfname2, field.getType()); + } catch (Exception e2) { + continue; + } + } + mv.visitVarInsn(ALOAD, 2); + mv.visitVarInsn(ALOAD, 1); + String td = Type.getDescriptor(field.getType()); + mv.visitFieldInsn(GETFIELD, srcClassName, sfname, td); + if (setter == null) { + mv.visitFieldInsn(PUTFIELD, destClassName, dfname, td); + } else { + mv.visitMethodInsn(destClass.isInterface() ? INVOKEINTERFACE : INVOKEVIRTUAL, destClassName, setter.getName(), Type.getMethodDescriptor(setter), destClass.isInterface()); + } + } + } + + for (java.lang.reflect.Method getter : srcClass.getMethods()) { + if (Modifier.isStatic(getter.getModifiers())) { + continue; + } + if (getter.getParameterTypes().length > 0) { + continue; + } + if ("getClass".equals(getter.getName())) { + continue; + } + if (Utility.contains(getter.getExceptionTypes(), throwPredicate)) { + continue; //setter方法带有非RuntimeException异常 + } + if (!getter.getName().startsWith("get") && !getter.getName().startsWith("is")) { + continue; + } + final String sfname = Utility.readFieldName(getter.getName()); + if (sfname.isEmpty()) { + continue; + } + if (srcColumnPredicate != null && !srcColumnPredicate.test(getter, sfname)) { + continue; + } + + final String dfname = names == null ? sfname : names.getOrDefault(sfname, sfname); + if (destIsMap) { + Class st = getter.getReturnType(); + if (allowMapNull || st.isPrimitive()) { + mv.visitVarInsn(ALOAD, 2); + mv.visitLdcInsn(dfname); + mv.visitVarInsn(ALOAD, 1); + mv.visitMethodInsn(srcClass.isInterface() ? INVOKEINTERFACE : INVOKEVIRTUAL, srcClassName, getter.getName(), Type.getMethodDescriptor(getter), srcClass.isInterface()); + if (st == boolean.class) { + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;", false); + } else if (st == byte.class) { + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Byte", "valueOf", "(B)Ljava/lang/Byte;", false); + } else if (st == short.class) { + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Short", "valueOf", "(S)Ljava/lang/Short;", false); + } else if (st == char.class) { + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Character", "valueOf", "(C)Ljava/lang/Character;", false); + } else if (st == int.class) { + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", false); + } else if (st == float.class) { + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;", false); + } else if (st == long.class) { + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;", false); + } else if (st == double.class) { + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;", false); + } + mv.visitMethodInsn(destClass.isInterface() ? INVOKEINTERFACE : INVOKEVIRTUAL, destClassName, "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", destClass.isInterface()); + mv.visitInsn(POP); + } else { + mv.visitVarInsn(ALOAD, 1); + mv.visitMethodInsn(srcClass.isInterface() ? INVOKEINTERFACE : INVOKEVIRTUAL, srcClassName, getter.getName(), Type.getMethodDescriptor(getter), srcClass.isInterface()); + mv.visitVarInsn(ASTORE, 3); + mv.visitVarInsn(ALOAD, 3); + Label ifLabel = new Label(); + mv.visitJumpInsn(IFNULL, ifLabel); + mv.visitVarInsn(ALOAD, 2); + mv.visitLdcInsn(dfname); + mv.visitVarInsn(ALOAD, 3); + mv.visitMethodInsn(destClass.isInterface() ? INVOKEINTERFACE : INVOKEVIRTUAL, destClassName, "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", destClass.isInterface()); + mv.visitInsn(POP); + mv.visitLabel(ifLabel); + mv.visitLineNumber(47, ifLabel); + mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); + } + } else { + java.lang.reflect.Method setter = null; + java.lang.reflect.Field srcField = null; + char[] cs = dfname.toCharArray(); + cs[0] = Character.toUpperCase(cs[0]); + String dfname2 = new String(cs); + try { + setter = destClass.getMethod("set" + dfname2, getter.getReturnType()); + if (Utility.contains(setter.getExceptionTypes(), throwPredicate)) { + continue; //setter方法带有非RuntimeException异常 + } + } catch (Exception e) { + try { + srcField = destClass.getField(dfname); + if (!getter.getReturnType().equals(srcField.getType())) { + continue; + } + } catch (Exception e2) { + continue; + } + } + mv.visitVarInsn(ALOAD, 2); + mv.visitVarInsn(ALOAD, 1); + mv.visitMethodInsn(srcClass.isInterface() ? INVOKEINTERFACE : INVOKEVIRTUAL, srcClassName, getter.getName(), Type.getMethodDescriptor(getter), srcClass.isInterface()); + if (srcField == null) { + mv.visitMethodInsn(destClass.isInterface() ? INVOKEINTERFACE : INVOKEVIRTUAL, destClassName, setter.getName(), Type.getMethodDescriptor(setter), destClass.isInterface()); + } else { + mv.visitFieldInsn(PUTFIELD, destClassName, dfname, Type.getDescriptor(getter.getReturnType())); + } + } + } + mv.visitVarInsn(ALOAD, 2); + mv.visitInsn(ARETURN); + mv.visitMaxs(3, 3); + mv.visitEnd(); + } + { + mv = (cw.visitMethod(ACC_PUBLIC + ACC_BRIDGE + ACC_SYNTHETIC, "apply", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", null, null)); + //mv.setDebug(true); + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ALOAD, 1); + mv.visitTypeInsn(CHECKCAST, srcClassName); + mv.visitVarInsn(ALOAD, 2); + mv.visitTypeInsn(CHECKCAST, destClassName); + mv.visitMethodInsn(INVOKEVIRTUAL, newDynName, "apply", "(" + srcDesc + destDesc + ")" + destDesc, false); + mv.visitInsn(ARETURN); + mv.visitMaxs(3, 3); + mv.visitEnd(); + } + cw.visitEnd(); + // ------------------------------------------------------------------------------ + byte[] bytes = cw.toByteArray(); + Class newClazz = new ClassLoader(loader) { + public final Class loadClass(String name, byte[] b) { + return defineClass(name, b, 0, b.length); + } + }.loadClass(newDynName.replace('/', '.'), bytes); + RedkaleClassLoader.putDynClass(newDynName.replace('/', '.'), bytes, newClazz); + RedkaleClassLoader.putReflectionDeclaredConstructors(newClazz, newDynName.replace('/', '.')); + try { + return (Copier) newClazz.getDeclaredConstructor().newInstance(); + } catch (Exception ex) { + throw new RedkaleException(ex); + } + } + + static class CopierInner { + + static final ConcurrentHashMap copierOneCaches = new ConcurrentHashMap(); + + static final ConcurrentHashMap> copierTwoCaches = new ConcurrentHashMap(); + + } + +} diff --git a/src/main/java/org/redkale/util/ResourceFactory.java b/src/main/java/org/redkale/util/ResourceFactory.java index a35668426..e915a4987 100644 --- a/src/main/java/org/redkale/util/ResourceFactory.java +++ b/src/main/java/org/redkale/util/ResourceFactory.java @@ -966,7 +966,12 @@ public final class ResourceFactory { } } if (rs == null && re == null && autoRegNull && rcname.indexOf(Resource.PARENT_NAME) < 0) { - register(rcname, gencType, null); //自动注入null的值 + if (rcname.startsWith("${")) { + String sub = rcname.substring(rcname.lastIndexOf("${") + 2, rcname.lastIndexOf('}')); + register(sub, gencType, null); //自动注入null的值 + } else { + register(rcname, gencType, null); //自动注入null的值 + } re = findEntry(rcname, gencType); } if (re != null) { diff --git a/src/test/java/org/redkale/test/util/ReproduceBeanMap.java b/src/test/java/org/redkale/test/util/CopierBeanMap.java similarity index 72% rename from src/test/java/org/redkale/test/util/ReproduceBeanMap.java rename to src/test/java/org/redkale/test/util/CopierBeanMap.java index e2701e986..d0078389f 100644 --- a/src/test/java/org/redkale/test/util/ReproduceBeanMap.java +++ b/src/test/java/org/redkale/test/util/CopierBeanMap.java @@ -4,16 +4,16 @@ package org.redkale.test.util; import java.util.Map; -import org.redkale.util.Reproduce; +import org.redkale.util.Copier; /** * * @author zhangjx */ -public class ReproduceBeanMap implements Reproduce, TestInterface> { +public class CopierBeanMap implements Copier> { @Override - public Map apply(Map dest, TestInterface src) { + public Map apply(TestInterface src, Map dest) { Object v; dest.put("id", src.getId()); @@ -24,7 +24,7 @@ public class ReproduceBeanMap implements Reproduce, TestInte return dest; } - public Map run(Map dest, TestBean src) { + public Map run(TestBean src, Map dest) { Object v; v = src.getName(); diff --git a/src/test/java/org/redkale/test/util/ReproduceMapBean.java b/src/test/java/org/redkale/test/util/CopierMapBean.java similarity index 84% rename from src/test/java/org/redkale/test/util/ReproduceMapBean.java rename to src/test/java/org/redkale/test/util/CopierMapBean.java index 3b3d017d4..972f648c2 100644 --- a/src/test/java/org/redkale/test/util/ReproduceMapBean.java +++ b/src/test/java/org/redkale/test/util/CopierMapBean.java @@ -9,10 +9,10 @@ import org.redkale.util.*; /** * @author zhangjx */ -public class ReproduceMapBean implements Reproduce { +public class CopierMapBean implements Copier { @Override - public TestBean apply(TestBean dest, HashMap src) { + public TestBean apply(HashMap src, TestBean dest) { src.forEach((k, v) -> { if ("id".equals(k) && v != null) { dest.setId(Utility.convertValue(int.class, v)); diff --git a/src/test/java/org/redkale/test/util/ReproduceTest.java b/src/test/java/org/redkale/test/util/CopierTest.java similarity index 82% rename from src/test/java/org/redkale/test/util/ReproduceTest.java rename to src/test/java/org/redkale/test/util/CopierTest.java index b963be205..5b363e9a3 100644 --- a/src/test/java/org/redkale/test/util/ReproduceTest.java +++ b/src/test/java/org/redkale/test/util/CopierTest.java @@ -13,10 +13,10 @@ import org.redkale.util.*; * * @author zhangjx */ -public class ReproduceTest { +public class CopierTest { public static void main(String[] args) throws Throwable { - ReproduceTest test = new ReproduceTest(); + CopierTest test = new CopierTest(); test.run1(); test.run2(); test.run3(); @@ -33,9 +33,9 @@ public class ReproduceTest { bean.time = 55555L; bean.setName("haha"); bean.setMap(Utility.ofMap("aa", "bbb")); - Map map = new TreeMap(Reproduce.copy(Map.class, bean)); + Map map = new TreeMap(Copier.copy(bean, Map.class)); System.out.println(JsonConvert.root().convertTo(map)); - TreeMap rs = Reproduce.copy(TreeMap.class, bean); + TreeMap rs = Copier.copy(bean, TreeMap.class); Assertions.assertEquals(bean.toString(), JsonConvert.root().convertTo(rs)); } @@ -47,7 +47,7 @@ public class ReproduceTest { bean.setName("haha"); bean.setMap(Utility.ofMap("aa", "bbb")); TreeMap rs = new TreeMap(); - Reproduce.load(Map.class, TestInterface.class).apply(rs, bean); + Copier.load(TestInterface.class, Map.class).apply(bean, rs); System.out.println(JsonConvert.root().convertTo(rs)); } @@ -59,8 +59,8 @@ public class ReproduceTest { map.put("id", "222"); map.put("map", Utility.ofMap("aa", "bbb")); TestBean bean = new TestBean(); - Reproduce.load(TestInterface.class, Map.class).apply(bean, map); - Assertions.assertEquals("{\"id\":222,\"map\":{\"aa\":\"bbb\"},\"time\":0}", JsonConvert.root().convertTo(bean)); + TestInterface ti = Copier.load(Map.class, TestInterface.class).apply(map, new TestBean());; + Assertions.assertEquals("{\"id\":222,\"map\":{\"aa\":\"bbb\"},\"time\":0}", JsonConvert.root().convertTo(ti)); } @Test @@ -71,7 +71,7 @@ public class ReproduceTest { map.put("time", "55555"); map.put("id", "222"); map.put("map", Utility.ofMap("aa", "bbb")); - Reproduce.load(TestBean.class, Map.class).apply(bean, map); + Copier.load(Map.class, TestBean.class).apply(map, bean); System.out.println(JsonConvert.root().convertTo(bean)); map.put("time", 55555L); map.put("id", 222); @@ -86,7 +86,7 @@ public class ReproduceTest { map.put("id", "222"); map.put("map", Utility.ofMap("aa", "bbb")); Map rs = new TreeMap(); - Reproduce.load(Map.class, Map.class).apply(rs, map); + Copier.load(Map.class, Map.class).apply(map, rs); System.out.println("Map: " + JsonConvert.root().convertTo(rs)); Assertions.assertEquals(JsonConvert.root().convertTo(map), JsonConvert.root().convertTo(rs)); } @@ -98,7 +98,7 @@ public class ReproduceTest { bean.time = 55555L; bean.setName(null); bean.setMap(Utility.ofMap("aa", "bbb")); - ConcurrentHashMap rs = Reproduce.copy(ConcurrentHashMap.class, bean); + ConcurrentHashMap rs = Copier.copy(bean, ConcurrentHashMap.class); System.out.println(JsonConvert.root().convertTo(rs)); System.out.println("------------------------------------------"); } @@ -111,7 +111,7 @@ public class ReproduceTest { map.put("time", "55555"); map.put("id", null); map.put("map", Utility.ofMap("aa", "bbb")); - Reproduce.load(TestBean.class, Map.class).apply(bean, map); + Copier.load(Map.class, TestBean.class).apply(map, bean); System.out.println(JsonConvert.root().convertTo(bean)); } } diff --git a/src/test/java/org/redkale/test/util/UntilTestMain.java b/src/test/java/org/redkale/test/util/UntilTestMain.java index 0078dd3a6..cc0b03373 100644 --- a/src/test/java/org/redkale/test/util/UntilTestMain.java +++ b/src/test/java/org/redkale/test/util/UntilTestMain.java @@ -18,7 +18,7 @@ import org.redkale.util.*; public class UntilTestMain { public static void main(String[] args) throws Throwable { - reproduce(args); + copy(args); attribute(args); aes(args); } @@ -46,17 +46,17 @@ public class UntilTestMain { System.out.println(Utility.binToHexString(secret.getBytes())); } - public static void reproduce(String[] args) throws Throwable { + public static void copy(String[] args) throws Throwable { final TestBean bean = new TestBean(); bean.setId(123456); bean.setName("zhangjx"); bean.time = 2000; final TestXBean beanx = new TestXBean(); - Reproduce action1 = Reproduce.create(TestXBean.class, TestBean.class); - Reproduce action2 = new Reproduce() { + Copier action1 = Copier.create(TestBean.class, TestXBean.class); + Copier action2 = new Copier() { @Override - public TestXBean apply(TestXBean dest, TestBean src) { + public TestXBean apply(TestBean src, TestXBean dest) { dest.time = src.time; dest.setId(src.getId()); dest.setName(src.getName()); @@ -67,13 +67,13 @@ public class UntilTestMain { final int count = 1_000_000; long s = System.nanoTime(); for (int i = 0; i < count; i++) { - action2.apply(beanx, bean); + action2.apply(bean, beanx); } long e = System.nanoTime() - s; System.out.println("静态Reproduce耗时: " + e); s = System.nanoTime(); for (int i = 0; i < count; i++) { - action1.apply(beanx, bean); + action1.apply(bean, beanx); } e = System.nanoTime() - s; System.out.println("动态Reproduce耗时: " + e);