没什么用系列1?如何把Java 改造的像node.js?

没什么用系列1?如何把Java 改造的像node.js?

前言


因为最近在上软件工程的课,学到了数据流图和其他一些设计图,我就在想能不能把数据流图或其他设计图高效地转换为JAVA代码,并且还要很方便地后期增加处理节点。我的第一个想法是用url来表示数据处理调用关系。类似node.js的中间件路由的方式。说干就干,于是就有了Java 改 node.js 的想法。

设计


我的设想是从同一条路径上看处理节点是同步的,从不同路径上看处理节点是异步的。比如url /a/b/a/c也就是说a绑定的Java类b绑定的Java类 是 有先后关系的。但bc 就没有了。

代码

路由类

package cn.lyf.utils.router;
import java.util.*;
import java.util.concurrent.*;
import com.alibaba.fastjson.*;
import cn.lyf.utils.event.*;
/*
 * 同一节点内是同步的
 * 同一个节点共用一个req,res
 */
public class Router {
    private static HashMap<String, CopyOnWriteArrayList<Middleware>> nodes = new HashMap<String, CopyOnWriteArrayList<Middleware>>();
    //私有消息
    private static EntityEventEmitter e = new EntityEventEmitter();

    static {
        e.on("post", new handers());
    }
    // 同步方法
    private static class handers extends Events {
        @Override
        public void run() {
            JSONObject p = (JSONObject) param, res = new JSONObject();
            String flow = p.getString("flow");
            awaitPost(flow, p.getJSONObject("data"), res);
            Middleware callBack = (Middleware) p.get("callback");
            if (callBack != null)
                callBack.core(p, res);
        }
    };
    // 线程安全
    private static void awaitPost(String flow, JSONObject req, JSONObject res) {
        String[] paths = flow.split("/");
        StringBuilder p = new StringBuilder();
        CopyOnWriteArrayList<Middleware> list = null;
        for (String s : paths) {
            p.append(s);
            p.append('/');
            list = nodes.get(p.toString());
            if (list != null) {
                for (int i = 0; i < list.size(); i++) {
                    list.get(i).core(req, res);
                }
            }
        }
    }
    // 只有初始化时调用,用同步简化
    static public synchronized void use(String flow, Middleware mid) {
        String[] paths = flow.split("/");
        StringBuilder p = new StringBuilder();
        CopyOnWriteArrayList<Middleware> list = null;
        for (String s : paths) {
            p.append(s);
            p.append('/');
            list = nodes.get(p.toString());
            if (list == null) {
                list = new CopyOnWriteArrayList<Middleware>();
                nodes.put(p.toString(), list);
            }
        }
        list.add(mid);
    }
    // 异步方法
    public static void asyncPost(JSONObject req) {
        e.emit("post", req);
    }
    //url
    public static void asyncPost(String req) {
        JSONObject param = JSONObject.parseObject(req);
        e.emit("post", param);
    }
    //url+callback
    public static void asyncPost(String req, Middleware callBack) {
        JSONObject param = JSONObject.parseObject(req);
        param.put("callback", callBack);
        e.emit("post", param);
    }
    //url+data+callback
    public static void asyncPost(String req,String data, Middleware callBack) {
        JSONObject param = JSONObject.parseObject(req);
        param.put("callback", callBack);
        param.put("data",JSONObject.parseObject(data));
        e.emit("post", param);
    }
}

写过node.js的应该都挺好理解这个的吧。这里我用了阿里的fastjson。数据交换都是用json格式的。其中EntityEventEmitter是我自己写的局部消息发射器。仿node.jsEventEmitter。而Mideware是我自定义的中间件接口。另外因为以后主要是并发的读取(初始化基本只有一次)所以就hash表就用了CopyOnWriteArrayList。所以用了至于为什么没用线程安全的hashMap。是因为这里添加映射都是同步的。
有内味了
有内味了,有没有!

  • 附录
    package cn.lyf.utils.router;
    import com.alibaba.fastjson.*;
    @FunctionalInterface
    public interface Middleware {
        void core(JSONObject req, JSONObject res);
    }
    

事件驱动

Events 类

package cn.lyf.utils.event;

public abstract class Events implements Runnable, Cloneable {
    public Object param = null;

    public Object clone() {
        Object o = null;
        try {
            o = super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return o;
    }
}

EventEmitter 抽象类

package cn.lyf.utils.event;

/*
*仿 Node.js 事件处理包
*/
// 在满足以下两个条件的情况下,volatile就能保证变量的线程安全问题:
// 运算结果并不依赖变量的当前值,或者能够确保只有单一的线程修改变量的值。
import java.util.*;
import java.util.concurrent.*;

abstract class EventEmitter {
    ThreadPoolExecutor pool;
    HashMap<String, CopyOnWriteArrayList<Runnable>> handers;
    Object lock;

    public void on(String event, Runnable listener) {
        CopyOnWriteArrayList<Runnable> list;
        // 读比较多,同步 影响小
        synchronized (lock) {// 同步
            list = handers.get(event);
            if (list == null) {
                list = new CopyOnWriteArrayList<Runnable>();
                handers.put(event, list);
            }
        }
        // CopyOnWriteArrayList写互斥
        list.add(listener);
    }

    void remove(String event) {
        CopyOnWriteArrayList<Runnable> list = handers.get(event);
        if (list != null && list.size() != 0) {
            list.clear();
        }
    }

    public void emit(String event) {
        // 并发读取
        CopyOnWriteArrayList<Runnable> list = handers.get(event);
        if (list != null) {
            for (int i = 0; i < list.size(); i++) {
                pool.execute(list.get(i));
            }
        }
    }

    public void emit(String event, Object param) {
        CopyOnWriteArrayList<Runnable> list = handers.get(event);
        if (list != null) {
            for (int i = 0; i < list.size(); i++) {
                // 保证线程安全
                Events obj = (Events) ((Events) list.get(i)).clone();
                obj.param = param;
                pool.execute(obj);
            }
        }
    }
}

全局EventEmitter

package cn.lyf.utils.event;

import java.util.*;
import java.util.concurrent.*;

public class GobalEventEmitter extends EventEmitter {
    // 守护线程池
    static ThreadPoolExecutor pool = new ThreadPoolExecutor(16, 64, 200, TimeUnit.MILLISECONDS,
            new ArrayBlockingQueue<Runnable>(48), new ThreadFactory() {
                public Thread newThread(Runnable r) {
                    Thread t = Executors.defaultThreadFactory().newThread(r);
                    t.setDaemon(true);
                    return t;
                }
            });
    static HashMap<String, CopyOnWriteArrayList<Runnable>> handers = new HashMap<String, CopyOnWriteArrayList<Runnable>>();
    final static Object lock = new Object();

    public GobalEventEmitter() {
        super.lock = lock;
        super.pool = pool;
        super.handers = handers;
    }
}

实体范围有效的EventEmitter

package cn.lyf.utils.event;

import java.util.*;
import java.util.concurrent.*;

public class EntityEventEmitter extends EventEmitter {
    // 非守护线程池
    ThreadPoolExecutor pool = new ThreadPoolExecutor(16, 64, 200, TimeUnit.MILLISECONDS,
            new ArrayBlockingQueue<Runnable>(48));
    HashMap<String, CopyOnWriteArrayList<Runnable>> handers = new HashMap<String, CopyOnWriteArrayList<Runnable>>();
    final Object lock = new Object();

    public EntityEventEmitter() {
        super.lock = lock;
        super.pool = pool;
        super.handers = handers;
    }

}

上面代码主要是两个类实现了抽象类EventEmitter
。其实一点也不抽象,因为一个抽象方法也没有。主要是用子类的属性替代父类的属性来实现全局和局部事件。这种奇怪的方式我还是第一次用,不知道工程上常不常见。

其中实体范围有效的EventEmitter(EntityEventEmitter)是非守护线程池。要用户自己关闭。GobalEventEmitter是守护线程,主程序结束了就关闭了。

脚本化

这前写java.swing的 import时敲出了个 javax.script 包。我就知道事情不简单。但可惜的是 似乎JAVA 15js 解析器移除了。获取解析器返回的都是null
于是自己写一个。

思路

把字符串传进去,截获编译后的字节码,存到Map<String, byte[]> classBytes中。截获主要是自定义JavaFileManager 让他重写getJavaFileForOutput 方法调用自定义的JavaFile流。通过返回一个重写了close 方法的FilterOutputStream 把内存中的字节存到Map<String, byte[]> classBytes

脚本加载器

package cn.lyf.utils.script;

import javax.tools.*;
import java.util.*;

public class ScriptLoader extends ClassLoader {
    static Map<String, byte[]> classBytes = new HashMap<String, byte[]>();
    static JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
    static StandardJavaFileManager stdManager = compiler.getStandardFileManager(null, null, null);
    static JavaFileObject javaFileObject;
    static ScriptFileManager manager = new ScriptFileManager(stdManager, classBytes);

    public static void compile(String name, String script) {
        javaFileObject = manager.getJavaFileObject(name, script);
        JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, null, null,
                Arrays.asList(javaFileObject));
        task.call();
    }

    @Override
    public Class<?> findClass(String name) throws ClassNotFoundException {
        byte[] buf = classBytes.get(name);
        if (buf == null)
            return Class.forName(name);
        // 节约 内存
        classBytes.remove(name);
        return defineClass(name, buf, 0, buf.length);
    }
}

脚本文件管理器

package cn.lyf.utils.script;

import javax.tools.*;
import java.io.*;
import java.util.*;

class ScriptFileManager extends ForwardingJavaFileManager<JavaFileManager> {
    Map<String, byte[]> classBytes ;

    // 重写getJavaFileForOutput
    public JavaFileObject getJavaFileForOutput(JavaFileManager.Location location, String className,
            JavaFileObject.Kind kind, FileObject sibling) throws IOException {
        if (kind == JavaFileObject.Kind.CLASS) {
            return new Interceptor(className, classBytes);
        } else {
            return super.getJavaFileForOutput(location, className, kind, sibling);
        }
    }

    // 获得 JavaFileObject
    JavaFileObject getJavaFileObject(String name, String code) {
        return new ScriptCode(name, code);
    }

    public ScriptFileManager(JavaFileManager fileManager,Map<String, byte[]> classBytes ) {
        super(fileManager);
        this.classBytes=classBytes;
    }

}

脚本代码类

package cn.lyf.utils.script;
import javax.tools.*;
import java.nio.*;
import java.net.*;

class ScriptCode extends SimpleJavaFileObject {
    private String code;
    ScriptCode(String name, String code) {
        super(URI.create("string:///" + name.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE);
        this.code = code;
    }

    public CharBuffer getCharContent(boolean ignoreEncodingErrors) {
        return CharBuffer.wrap(code);
    }
}

拦截器

package cn.lyf.utils.script;

import javax.tools.*;
import java.io.*;
import java.util.*;
import java.net.*;

class Interceptor extends SimpleJavaFileObject {
    private Map<String, byte[]> classBytes;
    private String name;

    public Interceptor(String name, Map<String, byte[]> classBytes) {
        super(URI.create("string:///" + name.replace('.', '/') + Kind.SOURCE.extension), Kind.CLASS);
        this.name = name;
        this.classBytes = classBytes;
    }

    // 截获 Class Bytes
    public OutputStream openOutputStream() {
        return new FilterOutputStream(new ByteArrayOutputStream()) {
            public void close() throws IOException {
                classBytes.put(name, ((ByteArrayOutputStream) out).toByteArray());
                out.close();
            }
        };
    }
}
不足

脚本导入上下文的方法还没写。但都是字符串了,还怕啥呢。拼串就完了。不知到有没有大神有更高级的写法。望不吝赐教。

后记

感觉最近想法很多,学的很少。有点思而不学则殆的感觉。轮子要造,但不要造太多。利用前人的智慧也是很重要的。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值