2018-7-13 by MartinDelophy
java 反向工程主要是通过将数据库中的表反向映射成java实体类和Repository文件,能够减轻开发者开发的困难。 这个项目主要是一年前实现的,在这里也就不详细解释了。 项目地址:https://github.com/MartinDelophy/javaBackEngineering
2018-7-12 by MartinDelophy
序言
:一晃做厦门海洋经济运行监测系统已经很长一段时间了,总的来说,收获颇多,接下来会陆续发布几篇博客来对这个项目收获到的经验来做一定的整理。
那么今天,我们主要来谈谈什么是持续发布。
发布:顾名思义就是将我们的代码打包后运行在我们的服务器上,能够让人们看到这个项目。 持续发布:那就是阶段性的发布到服务器上,能够给予用户较好的体验。
原因很简单,1.用户有时并不能明确自己需要的是什么。2.你有时未必正确理解了用户的想法。那么以上两点原因直接导致了你做的产品不一定满足了用户的需求,所以,需要持续性的部署才能,让用户有较好的体验。
通常,人们部署系统的方式是通过远程登陆的方案,然后将文件拷贝到某一个路径下,然后重新启动服务(这是一种比较low的方案),好一点的方案便是用jenkins这类工具集成、部署。但如果是在内网中,这样的方案时间开销也是巨大的。所以,今天,我来教大家一种持续发布的代码。
代码在:https://github.com/MartinDelophy/continousPublishOnWindows 可以看到,具体配置可以根据情况进行更改。
持续发布步骤说明: 1.通过net use指令来进行远成连接 2.复制文件到指定远程目录 3.设定schtasks来创建定时器(由于schtasks必须要设定启动时间,所以如果要立即执行,就要在这之后 run一下)启动远程脚本 4.启动远程脚本,运行项目
这样就实现了一键式的项目发布,很简单吧。
2018-5-18 by MartinDelophy
模版引擎,众所周知就是将你需要绑定的值从后端传到前端,帮助用户解决变量预留的问题。
那么我的想法就是去构建一个模版引擎,帮助用户去解决这个问题。 那么,首先我们应该怎么做呢? 我的设计想法如下:
首先,将html的整个内容塞到 jsbox 中,之后将内容中的有效绑定字段进行提取,传到后端,解析字段。 通过后端拿到相应的字段的值,绑定之后推到jsbox中,在将jsbox中的内容推到前端进行渲染。
为了验证这个想法,我写了一个demo,地址在:https://github.com/MartinDelophy/narko 相应的demo在test文件夹中,可以通过npm install 来对本模版引擎进行安装。 当然,这个模版有很多的地方需要改进,比如多网页模版,组建生命周期,双向绑定。
2018-5-15 by MartinDelophy
上回,我们很简单的对node-jvm进行了一定的分析,但问题还是停留在表面,仅仅只是介绍了node 里面的一些特点,和java并无很多的关系,今天让我们接着来讲后续的内容。 要想很清晰的知道后续的内容,我们首先要对java class类文件做一定的了解。 我想在网上这样的文章算是比较多了,我推荐几个链接可以去看看 1.https://blog.csdn.net/u010425776/article/details/51245055 2.https://www.cnblogs.com/Qian123/p/5707562.html 3.https://blog.csdn.net/u013309870/article/details/72975536 4.http://www.blogjava.net/DLevin/archive/2011/09/05/358033.html 5.http://www.blogjava.net/DLevin/archive/2011/09/05/358034.html
看了上面几个文章之后,我们开始实战来分析一下下面的代码 首先看到的是用来针对fib 进行计算的java文件
Main.java
public class Main { public static long fib(int n) { if (n <= 1) return n; return fib(n - 1) + fib(n - 2); } public static void main(String[] args) { if (args.length == 0) { System.out.print("help: java Fibonacci {Number}"); return; } int N = Integer.parseInt(args[0]); long start = System.currentTimeMillis(); System.out.format("Fibonacci from 1 to %s:\n", N); for (int i = 1; i <= N; i++) { System.out.println(i + ": " + fib(i)); } long stop = System.currentTimeMillis(); System.out.println("time: " + (stop - start) + "ms"); System.out.println("done."); } }
该部分代码,主要是实现了最基本的fib 功能,并且调用。
那么接下来我用make 生成Main.class文件,然后通过 javap -v -verbose Main 分析一下改该类文件中的内容,内容如下:
Last modified May 7, 2018; size 1286 bytes MD5 checksum 115352bb394b31575a6288f7bdf82f35 Compiled from "Main.java" public class Main minor version: 0 major version: 52 flags: ACC_PUBLIC, ACC_SUPER Constant pool: #1 = Methodref #9.#35 // java/lang/Object."<init>":()V #2 = Methodref #23.#36 // Main.fib:(I)J #3 = Fieldref #37.#38 // java/lang/System.out:Ljava/io/PrintStream; #4 = String #39 // help: java Fibonacci {Number} #5 = Methodref #40.#41 // java/io/PrintStream.print:(Ljava/lang/String;)V #6 = Methodref #42.#43 // java/lang/Integer.parseInt:(Ljava/lang/String;)I #7 = Methodref #37.#44 // java/lang/System.currentTimeMillis:()J #8 = String #45 // Fibonacci from 1 to %s:\n #9 = Class #46 // java/lang/Object #10 = Methodref #42.#47 // java/lang/Integer.valueOf:(I)Ljava/lang/Integer; #11 = Methodref #40.#48 // java/io/PrintStream.format:(Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/PrintStream; #12 = Class #49 // java/lang/StringBuilder #13 = Methodref #12.#35 // java/lang/StringBuilder."<init>":()V #14 = Methodref #12.#50 // java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder; #15 = String #51 // : #16 = Methodref #12.#52 // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; #17 = Methodref #12.#53 // java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder; #18 = Methodref #12.#54 // java/lang/StringBuilder.toString:()Ljava/lang/String; #19 = Methodref #40.#55 // java/io/PrintStream.println:(Ljava/lang/String;)V #20 = String #56 // time: #21 = String #57 // ms #22 = String #58 // done. #23 = Class #59 // Main #24 = Utf8 <init> #25 = Utf8 ()V #26 = Utf8 Code #27 = Utf8 LineNumberTable #28 = Utf8 fib #29 = Utf8 (I)J #30 = Utf8 StackMapTable #31 = Utf8 main #32 = Utf8 ([Ljava/lang/String;)V #33 = Utf8 SourceFile #34 = Utf8 Main.java #35 = NameAndType #24:#25 // "<init>":()V #36 = NameAndType #28:#29 // fib:(I)J #37 = Class #60 // java/lang/System #38 = NameAndType #61:#62 // out:Ljava/io/PrintStream; #39 = Utf8 help: java Fibonacci {Number} #40 = Class #63 // java/io/PrintStream #41 = NameAndType #64:#65 // print:(Ljava/lang/String;)V #42 = Class #66 // java/lang/Integer #43 = NameAndType #67:#68 // parseInt:(Ljava/lang/String;)I #44 = NameAndType #69:#70 // currentTimeMillis:()J #45 = Utf8 Fibonacci from 1 to %s:\n #46 = Utf8 java/lang/Object #47 = NameAndType #71:#72 // valueOf:(I)Ljava/lang/Integer; #48 = NameAndType #73:#74 // format:(Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/PrintStream; #49 = Utf8 java/lang/StringBuilder #50 = NameAndType #75:#76 // append:(I)Ljava/lang/StringBuilder; #51 = Utf8 : #52 = NameAndType #75:#77 // append:(Ljava/lang/String;)Ljava/lang/StringBuilder; #53 = NameAndType #75:#78 // append:(J)Ljava/lang/StringBuilder; #54 = NameAndType #79:#80 // toString:()Ljava/lang/String; #55 = NameAndType #81:#65 // println:(Ljava/lang/String;)V #56 = Utf8 time: #57 = Utf8 ms #58 = Utf8 done. #59 = Utf8 Main #60 = Utf8 java/lang/System #61 = Utf8 out #62 = Utf8 Ljava/io/PrintStream; #63 = Utf8 java/io/PrintStream #64 = Utf8 print #65 = Utf8 (Ljava/lang/String;)V #66 = Utf8 java/lang/Integer #67 = Utf8 parseInt #68 = Utf8 (Ljava/lang/String;)I #69 = Utf8 currentTimeMillis #70 = Utf8 ()J #71 = Utf8 valueOf #72 = Utf8 (I)Ljava/lang/Integer; #73 = Utf8 format #74 = Utf8 (Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/PrintStream; #75 = Utf8 append #76 = Utf8 (I)Ljava/lang/StringBuilder; #77 = Utf8 (Ljava/lang/String;)Ljava/lang/StringBuilder; #78 = Utf8 (J)Ljava/lang/StringBuilder; #79 = Utf8 toString #80 = Utf8 ()Ljava/lang/String; #81 = Utf8 println { public Main(); descriptor: ()V flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return LineNumberTable: line 1: 0 public static long fib(int); descriptor: (I)J flags: ACC_PUBLIC, ACC_STATIC Code: stack=4, locals=1, args_size=1 0: iload_0 1: iconst_1 2: if_icmpgt 8 5: iload_0 6: i2l 7: lreturn 8: iload_0 9: iconst_1 10: isub 11: invokestatic #2 // Method fib:(I)J 14: iload_0 15: iconst_2 16: isub 17: invokestatic #2 // Method fib:(I)J 20: ladd 21: lreturn LineNumberTable: line 4: 0 line 5: 5 line 6: 8 StackMapTable: number_of_entries = 1 frame_type = 8 /* same */ public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC Code: stack=6, locals=6, args_size=1 0: aload_0 1: arraylength 2: ifne 14 5: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream; 8: ldc #4 // String help: java Fibonacci {Number} 10: invokevirtual #5 // Method java/io/PrintStream.print:(Ljava/lang/String;)V 13: return 14: aload_0 15: iconst_0 16: aaload 17: invokestatic #6 // Method java/lang/Integer.parseInt:(Ljava/lang/String;)I 20: istore_1 21: invokestatic #7 // Method java/lang/System.currentTimeMillis:()J 24: lstore_2 25: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream; 28: ldc #8 // String Fibonacci from 1 to %s:\n 30: iconst_1 31: anewarray #9 // class java/lang/Object 34: dup 35: iconst_0 36: iload_1 37: invokestatic #10 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 40: aastore 41: invokevirtual #11 // Method java/io/PrintStream.format:(Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/PrintStream; 44: pop 45: iconst_1 46: istore 4 48: iload 4 50: iload_1 51: if_icmpgt 94 54: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream; 57: new #12 // class java/lang/StringBuilder 60: dup 61: invokespecial #13 // Method java/lang/StringBuilder."<init>":()V 64: iload 4 66: invokevirtual #14 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder; 69: ldc #15 // String : 71: invokevirtual #16 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 74: iload 4 76: invokestatic #2 // Method fib:(I)J 79: invokevirtual #17 // Method java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder; 82: invokevirtual #18 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 85: invokevirtual #19 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 88: iinc 4, 1 91: goto 48 94: invokestatic #7 // Method java/lang/System.currentTimeMillis:()J 97: lstore 4 99: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream; 102: new #12 // class java/lang/StringBuilder 105: dup 106: invokespecial #13 // Method java/lang/StringBuilder."<init>":()V 109: ldc #20 // String time: 111: invokevirtual #16 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 114: lload 4 116: lload_2 117: lsub 118: invokevirtual #17 // Method java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder; 121: ldc #21 // String ms 123: invokevirtual #16 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 126: invokevirtual #18 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 129: invokevirtual #19 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 132: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream; 135: ldc #22 // String done. 137: invokevirtual #19 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 140: return LineNumberTable: line 10: 0 line 11: 5 line 12: 13 line 15: 14 line 17: 21 line 18: 25 line 19: 45 line 20: 54 line 19: 88 line 22: 94 line 23: 99 line 25: 132 line 26: 140 StackMapTable: number_of_entries = 3 frame_type = 14 /* same */ frame_type = 254 /* append */ offset_delta = 33 locals = [ int, long, int ] frame_type = 250 /* chop */ offset_delta = 45 } SourceFile: "Main.java"
那么,首先,我们看到的是对于java文件信息的一些标识、本文件的版本信息等信息,这些不是很重要,所以省略。 我们重点来说说该处的常量池。
#1 = Methodref #9.#35 // java/lang/Object."<init>":()V
#1
主要表示的是常量池的程序计数器(从1开始的),Methodref 表示的是用于记录方法信息 从#9 计数器进入 #35处被调用。而下面的
{ public Main(); descriptor: ()V flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return LineNumberTable: line 1: 0 ...
表示的是类名、方法名,descriptor用来区分是类还是方法,flags 来表示方法是什么样的类型,code则是代码段, LineNumberTable 表示的是这个方法在原java的第几行。 那么我们着重分析一下
0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return
上面的三条指令:
aload_0:这个操作码是aload格式操作码中的一个。它们用来把对象引用加载到操作码栈。 表示正在被访问的局部变量数组的位置,但只能是0、1、2、3 中的一个。还有一些其它类似的操作码用来载入非对象引用的数据,如iload, lload, float 和 dload。其中 i 表示 int,l 表示 long,f 表示 float,d 表示 double。局部变量数组位置大于 3 的局部变量可以用 iload, lload, float, dload 和 aload 载入。这些操作码都只需要一个操作数,即数组中的位置。 invokespecial:这些操作码属于一组函数调用的操作码,包括:invokedynamic、invokeinterface、invokespecial、invokestatic、invokevirtual。在这个 class 文件中,invokespecial 和 invokevirutal 两个指令都用到了,两者的区别是,invokevirutal 指令调用一个对象的实例方法,invokespecial 指令调用实例初始化方法、私有方法、父类方法。 return:这个操作码属于ireturn、lreturn、freturn、dreturn、areturn 和 return 操作码组。每个操作码返回一种类型的返回值,其中 i 表示 int,l 表示 long,f 表示 float,d 表示 double,a 表示 对象引用。没有前缀类型字母的 return 表示返回 void。 对于上述的指令,都会有操作数栈来进行配合。如果操作数没用完则return ,则等待系统进行垃圾回收。若用完未取到则会跑出异常。 详细的指令信息可以在:https://docs.oracle.com/javase/specs/jvms/se7/html/index.html 中见到
通过上述对于class类的简要分析,我们对jvm class文件有了一定的了解。接下来将讲述详细的内容以及如何去应用。
2018-5-14 by MartinDelophy
前不久,在github不小心看到了一个node-jvm 不由得惊叹,node竟然也能写出jvm,故clone了一波来看看到底是何方神圣。 地址:https://github.com/YaroslavGaponov/node-jvm
首先那肯定是看一下官方案例的,官方主要是用fibonacci.js 来new了一个jvm,然后设定日志等级,载入class文件,设定异步事件,就开始执行了。感觉很ok
那么,我们先看看它jvm地方的源码吧,首先,看到的是这句话,无用的代码暂时省略
var JVM = module.exports = function() { if (this instanceof JVM) { JVM.super_.call(this); THREADS.add(new Thread("main")); this.entryPoint = { className: null, methodName: "main" }; } else { return new JVM(); } }
代码很直观,我们可以看到一开始,如果该对象是通过jvm new出来的对象就进行入 JVM.super_.call这句话中,如果不是则 new 一下返回。
这几个地方还是比较容易理解的,除了这句 JVM.super.call,我翻遍了代码,但是却没有找到这个super 方法,那么这个super_ 方法是哪里来的呢? 幸运的是,另外一句话也同样引起了我的注意
var util = require("util"); var EE = require("events").EventEmitter; util.inherits(JVM, EE);
util.inherits指的是将 require("events").EventEmitter 方法继承到JVM中,那么super_ 从哪里来的呢,我们还是不知道,通过 这篇blog的帮助(https://www.cnblogs.com/youlechang123/p/5602335.html),我们看到了一段代码
exports.inherits = function(ctor, superCtor) { ctor.super_ = superCtor; ctor.prototype = Object.create(superCtor.prototype, { constructor: { value: ctor, enumerable: false, writable: true, configurable: true } }); };
这下,我恍然大悟,原来JVM.super_ 不仅这样出来,而且其中存放的是 EventEmitter 这个函数,并且在prototype中存放了 EventEmitter的原型,也就是说JVM可以获得EventEmitter中的所有方法,这也就可以解释了为什么JVM new出之后可以设定异步事件。
node-jvm 是一个比较庞大的部分,今天先分析到这里,在下一章中我会告诉大家一个有效的工具。
2017-5-13 by MartinDelophy
从读研开始做OA项目一晃将近半年了,一直在用到java方面的注解,愚钝的我直到现在才开始考虑到如何自定义注解,从网上看到了一个好例子,分享出来给需要帮助的人使用。
那么首先我们先做一个接口,这个接口的作用是作为注解引用的,里面做了一个枚举类型,定义了一个Tt()方法,默认值为C
@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented public @interface test { public enum T{ A,B,C}; T Tt() default T.C; }
之后我们定义了一个 test2的类,外加一个主函数目的很简单,就是为了让test3去读我们test2的class类
public class test2 { @test(Tt= test.T.C) private String t; public static void main(String[] args) { test3.gettest(test2.class); } }
test3就是看test2的类中有没有test注解,有的话就是输出来看看
public class test3 { public static void gettest(Class<?> clazz) { String str=""; Field[] fields = clazz.getDeclaredFields(); for(Field field :fields) { if(field.isAnnotationPresent(test.class)){ test t=(test) field.getAnnotation(test.class); str=str+t.Tt(); System.out.println(str); } } } }
结果,控制台输出的是 C,因为一开始将枚举中,默认的C传入,再字符串拼接,显示