ResourceListener支持对Environment环境变量的支持

This commit is contained in:
Redkale
2022-12-01 20:31:28 +08:00
parent 18bb3b807b
commit b2e7160ce7
5 changed files with 244 additions and 60 deletions

View File

@@ -53,9 +53,9 @@ public abstract class PropertiesAgent {
public abstract void destroy(AnyValue conf);
protected void updateEnvironmentProperties(Application application, Properties props) {
Properties notifyCache = new Properties();
props.forEach((k, v) -> application.updateEnvironmentProperty(k.toString(), v, notifyCache));
application.resourceFactory.register(notifyCache);
Properties changeCache = new Properties();
props.forEach((k, v) -> application.updateEnvironmentProperty(k.toString(), v, changeCache));
application.resourceFactory.register(changeCache, "", Environment.class);
}
protected void putEnvironmentProperty(Application application, String key, Object value) {

View File

@@ -8,6 +8,7 @@ import java.util.function.BiConsumer;
/**
* 环境变量, 只读版Properties
* 只存放system.property.、mimetype.property.、redkale.cachesource(.|[)、redkale.datasource(.|[)和其他非redkale.开头的配置项
* 只有ResourceFactory.register(Properties properties, String environmentName, Class environmentType) 方法才能是Environment的ResourceListener起作用
*
* 详情见: https://redkale.org
*

View File

@@ -347,12 +347,12 @@ public final class ResourceFactory {
*
* @param <A> 泛型
* @param name 资源名
* @param rs 资源对象
* @param val 资源对象
*
* @return 旧资源对象
*/
public <A> A register(final String name, final A rs) {
return register(true, name, rs);
public <A> A register(final String name, final A val) {
return register(true, name, val);
}
/**
@@ -361,19 +361,19 @@ public final class ResourceFactory {
* @param <A> 泛型
* @param autoSync 是否同步已被注入的资源
* @param name 资源名
* @param rs 资源对象
* @param val 资源对象
*
* @return 旧资源对象
*/
public <A> A register(final boolean autoSync, final String name, final A rs) {
public <A> A register(final boolean autoSync, final String name, final A val) {
checkResourceName(name);
final Class<?> claz = rs.getClass();
final Class<?> claz = val.getClass();
ResourceType rtype = claz.getAnnotation(ResourceType.class);
if (rtype == null) {
return (A) register(autoSync, name, claz, rs);
return (A) register(autoSync, name, claz, val);
} else {
A old = null;
A t = (A) register(autoSync, name, rtype.value(), rs);
A t = (A) register(autoSync, name, rtype.value(), val);
if (t != null) old = t;
return old;
}
@@ -385,12 +385,12 @@ public final class ResourceFactory {
* @param <A> 泛型
* @param name 资源名
* @param clazz 资源类型
* @param rs 资源对象
* @param val 资源对象
*
* @return 旧资源对象
*/
public <A> A register(final String name, final Class<? extends A> clazz, final A rs) {
return register(true, name, clazz, rs);
public <A> A register(final String name, final Class<? extends A> clazz, final A val) {
return register(true, name, clazz, val);
}
/**
@@ -399,12 +399,12 @@ public final class ResourceFactory {
* @param <A> 泛型
* @param name 资源名
* @param clazz 资源类型
* @param rs 资源对象
* @param val 资源对象
*
* @return 旧资源对象
*/
public <A> A register(final String name, final Type clazz, final A rs) {
return register(true, name, clazz, rs);
public <A> A register(final String name, final Type clazz, final A val) {
return register(true, name, clazz, val);
}
/**
@@ -414,12 +414,12 @@ public final class ResourceFactory {
* @param autoSync 是否同步已被注入的资源
* @param name 资源名
* @param clazz 资源类型
* @param rs 资源对象
* @param val 资源对象
*
* @return 旧资源对象
*/
public <A> A register(final boolean autoSync, final String name, final Type clazz, final A rs) {
return register(autoSync, name, clazz, rs, null);
public <A> A register(final boolean autoSync, final String name, final Type clazz, final A val) {
return register(autoSync, name, clazz, val, null);
}
/**
@@ -429,50 +429,85 @@ public final class ResourceFactory {
*
*/
public void register(Properties properties) {
register(properties, null, null);
}
/**
* 将多个以指定资源名的String对象注入到资源池中
*
* @param properties 资源键值对
* @param environmentName 额外的资源名
* @param environmentType 额外的类名
*
*/
public <A> void register(Properties properties, String environmentName, Class<A> environmentType) {
if (properties == null) return;
List<ResourceChangeWrapper> wrappers = new ArrayList<>();
properties.forEach((k, v) -> register(true, k.toString(), String.class, v, wrappers));
if (wrappers.isEmpty()) return;
List<ResourceEvent> environmentEventList = new ArrayList<>();
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));
}
});
Map<Object, Method> envListenMap = new LinkedHashMap<>();
if (!environmentEventList.isEmpty() && environmentName != null && environmentType != null) {
ResourceEntry<A> entry = findEntry(environmentName, environmentType);
if (entry != null && entry.elements != null) {
for (ResourceElement element : entry.elements) {
Object dest = element.dest.get();
if (dest != null && element.listener != null) {
envListenMap.put(dest, element.listener);
}
}
}
}
if (wrappers.isEmpty() && envListenMap.isEmpty()) return;
Map<Object, List<ResourceChangeWrapper>> map = new LinkedHashMap<>();
for (ResourceChangeWrapper wrapper : wrappers) {
map.computeIfAbsent(wrapper.dest, k -> new ArrayList<>()).add(wrapper);
}
map.forEach((dest, list) -> {
Method listener = list.get(0).listener;
try {
ResourceEvent[] events = new ResourceEvent[list.size()];
for (int i = 0; i < list.size(); i++) {
events[i] = list.get(i).event;
if (!map.isEmpty()) {
map.forEach((dest, list) -> {
if (envListenMap.containsKey(dest)) return; //跳过含有@Resource Environment字段的对象
Method listener = list.get(0).listener;
try {
ResourceEvent[] events = new ResourceEvent[list.size()];
for (int i = 0; i < list.size(); i++) {
events[i] = list.get(i).event;
}
Object[] ps = new Object[]{events};
listener.invoke(dest, ps);
} catch (Exception e) {
logger.log(Level.SEVERE, dest + " resource change listener error", e);
}
Object[] ps = new Object[]{events};
listener.invoke(dest, ps);
} catch (Exception e) {
logger.log(Level.SEVERE, dest + " resource change listener error", e);
}
});
});
}
if (!envListenMap.isEmpty()) { //含有@Resource Environment字段的对象进行变更响应
ResourceEvent[] environmentEvents = environmentEventList.toArray(new ResourceEvent[environmentEventList.size()]);
envListenMap.forEach((dest, listener) -> {
try {
Object[] ps = new Object[]{environmentEvents};
listener.invoke(dest, ps);
} catch (Exception e) {
logger.log(Level.SEVERE, dest + " resource change listener error", e);
}
});
}
}
private <A> A register(final boolean autoSync, final String name, final Type clazz, final A rs, List<ResourceChangeWrapper> wrappers) {
private <A> A register(final boolean autoSync, final String name, final Type clazz, final A val, List<ResourceChangeWrapper> wrappers) {
checkResourceName(name);
Class clz = TypeToken.typeToClass(clazz);
if (clz != null && !clz.isPrimitive() && rs != null && !clz.isAssignableFrom(rs.getClass())) {
throw new RuntimeException(clz + "not isAssignableFrom (" + rs + ") class " + rs.getClass());
}
ConcurrentHashMap<String, ResourceEntry> map = this.store.get(clazz);
if (map == null) {
synchronized (clazz) {
map = this.store.get(clazz);
if (map == null) {
map = new ConcurrentHashMap();
store.put(clazz, map);
}
}
if (clz != null && !clz.isPrimitive() && val != null && !clz.isAssignableFrom(val.getClass())) {
throw new RuntimeException(clz + "not isAssignableFrom (" + val + ") class " + val.getClass());
}
ConcurrentHashMap<String, ResourceEntry> map = this.store.computeIfAbsent(clazz, k -> new ConcurrentHashMap());
ResourceEntry re = map.get(name);
if (re == null) {
map.put(name, new ResourceEntry(name, rs));
map.put(name, new ResourceEntry(name, val));
} else {
map.put(name, new ResourceEntry(name, rs, re.elements, wrappers, autoSync));
map.put(name, new ResourceEntry(name, val, re.elements, wrappers, autoSync));
}
return re == null ? null : (A) re.value;
}
@@ -630,11 +665,11 @@ public final class ResourceFactory {
int pos = name.indexOf("{system.property.");
if (pos < 0) return (name.contains(RESOURCE_PARENT_NAME) && parent != null) ? name.replace(RESOURCE_PARENT_NAME, parent) : name;
String prefix = name.substring(0, pos);
String subname = name.substring(pos + "{system.property.".length());
pos = subname.lastIndexOf('}');
String subName = name.substring(pos + "{system.property.".length());
pos = subName.lastIndexOf('}');
if (pos < 0) return (name.contains(RESOURCE_PARENT_NAME) && parent != null) ? name.replace(RESOURCE_PARENT_NAME, parent) : name;
String postfix = subname.substring(pos + 1);
String property = subname.substring(0, pos);
String postfix = subName.substring(pos + 1);
String property = subName.substring(0, pos);
return formatResourceName(parent, prefix + System.getProperty(property, "") + postfix);
}
@@ -704,7 +739,7 @@ public final class ResourceFactory {
}
}
boolean autoregnull = true;
boolean autoRegNull = true;
final String rcname = formatResourceName(srcResourceName, tname);
Object rs;
if (rcname.startsWith("system.property.")) {
@@ -722,7 +757,7 @@ public final class ResourceFactory {
ResourceTypeLoader it = findTypeLoader(genctype, field);
if (it != null) {
it.load(this, srcResourceName, srcObj, rcname, field, attachment);
autoregnull = it.autoNone();
autoRegNull = it.autoNone();
re = findEntry(rcname, genctype);
}
}
@@ -739,12 +774,12 @@ public final class ResourceFactory {
ResourceTypeLoader it = findTypeLoader(classtype, field);
if (it != null) {
it.load(this, srcResourceName, srcObj, rcname, field, attachment);
autoregnull = it.autoNone();
autoRegNull = it.autoNone();
re = findEntry(rcname, classtype);
}
}
}
if (re == null && autoregnull) {
if (re == null && autoRegNull) {
register(rcname, genctype, null); //自动注入null的值
re = findEntry(rcname, genctype);
}
@@ -867,7 +902,7 @@ public final class ResourceFactory {
}
//wrappers=null时才会触发listener的ResourceChangeEvent事件
public ResourceEntry(final String name, T value, final List<ResourceElement> elements, List<ResourceChangeWrapper> wrappers, boolean sync) {
public ResourceEntry(final String name, T value, final List<ResourceElement> elements, Collection<ResourceChangeWrapper> wrappers, boolean sync) {
this.name = name;
this.value = value;
this.elements = elements == null ? new CopyOnWriteArrayList<>() : elements;
@@ -1001,6 +1036,24 @@ public final class ResourceFactory {
return dest;
}
@Override
public int hashCode() {
int hash = 7;
hash = 97 * hash + Objects.hashCode(this.dest);
hash = 97 * hash + Objects.hashCode(this.listener);
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
final ResourceChangeWrapper other = (ResourceChangeWrapper) obj;
if (!Objects.equals(this.dest, other.dest)) return false;
return Objects.equals(this.listener, other.listener);
}
}
private static class ResourceChangeEvent<T> implements ResourceEvent<T> {

View File

@@ -10,7 +10,8 @@ import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* &#64;Resource资源被更新时的监听事件本注解只能标记在方法参数为ResourceEvent[]上
* &#64;Resource资源被更新时的监听事件, 本注解只能标记在方法参数为ResourceEvent[]上 <br>
* 注意: 一个类只能存在一个&#64;ResourceResourceListener的方法 多余的会被忽略。
* 方法在资源被更新以后调用。
*
* <blockquote><pre>
@@ -24,7 +25,7 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
*
* &#64;ResourceListener
* private void changeResource(ResourceEvent[] events) {
* for(ResourceEvent event : events) {
* for(ResourceEvent event : events) {
* System.out.println("@Resource = " + event.name() + " 资源变更: newVal = " + event.newValue() + ", oldVal = " + event.oldValue());
* }
* }

View File

@@ -0,0 +1,129 @@
/*
*/
package org.redkale.test.util;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicInteger;
import javax.annotation.Resource;
import org.junit.jupiter.api.*;
import org.redkale.util.*;
/**
*
* @author zhangjx
*/
public class ResourceListenerTest {
private boolean main;
public static void main(String[] args) throws Throwable {
ResourceListenerTest test = new ResourceListenerTest();
test.main = true;
test.run();
}
@Test
public void run() throws Exception {
AtomicInteger aCounter = new AtomicInteger();
Properties env = new Properties();
env.put("property.id", "2345");
ResourceFactory factory = ResourceFactory.create();
factory.register(new Environment(env));
AService aservice = new AService();
BService bservice = new BService();
ABService abservice = new ABService();
factory.inject(aservice);
factory.inject(bservice);
factory.inject(abservice);
Properties prop = new Properties();
prop.put("property.id", "7890");
prop.put("property.name", "my name");
factory.register(prop, "", Environment.class);
if (!main) {
Assertions.assertTrue(aservice.counter.get() == 1);
Assertions.assertTrue(bservice.counter.get() == 2);
Assertions.assertTrue(abservice.counter.get() == 2);
}
factory.register("property.id", "7777");
if (!main) {
Assertions.assertTrue(aservice.counter.get() == 2);
Assertions.assertTrue(bservice.counter.get() == 2);
Assertions.assertTrue(abservice.counter.get() == 3);
}
}
class AService {
public final AtomicInteger counter = new AtomicInteger();
@Resource(name = "property.id")
private String id;
@Resource(name = "property.desc")
private String desc;
@ResourceListener
private void changeResource(ResourceEvent[] events) {
for (ResourceEvent event : events) {
counter.incrementAndGet();
System.out.println(getClass().getSimpleName() + " @Resource = " + event.name() + " 资源变更: newVal = " + event.newValue() + ", oldVal = " + event.oldValue());
}
}
public String test() {
return "";
}
}
class BService {
public final AtomicInteger counter = new AtomicInteger();
@Resource
private Environment env;
@ResourceListener
private void changeResource(ResourceEvent[] events) {
for (ResourceEvent event : events) {
counter.incrementAndGet();
System.out.println(getClass().getSimpleName() + " @Resource = " + event.name() + " 资源变更: newVal = " + event.newValue() + ", oldVal = " + event.oldValue());
}
System.out.println(getClass().getSimpleName() + " env = " + env);
}
public String test() {
return "";
}
}
class ABService {
public final AtomicInteger counter = new AtomicInteger();
@Resource(name = "property.id")
private String id;
@Resource
private Environment env;
@ResourceListener
private void changeResource(ResourceEvent[] events) {
for (ResourceEvent event : events) {
counter.incrementAndGet();
System.out.println(getClass().getSimpleName() + " @Resource = " + event.name() + " 资源变更: newVal = " + event.newValue() + ", oldVal = " + event.oldValue());
}
System.out.println(getClass().getSimpleName() + " env = " + env);
}
public String test() {
return "";
}
}
}