首页 > 代码库 > [编织消息框架]数值与逻辑分离

[编织消息框架]数值与逻辑分离

为什么要分离?

业务需求是不停地变,如果把条件写进代码里,当用户需求变时要改代码发版本更新才能生效,这过程无疑是漫长的

就算是在开发期,不停的变开发者精力耗光在沟通,小修改上,无法专注逻辑部分

分离的根本目的是让开发者专注写引擎部分,无需关注太多业务上的边界,条件等

 

需要分离什么类型数值?

如活动开启时间,购买满足条件,购买上限等 这些不确定用户具体需求,全都可以弄成动态获取

 

分离技术实现有很多

如使用数据库mysql等

linux 常用的配置文本config

表格csv,json文件等

本项目用的是 java Properties 跟单例

技术分享
  1 import java.io.FileInputStream;  2 import java.io.InputStream;  3 import java.lang.reflect.Field;  4 import java.lang.reflect.Method;  5 import java.lang.reflect.Type;  6 import java.util.Collection;  7 import java.util.Collections;  8 import java.util.HashSet;  9 import java.util.Map; 10 import java.util.Properties; 11 import java.util.Set; 12  13 import javax.script.ScriptEngine; 14 import javax.script.ScriptEngineManager; 15  16 import org.apache.commons.lang3.reflect.TypeUtils; 17  18 import com.eyu.onequeue.reflect.anno.FieldValue; 19 import com.eyu.onequeue.util.SerialUtil; 20  21 /** 22  * @author solq 23  */ 24 public class PropertiesFactory { 25     static final ScriptEngine se = new ScriptEngineManager().getEngineByName("JavaScript"); 26  27     public static <T> T initField(Class<T> clz, String file) { 28     T ret = null; 29     try { 30         Properties pro = getProperties(file); 31         ret = clz.newInstance(); 32         Field[] fields = clz.getDeclaredFields(); 33         Set<Method> methods = new HashSet<>(); 34         Collections.addAll(methods, clz.getDeclaredMethods()); 35         // Field modifiersField = Field.class.getDeclaredField("modifiers"); 36         // modifiersField.setAccessible(true); 37         for (Field f : fields) { 38         FieldValue anno = f.getAnnotation(FieldValue.class); 39         if (anno == null) { 40             continue; 41         } 42  43         String fileName = anno.value(); 44         String tmp = (String) pro.get(fileName); 45         if (tmp == null) { 46             continue; 47         } 48         Object value =http://www.mamicode.com/ tmp; 49         Type type = f.getGenericType(); 50         if (type.equals(Integer.TYPE)) { 51             value =http://www.mamicode.com/ se.eval(tmp); 52         } else if (type.equals(Double.TYPE)) { 53             value =http://www.mamicode.com/ se.eval(tmp); 54         } else if (type.equals(Float.TYPE)) { 55             value =http://www.mamicode.com/ se.eval(tmp); 56         } else if (type.equals(Long.TYPE)) { 57             value =http://www.mamicode.com/ se.eval(tmp); 58         } else if (type.equals(Short.TYPE)) { 59             value =http://www.mamicode.com/ ((Integer) se.eval(tmp)).shortValue(); 60         } else if (type.equals(Byte.TYPE)) { 61             value =http://www.mamicode.com/ ((Integer) se.eval(tmp)).byteValue(); 62         } else if (type.equals(Boolean.TYPE)) { 63             value =http://www.mamicode.com/ se.eval(tmp); 64         } else if (TypeUtils.isAssignable(type, Map.class)) { 65             value =http://www.mamicode.com/ SerialUtil.readValue(tmp, type); 66         }else if (TypeUtils.isAssignable(type, Collection.class)) { 67             value =http://www.mamicode.com/ SerialUtil.readValue(tmp, type); 68         }else if (TypeUtils.isAssignable(type, Class.class)) { 69             value =http://www.mamicode.com/ Class.forName(tmp); 70         }else if (TypeUtils.isArrayType(type)) { 71             value =http://www.mamicode.com/ SerialUtil.readArray(tmp, type); 72         } 73           74         String callMethodName = "set" + f.getName(); 75         boolean flag = false; 76         try { 77             for (Method m : methods) { 78             if (m.getName().equals(callMethodName)) { 79                 m.setAccessible(true); 80                 m.invoke(ret, value); 81                 flag = true; 82                 break; 83             } 84             } 85         } catch (Exception e) { 86  87         } 88         if (flag) { 89             continue; 90         } 91         f.setAccessible(true); 92         // modifiersField.setInt(f, f.getModifiers() & ~Modifier.FINAL); 93  94         try { 95             f.set(ret, value); 96         } catch (Exception e1) { 97             e1.printStackTrace(); 98         } 99         }100     } catch (Exception e1) {101         e1.printStackTrace();102     }103     return ret;104     }105 106     public static Properties getProperties(String file) {107     Properties pro = new Properties();108     String path = ClassLoader.getSystemResource(file).getPath();109     try (InputStream fs = new FileInputStream(path);) {110         pro.load(fs);111     } catch (Exception e) {112         e.printStackTrace();113     }114     return pro;115     }116 }
PropertiesFactory

为什么解释字符串部分用到 ScriptEngine ?

有些配置 如 QM.COMPRESS_SIZE = 1024*2 是个公式运算,要用引擎才能执行

技术分享
 1 /*** 2  * qm config 所有配置 属性不能加 final 否则 jvm 会优化 3  *  4  * @author solq 5  */ 6 public class QMConfig { 7     private static QMConfig instance = null; 8  9     public static QMConfig getInstance() {10     if (instance != null) {11         return instance;12     }13     synchronized (QMConfig.class) {14         if (instance != null) {15         return instance;16         }17         instance = PropertiesFactory.initField(QMConfig.class, "qm.properties");18     }19     return instance;20     }21 22     // other23 24     /** 启动服务模式 影响 记录存储目录 **/25     public boolean SERVER_MODEL = true;26     /** 集群服务器 <address> client端不能为NULL **/27     @FieldValue("QM.CLUSTER_LIST")28     /** 本地名称,多应用不能相同,注册时用到 client端不能为NULL **/29     public String LOCALNAME;30     /** 集群服务器 <address> client端不能为NULL **/31     @FieldValue("QM.CLUSTER_LIST")32     public Set<String> CLUSTER_LIST;33     /** 订阅信息 topic : groupId 填写开启 **/34     @FieldValue("QM.TOPIC_INFO")35     public Set<String> TOPIC_INFO;36     /** 日志保存路径 <订阅,路径> consume端不能为NULL **/37     @FieldValue("QM.CONSUME_LOG_SAVE_DIRS")38     public Map<String, String> CONSUME_LOG_SAVE_DIRS = new HashMap<>(1);39 40     public Collection<QSubscribe> getTopics() {41     Collection<QSubscribe> ret = new HashSet<>();42     if (TOPIC_INFO == null) {43         return ret;44     }45     for (String str : TOPIC_INFO) {46         String[] s = str.split(":");47         ret.add(QSubscribe.of(s[0], s[1]));48     }49     return ret;50     }51 52     /** rpc param compress **/53     @FieldValue("QM.COMPRESS_SIZE")54     public int COMPRESS_SIZE = 1024;55 56     // NETTY57     /** server port **/58     @FieldValue("QM.NETTY_SERVER_PORT")59     public int NETTY_SERVER_PORT = 8080;60     /** server bossgroup **/61     @FieldValue("QM.NETTY_SERVER_BOSSGROUP")62     public int NETTY_SERVER_BOSSGROUP = 1;63     /** server workergroup **/64     @FieldValue("QM.NETTY_SERVER_WORKERGROUP")65     public int NETTY_SERVER_WORKERGROUP = 4;66     /** server socket option **/67     @FieldValue("QM.NETTY_SERVER_SESSION_OPTION")68     public Map<String, ?> NETTY_SERVER_SESSION_OPTION;69     /** child socket option **/70     @FieldValue("QM.NETTY_SERVER_CHILD_SESSION_OPTION")71     public Map<String, ?> NETTY_SERVER_CHILD_SESSION_OPTION;72     /** 处理模型实现 **/73     @FieldValue("QM.NETTY_SERVER_ACCEPTOR")74     public Class<? extends ServerSocketChannel> NETTY_SERVER_ACCEPTOR = NioServerSocketChannel.class;75 76  ..............................77 78 }
QMConfig

QMConfig 是个单例使用时必须初始化配置文件 qm.properties 才能正常使用

使用properties 跟单例可以做出很好的效果,看了大多数框架花大多精力在配置上,有时复杂的不一定是最优

重复一下观点 出自UNIX编程艺术

分离的根本目的是让开发者专注写引擎部分,无需关注太多业务上的边界,条件等

[编织消息框架]数值与逻辑分离