Java jdk1.6 新特性(jdk1.5到jdk1.8的新特性系列)

接着上一篇,我们来讲讲 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语言也提供了以下好处。

  1. 许多脚本语言都有动态特性,比如,你不需要用一个变量之前先声明它,你可以用一个变量存放完全不同类型的对象,你不需要做强制类型转换,因为转换都是自动的。现在Java语言也可以通过对脚本语言的支持间接获得这种灵活性。
  2. 可以用脚本语言快速开发产品原型,因为现在可以Edit-Run,而无需Edit-Compile-Run,当然,因为Java有非常好的IDE支持,我 们完全可以在IDE里面编辑源文件,然后点击运行(隐含编译),以此达到快速开发原型的目的,所以这点好处基本上可以忽略。
  3. 通过引入脚本语言可以轻松实现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的最简单方式只需下面三步

  1. 创建一个ScriptEngineManager对象
  2. 通过ScriptEngineManager获得ScriptEngine对象
  3. 用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不是一个孤立的框架,它依赖于众多其他的规范,本质上它由以下几部分组成

  1. 用来开发Web Services的Java API
  2. 用来处理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的内部状态
  3. 众多元数据(Annotations)会被JAX-WS用来描述Web Services的相关类,包括Common Annotations, Web Services Metadata, JAXB2的元数据和JAX-WS2.0规范自己的元数据.
  4. 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产生并编译必要的帮助类和相关支持文件.
  5. 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配置过程:

  1. 下载db-derby-10.1.3.1-bin.tar.gz,derby_core_plugin_10.1.3.zip和derby_ui_plugin_1.1.0.zip,把两个插件安装到eclipse上
  2. 打开ecllipse,新建一个project
  3. 右键这个project,选择Apache Derby,再选择add apache derby native,发现只是给我的project添加了几个derby的jar,还不是在我看着顺眼的lib目录里,索性干掉,换上db-derby- 10.1.3.1-bin.tar.gz解压出来以后lib目录下的jar文件,在Build Path里设置一下;
  4. 右键Project,在apache derby里选择start apache derby network server,控制台可以看到derby启动后打出的“服务器准备在端口 1527 上接受连接。”
  5. 右键Project,在apache derby里选择ij(Interactive SQL),启动SQL控制台;
  6. 输入connect jdbc:derby:testdb;create=true; 注意要有单引号,可以在工程跟目录下创建testdb数据库,可以看到一个新建的目录testdb,那里的文件就是数据库咯;
  7. 用标准的SQL语句来建一个数据库试试:
    create table test (a varchar(4) not null, b char(2) primary key);
    居然可以用,太神奇了,呵呵
  8. 再插入一条语句试试呢,insert into test(a,b) values(a,11);,嗯,不错,可以用select 查出来的哦。
  9. 再插一下:insert into test(a,b) values(a,11);,哦哦,报错了,“错误 23505:语句异常终止,因为它导致“TEST”上所定义的“SQL060710092132480”标识的唯一或主键约束或唯一索引中出现重复键值。” 呵呵。
  10. 好了,现在可以像你控制的其他数据库一样来控制Derby了。

如果上述方法不行,或者你习惯了在eclipse之外使用和管理数据库,那么可以很方便的把Derby“装”在系统里。下面我说一下步骤:

  1. 把db-derby-10.1.3.1-bin.tar.gz解压到c:/derby,使lib和framework两个目录在c:/derby下边即可

  2. 设置环境变量

  3. 设置一个c:/derby/framework/embeded/bin或c:/derby/framework/NetworkServe/bin到Path中,这样我们就可以直接执行上边介绍的connect这样的命令而不用每次钻到那个目录下去执行了 
    设置c:/derby/lib/derby.jar;c:/derby/lib/derbytoos.jar到CLASSPATH中,以便让这些java编成的命令能够正确执行;

  4. 打开cmd

  5. 敲入startNetworkServer,可以看到像在eclisp中提示的那样启动了server

  6. 再打开一个cmd,敲入sysinfo,可以看到derby的环境信息了,注意在java user dir这一项,也许是java用户目录上和上边看到的会有所不同哦,这样在connect jdbc:derby:testdb;create=true;的建的数据库目录就不一样咯。

  7. 敲入ij,好了,进入到上边的交互界面,可以建一个数据库看看了。

  8. 最后在另外一个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的新特性系列)

: » Java jdk1.6 新特性(jdk1.5到jdk1.8的新特性系列)

原创文章,作者:dweifng,如若转载,请注明出处:https://blog.ytso.com/251493.html

(0)
上一篇 2022年5月3日
下一篇 2022年5月3日

相关推荐

发表回复

登录后才能评论