[编织消息框架][JAVA核心技术]数值与逻辑分离
为什么要分离?
业务需求是不停地变,如果把条件写进代码里,当用户需求变时要改代码发版本更新才能生效,这过程无疑是漫长的
就算是在开发期,不停的变开发者精力耗光在沟通,小修改上,无法专注逻辑部分
分离的根本目的是让开发者专注写引擎部分,无需关注太多业务上的边界,条件等
需要分离什么类型数值?
如活动开启时间,购买满足条件,购买上限等 这些不确定用户具体需求,全都可以弄成动态获取
分离技术实现有很多
如使用数据库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核心技术]数值与逻辑分离的更多相关文章
- [编织消息框架][JAVA核心技术]动态代理应用12-总结
动态代理这篇比较长,是框架组成的重要基础 回顾下学到的应用技术 1.异常应用 2.annotation技术 3.数值与逻辑分享 4.jdk.cglib.javassist等动态代理技术 5.懒处理.预 ...
- [编织消息框架][JAVA核心技术]异常应用
QException是项目业务异常基类 按模块划分子类异常,方便定位那块出错 有个来源码属性code作用定位某个功能处理出错逻辑,数字类型节省内存空间,同时减少创建子类的子类 QSocketExcep ...
- [编织消息框架][JAVA核心技术]jdk动态代理
需要用到的工具 jdk : javac javap class 反编译 :JD-GUI http://jd.benow.ca/ import java.lang.reflect.Invocation ...
- [编织消息框架][JAVA核心技术]动态代理应用2
接下来如何实现 第一步:先把服务类,调用方法转换成数字,方便传输 第二步:提取元信息,提取又有三种方式,三种各有优点,最优方式是第一种 1.编译java时处理 2.程序启动时处理,预处理 3.调用时处 ...
- [编织消息框架][JAVA核心技术]动态代理应用4
基础部份: 接下来讲编译JAVA时,生成自定义class 我们用 javax.annotation.processing.AbstractProcessor 来处理 public abstract c ...
- [编织消息框架][JAVA核心技术]异常基础
Java异常体系结构 Thorwable类所有异常和错误的超类,有两个子类Error和Exception,分别表示错误和异常. 其中异常类Exception又分为运行时异常(RuntimeExcept ...
- [编织消息框架][JAVA核心技术]动态代理介绍
由于java是种强类型静态语言,在执行时无法动态生成代码,静态语言基本都有这特性 动态生成代码有几种好处,也是弱类型语言的优点 1.部份逻辑可以实现热更新 2.远程调用实现非常适合 3.能动态生成扩展 ...
- [编织消息框架][JAVA核心技术]cglib动态代理
先在mavne项目里添加cglib库 maven仓库搜索cglib版本 maven地址:http://mvnrepository.com/ 点击最新的版本 3.2.5 复制到pom.xml depe ...
- [编织消息框架][JAVA核心技术]annotation基础
应用动态代理技术要先掌握annotation技术 注解是JDK1.5之后才有的新特性,JDK1.5之后内部提供的三个注解 @Deprecated 意思是“废弃的,过时的” @Override 意思是“ ...
随机推荐
- mysql双主热备
先搭建mysql主从模式,主从请参考mysql 主从笔记 然后在在配置文件里添加如下配置 1 log_slave_updates= #双主热备的关键参数.默认情况下从节点从主节点中同步过来的修改事件是 ...
- Perl 基础语法
Perl 基础语法 Perl借用了C.sed.awk.shell脚本以及很多其他编程语言的特性,语法与这些语言有些类似,也有自己的特点. Perl 程序有声明与语句组成,程序自上而下执行,包含了循环, ...
- SpringCloud网关无法加载权限及IP黑名单白名单
启动springcloud服务注册中心base,再启动网关远程调用base的接口读取权限等数据,控制台出现加载null权限ERROR提示.在远程调用处打断点,先进入代理,找到抛出异常的原因是reque ...
- Js 数组的各种方法及操作
一.数组去重 var arr = [0,1,20,3,0,45,6,0]; Array.prototype.unrepeat = function(){ var array = []; for(var ...
- 在.net core上,Web网站调用微信支付-统一下单接口(xml传参)一直返回错误:mch_id参数格式错误
这是 微信支付-统一下单 接口文档 一.问题描述 在调用统一下单接口时,报mch_id参数格式错误,但商户ID确实是10位数字正确的,可就是一直报这个错误 返回的错误xml如下: 二.排错过程 1.多 ...
- js实现前端动态筛选表格内容
代码参考: http://www.sharejs.com/codes/javascript/4289 http://www.jb51.net/article/103420.htm https://ww ...
- iOS开发之SceneKit框架--SCNCamera.h
1.SCNCamera简介 被称为照相机或者摄像机,可以附加到节点以提供显示场景的角度.其实就是用户视角和人的眼睛一样. 2.相关API简介 初始化 //懒加载 + (instancetype)cam ...
- 第九篇:Spring的applicationContext.xml配置总结
在前面的一篇日志中,记录了web.xml配置启动的顺序,web启动到监听器ContextLoaderListener时,开始加载spring的配置文件applicationContext.xml(通常 ...
- D3.js+Es6+webpack构建人物关系图(力导向图)
功能列表:1. 增加下载SVG转PNG功能,图片尺寸超出可视区域也能够下载全部显示出来2. 增加图谱放大缩小平移功能3. 增加图谱初始化加载时自动缩放功能4. 增加导出excel功能,配合后台工具类达 ...
- chown命令使用
1.原文件为root权限,改为用户所属权限包括文件夹以下的目录这里必须有R chown -R usrname:username /file 2.修改 tmp 目录为可写权限 chmod -R 777 ...