刚刚在javaeye看到一个名为Douyu的国人项目,认为搞下去未来可能非常有意思,放到blog上做个标记。
——————下面是转载的作者原文———————
原文地址例如以下:http://zhh2009.javaeye.com/blog/517796
标题的构思来源于Rod Johnson的那本"Without EJB"以及CCTV5中一句耳熟能详的广告词,
只是此文并非用来批判SSH(Struts、Spring、Hibernate)/JSP/Servlet的,
也不是为某品牌做广告,而是用来分享这将近一年来的研究心得。
去年圣诞节时曾在JavaEye发过一两篇文章,只是如今找不到了,
文章内容提到要在3个月左右的时间内设计出一个有别于SSH的新型MVC框架,
设计的起点最初是以JSP/Servlet为基础的,尽管在两个多月后有了个雏形,
可是跟Rails这种框架相比还是没有明显的优势,
比方在不使用反射的情况下,
非常难将不同的uri相应到Servlet类中的public方法。
(Servlet类指的是继承自javax.servlet.http.HttpServlet的类)
每次改动Servlet类的源码时总得经过烦人的手工编译步骤(有时还不得不重新启动Tomcat),
还有与数据库打交道的模型层也得人工干预,一堆烦人的映射配置。
那三个月内时常有沮丧感,似乎已走近了死胡同!
后来心一狠,决心甩开JSP/Servlet那一堆条条框框,把设计的起点再往下深一个层次。
由于2007年曾具体研究过Java语言的编译器(javac)实现细节,所以从编译器着手,
可是编译器的强项在于分析Java源码,无法处理Http请求,
接着在网上把Tomcat6的源码下下来研究了三个月,
期间顺便研究了Sun公司的超轻量级Http服务器"com.sun.net.httpserver"的源码,
同一时候具体学习HTTP/1.0(RFC1945)与HTTP/1.1(RFC2616)协议。
可是Tomcat6过于臃肿了,包括的Java文件超过了1300个,
光是解析server.xml与web.xml的代码看完后就让人有烦躁感。
(如org/apache/tomcat/util/digester与org/apache/catalina/startup包中的非常多类)
另外最重要一点,Tomcat6採用的是Eclipse JDT编译器,不过用来编译JSP文件,
编译器在控制层没有发挥一点作用。
而Sun公司的超轻量级Httpserver又过于简单了,连HTTP/1.1的大多数功能都没实现,
除了參考一下它的SSL实现外基本上毫无价值。
本想在现有的JSP/Servlet容器上做一下简单扩展就得了,
哪知也是四处碰壁(还下过Jetty的源码下来看了一会,结果发现比Tomcat6还糟),
后来决定对Tomcat6与Sun的Httpserver进行大刀阔斧的改造,
完毕了一个精简版的改良后的基于NIO的Httpserver(眼下的版本号仅仅有60个左右的Java源文件),
而且能跟Javac编译器完美结合,能直接执行Java源文件。
在模型层这一块,最初是从书上和网络上对Hibernate进行应用层次的研究,
可是并不想深入源码,由于代码量也实在是太多了,倒是对Ibatis2.0深入研究了一下,
Ibatis2.0代码量比較少,也简单,看了不到一星期就基本上看完了,只是如今并没留下深刻映象,
由于并没发现什么特别出彩的地方,Ibatis2.0还是离不开xml,而我想要全然抛弃xml。
当然,无论Hibernate也好,Ibatis2.0也好,相比Rails的ActiveRecord还是逊色了点,
只是我的目标并非要造一个Hibernate、Ibatis2.0或ActiveRecord这种轮子,
我的要求更高,我在想怎样才干写更少的代码,怎样才干实现自己主动化?
可不能够在server启动时或执行时动态解析数据库的元数据,
让编译器跟据这些元数据动态生成类呢?
接着我转去研究JDBC-1.2/JDBC-2.1/JDBC-3.0/JDBC-4.0规范,研究数据库驱动的开发手冊。
我得从零開始,我眼下的实现是这样做的:你能够在你自己的Java源文件里直接引用动态生成的类,
就像这些类是你自己写的一样,ORM已基本上实现自己主动化了,2.9 节专门讲Douyu的ORM。
最后一点值得一提的是,我在Java语言层次引入了权限管理模型,
只是你别操心,我并没有引入新的Java语言语法,
仅仅是借助Annotation扩充了某些特殊的语义。
眼下这个权限管理模型的粒度仅仅是划分为功能、字段权限两个等级,
并没有实现与详细业务相关的数据权限,只是在未来的路线图中有打算引入工作流模型,
到时会努力尝试各种实现数据权限的方案。
与权限相关的细节请看2.8节 Douyu的权限模型 。
折腾了半年后,发现已不再是个MVC框架了,我想称为平台更合适,
一种执行在JVM之上的新型平台,我给她起了个名字: Douyu
(呵呵,名字的由来临时保密,或许你能猜出来。。。)
尽管孤军奋战将近一年,自我感觉小有成就,可是还有非常多不怎么惬意的地方,
各位大牛们或许更牛,看见不爽砸砖头便是。
Ok,上干货。
1. 安装配置
(这里仅仅针对Windows平台,特别是XP操作系统,由于我没其它试验环境)
1.1 安装JDK
Douyu是在JDK1.6下开发的,不支持也不打算支持JDK1.4及更早的版本号,JDK1.5我没有測试过,
所以我仅仅能推荐你安装JDK1.6了 ,安装细节我想你都会,
唯一要注意的一点是:最好是建个JAVA_HOME环境变量,然后把%JAVA_HOME%/bin增加到Path中,
由于在Douyuserver的启动脚本中并没有进行过多的环境检測,
而是直接使用了%JAVA_HOME%/bin文件夹下的java命令来启动Java HotSpot VM。
1.2 安装Douyuserver
Douyu项目主页眼下放在:
http://code.google.com/p/douyu/
请先下载二进制版的压缩文件:
http://douyu.googlecode.com/files/Douyu_0_1_0.rar
眼下的版本号是:0.1.0,版本号号非常小,但大多数功能都包括了,
我并不推荐你用于工业级别的产品开发,
由于还不稳定,眼下仅仅适合分享、交流、尝鲜目的。
下下来后直接解压到一个你选定的文件夹(假定你解压到了D:/Douyu文件夹)
D:/Douyu文件夹里头有以下7个文件夹(跟Tomcat6差点儿相同):
- apps //应用程序的源码放在这里,里头有一些java源文件是以下的演示中用到的,当然你能够全都删了。
- bin //server的启动脚本和执行时类库都在这里
- conf //server的配置文件放在这里
- lib //应用程序使用到的第三方类库(比方数据库驱动)都放在这里,初始情况下是个空文件夹
- logs //存放server执行期间的日志(眼下日志仅仅是输出到控制台),初始情况下是个空文件夹
- temp //server执行期间用到的暂时文件夹(比方上传文件时可能会用到),初始情况下是个空文件夹
- work //server执行期间的工作文件夹,初始情况下是个空文件夹
apps //应用程序的源码放在这里,里头有一些java源文件是以下的演示中用到的,当然你能够全都删了。
bin //server的启动脚本和执行时类库都在这里
conf //server的配置文件放在这里
lib //应用程序使用到的第三方类库(比方数据库驱动)都放在这里,初始情况下是个空文件夹
logs //存放server执行期间的日志(眼下日志仅仅是输出到控制台),初始情况下是个空文件夹
temp //server执行期间用到的暂时文件夹(比方上传文件时可能会用到),初始情况下是个空文件夹
work //server执行期间的工作文件夹,初始情况下是个空文件夹
了解了这些就足够了,眼下你不须要做不论什么配置。
2. 体验Douyu
2.1 怎样执行Douyuserver?
点"開始->执行",输入cmd,打开一个控制台,切换到D:/Douyu/bin文件夹,
然后输入 douyu 启动Douyuserver (要关闭Douyuserver连按两次Ctrl+C既可)
见下图:
假设你是第一次打开操作系统第一次启动JVM执行Java程序
或是隔了一个小时左右又一次启动JVM执行Java程序,这时可能要等待几秒钟(5--10秒),
出现这样的情况并非Douyuserver的问题,而是JVM本身或操作系统的问题,
通常启动Douyuserver假设不载入数据库的话,一般在一秒钟内就能启动完毕了。
Douyuserver默认情况下监听的主机名是: localhost,port: 8000
假设你不喜欢这种默认配置,
或者最常见的情况是port8000被占用了
(一般抛出异常: java.net.BindException: Address already in use)
你能够打开conf/server.java这个服务器配置文件,
配置文件本身就是一个java源文件,參数的配置使用Java语言的Annotation语法,
全部与server配置有关的都是Annotation或是Enum,全都在com.douyu.config包中定义。
- import com.douyu.config.*;
- @Server (
- port=8000 ,
- .................
import com.douyu.config.*;
@Server(
port=8000,
.................
要改动默认主机名和端口,请改动hostName和port的值,
hostName是一个字符串,能够用IP地址来表示,port是一个整型(int)值。
其它非常多參数先不罗列了,使用到时再具体说明。
当你改动了conf/server.java后,你也不须要自己去手工编译它,
启动Douyuserver时,Douyu会自行决定是否要编译它。
假设conf/server.java存在语法错误,那么编译失败,
Douyuserver的启动也会失败,同一时候向你显示编译错误信息。
下文中假定Douyuserver已启动,监听的主机名是: localhost,port是: 8000
下面全部样例都经过严格測试了,
我的JRE版本号:
D:/Douyu/bin>java -version
java version "1.6.0_16"
Java(TM) SE Runtime Environment (build 1.6.0_16-b01)
Java HotSpot(TM) Client VM (build 14.2-b01, mixed mode, sharing)
測试浏览器用了两个:
傲游浏览器(IE6.0),
谷歌浏览器(Chrome 3.0.195.27)
2.2 Hello World!
2.2.1 程序代码
- //相应apps/HelloWorld.java文件
- import java.io.PrintWriter;
- import com.douyu.main.Controller;
- @Controller
- public class HelloWorld {
- public void index(PrintWriter out) {
- out.println("Hello World!" );
- }
- }
//相应apps/HelloWorld.java文件
import java.io.PrintWriter;
import com.douyu.main.Controller;
@Controller
public class HelloWorld {
public void index(PrintWriter out) {
out.println("Hello World!");
}
}
2.2.2 手工编译已经Out了,你再也不须要这一步了。
2.2.3 执行HelloWorld
打开你心爱的浏览器,输入 http://localhost:8000/HelloWorld
假设你能看到下图中所看到的内容,恭喜你,你己经进入了Douyu的精彩世界。
(注:这是你第一次直接执行Java源文件,可能会等几秒钟(2--4秒),由于Douyu得初始化编译器)
2.2.4 程序代码说明
com.douyu.main包中的类大多数是Annotation,还包括一些重要的接口和类,
相当于java.lang,是你用Douyu开发程序时最经常使用到的,也是通往其它模块的高速入口,
本想让com.douyu.main包中的类像java.lang一样让编译器自己主动导入的,
可是考虑到非常多开发者更偏爱使用IDE,不同IDE内置的编译器不一样,
从而会引起找不到com.douyu.main包中的类的问题,所以最后决定放弃这种设计了。
@Controller 这个Annotation是用来告诉Douyu这是一个控制器,
当你在浏览器的地址栏中输入http://localhost:8000/HelloWorld 这种uri时,
浏览器内部一般会生成一个HTTP GET请求消息,消息内容相似这样:
- GET /HelloWorld HTTP/1.1
- Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg,......
- Accept-Language: zh-cn
- Accept-Encoding: gzip, deflate
- User-Agent: Mozilla/4.0 (compatible; MSIE 6.0 ; Windows NT 5.1 ; SV1; Maxthon)
- Host: localhost:8000
- Connection: Keep-Alive
GET /HelloWorld HTTP/1.1
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg,......
Accept-Language: zh-cn
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Maxthon)
Host: localhost:8000
Connection: Keep-Alive
只是这里并不打算介绍HTTP协议,假设你有兴趣,能够把RFC2616下下来研究。
Douyuserver收到浏览器发来的请求消息后,
特别留意 "GET /HelloWorld HTTP/1.1" 这一行消息,
当中的"/HelloWorld"表示想要获取Douyuserver上的哪些资源,
资源有静态的(如html、jpg等文件),也有动态的,在Douyuserver中动态资源仅仅有一种,
凡是带有@Controller这个Annotation的Java源文件都是能够直接通过uri訪问的动态资源。
只是Douyuserver不能依据uri的表面特征一眼就看出它是动态的还是静态资源,
server内部有一个专用的资源装载器,装载器的搜索根文件夹是从apps这个地方開始的,
资源装载器会尝试将apps文件夹与uri组合成一个java.io.File对象,
假设File对象存在,那么它就是一个静态资源,
然后由Douyuserver内部的静态资源处理器给浏览器发送包括有文件内容的响应消息;
假设File对象不存在,资源装载器把请求的uri当成一个类名,
然后尝试採用类装载器的方式装载类,假设找不到那么就直接返回未找到(404)消息;
假设找到了,而且uri是第一次请求的,资源装载器会返回java源文件,
然后把java源文件交给Douyuserver内置的编译器处理,编译器的处理过程非常复杂,
这里就不深入说明了,总之它会为你动态生成HelloWorld的实例,
然后调用它的index这个缺省的public方法,
之后调用out.println()方法把"Hello World!"发送给浏览器。
——————转载结束———————
在我们这个总喜欢以“不要反复发明轮子”为口头禅的国度里,其实不管是“反复发明的”抑或“自己独创的”现代事物全都屈指可数,出现Douyu这样一个“另类”的项目,不管怎么说都是非常有纪念意义的,起码来讲,它敢于牺牲ide支持,重构部分javac代码,以换取实时编译的举动,偶就肯定做不出来……另外在它是“平台”而非“框架”的问题上,偶坚定的支持原作者,由于标准的javaserver会和它有兼容性问题,它仅仅能自己充当平台…… 就我看来, Douyu要想做大做强,最简单的一条路就是作者自己开公司做应用,以应用推平台。否则,未来其研究意义或者远大于有用意义,毕竟使用Douyu不光是使用一个框架,也意味着放弃一系列Java现有体系,而使用它的一整套“平台“。
Douyu已实现的代码量并不大,加之暂不开源,所以我们无法做太多的评判。 但有些人用Douyu与play!framework对照,就我看来,如今还太早了些。最起码来说, play!framework使用REST隐藏了HTTP,而Douyu现今仅仅是通过HTTP协议明码进行get与post传递,Play支持模板,而 Douyu临时仅仅能用静态的html作为页面,尽管两者都不用 又一次编译就能够部署文件, 但play!framework使用自己定义ClassLoader动态载入class,用Javassist改动字节码,使用自己修正的Compiler方式编译java源代码与模板,而 Douyu主要通过重写javac的java源代码部分实现动态编译,Play加上支持库等体积有40多MB,而 Douyu算上更改的javac部分也只是1MB多个一个jar 。从其实讲,Douyu更像一个play的原型系统,或者说一个未完好的play!framework,假设不照着play!framework的老路走下去,那么Douyu未来会变成什么样子如今还未可知,演化成一个我们无法想象的平台也大有可能。
此项目名为Douyu,就作者自己所言,似乎取自“斗鱼”的意思,而不是有些人所说的“多余”或者我第一印象的“都晕”,只是嘛,斗鱼这个名字事实上并不太好,由于不管日漫的《斗鱼》抑或台剧的《斗鱼》,主角都没能逃脱“骗子,流氓,赌徒”的阴影(尽管人都非常帅)……该项目未来的走势怎样,还是让我们拭目以待吧……