接着上一篇,我们来讲讲 jdk1.6 的新特性。
Web服务元数据
Java 里的Web服务元数据跟微软的方案基本没有语义上的区别,自从JDK5添加了元数据功能(Annotation)之后,SUN几乎重构了整个J2EE体 系, 由于变化很大,干脆将名字也重构为Java EE, Java EE(当前版本为5.0)将元数据纳入很多规范当中,这其中就包括Web Services的相关规范, 加入元数据之后的Web Services服务器端编程模型就跟上面看到的C#片断差不多了, 这显然比以前的JAX-RPC编程模型简单(当然, Axis的编程模型也很简单).这里要谈的Web服务元数据(JSR 181)只是Java Web 服务规范中的一个,它跟Common Annotations, JAXB2, StAX, SAAJ和JAX-WS等共同构成Java EE 5的Web Services技术堆栈.
package WebServices; import java.io.File; import java.io.IOException; import javax.jws.Oneway; import javax.jws.WebMethod; import javax.jws.WebParam; import javax.jws.WebResult; import javax.jws.WebService; import javax.xml.ws.Endpoint; @WebService(targetNamespace="http://www.xttblog.com/taoge",serviceName="HelloService") public class WSProvider { @WebResult(name="Greetings")//自定义该方法返回值在WSDL中相关的描述 @WebMethod public String sayHi(@WebParam(name="MyName") String name){ return "Hi,"+name; //@WebParam是自定义参数name在WSDL中相关的描述 } @Oneway //表明该服务方法是单向的,既没有返回值,也不应该声明检查异常 @WebMethod(action="printSystemTime",operationName="printSystemTime")//自定义该方法在WSDL中相关的描述 public void printTime(){ System.out.println(System.currentTimeMillis()); } public static void main(String[] args) { Thread wsPublisher = new Thread(new WSPublisher()); wsPublisher.start(); } private static class WSPublisher implements Runnable{ public void run() { //发布WSProvider到http://www.xttblog.com/taoge/WSProvider这个地址,之前必须调用wsgen命令 //生成服务类WSProvider的支持类,命令如下: //wsgen -cp . WebServices.WSProvider Endpoint.publish("http://www.xttblog.com/taoge/WSProvider",new WSProvider()); } } }
如果想看到Web Services Engine生成的WSDL文件是否遵守上面的元数据, 我们没有必要将上面的WSProvider部署到支持JSR-181的应用服务器或Servlet形式的Web Services Engine,现在JDK6已经提供了一个很简单的机制可以用来测试和发布Web Services,下面讲讲如何在JDK6环境下发布Web Services和查看生成的WSDL
- 将<JDK_HOME>/bin加入path环境变量
- 在命令行下切换当前目录到WSProvider的class文件所在的目录,运行下面命令
wsgen -cp . WebServices.WSProvider
在这个例子中会生成以下3个类的源代码文件及class文件
SayHi
SayHiResponse
PrintTime
执行如下代码发布WSProvider到http://www.xttblog.com/taoge/WSProvider,在这里可以执行WSProvider类的main方法就可以
Endpoint.publish("http://www.xttblog.com/taoge/WSProvider",new WSProvider());
在浏览器输入http://www.xttblog.com/taoge/WSProvider?wsdl就可以看到生成的WSDL文件,为了节省篇幅,这里就不把生成的WSDL文件贴上了,大家可以自己动手试试.
脚本语言支持
JDK6增加了对脚本语言的支持(JSR 223),原理上是将脚本语言编译成bytecode,这样脚本语言也能享用Java平台的诸多优势,包括可移植性,安全等,另外,由于现在是编译成bytecode后再执行,所以比原来边解释边执行效率要高很多。加入对脚本语言的支持后,对Java语言也提供了以下好处。
- 许多脚本语言都有动态特性,比如,你不需要用一个变量之前先声明它,你可以用一个变量存放完全不同类型的对象,你不需要做强制类型转换,因为转换都是自动的。现在Java语言也可以通过对脚本语言的支持间接获得这种灵活性。
- 可以用脚本语言快速开发产品原型,因为现在可以Edit-Run,而无需Edit-Compile-Run,当然,因为Java有非常好的IDE支持,我 们完全可以在IDE里面编辑源文件,然后点击运行(隐含编译),以此达到快速开发原型的目的,所以这点好处基本上可以忽略。
-
通过引入脚本语言可以轻松实现Java应用程序的扩展和自定义,我们可以把原来分布在在Java应用程序中的配置逻辑,数学表达式和业务规则提取出来,转用JavaScript来处理。
Sun的JDK6实现包含了一个基于Mozilla Rhino的 脚本语言引擎,支持JavaScript,这并不是说明JDK6只支持JavaScript,任何第三方都可以自己实现一个JSR-223兼容的脚本引擎 使得JDK6支持别的脚本语言,比如,你想让JDK6支持Ruby,那你可以自己按照JSR 223的规范实现一个Ruby的脚本引擎类,具体一点,你需要实现javax.script.ScriptEngine(简单起见,可以继承javax.script.AbstractScriptEngine)和javax.script.ScriptEngineFactory两个接口。当然,在你实现自己的脚本语言引擎之前,先到scripting.dev.java.net project 这里看看是不是有人已经帮你做了工作,这样你就可以直接拿来用就行。
Scripting API
Scripting API是用于在Java里面编写脚本语言程序的API, 在Javax.script中可以找到Scripting API,我们就是用这个API来编写JavaScript程序,这个包里面有一个ScriptEngineManager类,它是使用Scripting API的入口,ScriptEngineManager可以通过jar服务发现(service discovery)机制寻找合适的脚本引擎类(ScriptEngine),使用Scripting API的最简单方式只需下面三步
- 创建一个ScriptEngineManager对象
- 通过ScriptEngineManager获得ScriptEngine对象
- 用ScriptEngine的eval方法执行脚本
下面是一个Hello World程序
public class HelloScript { public static void main(String[] args) throws Exception { ScriptEngineManager factory = new ScriptEngineManager();//step 1 ScriptEngine engine = factory.getEngineByName("JavaScript");//Step 2 engine.eval("print('Hello, Scripting')");//Step 3 } }
运行上面程序,控制台会输出Hello, Scripting上面这个简单的Scripting程序演示了如何在Java里面运行脚本语言,除此之外,我们还可以利用Scripting API实现以下功能1、暴露Java对象为脚本语言的全局变量2、在Java中调用脚本语言的方法3、脚本语言可以实现Java的接口4、脚本语言可以像Java一样使用JDK平台下的类下面的类演示了以上4种功能
package Scripting; import java.io.File; import javax.script.Invocable; import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; import javax.script.ScriptException; public class ScriptingAPITester { public static void main(String[] args) throws Exception { ScriptEngineManager manager = new ScriptEngineManager(); ScriptEngine engine = manager.getEngineByName("JavaScript"); testScriptVariables(engine);//演示如何暴露Java对象为脚本语言的全局变量 testInvokeScriptMethod(engine);//演示如何在Java中调用脚本语言的方法 testScriptInterface(engine);//演示脚本语言如何实现Java的接口 testUsingJDKClasses(engine);//演示脚本语言如何使用JDK平台下的类 } public static void testScriptVariables(ScriptEngine engine) throws ScriptException{ File file = new File("test.txt"); engine.put("f", file); engine.eval("println('TotalSpace:'+f.getTotalSpace())"); } public static void testInvokeScriptMethod(ScriptEngine engine) throws Exception{ String script = "function hello(name) { return 'Hello,' + name;}"; engine.eval(script); Invocable inv = (Invocable) engine; String res = (String)inv.invokeFunction("hello", "Scripting" ); System.out.println("res:"+res); } public static void testScriptInterface(ScriptEngine engine) throws ScriptException{ String script = "var obj = new Object(); obj.run = function() { println('run method called'); }"; engine.eval(script); Object obj = engine.get("obj"); Invocable inv = (Invocable) engine; Runnable r = inv.getInterface(obj,Runnable.class); Thread th = new Thread(r); th.start(); } public static void testUsingJDKClasses(ScriptEngine engine) throws Exception{ //Packages是脚本语言里的一个全局变量,专用于访问JDK的package String js = "function doSwing(t){ var f=new Packages.javax.swing.JFrame(t);f.setSize(400,300); f.setVisible(true);}"; engine.eval(js); Invocable inv = (Invocable) engine; inv.invokeFunction("doSwing", "Scripting Swing" ); } }
SUN提供的JDK6中有一个命令行工具??jrunscript,你可以在<JDK6_Home>/bin下面找到这个工具,jrunscript是一个脚本语言的解释程序,它独立于脚本语言,但默认是用JavaScript,我们可以用jrunscript来测试自己写的脚本语言是否正确,下面是一个在命令行运行jrunscript的简单例子
jrunscript
js>println("Hello,JrunScript");
Hello,JrunScript
js>9*8
72.0
js>
JTable的排序和过滤
原来的JTable基本上是只能显示数据,在JDK6新增了对JTable的排序和过滤功能,下面代码演示了这两个功能
public class JTableTester { static String data[][] = { {"China","Beijing","Chinese"}, {"America","Washington","English"}, {"Korea","Seoul","Korean"}, {"Japan","Tokyo","Japanese"}, {"France","Paris","French"}, {"England","London","English"}, {"Germany","Berlin","German"},}; static String titles[] = {"Country","Capital","Language"}; public static void main(String[] args) { DefaultTableModel m = new DefaultTableModel(data,titles); JTable t = new JTable(m); final TableRowSorter sorter = new TableRowSorter(m); t.setRowSorter(sorter); //为JTable设置排序器 JScrollPane sPane = new JScrollPane(); sPane.setViewportView(t); JPanel p = new JPanel(); p.setLayout(new BoxLayout(p,BoxLayout.X_AXIS)); JLabel l = new JLabel("Criteria:"); final JTextField tf = new JTextField(); JButton b = new JButton("Do Filter"); p.add(l); p.add(tf); p.add(b); b.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if(tf.getText().length()==0){ sorter.setRowFilter(null); }else{ sorter.setRowFilter(RowFilter.regexFilter(tf.getText())); //为JTable设置基于正则表达式的过滤条件 } } }); JFrame f = new JFrame("JTable Sorting and Filtering"); f.getContentPane().add(sPane,BorderLayout.CENTER); f.getContentPane().add(p,BorderLayout.SOUTH); f.setSize(400,300); f.setVisible(true); } }
运行上面程序,单击JTable的某一个title,这个title对应的列就会按照升序/降序重新排列;在下面的Criteria文本框中输入"ese",点击"Do Filter"按钮,JTable将只显示带有"ese"字符串的行,也就是China和Japan两行,如果文本框里面什么都没有,点击"Do Filter"按钮,这时JTable会显示所有的行。
更简单,更强大的JAX-WS
JAX-WS2.0的来历
JAX-WS(JSR-224) 是Java Architecture for XML Web Services的缩写,简单说就是一种用Java和XML开发Web Services应用程序的框架, 目前版本是2.0, 它是JAX-RPC 1.1的后续版本, J2EE 1.4带的就是JAX-RPC1.1, 而Java EE 5里面包括了JAX-WS 2.0,但为了向后兼容,仍然支持JAX-RPC. 现在,SUN又把JAX-WS直接放到了Java SE 6里面,由于JAX-WS会用到Common Annotation(JSR 250),Java Web Services Metadata(JSR 181), JAXB2(JSR 222), StAX(JSR 173), 所以SUN也必须把后几个原属于Java EE范畴的Components下放到Java SE, 现在我们可以清楚地理解了为什么Sun要把这些看似跟Java SE没有关系的Components放进来,终极目的就是要在Java SE里面支持Web Services.
JAX-WS2.0的架构
JAX-WS不是一个孤立的框架,它依赖于众多其他的规范,本质上它由以下几部分组成
- 用来开发Web Services的Java API
- 用来处理Marshal/Unmarshal的XML Binding机制,JAX-WS2.0用JAXB2来处理Java Object与XML之间的映射,Marshalling就是把Java Object映射到XML,Unmarshalling则是把XML映射到Java Object.之所以要做Java Object与XML的映射,是因为最终作为方法参数和返回值的Java Object要通过网络传输协议(一般是SOAP)传送,这就要求必须对Java Object做类似序列化和反序列化的工作,在SOAP中就是要用XML来表示Java object的内部状态
- 众多元数据(Annotations)会被JAX-WS用来描述Web Services的相关类,包括Common Annotations, Web Services Metadata, JAXB2的元数据和JAX-WS2.0规范自己的元数据.
- Annotation Processing Tool(APT)是JAX-WS重要的组成部分,由于JAX-WS2.0规范用到很多元数据,所以需要APT来处理众多的Annotations.在<JDK_HOME>/bin下有两个命令wsgen和wsimport,就是用到APT和Compiler API来处理碰到的Annotations,wsgen可以为Web Services Provider产生并编译必要的帮助类和相关支持文件,wsimport以WSDL作为输入为Web Service Consumer产生并编译必要的帮助类和相关支持文件.
- JAX-WS还包括JAX-WS Runtime与应用服务器和工具之间的契约关系
JAX-WS2.0的编程模型
现在用JAX-WS2.0来编写Web Services非常简单,不像JAX-RPC,JAX-WS可以把任意POJO暴露为Web Services,服务类不需要实现接口,服务方法也没有必要抛出RMI异常.下面介绍在JDK6环境下用JAX-WS2.0开发和测试Web Services的
步骤:
编写服务类,并用Web Services Metadata(JSR-181)标注这个服务类,我用我的另一篇BlogJDK6的新特性之十:Web服务元数据中的WSProvider类作为服务类的例子,在此我重复贴一下WSProvider类的源代码:
@WebService(targetNamespace="http://localhost:8080/taoge",serviceName="HelloService") public class WSProvider { @WebResult(name="Greetings")//自定义该方法返回值在WSDL中相关的描述 @WebMethod public String sayHi(@WebParam(name="MyName") String name){ return "Hi,"+name; //@WebParam是自定义参数name在WSDL中相关的描述 } @Oneway //表明该服务方法是单向的,既没有返回值,也不应该声明检查异常 @WebMethod(action="printSystemTime",operationName="printSystemTime")//自定义该方法在WSDL中相关的描述 public void printTime(){ System.out.println(System.currentTimeMillis()); } public static void main(String[] args) { Thread wsPublisher = new Thread(new WSPublisher()); wsPublisher.start(); } private static class WSPublisher implements Runnable{ public void run() { //发布WSProvider到http://www.xttblog.com/taoge/WSProvider这个地址,之前必须调用wsgen命令 //生成服务类WSProvider的支持类,命令如下: //wsgen -cp . WebServices.WSProvider Endpoint.publish("http://www.xttblog.com/taoge/WSProvider",new WSProvider()); } } }
用wsgen生成上面服务类的必要的帮助类,然后调用用EndPoint类的静态方法publish发布服务类(步骤请参考我的另一篇Blog JDK6的新特性之十:Web服务元数据),我在这里是将服务类发布到
http://www.xttblog.com/taoge/WSProvider
用wsimport为服务消费者(也就是服务的客户端)生成必要的帮助类,命令如下:
wsimport http://www.xttblog.com/taoge/WSProvider?wsdl
这会在<当前目录>/net/csdn/blog/chinajash下生成客户端的帮助类,在这个例子中会生成7个类
HelloService.class
ObjectFactory.class
package-info.class
PrintSystemTime.class
SayHi.class
SayHiResponse.class
WSProvider.class
在客户端用下面代码即可调用步骤1定义的Web Service
HelloService hs = new HelloService(); WSProvider ws = hs.getWSProviderPort(); System.out.println(ws.sayHi("taoge")); ws.printSystemTime();
调用上述代码后客户端控制台输出
hi,taoge
服务端控制台输出服务器当前系统时间
轻量级Http Server
JDK6提供了一个简单的Http Server API,据此我们可以构建自己的嵌入式Http Server,它支持Http和Https协议,提供了HTTP1.1的部分实现,没有被实现的那部分可以通过扩展已有的Http Server API来实现,程序员必须自己实现HttpHandler接口,HttpServer会调用HttpHandler实现类的回调方法来处理客户端请求,在这里,我们把一个Http请求和它的响应称为一个交换,包装成HttpExchange类,HttpServer负责将HttpExchange传给HttpHandler实现类的回调方法.下面代码演示了怎样创建自己的Http Server
public class HTTPServerAPITester { public static void main(String[] args) { try { HttpServer hs = HttpServer.create(new InetSocketAddress(8888),0);//设置HttpServer的端口为8888 hs.createContext("/taoge", new MyHandler());//用MyHandler类内处理到/chinajash的请求 hs.setExecutor(null); hs.start(); } catch (IOException e) { e.printStackTrace(); } } } class MyHandler implements HttpHandler { public void handle(HttpExchange t) throws IOException { InputStream is = t.getRequestBody(); String response = "<h3>Happy New Year 2007!--taoge</h3>"; t.sendResponseHeaders(200, response.length()); OutputStream os = t.getResponseBody(); os.write(response.getBytes()); os.close(); } }
运行程序后,在浏览器内输入http://localhost:8888/xx,浏览器输出
嵌入式数据库 Derby
Derby是IBM送给开源社区的又一个礼物,是一个pure java的数据库,现在已经被列入到java1.6中。
不知道对于大数据量的性能如何,但传说中启动derby只会给JVM添加2M的内存,对那些小数据库应用,比如像用access那种应该是挺有诱惑力的。
另外,麻雀虽小,五脏俱全,功能要比access多得多咯,包括事务处理,并发,触发器都有,管理又简单,因此自己用来做点工具正好合适。
废话少说,介绍一下我折腾了半天的经验吧。
我的Derby配置过程:
- 下载db-derby-10.1.3.1-bin.tar.gz,derby_core_plugin_10.1.3.zip和derby_ui_plugin_1.1.0.zip,把两个插件安装到eclipse上
- 打开ecllipse,新建一个project
- 右键这个project,选择Apache Derby,再选择add apache derby native,发现只是给我的project添加了几个derby的jar,还不是在我看着顺眼的lib目录里,索性干掉,换上db-derby- 10.1.3.1-bin.tar.gz解压出来以后lib目录下的jar文件,在Build Path里设置一下;
- 右键Project,在apache derby里选择start apache derby network server,控制台可以看到derby启动后打出的“服务器准备在端口 1527 上接受连接。”
- 右键Project,在apache derby里选择ij(Interactive SQL),启动SQL控制台;
- 输入connect jdbc:derby:testdb;create=true; 注意要有单引号,可以在工程跟目录下创建testdb数据库,可以看到一个新建的目录testdb,那里的文件就是数据库咯;
-
用标准的SQL语句来建一个数据库试试:
create table test (a varchar(4) not null, b char(2) primary key);
居然可以用,太神奇了,呵呵 - 再插入一条语句试试呢,insert into test(a,b) values(a,11);,嗯,不错,可以用select 查出来的哦。
- 再插一下:insert into test(a,b) values(a,11);,哦哦,报错了,“错误 23505:语句异常终止,因为它导致“TEST”上所定义的“SQL060710092132480”标识的唯一或主键约束或唯一索引中出现重复键值。” 呵呵。
- 好了,现在可以像你控制的其他数据库一样来控制Derby了。
如果上述方法不行,或者你习惯了在eclipse之外使用和管理数据库,那么可以很方便的把Derby“装”在系统里。下面我说一下步骤:
-
把db-derby-10.1.3.1-bin.tar.gz解压到c:/derby,使lib和framework两个目录在c:/derby下边即可
-
设置环境变量
-
设置一个c:/derby/framework/embeded/bin或c:/derby/framework/NetworkServe/bin到Path中,这样我们就可以直接执行上边介绍的connect这样的命令而不用每次钻到那个目录下去执行了
设置c:/derby/lib/derby.jar;c:/derby/lib/derbytoos.jar到CLASSPATH中,以便让这些java编成的命令能够正确执行; -
打开cmd
-
敲入startNetworkServer,可以看到像在eclisp中提示的那样启动了server
-
再打开一个cmd,敲入sysinfo,可以看到derby的环境信息了,注意在java user dir这一项,也许是java用户目录上和上边看到的会有所不同哦,这样在connect jdbc:derby:testdb;create=true;的建的数据库目录就不一样咯。
-
敲入ij,好了,进入到上边的交互界面,可以建一个数据库看看了。
-
最后在另外一个cmd中敲入stopNetworkServer就可以关闭数据库了。
如果你两种方法都试过了,那么需要注意的,还是上边步骤5的问题,这个问题是你可能随时会启动一个数据库或新建一个数据库,但如果你刚刚使用derby,你可能还没有察觉。
derby实际上有两种启动方式,一种是嵌入式的,一种是网络服务器的启动。
我们在eclipse中右键start apache derby network server那个,就是网络服务器的启动方式,在这种方式下可以用另外一台计算机在ij中以:
connect jdbc:derby://192.168.0.28:1527/testdb
的方式进行链接。
第二种启动方式是在ij里边就直接
connect jdbc:derby:testdb
这实际是在连当前配置环境下java user dir下那个目录的数据库。
看到这里可能有点糊涂了,这么就会出问题了那?
实际上derby的访问更像是一种使用derby driver对本地文件系统的访问,不管启动不启动网络服务器,都可以用driver访问本地的数据库。这样,在ij里边像第二种方式那样建立连接是完全可以的。启动了网络服务器,只不过是能够让其他主机访问罢了。
另外一个问题是,在eclipse中和在系统中连接服务器,在connect的时候这个当前配置环境是不一样的,eclipse默认工程所在路径是数据库的所在路径,而在系统中“装”derby则会认为 c:/document and settings下边那个用户目录是数据库的所在路径。
: » Java jdk1.6 新特性(jdk1.5到jdk1.8的新特性系列)
原创文章,作者:dweifng,如若转载,请注明出处:https://blog.ytso.com/251493.html