什么是Jetty? <转>
作者: 来源:www.matrix.org.cn 发布时间:2006-08-14 01:48:12.437 |
Jetty 是一个开源的servlet容器,它为基于Java的web内容,例如JSP和servlet提供运行环境。Jetty是使用Java语言编写的,它的API以一组JAR包的形式发布。开发人员可以将Jetty容器实例化成一个对象,可以迅速为一些独立运行(stand-alone)的Java应用提供网络和web连接。 Server service = new Server() ; 这样一来,Service对象就像一个没有门的宾馆:没有人能够进入并且使用,所以还是没有用的。接下来的一行代码设置容器在localhost,端口7501监听。 service.addListener( "localhost:7501" ) ; 为了在所有的interface上监听,不使用主机名("addListener( ":7501" )")。就像名字暗示的那样,你可以调用addListener()多次来在多个interface上监听。 注意到示例代码中维护了Server对象的一个引用,这是将来要停止容器需要用到的。 将一个web应用映射到Service是很直观的: service.addWebApplication( 这个调用将处理一个web应用中的web.xml部署描述符(descriptor)来映射其中的过滤器servlet和servlet,就像其他容器所做的那样。第一个参数是context path,这个web应用的所有servlet和JSP都会被映射成相对于这个路径的URI。第二个参数是web应用本身。可以是一个打包的WAR文件或者目录格式的web应用。再次调用addWebApplication()可以用来添加其他的web应用。 注意到Jetty并不需要一个完整的符合规范的WAR文件来部署servlet。如果编写了一个搭载于HTTP协议的定制应用程序协议,你可以加载一个单一的servlet并且将其通过网络提供出去。并没有必要使用WAR文件仅仅为了使一个非web应用具有通过HTTP协议访问的功能。 为了映射这种一次性的servlet,通过在Service对象上调用getContext()动态的建立一个context。这个示例代码建立了一个叫做/embed的context。 ServletHttpContext ctx = (ServletHttpContext) 如果context不存在地话,调用getContext()将会创建一个新的context 接下来,调用addServlet()将一个servlet类映射到一个URI ctx.addServlet( 第一个参数是该servlet的一个描述性的名字。第二个参数是要映射的路径,等同于web.xml servlet映射中的<url-pattern>。这个映射路径是相对于context path的,这里是/embed。”/*”表示这个servlet接收/embed/TryThis这样一个URI,同时它也会接收所有以此开头的URI,例如/embed/TryThis/123。在使用一个单一的servlet来作为一个大系统的入口的时候,这种映射方式非常有用。Struts和Axis就是实际应用中使用这样的映射方式的例子。 有时候你可能想让你的context成为root context,或者说“/”,这样更像一个普通的HTTP服务。Jetty通过Service.setRootWebapp()来支持此功能。 唯一的一个参数是一个web应用的路径。 容器在此时还是不活动的。而且它也没有试图去绑定要监听的socket,启动容器需要调用: service.start() ; 这个方法会立即返回,因为Jetty将服务在一个独立的线程中运行。因此,当容器运行的时候,main()可以来做其他任何事情。 其余的代码是使用一组URL来调用这个嵌入式容器。这些调用确保容器已经在运行并且servlet按照期望的方式工作。 关闭容器就像启动它一样直观 service.stop() ; 注意最外层try/catch块中的catch语句。 { 显示的调用System.exit()确保容器在发生异常的时候被关闭。否则,容器会持续运行因此整个应用程序也不会退出。 必须记住Jetty web应用并不限于使用代码来访问。如果我将service.stop()从刚才的代码中去掉,那么容器将一直运行并且我可以在浏览器中调用servlet,例如 http://localhost:7501/embed/TryThis/SomeExtraInfo 你并不一定要完全按照我说的去做。这个示例代码可以作为一个Eclipse项目运行。而且你也可以写一段shell脚本使其运行在Unix/Linux命令行中。在上面两种情况下,确信Jetty在你的classpath中。 将配置从代码中独立出来: XML驱动的配置文件 尽管Jetty的API非常直观简练,但是直接的调用Jetty API会将大量的配置信息——端口号,context path,servlet类名——埋藏在代码之中。Jetty提供了一种基于XML的配置方式来替代直接调用API,这样你就可以将这些配置信息都放在代码外面而使你的代码保持清洁。 Jetty的XML配置文件是基于Java反射的。java.lang.reflect中的类代表了Java中的方法和类,这样你可以实例化一个对象并且使用方法的名字和参数类型来调用它的方法。这种情况下,Jetty的XML配置文件解析器会将XML的element和属性翻译成反射方法调用。 这段节选自Step2Driver示例类中的代码是Step1Driver的一个改良版本。要是使用到了配置文件,就必须有一定的Jetty代码来加载它。 URL serviceConfig = /* load XML file */ ; 不可否认,这不比Step1Driver示例节省多少代码,但是,即使你要添加新的servlet或者web应用,Step2Driver的代码不会因此而增加。而直接调用Service和context对象的方法在配置逐渐增加的情况下会越来越差。 列表1是Step2Driver加载的XML文件。顶层的<Configure> element 的属性指明了要实例化那个类。这里是Jetty Server对象。 <!-- 1 --> <Call> element代表要在Server对象上调用的方法。这里要调用addListener(),如标记(2)处,它自己又有一个子element叫做<Arg>,这指明了方法的参数。这里我只能传递一个字符串值作为监听的地址,而addListener()却需要接受一个SocketListener对象作为参数。因此,我要使用<New>在标记(3)处实例化一个新的SocketListener对象。标记2和3处的代码等同于以下代码: server.addListener( 为了配置SocketListener自己,必须使用一个<Call>来调用它的setHost()方法,既然这个方法遵循了JavaBean的命名规则,示例代码因此使用了<Set> element(4)作为一种快捷方式。在后台,Jetty给set中name属性所指定的属性赋值,并且决定调用什么方法,这里是setHost() setHost()的参数这里没有显示给出,而是使用了<SystemProperty>来从系统属性中来获取参数的值,这里从系统参数service.listen.host 和 service.listen.port。如果系统属性没有定义,你可以使用default来指定一个默认值。这里,4和5等同于以下调用: socketListener.setHost( 最后注意标记6处的<Call> element位于调用getContext方法的<Call>中。内部的<Call>是作用在外部的<Call>的返回的对象上的,这里,调用的是getServlet()返回的context上的addServlet()方法: server.getContext().addServlet( ... ) ; Jetty 小组的英明在于这个XML配置文件的进一步深入处理:我们可以注意到列表1中所有的Jetty特定的调用都是element和属性的值,而不是名字,这就意味着XML配置文件可以被用在任何类上,而不仅仅是Jetty的类中。根据你的应用程序的编写方式,你可以全部使用Jetty的XML配置文件来配置。 可执行JAR包 如果你使用Jetty的XML来配置你的应用,你需要使用大量的重复的代码来加载你的config文件并且运行你的应用。不过你可以使用Jetty的可执行的start.jar来为你加载文件,这会让你节省更多的代码。 例如,你可以使用以下的命令行来加载Step2Driver中的Jetty服务。 CLASSPATH= ...various Jetty JARs... 注意到这个命令仅仅加载xml文件来建立容器和监听器,因此,它并不会调用示例代码中用来测试URL的代码。 结论 一个嵌入式的Jetty servlet容器可以让你的web使用Java应用而不用打包成正式的web应用的形式。这提供了多种可能性,让Jetty成为你的工具箱中的一个多才多艺的帮手。 当然,我这里所写的东西并不能包含Jetty的所有内容。我建议你去访问Jetty的网站来获取更多的文档和示例代码。 |