优化Application.updateSourceProperties方法实现

This commit is contained in:
Redkale
2022-12-05 17:43:39 +08:00
parent f8eb3d03ad
commit e781be25a0
6 changed files with 224 additions and 144 deletions

View File

@@ -6,6 +6,15 @@
### true: auto ddl;
#redkale.datasource[platf].table-autoddl = true
############ DataSource @Resource(name="user") ############
#redkale.datasource[user].read.url = jdbc:mysql://127.0.0.1:3306/user_r?allowPublicKeyRetrieval=true&rewriteBatchedStatements=true&serverTimezone=UTC&characterEncoding=utf8
#redkale.datasource[user].read.user = root
#redkale.datasource[user].read.password = 12345678
#redkale.datasource[user].write.url = jdbc:mysql://127.0.0.1:3306/user_w?allowPublicKeyRetrieval=true&rewriteBatchedStatements=true&serverTimezone=UTC&characterEncoding=utf8
#redkale.datasource[user].write.user = root
#redkale.datasource[user].write.password = 12345678
############ CacheSource @Resource(name="usersession") ############
#redkale.cachesource[usersession].node[0].url = redis://127.0.0.1:6363

View File

@@ -152,11 +152,8 @@ public final class Application {
//Source 原始的配置资源, 只会存在redkale.datasource(.|[) redkale.cachesource(.|[)开头的配置项
final Properties sourceProperties = new Properties();
//CacheSource 配置信息
final Map<String, AnyValue> cacheResources = new ConcurrentHashMap<>();
//DataSource 配置信息
final Map<String, AnyValue> dataResources = new ConcurrentHashMap<>();
//sourceProperties对应的AnyValue类型对象
AnyValue sourceConfig;
//CacheSource 资源
final List<CacheSource> cacheSources = new CopyOnWriteArrayList<>();
@@ -733,7 +730,7 @@ public final class Application {
if (persist.isFile() && persist.canRead()) {
logger.log(Level.WARNING, "persistence.xml is deprecated, replaced by source.properties");
InputStream in = new FileInputStream(persist);
dataResources.putAll(DataSources.loadAnyValuePersistenceXml(in));
sourceProperties.putAll(DataSources.loadSourceProperties(in));
in.close();
}
}
@@ -756,7 +753,7 @@ public final class Application {
try {
final URI xmlURI = RedkaleClassLoader.getConfResourceAsURI(configFromCache ? null : confDir, "persistence.xml");
InputStream in = xmlURI.toURL().openStream();
dataResources.putAll(DataSources.loadAnyValuePersistenceXml(in));
sourceProperties.putAll(DataSources.loadSourceProperties(in));
in.close();
logger.log(Level.WARNING, "persistence.xml is deprecated, replaced by source.properties");
} catch (Exception e) { //没有文件 跳过
@@ -773,7 +770,7 @@ public final class Application {
String key = prop.getValue("name");
String value = prop.getValue("value");
if (key == null || value == null) continue;
updateEnvironmentProperty(key, value, null);
updateEnvironmentProperty(key, value, null, null);
}
String dfloads = propertiesConf.getValue("load");
if (dfloads != null) {
@@ -788,7 +785,7 @@ public final class Application {
in.close();
if (logger.isLoggable(Level.FINEST)) logger.log(Level.FINEST, "load properties(" + dfload + ") size = " + ps.size());
ps.forEach((x, y) -> { //load中的配置项除了redkale.cachesource.和redkale.datasource.开头不应该有其他redkale.开头配置项
updateEnvironmentProperty(x.toString(), y, null);
updateEnvironmentProperty(x.toString(), y, null, null);
});
} catch (Exception e) {
logger.log(Level.WARNING, "load properties(" + dfload + ") error", e);
@@ -824,30 +821,8 @@ public final class Application {
}
final AnyValue[] sourceConfs = resources.getAnyValues("source");
if (sourceConfs != null && sourceConfs.length > 0) {
//兼容 <source>节点 【已废弃】
logger.log(Level.WARNING, "<source> in application.xml is deprecated, replaced by source.properties");
for (AnyValue sourceConf : sourceConfs) {
cacheResources.put(sourceConf.getValue("name"), sourceConf);
}
}
}
//sourceProperties转换成cacheResources、dataResources的AnyValue
if (!sourceProperties.isEmpty()) {
AnyValue sourceConf = AnyValue.loadFromProperties(sourceProperties);
AnyValue redNode = sourceConf.getAnyValue("redkale");
if (redNode != null) {
AnyValue cacheNode = redNode.getAnyValue("cachesource");
if (cacheNode != null) cacheNode.forEach(null, (k, v) -> {
if (v.getValue("name") != null) logger.log(Level.WARNING, "cachesource[" + k + "].name " + v.getValue("name") + " replaced by " + k);
((DefaultAnyValue) v).setValue("name", k);
cacheResources.put(k, v);
});
AnyValue dataNode = redNode.getAnyValue("datasource");
if (dataNode != null) dataNode.forEach(null, (k, v) -> {
if (v.getValue("name") != null) logger.log(Level.WARNING, "datasource[" + k + "].name " + v.getValue("name") + " replaced by " + k);
((DefaultAnyValue) v).setValue("name", k);
dataResources.put(k, v);
});
//<source>节点 【已废弃】
throw new RuntimeException("<source> in application.xml is deprecated, replaced by source.properties");
}
}
@@ -1002,11 +977,29 @@ public final class Application {
initResources();
}
private AnyValue findSourceConfig(String sourceName, String sourceType) {
if (sourceConfig == null) {
synchronized ((sourceProperties)) {
if (sourceConfig == null) {
sourceConfig = AnyValue.loadFromProperties(sourceProperties);
}
}
}
AnyValue redNode = sourceConfig.getAnyValue("redkale");
if (redNode != null) {
AnyValue sourceNode = redNode.getAnyValue(sourceType);
if (sourceNode != null) {
return sourceNode.getAnyValue(sourceName);
}
}
return null;
}
CacheSource loadCacheSource(final String sourceName, boolean autoMemory) {
long st = System.currentTimeMillis();
CacheSource old = resourceFactory.find(sourceName, CacheSource.class);
if (old != null) return old;
final AnyValue sourceConf = cacheResources.get(sourceName);
final AnyValue sourceConf = findSourceConfig(sourceName, "cachesource");
if (sourceConf == null) {
if (!autoMemory) return null;
CacheSource source = new CacheMemorySource(sourceName);
@@ -1017,10 +1010,10 @@ public final class Application {
logger.info("[" + Thread.currentThread().getName() + "] Load CacheSource resourceName = " + sourceName + ", source = " + source + " in " + (System.currentTimeMillis() - st) + " ms");
return source;
}
String classval = sourceConf.getValue("type");
String classVal = sourceConf.getValue("type");
try {
CacheSource source = null;
if (classval == null || classval.isEmpty()) {
if (classVal == null || classVal.isEmpty()) {
RedkaleClassLoader.putServiceLoader(CacheSourceProvider.class);
List<CacheSourceProvider> providers = new ArrayList<>();
Iterator<CacheSourceProvider> it = ServiceLoader.load(CacheSourceProvider.class, serverClassLoader).iterator();
@@ -1041,7 +1034,7 @@ public final class Application {
}
}
} else {
Class sourceType = serverClassLoader.loadClass(classval);
Class sourceType = serverClassLoader.loadClass(classVal);
RedkaleClassLoader.putReflectionPublicConstructors(sourceType, sourceType.getName());
source = (CacheSource) sourceType.getConstructor().newInstance();
}
@@ -1062,7 +1055,7 @@ public final class Application {
DataSource loadDataSource(final String sourceName, boolean autoMemory) {
DataSource old = resourceFactory.find(sourceName, DataSource.class);
if (old != null) return old;
final AnyValue sourceConf = dataResources.get(sourceName);
final AnyValue sourceConf = findSourceConfig(sourceName, "datasource");
if (sourceConf == null) {
if (!autoMemory) return null;
DataSource source = new DataMemorySource(sourceName);
@@ -1712,18 +1705,22 @@ public final class Application {
return value == null ? value : value.replace("${APP_HOME}", homePath).replace("${APP_NAME}", name);
}
//初始化加载时:changeCache=null
//配置项动态变更时 changeCache!=null, 由调用方统一执行ResourceFactory.register(notifyCache)
//初始化加载时:envChangeCache=null
//配置项动态变更时 envChangeCache!=null, 由调用方统一执行ResourceFactory.register(envChangeCache)
//key只会是system.property.、mimetype.property.、redkale.cachesource(.|[)、redkale.datasource(.|[)和其他非redkale.开头的配置项
void updateEnvironmentProperty(String key, Object value, Properties changeCache) {
void updateEnvironmentProperty(String key, Object value, Properties envChangeCache, Properties sourceChangeCache) {
if (key == null || value == null) return;
String val = replaceValue(value.toString());
if (key.startsWith("redkale.datasource.") || key.startsWith("redkale.datasource[")
|| key.startsWith("redkale.cachesource.") || key.startsWith("redkale.cachesource[")) {
sourceProperties.put(key, val);
if (sourceChangeCache == null) {
sourceProperties.put(key, val);
} else {
sourceChangeCache.put(key, val);
}
} else if (key.startsWith("system.property.")) {
String propName = key.substring("system.property.".length());
if (changeCache != null || System.getProperty(propName) == null) { //命令行传参数优先级高
if (envChangeCache != null || System.getProperty(propName) == null) { //命令行传参数优先级高
System.setProperty(propName, val);
}
} else if (key.startsWith("mimetype.property.")) {
@@ -1732,10 +1729,10 @@ public final class Application {
Object old = resourceFactory.find(key, String.class);
if (!Objects.equals(val, old)) {
envProperties.put(key, val);
if (changeCache == null) {
if (envChangeCache == null) {
resourceFactory.register(key, val);
} else {
changeCache.put(key, val);
envChangeCache.put(key, val);
}
}
} else {
@@ -1746,15 +1743,62 @@ public final class Application {
Object old = resourceFactory.find(newkey, String.class);
if (!Objects.equals(val, old)) {
envProperties.put(key, val);
if (changeCache == null) {
if (envChangeCache == null) {
resourceFactory.register(newkey, val);
} else {
changeCache.put(newkey, val);
envChangeCache.put(newkey, val);
}
}
}
}
void updateSourceProperties(Properties sourceChangeCache) {
if (sourceChangeCache == null || sourceChangeCache.isEmpty()) return;
boolean same = true;
for (Map.Entry<Object, Object> en : sourceChangeCache.entrySet()) {
String key = en.getKey().toString();
if (key.startsWith("redkale.datasource.") || key.startsWith("redkale.datasource[")
|| key.startsWith("redkale.cachesource.") || key.startsWith("redkale.cachesource[")) {
if (!Objects.equals(en.getValue(), sourceProperties.get(key))) {
same = false;
}
} else {
throw new RuntimeException("source properties contains illegal key: " + key);
}
}
if (same) return; //无内容改变
AnyValue redNode = AnyValue.loadFromProperties(sourceChangeCache).getAnyValue("redkale");
AnyValue cacheNode = redNode.getAnyValue("cachesource");
if (cacheNode != null) {
cacheNode.forEach(null, (name, conf) -> {
CacheSource source = Utility.find(cacheSources, s -> Objects.equals(s.resourceName(), name));
if (source == null) return;
List<ResourceEvent> events = new ArrayList<>();
AnyValue old = findSourceConfig(name, "cachesource");
conf.forEach((k, v) -> {
events.add(ResourceEvent.create(k, v, old == null ? null : old.getValue(k)));
((DefaultAnyValue) old).setValue(k, v);
});
((AbstractCacheSource) source).onChange(events.toArray(new ResourceEvent[events.size()]));
});
}
AnyValue sourceNode = redNode.getAnyValue("datasource");
if (sourceNode != null) {
sourceNode.forEach(null, (name, conf) -> {
DataSource source = Utility.find(dataSources, s -> Objects.equals(s.resourceName(), name));
if (source == null) return;
List<ResourceEvent> events = new ArrayList<>();
AnyValue old = findSourceConfig(name, "datasource");
conf.forEach((k, v) -> {
events.add(ResourceEvent.create(k, v, old == null ? null : old.getValue(k)));
((DefaultAnyValue) old).setValue(k, v);
});
((AbstractDataSource) source).onChange(events.toArray(new ResourceEvent[events.size()]));
});
}
sourceProperties.putAll(sourceChangeCache);
}
private static String generateHelp() {
return ""
+ "Usage: redkale [command] [arguments]\r\n"

View File

@@ -53,13 +53,19 @@ public abstract class PropertiesAgent {
public abstract void destroy(AnyValue conf);
protected void updateEnvironmentProperties(Application application, Properties props) {
Properties changeCache = new Properties();
props.forEach((k, v) -> application.updateEnvironmentProperty(k.toString(), v, changeCache));
application.resourceFactory.register(changeCache, "", Environment.class);
Properties envChangeCache = new Properties();
Properties sourceChangeCache = new Properties();
props.forEach((k, v) -> application.updateEnvironmentProperty(k.toString(), v, envChangeCache, sourceChangeCache));
if (!envChangeCache.isEmpty()) {
application.resourceFactory.register(envChangeCache, "", Environment.class);
}
if (!sourceChangeCache.isEmpty()) {
application.updateSourceProperties(sourceChangeCache);
}
}
protected void putEnvironmentProperty(Application application, String key, Object value) {
application.updateEnvironmentProperty(key, value, null);
application.updateEnvironmentProperty(key, value, null, null);
}
protected void reconfigLogging(Application application, Properties loggingProperties) {

View File

@@ -9,7 +9,6 @@ import java.io.*;
import java.net.*;
import java.util.*;
import org.redkale.util.AnyValue;
import org.redkale.util.AnyValue.DefaultAnyValue;
import org.redkale.util.RedkaleClassLoader;
/**
@@ -228,27 +227,28 @@ public final class DataSources {
return createDataSource(unitName, readprop, writeprop);
}
//@since 2.7.0
public static Map<String, AnyValue> loadAnyValuePersistenceXml(final InputStream in) {
//@since 2.8.0 临时给Application使用直到DataSources整个类移除
public static Properties loadSourceProperties(final InputStream in) {
try {
Map<String, Properties> map = loadPersistenceXml(in);
Map<String, AnyValue> rs = new HashMap<>();
final Properties sourceProperties = new Properties();
map.forEach((unitName, prop) -> {
if (unitName.endsWith(".write")) return;
DefaultAnyValue v = parseProperties(prop);
if (unitName.endsWith(".read")) {
String name = unitName.replace(".read", "");
DefaultAnyValue parent = DefaultAnyValue.create();
parent.addValue("read", v);
parent.addValue("write", parseProperties(map.get(name + ".write")));
parent.setValue("name", name);
rs.put(name, parent);
prop.forEach((k, v) -> {
sourceProperties.put("redkale.datasource[" + name + "].read." + transferKeyName(k.toString()), v);
});
map.get(name + ".write").forEach((k, v) -> {
sourceProperties.put("redkale.datasource[" + name + "].write." + transferKeyName(k.toString()), v);
});
} else {
v.setValue("name", unitName);
rs.put(unitName, v);
prop.forEach((k, v) -> {
sourceProperties.put("redkale.datasource[" + unitName + "]." + transferKeyName(k.toString()), v);
});
}
});
return rs;
return sourceProperties;
} catch (RuntimeException e) {
throw e;
} catch (Exception ex) {
@@ -256,52 +256,44 @@ public final class DataSources {
}
}
private static DefaultAnyValue parseProperties(Properties prop) {
DefaultAnyValue v = DefaultAnyValue.create();
prop.forEach((x, y) -> {
if (JDBC_TABLE_AUTODDL.equalsIgnoreCase(x.toString())) {
x = AbstractDataSource.DATA_SOURCE_TABLE_AUTODDL;
} else if (JDBC_CACHE_MODE.equalsIgnoreCase(x.toString())) {
x = AbstractDataSource.DATA_SOURCE_CACHEMODE;
} else if (JDBC_CONNECTIONS_LIMIT.equalsIgnoreCase(x.toString())) {
x = AbstractDataSource.DATA_SOURCE_MAXCONNS;
} else if (JDBC_CONNECTIONSCAPACITY.equalsIgnoreCase(x.toString())) {
x = AbstractDataSource.DATA_SOURCE_CONNECTIONS_CAPACITY;
} else if (JDBC_CONTAIN_SQLTEMPLATE.equalsIgnoreCase(x.toString())) {
x = AbstractDataSource.DATA_SOURCE_CONTAIN_SQLTEMPLATE;
} else if (JDBC_NOTCONTAIN_SQLTEMPLATE.equalsIgnoreCase(x.toString())) {
x = AbstractDataSource.DATA_SOURCE_NOTCONTAIN_SQLTEMPLATE;
} else if (JDBC_TABLENOTEXIST_SQLSTATES.equalsIgnoreCase(x.toString())) {
x = AbstractDataSource.DATA_SOURCE_TABLENOTEXIST_SQLSTATES;
} else if (JDBC_TABLECOPY_SQLTEMPLATE.equalsIgnoreCase(x.toString())) {
x = AbstractDataSource.DATA_SOURCE_TABLECOPY_SQLTEMPLATE;
} else if (JDBC_CONNECTTIMEOUT_SECONDS.equalsIgnoreCase(x.toString())) {
x = AbstractDataSource.DATA_SOURCE_CONNECTTIMEOUT_SECONDS;
} else if (JDBC_URL.equalsIgnoreCase(x.toString())) {
x = AbstractDataSource.DATA_SOURCE_URL;
} else if (JDBC_USER.equalsIgnoreCase(x.toString())) {
x = AbstractDataSource.DATA_SOURCE_USER;
} else if (JDBC_PWD.equalsIgnoreCase(x.toString())) {
x = AbstractDataSource.DATA_SOURCE_PASSWORD;
} else if (JDBC_AUTO_MAPPING.equalsIgnoreCase(x.toString())) {
x = AbstractDataSource.DATA_SOURCE_AUTOMAPPING;
} else if (JDBC_ENCODING.equalsIgnoreCase(x.toString())) {
x = AbstractDataSource.DATA_SOURCE_ENCODING;
}
v.addValue(x.toString(), y.toString());
});
return v;
private static String transferKeyName(String key) {
if (JDBC_TABLE_AUTODDL.equalsIgnoreCase(key)) {
return AbstractDataSource.DATA_SOURCE_TABLE_AUTODDL;
} else if (JDBC_CACHE_MODE.equalsIgnoreCase(key)) {
return AbstractDataSource.DATA_SOURCE_CACHEMODE;
} else if (JDBC_CONNECTIONS_LIMIT.equalsIgnoreCase(key)) {
return AbstractDataSource.DATA_SOURCE_MAXCONNS;
} else if (JDBC_CONNECTIONSCAPACITY.equalsIgnoreCase(key)) {
return AbstractDataSource.DATA_SOURCE_CONNECTIONS_CAPACITY;
} else if (JDBC_CONTAIN_SQLTEMPLATE.equalsIgnoreCase(key)) {
return AbstractDataSource.DATA_SOURCE_CONTAIN_SQLTEMPLATE;
} else if (JDBC_NOTCONTAIN_SQLTEMPLATE.equalsIgnoreCase(key)) {
return AbstractDataSource.DATA_SOURCE_NOTCONTAIN_SQLTEMPLATE;
} else if (JDBC_TABLENOTEXIST_SQLSTATES.equalsIgnoreCase(key)) {
return AbstractDataSource.DATA_SOURCE_TABLENOTEXIST_SQLSTATES;
} else if (JDBC_TABLECOPY_SQLTEMPLATE.equalsIgnoreCase(key)) {
return AbstractDataSource.DATA_SOURCE_TABLECOPY_SQLTEMPLATE;
} else if (JDBC_CONNECTTIMEOUT_SECONDS.equalsIgnoreCase(key)) {
return AbstractDataSource.DATA_SOURCE_CONNECTTIMEOUT_SECONDS;
} else if (JDBC_URL.equalsIgnoreCase(key)) {
return AbstractDataSource.DATA_SOURCE_URL;
} else if (JDBC_USER.equalsIgnoreCase(key)) {
return AbstractDataSource.DATA_SOURCE_USER;
} else if (JDBC_PWD.equalsIgnoreCase(key)) {
return AbstractDataSource.DATA_SOURCE_PASSWORD;
} else if (JDBC_AUTO_MAPPING.equalsIgnoreCase(key)) {
return AbstractDataSource.DATA_SOURCE_AUTOMAPPING;
} else if (JDBC_ENCODING.equalsIgnoreCase(key)) {
return AbstractDataSource.DATA_SOURCE_ENCODING;
}
return key;
}
@Deprecated //@deprecated @since 2.7.0
public static Map<String, Properties> loadPersistenceXml(final InputStream in0) {
final Map<String, Properties> map = new TreeMap<>();
boolean flag = false;
try (final InputStream in = in0) {
AnyValue config = AnyValue.loadFromXml(in).getAnyValue("persistence");
for (AnyValue conf : config.getAnyValues("persistence-unit")) {
Properties result = new Properties();
conf.forEach(null, (n, c) -> {

View File

@@ -25,4 +25,68 @@ public interface ResourceEvent<T> {
}
return false;
}
public static <V> ResourceEvent<V> create(String name, V newValue, V oldValue) {
return new ResourceChangeEvent<>(name, newValue, oldValue);
}
public static class ResourceChangeEvent<T> implements ResourceEvent<T> {
protected String name;
protected T newValue;
protected T oldValue;
@ConstructorParameters({"name", "newValue", "oldValue"})
public ResourceChangeEvent(String name, T newValue, T oldValue) {
this.name = name;
this.newValue = newValue;
this.oldValue = oldValue;
}
@Override
public String name() {
return name;
}
@Override
public T newValue() {
return newValue;
}
@Override
public T oldValue() {
return oldValue;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public T getNewValue() {
return newValue;
}
public void setNewValue(T newValue) {
this.newValue = newValue;
}
public T getOldValue() {
return oldValue;
}
public void setOldValue(T oldValue) {
this.oldValue = oldValue;
}
@Override
public String toString() {
return "{name = " + name() + ", newValue = " + newValue() + ", oldValue = " + oldValue() + "}";
}
}
}

View File

@@ -447,7 +447,7 @@ public final class ResourceFactory {
properties.forEach((k, v) -> {
Object old = register(true, k.toString(), String.class, v, wrappers);
if (!Objects.equals(v, old)) {
environmentEventList.add(new ResourceChangeEvent(k.toString(), v, old));
environmentEventList.add(ResourceEvent.create(k.toString(), v, old));
}
});
Map<Object, Method> envListenMap = new LinkedHashMap<>();
@@ -901,7 +901,7 @@ public final class ResourceFactory {
this.elements = new CopyOnWriteArrayList<>();
}
//wrappers=null时才会触发listener的ResourceChangeEvent事件
//wrappers=null时才会触发listener的ResourceEvent事件
public ResourceEntry(final String name, T value, final List<ResourceElement> elements, Collection<ResourceChangeWrapper> wrappers, boolean sync) {
this.name = name;
this.value = value;
@@ -951,10 +951,10 @@ public final class ResourceFactory {
try {
if (!element.different || !Objects.equals(newVal, oldVal)) {
if (wrappers == null) {
Object[] ps = new Object[]{new ResourceEvent[]{new ResourceChangeEvent(name, newVal, oldVal)}};
Object[] ps = new Object[]{new ResourceEvent[]{ResourceEvent.create(name, newVal, oldVal)}};
element.listener.invoke(dest, ps);
} else {
wrappers.add(new ResourceChangeWrapper(dest, element.listener, new ResourceChangeEvent(name, newVal, oldVal)));
wrappers.add(new ResourceChangeWrapper(dest, element.listener, ResourceEvent.create(name, newVal, oldVal)));
}
}
} catch (Throwable e) {
@@ -1024,9 +1024,9 @@ public final class ResourceFactory {
public Method listener;
public ResourceChangeEvent event;
public ResourceEvent event;
public ResourceChangeWrapper(Object dest, Method listener, ResourceChangeEvent event) {
public ResourceChangeWrapper(Object dest, Method listener, ResourceEvent event) {
this.dest = dest;
this.listener = listener;
this.event = event;
@@ -1056,41 +1056,6 @@ public final class ResourceFactory {
}
private static class ResourceChangeEvent<T> implements ResourceEvent<T> {
public String name;
public T newValue;
public T oldValue;
public ResourceChangeEvent(String name, T newValue, T oldValue) {
this.name = name;
this.newValue = newValue;
this.oldValue = oldValue;
}
@Override
public String name() {
return name;
}
@Override
public T newValue() {
return newValue;
}
@Override
public T oldValue() {
return oldValue;
}
@Override
public String toString() {
return "{name = " + name() + ", newValue = " + newValue() + ", oldValue = " + oldValue() + "}";
}
}
// public static class SimpleResourceTypeLoader implements ResourceTypeLoader {
//
// protected Class<?> type;