为什么要分离?

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

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

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

需要分离什么类型数值?

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

分离技术实现有很多

如使用数据库mysql等

linux 常用的配置文本config

表格csv,json文件等

本项目用的是 java Properties 跟单例

 import java.io.FileInputStream;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set; import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager; import org.apache.commons.lang3.reflect.TypeUtils; import com.eyu.onequeue.reflect.anno.FieldValue;
import com.eyu.onequeue.util.SerialUtil; /**
* @author solq
*/
public class PropertiesFactory {
static final ScriptEngine se = new ScriptEngineManager().getEngineByName("JavaScript"); public static <T> T initField(Class<T> clz, String file) {
T ret = null;
try {
Properties pro = getProperties(file);
ret = clz.newInstance();
Field[] fields = clz.getDeclaredFields();
Set<Method> methods = new HashSet<>();
Collections.addAll(methods, clz.getDeclaredMethods());
// Field modifiersField = Field.class.getDeclaredField("modifiers");
// modifiersField.setAccessible(true);
for (Field f : fields) {
FieldValue anno = f.getAnnotation(FieldValue.class);
if (anno == null) {
continue;
} String fileName = anno.value();
String tmp = (String) pro.get(fileName);
if (tmp == null) {
continue;
}
Object value = tmp;
Type type = f.getGenericType();
if (type.equals(Integer.TYPE)) {
value = se.eval(tmp);
} else if (type.equals(Double.TYPE)) {
value = se.eval(tmp);
} else if (type.equals(Float.TYPE)) {
value = se.eval(tmp);
} else if (type.equals(Long.TYPE)) {
value = se.eval(tmp);
} else if (type.equals(Short.TYPE)) {
value = ((Integer) se.eval(tmp)).shortValue();
} else if (type.equals(Byte.TYPE)) {
value = ((Integer) se.eval(tmp)).byteValue();
} else if (type.equals(Boolean.TYPE)) {
value = se.eval(tmp);
} else if (TypeUtils.isAssignable(type, Map.class)) {
value = SerialUtil.readValue(tmp, type);
}else if (TypeUtils.isAssignable(type, Collection.class)) {
value = SerialUtil.readValue(tmp, type);
}else if (TypeUtils.isAssignable(type, Class.class)) {
value = Class.forName(tmp);
}else if (TypeUtils.isArrayType(type)) {
value = SerialUtil.readArray(tmp, type);
} String callMethodName = "set" + f.getName();
boolean flag = false;
try {
for (Method m : methods) {
if (m.getName().equals(callMethodName)) {
m.setAccessible(true);
m.invoke(ret, value);
flag = true;
break;
}
}
} catch (Exception e) { }
if (flag) {
continue;
}
f.setAccessible(true);
// modifiersField.setInt(f, f.getModifiers() & ~Modifier.FINAL); try {
f.set(ret, value);
} catch (Exception e1) {
e1.printStackTrace();
}
}
} catch (Exception e1) {
e1.printStackTrace();
}
return ret;
} public static Properties getProperties(String file) {
Properties pro = new Properties();
String path = ClassLoader.getSystemResource(file).getPath();
try (InputStream fs = new FileInputStream(path);) {
pro.load(fs);
} catch (Exception e) {
e.printStackTrace();
}
return pro;
}
}

PropertiesFactory

 /***
* qm config 所有配置 属性不能加 final 否则 jvm 会优化
*
* @author solq
*/
public class QMConfig {
private static QMConfig instance = null; public static QMConfig getInstance() {
if (instance != null) {
return instance;
}
synchronized (QMConfig.class) {
if (instance != null) {
return instance;
}
instance = PropertiesFactory.initField(QMConfig.class, "qm.properties");
}
return instance;
} // other /** 启动服务模式 影响 记录存储目录 **/
public boolean SERVER_MODEL = true;
/** 集群服务器 <address> client端不能为NULL **/
@FieldValue("QM.CLUSTER_LIST")
/** 本地名称,多应用不能相同,注册时用到 client端不能为NULL **/
public String LOCALNAME;
/** 集群服务器 <address> client端不能为NULL **/
@FieldValue("QM.CLUSTER_LIST")
public Set<String> CLUSTER_LIST;
/** 订阅信息 topic : groupId 填写开启 **/
@FieldValue("QM.TOPIC_INFO")
public Set<String> TOPIC_INFO;
/** 日志保存路径 <订阅,路径> consume端不能为NULL **/
@FieldValue("QM.CONSUME_LOG_SAVE_DIRS")
public Map<String, String> CONSUME_LOG_SAVE_DIRS = new HashMap<>(1); public Collection<QSubscribe> getTopics() {
Collection<QSubscribe> ret = new HashSet<>();
if (TOPIC_INFO == null) {
return ret;
}
for (String str : TOPIC_INFO) {
String[] s = str.split(":");
ret.add(QSubscribe.of(s[0], s[1]));
}
return ret;
} /** rpc param compress **/
@FieldValue("QM.COMPRESS_SIZE")
public int COMPRESS_SIZE = 1024; // NETTY
/** server port **/
@FieldValue("QM.NETTY_SERVER_PORT")
public int NETTY_SERVER_PORT = 8080;
/** server bossgroup **/
@FieldValue("QM.NETTY_SERVER_BOSSGROUP")
public int NETTY_SERVER_BOSSGROUP = 1;
/** server workergroup **/
@FieldValue("QM.NETTY_SERVER_WORKERGROUP")
public int NETTY_SERVER_WORKERGROUP = 4;
/** server socket option **/
@FieldValue("QM.NETTY_SERVER_SESSION_OPTION")
public Map<String, ?> NETTY_SERVER_SESSION_OPTION;
/** child socket option **/
@FieldValue("QM.NETTY_SERVER_CHILD_SESSION_OPTION")
public Map<String, ?> NETTY_SERVER_CHILD_SESSION_OPTION;
/** 处理模型实现 **/
@FieldValue("QM.NETTY_SERVER_ACCEPTOR")
public Class<? extends ServerSocketChannel> NETTY_SERVER_ACCEPTOR = NioServerSocketChannel.class; .............................. }

QMConfig

源码解读

PropertiesFactory

25行 用到JavaScript

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

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

108行通过ClassLoader来定位某个资源文件路径,然后再以文件读取的方式加载,不用ClassLoader加载,原因ClassLoader加载过有缓存,不能在线重载配置

initField整个函数用到java 反射技术,动态生成类,看似很多条件处理,按类型划分其实不多,常用处理五种

1.java基本类型 如int,long,short,byte,double,float,boolean,string等

2.数组同集合 array,collection

3.map字典

4.class类

5.java object

注意:java.lang.reflect.Type 非常重要,能保留泛型信息

处理Type工具 org.apache.commons.lang3.reflect.TypeUtils

QMConfig

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

@FieldValue 注解的值对应配置文件上的KEY,命名风格 QM.+属性名 保持一致

如果配置文件上没有填写,以属性默认值为准,减少执行期初始化代码

具体每个属性作为在那里,以后分析每个模块会讲到

小结:

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

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

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

[编织消息框架][JAVA核心技术]数值与逻辑分离的更多相关文章

  1. [编织消息框架][JAVA核心技术]动态代理应用12-总结

    动态代理这篇比较长,是框架组成的重要基础 回顾下学到的应用技术 1.异常应用 2.annotation技术 3.数值与逻辑分享 4.jdk.cglib.javassist等动态代理技术 5.懒处理.预 ...

  2. [编织消息框架][JAVA核心技术]异常应用

    QException是项目业务异常基类 按模块划分子类异常,方便定位那块出错 有个来源码属性code作用定位某个功能处理出错逻辑,数字类型节省内存空间,同时减少创建子类的子类 QSocketExcep ...

  3. [编织消息框架][JAVA核心技术]jdk动态代理

    需要用到的工具  jdk : javac javap class 反编译 :JD-GUI http://jd.benow.ca/ import java.lang.reflect.Invocation ...

  4. [编织消息框架][JAVA核心技术]动态代理应用2

    接下来如何实现 第一步:先把服务类,调用方法转换成数字,方便传输 第二步:提取元信息,提取又有三种方式,三种各有优点,最优方式是第一种 1.编译java时处理 2.程序启动时处理,预处理 3.调用时处 ...

  5. [编织消息框架][JAVA核心技术]动态代理应用4

    基础部份: 接下来讲编译JAVA时,生成自定义class 我们用 javax.annotation.processing.AbstractProcessor 来处理 public abstract c ...

  6. [编织消息框架][JAVA核心技术]异常基础

    Java异常体系结构 Thorwable类所有异常和错误的超类,有两个子类Error和Exception,分别表示错误和异常. 其中异常类Exception又分为运行时异常(RuntimeExcept ...

  7. [编织消息框架][JAVA核心技术]动态代理介绍

    由于java是种强类型静态语言,在执行时无法动态生成代码,静态语言基本都有这特性 动态生成代码有几种好处,也是弱类型语言的优点 1.部份逻辑可以实现热更新 2.远程调用实现非常适合 3.能动态生成扩展 ...

  8. [编织消息框架][JAVA核心技术]cglib动态代理

    先在mavne项目里添加cglib库 maven仓库搜索cglib版本 maven地址:http://mvnrepository.com/ 点击最新的版本 3.2.5 复制到pom.xml  depe ...

  9. [编织消息框架][JAVA核心技术]annotation基础

    应用动态代理技术要先掌握annotation技术 注解是JDK1.5之后才有的新特性,JDK1.5之后内部提供的三个注解 @Deprecated 意思是“废弃的,过时的” @Override 意思是“ ...

随机推荐

  1. NIO 详解

    同步非阻塞 NIO之所以是同步,是因为它的accept read write方法的内核I/O操作都会阻塞当前线程 IO模型 IO NIO 通信 面向流(Stream Oriented) 面向缓冲区(B ...

  2. R语言 循环

    R语言循环 可能有一种情况,当你需要执行一段代码几次. 通常,顺序执行语句. 首先执行函数中的第一个语句,然后执行第二个语句,依此类推. 编程语言提供允许更复杂的执行路径的各种控制结构. 循环语句允许 ...

  3. 一张图轻松掌握 Flink on YARN 应用启动全流程(上)

    Flink 支持 Standalone 独立部署和 YARN.Kubernetes.Mesos 等集群部署模式,其中 YARN 集群部署模式在国内的应用越来越广泛.Flink 社区将推出 Flink ...

  4. [JZOJ 5698] 密码锁

    思路: 差分+排序 #include <bits/stdc++.h> using namespace std; #define ll long long const int maxn = ...

  5. (转)获取android手机内部存储空间和外部存储空间的参数 && 如何决定一个apk的安装位置

    转:http://blog.csdn.net/zhandoushi1982/article/details/8560233 获取android文件系统的信息,需要Environment类和StatFs ...

  6. windows sdk版本 之 并查集生成迷宫

    #include <cstdlib> #include <ctime> #include<algorithm> using namespace std; exter ...

  7. PAT甲级——A1137 Final Grading【25】

    For a student taking the online course "Data Structures" on China University MOOC (http:// ...

  8. Spring事物管理机制简单学习

    首先spring并不直接管理事物,而是提供了多种事物管理器,他们将事务管理的职责委托给Hibernate或者JTA等持久化机制所提供的相关平台框架的事务来实现. Spring管理事物的接口是org.s ...

  9. 微信小程序 主题皮肤切换(switch开关)

    示例效果: 功能点分析: 1.点击switch开关,切换主题皮肤(包括标题栏.底部tabBar):2.把皮肤设置保存到全局变量,在访问其它页面时也能有效果3.把设置保存到本地,退出应用再进来时,依然加 ...

  10. 1.2_springboot2.x中redis缓存&原理介绍

    1.整合redis作为缓存 说明这里springboot版本2.19 Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库.缓存和消息中间件. 它支持多种类型的数据结构 ...