All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.redkale.util.ResourceFactory Maven / Gradle / Ivy

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package org.redkale.util;

import java.lang.ref.WeakReference;
import java.lang.reflect.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.function.*;
import java.util.logging.*;
import javax.annotation.Resource;

/**
 *
 * 依赖注入功能主类   
* * 如果@Resource(name = "$") 表示资源name采用所属对象的name
* 如果没有@Resource且对象实现了Resourcable, 则会取对象的resourceName()方法值 *
 * name规则:
 *    1: "$"有特殊含义, 不能表示"$"资源本身
 *    2: 只能是字母、数字、(短横)-、(下划线)_、点(.)的组合
 * 
*

* 详情见: https://redkale.org * * @author zhangjx */ @SuppressWarnings("unchecked") public final class ResourceFactory { public static final String RESOURCE_PARENT_NAME = "$"; private static final Logger logger = Logger.getLogger(ResourceFactory.class.getSimpleName()); private final ResourceFactory parent; private static final ResourceFactory instance = new ResourceFactory(null); private final List> chidren = new CopyOnWriteArrayList<>(); private final ConcurrentHashMap loadermap = new ConcurrentHashMap(); private final ConcurrentHashMap> store = new ConcurrentHashMap(); private ResourceFactory(ResourceFactory parent) { this.parent = parent; } /** * 获取根ResourceFactory * * @return ResourceFactory */ public static ResourceFactory root() { return instance; } /** * 创建ResourceFactory子节点 * * @return ResourceFactory */ public ResourceFactory createChild() { ResourceFactory child = new ResourceFactory(this); this.chidren.add(new WeakReference<>(child)); return child; } /** * 获取所有ResourceFactory子节点 * * @return List */ public List getChildren() { List result = new ArrayList<>(); for (WeakReference ref : chidren) { ResourceFactory rf = ref.get(); if (rf != null) result.add(rf); } return result; } /** * 清空当前ResourceFactory注入资源 * */ public void release() { this.store.clear(); } /** * 检查资源名是否合法 *

     * name规则:
     *    1: "$"有特殊含义, 表示资源本身,"$"不能单独使用
     *    2: 只能是字母、数字、(短横)-、(下划线)_、点(.)的组合
     * 
* * @param name String */ public static void checkResourceName(String name) { if (name == null || (!name.isEmpty() && !name.matches("^[a-zA-Z0-9_;\\-\\.\\[\\]\\(\\)]+$"))) { throw new IllegalArgumentException("name(" + name + ") contains illegal character, must be (a-z,A-Z,0-9,_,.,(,),-,[,])"); } } /** * 将对象指定类型且name=""注入到资源池中,并同步已被注入的资源 * * @param 泛型 * @param clazz 资源类型 * @param rs 资源对象 * * @return 旧资源对象 */ public A register(final Class clazz, final A rs) { return register(true, clazz, rs); } /** * 将对象指定类型且name=""注入到资源池中 * * @param 泛型 * @param autoSync 是否同步已被注入的资源 * @param clazz 资源类型 * @param rs 资源对象 * * @return 旧资源对象 */ public A register(final boolean autoSync, final Class clazz, final A rs) { return register(autoSync, "", clazz, rs); } /** * 将对象以name=""注入到资源池中,并同步已被注入的资源 * * @param 泛型 * @param rs 资源对象 * * @return 旧资源对象 */ public A register(final A rs) { return register(true, rs); } /** * 将对象以name=""注入到资源池中,并同步已被注入的资源 * * @param 泛型 * @param autoSync 是否同步已被注入的资源 * @param rs 资源对象 * * @return 旧资源对象 */ public A register(final boolean autoSync, final A rs) { if (rs == null) return null; return (A) register(autoSync, "", rs); } /** * 将boolean对象以指定资源名注入到资源池中,并同步已被注入的资源 * * @param name 资源名 * @param value 资源值 * */ public void register(final String name, final boolean value) { register(true, name, boolean.class, value); } /** * 将boolean对象以指定资源名注入到资源池中 * * @param autoSync 是否同步已被注入的资源 * @param name 资源名 * @param value 资源值 * */ public void register(final boolean autoSync, final String name, final boolean value) { register(autoSync, name, boolean.class, value); } /** * 将byte对象以指定资源名注入到资源池中,并同步已被注入的资源 * * @param name 资源名 * @param value 资源值 * */ public void register(final String name, final byte value) { register(true, name, byte.class, value); } /** * 将byte对象以指定资源名注入到资源池中 * * @param autoSync 是否同步已被注入的资源 * @param name 资源名 * @param value 资源值 * */ public void register(final boolean autoSync, final String name, final byte value) { register(autoSync, name, byte.class, value); } /** * 将short对象以指定资源名注入到资源池中,并同步已被注入的资源 * * @param name 资源名 * @param value 资源值 * */ public void register(final String name, final short value) { register(true, name, short.class, value); } /** * 将short对象以指定资源名注入到资源池中 * * @param autoSync 是否同步已被注入的资源 * @param name 资源名 * @param value 资源值 * */ public void register(final boolean autoSync, final String name, final short value) { register(autoSync, name, short.class, value); } /** * 将int对象以指定资源名注入到资源池中,并同步已被注入的资源 * * @param name 资源名 * @param value 资源值 * */ public void register(final String name, final int value) { register(true, name, int.class, value); } /** * 将int对象以指定资源名注入到资源池中 * * @param autoSync 是否同步已被注入的资源 * @param name 资源名 * @param value 资源值 * */ public void register(final boolean autoSync, final String name, final int value) { register(autoSync, name, int.class, value); } /** * 将float对象以指定资源名注入到资源池中,并同步已被注入的资源 * * @param name 资源名 * @param value 资源值 * */ public void register(final String name, final float value) { register(true, name, float.class, value); } /** * 将float对象以指定资源名注入到资源池中 * * @param autoSync 是否同步已被注入的资源 * @param name 资源名 * @param value 资源值 * */ public void register(final boolean autoSync, final String name, final float value) { register(autoSync, name, float.class, value); } /** * 将long对象以指定资源名注入到资源池中,并同步已被注入的资源 * * @param name 资源名 * @param value 资源值 * */ public void register(final String name, final long value) { register(true, name, long.class, value); } /** * 将long对象以指定资源名注入到资源池中 * * @param autoSync 是否同步已被注入的资源 * @param name 资源名 * @param value 资源值 * */ public void register(final boolean autoSync, final String name, final long value) { register(autoSync, name, long.class, value); } /** * 将double对象以指定资源名注入到资源池中,并同步已被注入的资源 * * @param name 资源名 * @param value 资源值 * */ public void register(final String name, final double value) { register(true, name, double.class, value); } /** * 将double对象以指定资源名注入到资源池中 * * @param autoSync 是否同步已被注入的资源 * @param name 资源名 * @param value 资源值 * */ public void register(final boolean autoSync, final String name, final double value) { register(autoSync, name, double.class, value); } /** * 将对象以指定资源名注入到资源池中,并同步已被注入的资源 * * @param 泛型 * @param name 资源名 * @param rs 资源对象 * * @return 旧资源对象 */ public A register(final String name, final A rs) { return register(true, name, rs); } /** * 将对象以指定资源名注入到资源池中 * * @param 泛型 * @param autoSync 是否同步已被注入的资源 * @param name 资源名 * @param rs 资源对象 * * @return 旧资源对象 */ public A register(final boolean autoSync, final String name, final A rs) { checkResourceName(name); final Class claz = rs.getClass(); ResourceType rtype = claz.getAnnotation(ResourceType.class); if (rtype == null) { return (A) register(autoSync, name, claz, rs); } else { A old = null; A t = (A) register(autoSync, name, rtype.value(), rs); if (t != null) old = t; return old; } } /** * 将对象以指定资源名和类型注入到资源池中,并同步已被注入的资源 * * @param 泛型 * @param name 资源名 * @param clazz 资源类型 * @param rs 资源对象 * * @return 旧资源对象 */ public A register(final String name, final Class clazz, final A rs) { return register(true, name, clazz, rs); } /** * 将对象以指定资源名和类型注入到资源池中,并同步已被注入的资源 * * @param 泛型 * @param name 资源名 * @param clazz 资源类型 * @param rs 资源对象 * * @return 旧资源对象 */ public A register(final String name, final Type clazz, final A rs) { return register(true, name, clazz, rs); } /** * 将对象以指定资源名和类型注入到资源池中 * * @param 泛型 * @param autoSync 是否同步已被注入的资源 * @param name 资源名 * @param clazz 资源类型 * @param rs 资源对象 * * @return 旧资源对象 */ public A register(final boolean autoSync, final String name, final Type clazz, final A rs) { checkResourceName(name); ConcurrentHashMap 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); } } } ResourceEntry re = map.get(name); if (re == null) { map.put(name, new ResourceEntry(rs)); } else { map.put(name, new ResourceEntry(rs, name, re.elements, autoSync)); } return re == null ? null : (A) re.value; } /** * 判断是否包含指定资源名和资源类型的资源对象 * * @param 泛型 * @param recursive 是否遍历父节点 * @param name 资源名 * @param clazz 资源类型 * * @return 是否存在 */ public boolean contains(boolean recursive, String name, Class clazz) { Map map = this.store.get(clazz); return map == null ? ((recursive && parent != null) ? parent.contains(recursive, name, clazz) : false) : map.containsKey(name); } /** * 查找指定资源名和资源类型的资源对象所在的ResourceFactory, 没有则返回null * * @param name 资源名 * @param clazz 资源类型 * * @return ResourceFactory */ public ResourceFactory findResourceFactory(String name, Type clazz) { Map map = this.store.get(clazz); if (map != null && map.containsKey(name)) return this; if (parent != null) return parent.findResourceFactory(name, clazz); return null; } public A find(Class clazz) { return find("", clazz); } public A find(String name, Type clazz) { ResourceEntry re = findEntry(name, clazz); return re == null ? null : (A) re.value; } public A find(String name, Class clazz) { ResourceEntry re = findEntry(name, clazz); return re == null ? null : re.value; } public A findChild(final String name, final Class clazz) { A rs = find(name, clazz); if (rs != null) return rs; for (Map.Entry> en : this.store.entrySet()) { if (!(en.getKey() instanceof Class)) continue; if (!clazz.isAssignableFrom((Class) en.getKey())) continue; ResourceEntry v = en.getValue().get(name); if (v != null) return (A) v.value; } return null; } private ResourceEntry findEntry(String name, Type clazz) { Map map = this.store.get(clazz); if (map != null) { ResourceEntry re = map.get(name); if (re != null) return re; } if (parent != null) return parent.findEntry(name, clazz); return null; } public List query(Class clazz) { return query(new ArrayList<>(), clazz); } public List query(Type clazz) { return query(new ArrayList<>(), clazz); } private List query(final List list, Type clazz) { Map map = this.store.get(clazz); if (map != null) { for (ResourceEntry re : map.values()) { if (re.value != null) list.add((A) re.value); } } if (parent != null) parent.query(list, clazz); return list; } public List query(final BiPredicate predicate) { return query(new ArrayList<>(), predicate); } private List query(final List list, final BiPredicate predicate) { if (predicate == null) return list; for (ConcurrentHashMap map : this.store.values()) { for (Map.Entry en : map.entrySet()) { if (predicate.test(en.getKey(), en.getValue().value)) { list.add((A) en.getValue().value); } } } if (parent != null) parent.query(list, predicate); return list; } private ResourceEntry findEntry(String name, Class clazz) { Map map = this.store.get(clazz); if (map != null) { ResourceEntry rs = map.get(name); if (rs != null) return rs; } if (parent != null) return parent.findEntry(name, clazz); return null; } public boolean inject(final Object src) { return inject(src, null); } public boolean inject(final Object src, final T attachment) { return inject(src, attachment, null); } public boolean inject(final Object src, final BiConsumer consumer) { return inject(src, null, consumer); } public boolean inject(final Object src, final T attachment, final BiConsumer consumer) { return inject(src, attachment, consumer, new ArrayList()); } public static String formatResourceName(String name) { if (name == null) return null; int pos = name.indexOf("{system.property."); if (pos < 0) return name; String prefix = name.substring(0, pos); String subname = name.substring(pos + "{system.property.".length()); pos = subname.lastIndexOf('}'); if (pos < 0) return name; String postfix = subname.substring(pos + 1); String property = subname.substring(0, pos); return formatResourceName(prefix + System.getProperty(property, "") + postfix); } private boolean inject(final Object src, final T attachment, final BiConsumer consumer, final List list) { if (src == null) return false; try { list.add(src); Class clazz = src.getClass(); do { for (Field field : clazz.getDeclaredFields()) { if (Modifier.isStatic(field.getModifiers())) continue; field.setAccessible(true); final Class classtype = field.getType(); final Type genctype = field.getGenericType(); Resource rc = field.getAnnotation(Resource.class); if (rc == null) { //深度注入 boolean flag = true; //是否没有重复 Object ns = field.get(src); for (Object o : list) { if (o == ns) { flag = false; break; } } if (ns == null) continue; if (ns.getClass().isPrimitive() || ns.getClass().isArray() || ns.getClass().getName().startsWith("java.") || ns.getClass().getName().startsWith("javax.") || ns.getClass().getName().startsWith("jdk.") || ns.getClass().getName().startsWith("sun.")) continue; if (flag) this.inject(ns, attachment, consumer, list); continue; } if (Modifier.isFinal(field.getModifiers())) continue; if (consumer != null) consumer.accept(src, field); String tname = rc.name(); if (tname.contains(RESOURCE_PARENT_NAME)) { Resource res = src.getClass().getAnnotation(Resource.class); if (res == null) { if (src instanceof Resourcable) { tname = tname.replace(RESOURCE_PARENT_NAME, ((Resourcable) src).resourceName()); } else { logger.log(Level.SEVERE, src.getClass().getName() + " not found @Resource on Class or not implements Resourcable"); } } else { tname = tname.replace(RESOURCE_PARENT_NAME, res.name()); } } boolean autoregnull = true; final String rcname = formatResourceName(tname); Object rs; if (rcname.startsWith("system.property.")) { rs = System.getProperty(rcname.substring("system.property.".length())); } else { ResourceEntry re = findEntry(rcname, genctype); if (re == null) { if (rcname.startsWith("property.")) { re = findEntry(rcname, String.class); } else { re = findEntry(rcname, classtype); } } if (re == null) { ResourceLoader it = findLoader(genctype, field); if (it != null) { it.load(this, src, rcname, field, attachment); autoregnull = it.autoNone(); re = findEntry(rcname, genctype); } } if (re == null && genctype != classtype) { re = findEntry(rcname, classtype); if (re == null) { if (rcname.startsWith("property.")) { re = findEntry(rcname, String.class); } else { re = findEntry(rcname, classtype); } } if (re == null) { ResourceLoader it = findLoader(classtype, field); if (it != null) { it.load(this, src, rcname, field, attachment); autoregnull = it.autoNone(); re = findEntry(rcname, classtype); } } } if (re == null && autoregnull) { register(rcname, genctype, null); //自动注入null的值 re = findEntry(rcname, genctype); } if (re == null) continue; re.elements.add(new ResourceElement<>(src, field)); rs = re.value; } if (rs != null && !rs.getClass().isPrimitive() && classtype.isPrimitive()) { if (classtype == int.class) { rs = Integer.decode(rs.toString()); } else if (classtype == long.class) { rs = Long.decode(rs.toString()); } else if (classtype == short.class) { rs = Short.decode(rs.toString()); } else if (classtype == boolean.class) { rs = "true".equalsIgnoreCase(rs.toString()); } else if (classtype == byte.class) { rs = Byte.decode(rs.toString()); } else if (classtype == float.class) { rs = Float.parseFloat(rs.toString()); } else if (classtype == double.class) { rs = Double.parseDouble(rs.toString()); } } if (rs != null) field.set(src, rs); } } while ((clazz = clazz.getSuperclass()) != Object.class); return true; } catch (Exception ex) { logger.log(Level.SEVERE, "inject " + src + " error", ex); return false; } } public void register(final ResourceLoader rs, final Type... clazzs) { if (clazzs == null || rs == null) return; for (Type clazz : clazzs) { loadermap.put(clazz, rs); } } private ResourceLoader findMatchLoader(Type ft, Field field) { ResourceLoader it = this.loadermap.get(ft); if (it == null && field != null) it = this.loadermap.get(field.getType()); if (it != null) return it; return parent == null ? null : parent.findMatchLoader(ft, field); } private ResourceLoader findRegxLoader(Type ft, Field field) { if (field == null) return null; Class c = field.getType(); for (Map.Entry en : this.loadermap.entrySet()) { Type t = en.getKey(); if (t == ft) return en.getValue(); if (t instanceof Class && (((Class) t)).isAssignableFrom(c)) return en.getValue(); } return parent == null ? null : parent.findRegxLoader(ft, field); } public ResourceLoader findLoader(Type ft, Field field) { ResourceLoader it = this.findMatchLoader(ft, field); return it == null ? findRegxLoader(ft, field) : it; } private static class ResourceEntry { public final T value; public final List elements; public ResourceEntry(T value) { this.value = value; this.elements = new CopyOnWriteArrayList<>(); } public ResourceEntry(T value, final String name, final List elements, boolean sync) { this.value = value; this.elements = elements == null ? new CopyOnWriteArrayList<>() : elements; if (sync && elements != null && !elements.isEmpty()) { for (ResourceElement element : elements) { Object dest = element.dest.get(); if (dest == null) continue; Object rs = value; final Class classtype = element.fieldType; if (rs != null && !rs.getClass().isPrimitive() && classtype.isPrimitive()) { if (classtype == int.class) { rs = Integer.decode(rs.toString()); } else if (classtype == long.class) { rs = Long.decode(rs.toString()); } else if (classtype == short.class) { rs = Short.decode(rs.toString()); } else if (classtype == boolean.class) { rs = "true".equalsIgnoreCase(rs.toString()); } else if (classtype == byte.class) { rs = Byte.decode(rs.toString()); } else if (classtype == float.class) { rs = Float.parseFloat(rs.toString()); } else if (classtype == double.class) { rs = Double.parseDouble(rs.toString()); } } if (rs == null && classtype.isPrimitive()) rs = Array.get(Array.newInstance(classtype, 1), 0); Object oldVal = null; if (element.listener != null) { try { oldVal = element.field.get(dest); } catch (Exception e) { e.printStackTrace(); } } try { element.field.set(dest, rs); } catch (Exception e) { e.printStackTrace(); } if (element.listener != null) { try { element.listener.invoke(dest, name, rs, oldVal); } catch (Exception e) { logger.log(Level.SEVERE, dest + " resource change listener error", e); } } } } } } private static class ResourceElement { private static final HashMap listenerMethods = new HashMap<>(); //不使用ConcurrentHashMap是因为value不能存null public final WeakReference dest; public final Field field; //Resource 字段 public final Class fieldType; public final Method listener; public ResourceElement(T dest, Field field) { this.dest = new WeakReference(dest); this.field = field; this.fieldType = field.getType(); Class t = dest.getClass(); String tn = t.getName(); this.listener = tn.startsWith("java.") || tn.startsWith("javax.") ? null : findListener(t, field.getType()); } private static Method findListener(Class clazz, Class fieldType) { synchronized (listenerMethods) { Class loop = clazz; Method m = listenerMethods.get(clazz.getName() + "-" + fieldType.getName()); if (m != null) return m; do { for (Method method : loop.getDeclaredMethods()) { if (method.getAnnotation(ResourceListener.class) != null && method.getParameterCount() == 3 && String.class.isAssignableFrom(method.getParameterTypes()[0]) && method.getParameterTypes()[1] == method.getParameterTypes()[2] && method.getParameterTypes()[1].isAssignableFrom(fieldType)) { m = method; m.setAccessible(true); break; } } } while ((loop = loop.getSuperclass()) != Object.class); listenerMethods.put(clazz.getName() + "-" + fieldType.getName(), m); return m; } } } @FunctionalInterface public static interface ResourceLoader { public void load(ResourceFactory factory, Object src, String resourceName, Field field, Object attachment); // 返回true 表示调用ResourceLoader之后资源仍不存在,则会在ResourceFactory里注入默认值null,返回false表示资源不存在下次仍会调用ResourceLoader自行处理 default boolean autoNone() { return true; } } }