JAVA EE 7 SDK Tutorial分析

隨着Java平臺企業版(Java EE),Java企業應用程序的開發從未如此簡單或更快。在Java EE 7平臺的目的是向開發人員提供了一套強大的API,同時縮短開發時間,下降了應用的複雜性,並提升應用程序的性能。javascript

在Java EE 7平臺引入了一個簡化的編程模型。隨着Java EE 7的技術,XML部署描述符如今是可選的。相反,開發人員能夠簡單地輸入信息做爲註釋直接到Java源文件,以及Java EE服務器將配置組件在部署和運行。這些註解一般用於,不然將在一個部署描述符中提供一個節目數據嵌入。使用註釋,規範信息直接把你的代碼下一個程序單元,它的影響。html

本文分析了Servlet 3.一、JAX-RS 2.0、JSON Processing 1.0、WebSocket 1.0相關技術的幾個示例,以及相關技術下的應用。
java

原理

新特性

開發人員如今愈來愈多地認識到須要分佈式事務,並利用速度,安全性和服務器端技術的可靠性便攜式應用。在信息技術的世界中,企業應用程序必須設計,建設和生產用更少的錢,以更快的速度,並以更少的資源。
Java EE平臺是經過Java進程(JCP),開發了負責全部的Java技術。有關方面組成的專家小組已經建立Java規範請求(JSR)來定義各類Java EE技術。 Java社區在JCP程序的工做有助於確保Java技術的標準,穩定性和跨平臺的兼容性。web

Java EE平臺使用簡化的編程模型。 XML部署描述符是可選的。相反,開發人員能夠簡單地輸入信息做爲註釋直接插入Java源文件,以及Java EE服務器將配置組件在部署和運行。這些註解一般用於嵌入,不然將在部署來佈置一個節目數據描述符。使用註釋,你把規範的信息在你的代碼下一個程序單元的影響。數據庫

在Java EE平臺,依賴注入能夠應用於全部資源組件的需求,從而有效地隱藏資源的建立和查詢應用程序代碼。依賴注入能夠在企業JavaBeans(EJB)中使用容器,Web容器和應用程序客戶端。依賴注入容許Java EE容器自動插入引用其餘所需的組件或
資源,使用註釋。apache

Java持久性API提供了在企業Bean,Web組件和應用程序的客戶管理關係數據的對象/關係映射。它也能夠在Java SE應用程序所使用的,Java EE的環境以外。編程


JAVA EE 7新特性

主要包括增強對 HTML5 動態可伸縮應用程序的支持、提升開發人員的生產力和知足苛刻的企業需求。json

(1)提升開發人員的生產力
經過一個緊密集成的平臺簡化了應用架構,減小樣板代碼和增強對註釋的使用來提升效率,另外借助標準 RESTful Web 服務對客戶端的支持提升了應用程序的可移植性。後端

(2)增強對 HTML 5 動態可伸縮應用程序的支持
基 於其可擴展的基礎架構,Java EE 7 推進了對 HTML 5 應用的構建和支持。在新的平臺中,藉助具備行業標準的 JSON 簡化了數據分析和交換,並經過低延遲和雙向通訊的 WebSockets 減小了響應時間。以及利用改進的 JAX-RS 2.0 更好地支持異步的、可擴展的、高性能的 RESTful 服務,從而更好地支持多用戶的併發操做。api

(3)知足苛刻的企業需求
爲更好地知足企業的需求,Java EE 7 提供了許多新功能:

  • 細化批處理做業,造成可管理的區塊,以實現不間斷的 OLTP 性能;
  • 簡化多線程併發任務的定義,以提升可擴展性;
  • 以及提供具備選擇性和靈活性的事務應用程序等。

應用模型

在Java EE應用程序模型從Java編程語言和Java虛擬機。事實證實,便攜性,安全性和開發人員的生產力提供造成所述應用模型的基礎。

Java EE的旨在支持實現客戶,員工,供應商的企業服務的應用程序,合做夥伴和其餘人誰做出要求或貢獻的企業。這樣應用本質上是複雜,從各類可能訪問數據源和應用程序分發到各類客戶端。爲了更好地控制和管理這些應用中,業務功能,以支持這些不一樣的用戶在中間層進行。中間層表明這是密切企業的信息化控制的環境部門。中間層一般運行在專門的服務器硬件,並具備訪問企業的全程服務。

在Java EE應用程序模型定義的架構實施服務可以提供可擴展性,可訪問性和可管理性多層應用程序及分佈式多層應用程序須要企業級的應用。這種模式劃分所需的工做
實現的多層服務分爲如下幾個部分:

(1)業務和表示邏輯由開發商實施

(2)由Java EE平臺提供的標準的系統服務,能夠依靠在平臺上,以提供所述硬系統級解決方案開發多層服務的問題。

分佈式多層應用程序

Java EE平臺使用,爲企業的分佈式多層應用模型和應用程序。應用邏輯被分紅部件根據功能,和該應用程序組件,使Java EE應用程序被安裝在根據層的多層Java EE的環境中各類機械該應用程序組件所屬。


兩個多層的Java EE應用程序分爲描述的層次

在下面的列表中。在圖1-1所示的Java EE應用程序部分呈現的Java EE組件。

(1)客戶端層組件的客戶端機器上運行。

(2)在Java EE服務器上運行的Web層組件。

(3)業務層組件的Java EE服務器上運行。

(4)企業信息系統(EIS)層軟件的EIS服務器上運行。

儘管Java EE應用程序能夠由圖1-1中,Java EE的顯示全部層多層應用程序一般被認爲是三層應用由於它們分佈在三個地方:客戶端機器上,Java EE服務器機,數據庫或傳統的機器在後端。三層以這種方式運行的應用程序擴展標準雙層客戶機和服務器
模型經過將多線程應用程序服務器的客戶端應用程序之間和後端存儲。

Web層

Java EE Web組件使用JavaServer建立或者servlet或網頁Faces技術和/或JSP技術(JSP頁面)。

Servlet是Java編程語言類動態處理請求並構建響應。 JSP頁面是基於文本的文檔爲servlet執行,但容許更天然的方法來建立靜態內容。

JavaServer Faces技術創建在servlet和JSP技術,並提供了用於網絡應用程序用戶界面組件的框架。

靜態的HTML頁面和小應用程序捆綁在一塊兒的應用程序中的Web組件裝配,但不被視爲Web組件由Java EE規範。

服務器端實用工具類也能夠綁定Web組件,像HTML頁面,不被視爲Web組件。

Web層,就像客戶層,可能包括一個JavaBeans組件來管理用戶輸入和發送輸入到企業Bean運行在業務層進行處理。


Web層和Java EE應用程序

源碼解析

Servlet 3.1

Annotations Servlet

Servlet3.1規範大量使用註解來聲明Servlet中,過濾器,監聽器和安全性。配置文件web.xml中如今是可選的。
源碼以下:

package sample;

import java.io.IOException;
import java.io.*;
import javax.servlet.*;
import javax.servlet.annotation.*;
import javax.servlet.http.*;


@WebServlet(name="testServlet", urlPatterns={"/hello"},
        initParams={ @WebInitParam(name="simpleParam", value="paramValue") } )

public class TestServlet extends HttpServlet {

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        PrintWriter out = response.getWriter();
        String simpleParam = getServletConfig().getInitParameter("simpleParam");
            out.println("Hello World "+simpleParam);
                out.close();
    }
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
         doGet(request,response);
    }
}

在上面的代碼,咱們已經註冊了的TestServlet下的URL模式「/你好」(注意複數形式能夠有一個以上的)。此外,咱們已經設置了一個名爲「simpleParam」的初始參數。不須要web.xml中運行這個servlet。

你能夠聲明以及器過濾器使用註釋,想在這個exampleYou能夠聲明以及過濾器在這個例子中使用註釋,代碼以下:

import java.io.*;
import javax.servlet.*;
import javax.servlet.annotation.*;

@WebFilter(urlPatterns={"/*"},
        initParams={ @WebInitParam(name="simpleParam", value="paramValue") })
        public class TestFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
        StringWriter sw = new StringWriter();
        PrintWriter writer = new PrintWriter(sw);

        writer.println("===============");
        writer.println("Filter intercepted!");
        writer.println("===============");

        // Log the resulting string
        writer.flush();
        filterConfig.getServletContext().
        log(sw.getBuffer().toString());

        chain.doFilter(request, response);

    }
    private FilterConfig filterConfig = null;
    public void init(FilterConfig filterConfig) 
    throws ServletException {
        this.filterConfig = filterConfig;
    }

    @Override
    public void destroy() {    }
}

上述過濾器將攔截髮到網絡環境中的全部請求。
下邊介紹另外一個有用的註釋是@WebListener註釋能夠用來標記一個Java類爲WebListener,代碼以下:

package sample;
import javax.servlet.*;
@javax.servlet.annotation.WebListener
public class SessionListener implements ServletContextListener {
    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        System.out.println("Context destroyed!");

    }
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        System.out.println("Context created!");
    }
}

Absolute Ordering of Web Fragments

爲了更好地進行適配,減小配置,在Servlet的3.1h中引入了web-fragment的概念。一個web-fragment是可指定的,幷包括在一個庫或框架jar文件中的web.xml的一部分或所有。若是有不少個web-fragment jars時,那麼人們可能會喜歡指定處理Web-fragment.xml之和註釋的順序。這個很重要。例如,過濾器能夠爲在web.xml中指定的順序被執行,相似於監聽。在Servlet3.1中,引入了web.xml 中的的標籤和web-fragment.xml中的標籤。

Web Fragments的順序被指定在如下優先級:

(1)在web.xml中若是存在

(2)若是存在於每個web-fragment.xml

(3)其餘未指定

在web.xml的 中提供了一種方法,以指定加載web的fragment.xml之和web fragments的註釋處理的順序。代碼以下:

<web-app>
        ...
        <absolute-ordering>
            <name>A</name>
             <others/>
            <name>B</name>
        <absolute-ordering>
</web-app>

另外,在上述例子中,web fragment A 將被第一個處理,web fragment B 被最後處理。名稱A和B在web-fragment.xml之中的元素指定的(見下面的例子)。

排序是在web-fragment.xml中被指定的。若是在web.xml中沒有,會查找web-fragment.xml中的。
僅僅在web-fragment.xml存在一個的jar包,代碼以下

<web-fragment>
            <name>A</name>
            ...
            <ordering>
                <before>
                    <others/>
                </before>
            </ordering>
</web-fragment>

在這種狀況下,web-fragment A將首先被處理。
下面是在web-fragment.xml存在兩個的示例,代碼以下:
web-fragment A

<web-fragment>
            <name>A</name>
            ...
            <ordering>
                <before>
                    <others/>
                </before>
            </ordering>
</web-fragment>

web-fragment B

<web-fragment>
            <name>B</name>
            ...
            <ordering>
                <before>
                    <others/>
                </before>
            </ordering>
</web-fragment>

這時web-fragment A和web-fragment B會首先被處理。在這種狀況下,人們只能保證web-fragment A和web-fragment B在其餘web-fragment以前處理。可是A和B的順序並不肯定,在這種狀況下這是隨機的。
有兩個包含 的jars 存在於web-fragment.xml之中,以下

web-fragment A

<web-fragment>
            <name>A</name>
            ...
            <ordering>
                <before>
                    <others/>
                </before>
            </ordering>
</web-fragment>

web-fragment B

<web-fragment>
            <name>B</name>
            ...
            <ordering>
                <after>
                    <name>A</name>
                </after>
                <before>
                    <others/>
                </before>
            </ordering>
</web-fragment>

在這種狀況下,A將首先被處理,其次是B,而後其餘web fragments。若是想有一個肯定的順序,那麼建議使用在web.xml中的absolute-ordering。
如何存放web fragments?若是一個框架被打包爲一個jar文件,並在部署描述符的形式的元數據信息,那麼Web fragments須要被放置在jar文件的META-INF/文件夾。

另外一方面,若是一個框架,優先使用web fragment.xml這種方法,並且它加強了Web應用程序的web.xml,該框架必須在Web應用程序中被放置在的WEB-INF/ lib目錄中。

File Upload

File Upload示例應用程序由一個單一的servlet和HTML表單。這使得上載文件到servlet。這個例子包括兩個字段,文件和目標很是簡單的HTML表單。輸入類型,文件,使得用戶可以瀏覽本地文件系統,選擇該文件。

當選擇了文件時,它被髮送給服務器做爲POST請求的一部分。在這一過程當中,下面有兩個強制性限制應用於具備輸入類型的文件的形式。

(1)該enctype屬性必須設置爲multipart / form-數據的值。

(2)它的方法必須是POST。

當以這種方式指定的形式時,整個請求被髮送到服務器編碼形式。而後servlet使用它本身的方式來處理,以處理該請求傳入的文件數據,並提取從流的一個文件。目的地是路徑某個位置的文件會被保存在電腦上。

在按下上傳按鈕
表格的下方張貼數據到servlet,它保存在文件中指定的目的地。
index.html中的HTML格式以下:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
  <title>File Upload</title>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>

<body>
  <form method="post" action="upload" enctype="multipart/form-data">
    File: <input type="file" name="file" id="file" /><br />
    Destination: <input type="text" value="/tmp" name="destination" /><br />
    <input type="submit" value="Upload" name="upload" id="upload" />
  </form>
</body>
</html>

當客戶端須要發送數據到服務器做爲POST請求方法用於的要求,上傳文件或提交填妥的表格時等部分。相反,GET請求方法發出URL和headers 僅給服務器,而POST請求還包括消息正文。這容許隨機長度的數據鍵入要發送到服務器。

在POST請求中的報頭字段一般指示消息正文的互聯網媒體類型。當提交表單時,瀏覽器流的內容,聯合各個部分,與每一個部分表明一個形式下一個字段。部分被命名爲輸入元素後,相互之間用命名邊界字符串分隔。
從文件上傳表單提交的數據看,選擇sample.txt的做爲將要上傳到tmp目錄上的本地文件,代碼以下:

POST /fileupload/upload HTTP/1.1
Host: localhost:8080
Content-Type: multipart/form-data;
boundary=---------------------------263081694432439
Content-Length: 441
-----------------------------263081694432439
Content-Disposition: form-data; name="file"; filename="sample.txt"
Content-Type: text/plain
Data from sample file
-----------------------------263081694432439
Content-Disposition: form-data; name="destination"
/tmp
-----------------------------263081694432439
Content-Disposition: form-data; name="upload"
Upload
-----------------------------263081694432439--

該servlet FileUploadServlet.java開頭以下:

@WebServlet(name = "FileUploadServlet", urlPatterns = {"/upload"})
@MultipartConfig
public class FileUploadServlet extends HttpServlet {
    private final static Logger LOGGER =
        Logger.getLogger(FileUploadServlet.class.getCanonicalName());
  • @WebServlet標註使用URL模式屬性來定義的servlet映射。

  • @MultipartConfig註釋指示該servlet的指望請求被使用的multipart / form-data的MIME類型進行。

  • processRequest方法從請求檢索目的地和文件的一部分,而後調用 getFileName方法來檢索從文件部分的文件名。該方法而後建立一個- FileOutputStream並將該文件複製到指定的目的地。該方法捕獲的錯誤處理部和處理一些最多見的緣由,一個文件就不會被發現,其中的processRequest和getFileName方法是這樣的:

protected void processRequest(HttpServletRequest request,
                              HttpServletResponse response)
throws ServletException, IOException
{
    response.setContentType("text/html;charset=UTF-8");
    // Create path components to save the file
    final String path = request.getParameter("destination");
    final Part filePart = request.getPart("file");
    final String fileName = getFileName(filePart);
    OutputStream out = null;
    InputStream filecontent = null;
    final PrintWriter writer = response.getWriter();
    try
    {
        out = new FileOutputStream(new File(path + File.separator
             + fileName));
        filecontent = filePart.getInputStream();
        int read = 0;
        final byte[] bytes = new byte[1024];
        while ((read = filecontent.read(bytes)) != -1)
        {
            out.write(bytes, 0, read);
        }
        writer.println("New file " + fileName + " created at " + path);
        LOGGER.log(Level.INFO, "File{0}being uploaded to {1}",
                   new Object[] {fileName, path});
    }
    catch (FileNotFoundException fne)
    {
        writer.println("You either did not specify a file to upload or are "
                       + "trying to upload a file to a protected or nonexistent "
                       + "location.");
        writer.println("<br/> ERROR: " + fne.getMessage());
        LOGGER.log(Level.SEVERE, "Problems during file upload. Error: {0}",
                   new Object[] {fne.getMessage()});
    }
    finally
    {
        if (out != null)
        {
            out.close();
        }
        if (filecontent != null)
        {
            filecontent.close();
        }
        if (writer != null)
        {
            writer.close();
        }
    }
}
private String getFileName(final Part part)
{
    final String partHeader = part.getHeader("content-disposition");
    LOGGER.log(Level.INFO, "Part Header = {0}", partHeader);
    for (String content : part.getHeader("content-disposition").split(";"))
    {
        if (content.trim().startsWith("filename"))
        {
            return content.substring(
                       content.indexOf('=') + 1).trim().replace("\"", "");
        }
    }
    return null;

JAX-RS 2.0

Asynchronous Chat JAX-RS

該示例應用用程序有三個部分。

(1)客戶和地址實體類。這些類模型的數據應用和含有JAXB註解。

(2)客戶示例應用程序:CustomerService類。此類包含JAX-RS資源方法
上表示爲XML或JSON數據的客戶實例執行操做使用JAXB。

(3)CustomerBean會話bean充當輔助bean的Web客戶端。CustomerBean使用JAX-RS客戶端API調用的CustomerService的方法。

客戶和地址實體類

地址實體類:

@Entity
@Table(name = "CUSTOMER_ADDRESS")
@XmlRootElement(name = "address")
@XmlAccessorType(XmlAccessType.FIELD)
public class Address {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    @XmlElement(required = true)
    protected int number;
    @XmlElement(required = true)
    protected String street;
    @XmlElement(required = true)
    protected String city;
    @XmlElement(required = true)
    protected String province;
    @XmlElement(required = true)
    protected String zip;
    @XmlElement(required = true)
    protected String country;
    public Address() { }

            public Long getId() {
                return id;
            }

            public void setId(Long id) {
                this.id = id;
            }

            public int getNumber() {
                return number;
            }

            public void setNumber(int number) {
                this.number = number;
            }

            public String getStreet() {
                return street;
            }

            public void setStreet(String street) {
                this.street = street;
            }

            public String getCity() {
                return city;
            }

            public void setCity(String city) {
                this.city = city;
            }

            public String getProvince() {
                return province;
            }

            public void setProvince(String province) {
                this.province = province;
            }

            public String getZip() {
                return zip;
            }

            public void setZip(String zip) {
                this.zip = zip;
            }

            public String getCountry() {
                return country;
            }

            public void setCountry(String country) {
                this.country = country;
            }

}
  • @XmlRootElement(name = 「address」)標註這個類映射到地址XML元素。

  • @XmlAccessorType(XmlAccessType.FIELD)註解指定這個類的全部字段默認綁定到XML。

  • @XmlElement(required=true)註解指定一個元素必須出如今XML中表示。

客戶實體類:

@Entity
@Table(name = "CUSTOMER_CUSTOMER")
@NamedQuery(
    name = "findAllCustomers",
    query = "SELECT c FROM Customer c " +
            "ORDER BY c.id"
)
@XmlRootElement(name = "customer")
@XmlAccessorType(XmlAccessType.FIELD)
public class Customer {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @XmlAttribute(required = true)
    protected int id;
    @XmlElement(required = true)
    protected String firstname;
    @XmlElement(required = true)
    protected String lastname;
    @XmlElement(required = true)
    @OneToOne
    protected Address address;
    @XmlElement(required = true)
    protected String email;
    @XmlElement (required = true)
    protected String phone;
    public Customer()
    {

    }
    public int getId()
    {
        return id;
    }

    public void setId(int id)
    {
        this.id = id;
    }

    public String getFirstname()
    {
        return firstname;
    }

    public void setFirstname(String firstname)
    {
        this.firstname = firstname;
    }

    public String getLastname()
    {
        return lastname;
    }

    public void setLastname(String lastname)
    {
        this.lastname = lastname;
    }

    public Address getAddress()
    {
        return address;
    }

    public void setAddress(Address address)
    {
        this.address = address;
    }

    public String getEmail()
    {
        return email;
    }

    public void setEmail(String email)
    {
        this.email = email;
    }

    public String getPhone()
    {
        return phone;
    }

    public void setPhone(String phone)
    {
        this.phone = phone;
    }

}

Customer類包含相同的JAXB註解,除了爲@XmlAttribute(required=true)標註,它的屬性映射到表明類的XML元素的屬性。

客戶類包含一個屬性,其類型爲另外一個實體,Address類。這種機制容許你定義在Java代碼中的層次關係無需編寫.xsd文件本身的實體之間。

JAXB生成前兩個類用如下XML模式定義:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<xs:schema version="1.0" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="address" type="address" />
  <xs:element name="customer" type="customer" />
  <xs:complexType name="address">
    <xs:sequence>
      <xs:element name="id" type="xs:long" minOccurs="0" />
      <xs:element name="number" type="xs:int" />
      <xs:element name="street" type="xs:string" />
      <xs:element name="city" type="xs:string" />
      <xs:element name="province" type="xs:string" />
      <xs:element name="zip" type="xs:string" />
      <xs:element name="country" type="xs:string" />
    </xs:sequence>
  </xs:complexType>
  <xs:complexType name="customer">
    <xs:sequence>
      <xs:element name="firstname" type="xs:string" />
      <xs:element name="lastname" type="xs:string" />
      <xs:element ref="address" />
      <xs:element name="email" type="xs:string" />
      <xs:element name="phone" type="xs:string" />
    </xs:sequence>
    <xs:attribute name="id" type="xs:int" use="required" />
  </xs:complexType>
</xs:schema>

CustomerService類

CustomerService類在建立一個客戶類的createCustomer方法資源的基礎上,並返回給客戶類一個新的URI資源,代碼以下

@Stateless
@Path("/Customer")
public class CustomerService {
    public static final Logger logger =
        Logger.getLogger(CustomerService.class.getCanonicalName());
    @PersistenceContext
    private EntityManager em;
    private CriteriaBuilder cb;
    @PostConstruct
    private void init()
    {
        cb = em.getCriteriaBuilder();
    }

    @POST
    @Consumes( {MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
    public Response createCustomer(Customer customer)
    {
        try
        {
            long customerId = persist(customer);
            return Response.created(URI.create("/" + customerId)).build();
        }
        catch (Exception e)
        {
            logger.log(Level.SEVERE,
                       "Error creating customer for customerId {0}. {1}",
                       new Object[] {customer.getId(), e.getMessage()});
            throw new WebApplicationException(e,
                                              Response.Status.INTERNAL_SERVER_ERROR);
        }
    }

    private long persist(Customer customer)
    {
        try
        {
            Address address = customer.getAddress();
            em.persist(address);
            em.persist(customer);
        }
        catch (Exception ex)
        {
            logger.warning("Something went wrong when persisting the customer");
        }
        return customer.getId();
    }

返回到客戶端的響應具備一個新建立的URI資源。返回類型是從與狀態碼的響應的屬性映射的實體主體經過響應的狀態屬性指定。

WebApplicationException客戶示例應用程序的RuntimeException用來包裹適當的HTTP錯誤狀態代碼,例如404,406,415或500。

@Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})和@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})

註釋設置請求和響應媒體類型使用適當的MIME客戶。這些註釋能夠應用於一個資源的方法,資源類,或甚至一個實體提供者。若是不使用這些註釋,JAX-RS容許使用任何媒體類型 (」*/*」)

下面的代碼 fragments顯示了getCustomer的實現和findbyId方法。該getCustomer方法使用@Produces註釋和返回一個客戶對象,它被轉換成XML或JSON表示根據接收由客戶指定的headers。

@GET
@Path("{id}")
@Produces( {MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public Customer getCustomer(@PathParam("id") String customerId)
{
    Customer customer = null;
    try
    {
        customer = findById(customerId);
    }
    catch (Exception ex)
    {
        logger.log(Level.SEVERE,
                   "Error calling findCustomer() for customerId {0}. {1}",
                   new Object[] {customerId, ex.getMessage()});
    }
    return customer;
}

private Customer findById(String customerId)
{
    Customer customer = null;
    try
    {
        customer = em.find(Customer.class, customerId);
        return customer;
    }
    catch (Exception ex)
    {
        logger.log(Level.WARNING,
                   "Couldn't find customer with ID of {0}", customerId);
    }
    return customer;
}

CustomerBean類

使用JAX-RS客戶端API來編寫客戶端爲客戶示例應用程序。CustomerBean類調用JAX-RS客戶端API測試,啓動CustomerService Web服務:

@Named
@Stateless
public class CustomerBean {
    protected Client client;
    private static final Logger logger =
        Logger.getLogger(CustomerBean.class.getName());
    @PostConstruct
    private void init()
    {
        client = ClientBuilder.newClient();
    }
    @PreDestroy
    private void clean()
    {
        client.close();
    }
    public String createCustomer(Customer customer)
    {
        if (customer == null)
        {
            logger.log(Level.WARNING, "customer is null.");
            return "customerError";
        }
        String navigation;
        Response response =
            client.target("http://localhost:8080/customer/webapi/Customer")
            .request(MediaType.APPLICATION_XML)
            .post(Entity.entity(customer, MediaType.APPLICATION_XML),
                  Response.class);
        if (response.getStatus() == Status.CREATED.getStatusCode())
        {
            navigation = "customerCreated";
        }
        else
        {
            logger.log(Level.WARNING, "couldn''t create customer with " +
                       "id {0}. Status returned was {1}",
                       new Object[] {customer.getId(), response.getStatus()});
            navigation = "customerError";
        }
        return navigation;
    }
    public String retrieveCustomer(String id)
    {
        String navigation;
        Customer customer =
            client.target("http://localhost:8080/customer/webapi/Customer")
            .path(id)
            .request(MediaType.APPLICATION_XML)
            .get(Customer.class);
        if (customer == null)
        {
            navigation = "customerError";
        }
        else
        {
            navigation = "customerRetrieved";
        }
        return navigation;
    }
    public List<Customer> retrieveAllCustomers()
    {
        List<Customer> customers =
            client.target("http://localhost:8080/customer/webapi/Customer")
            .path("all")
            .request(MediaType.APPLICATION_XML)
        .get(new GenericType<List<Customer>>() {});
        return customers;
    }
}

不難看出,此客戶端使用了POST和GET方法。
HTTP狀態代碼表示

success:201 POST
200 GET
204 DELETE

JSON Processing 1.0

JAX-RS JSONP

JAX-RS可自動讀取並使用JAXB寫入XML,但它也能夠讀寫JSON數據。 JSON是從得到的數據交換一個簡單的基於文本的格式JavaScript的。對於前述示例,一個產品的XML表示是

<?xml version="1.0" encoding="utf-8"?>
<product>
  <id>1</id>
  <name>Mattress</name>
  <description>Queen size mattress</description>
  <price>500</price>
</product>

用json格式表示爲:

{
"id":"1",
"name":"Mattress",
"description":"Queen size mattress",
"price":500 }

添加格式的應用程序/ JSON或MediaType.APPLICATION_JSON到
@Produces註釋資源的方法來生產使用JSON數據響應:

@GET
@Path("/get")
@Produces({"application/xml","application/json"})
public Product getProduct() { ... }

這個例子中,默認響應是XML,但反應是一個JSON對象,若是客戶端發出包含這個頭的GET請求:

Accept: application/json

方法還能夠接受JSON數據和JAXB註釋類:

@POST
@Path("/create")
@Consumes({"application/xml","application/json"})
public Response createProduct(Product prod) { ...

則必須包含request post:

Content-Type: application/json

根據JAX-RS2.0規範,爲JSON處理JSR-353的Java API的支持是強制性的要求,意味着消息reader(s)/writer(s)爲如下幾種類型的存在:JsonStructure,JsonArray和的JSONObject。在Apache CXF提供JsrJsonpProvider提供者的形式,例如一個支持被Apache CXF的JAX-RS擴展模塊提供商(cxf-rt-rs-extension-providers)代碼以下。

<jaxrs:providers>
   <bean class="org.apache.cxf.jaxrs.provider.jsrjsonp.JsrJsonpProvider"/>
</jaxrs:providers>

單獨加入JsrJsonpProvider提供商(或與其餘提供者的組合)容許JAX-RS資源,以自己使用JsonStructure,JsonArray的JSONObject對象做爲輸入參數或返回值。 例如:
GET

@Path("/books")
@Produces(MediaType.APPLICATION_JSON)
public JsonArray getBooks() {
    // Implementation here
}

@GET
@Path("/books/{bookId}")
@Produces(MediaType.APPLICATION_JSON)
public JsonObject getBook(@PathParam("bookId") Long id) {
    // Implementation here
}

@POST
@Path("/books")
@Consumes(MediaType.APPLICATION_JSON)
public Response addBook(@Context final UriInfo uriInfo, JsonObject obj) {
    // Implementation here
}

WebSocket 1.0

做爲HTML5新特性之一的WebSocket組件,在實時性有必定要求的WEB應用開發 中仍是有必定用武之地,高版本的IE、Chrome、FF瀏覽器都支持Websocket,標準的Websocket通訊是基於RFC6455實現服務器 端與客戶端握手與消息接發的。若是對Websocket通訊不是太理解,能夠查看RFC文檔便可,簡單說就是經過發送HTTP請求,實現雙方握手,將無狀 態的HTTP通訊協議進一步升級成有狀態的通訊協議,同時Websocket還支持子協議選項與安全傳輸。標準的websocket鏈接URL以ws開 頭,若是是基於TLS的則以wss開頭。

Java EE平臺包括的WebSocket(JSR356),這使的Java API
您能夠建立,配置和在Web應用程序部署的WebSocket端點。該
在JSR356中指定的WebSocket客戶端API,您還能夠訪問遠程的WebSocket端點從任何Java應用程序。

Echo WebSocket

websocket回聲服務器

package com.websocket.demo;

import java.io.IOException;
import java.nio.ByteBuffer;

import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

@ServerEndpoint(value = "/echo")
public class EchoExample {

    @OnMessage
    public void echoTextMessage(Session session, String msg, boolean last) {
        try {
            if (session.isOpen()) {
                System.out.println("received from client message = " + msg);
                session.getBasicRemote().sendText(msg, last);
            }
        } catch (IOException e) {
            try {
                session.close();
            } catch (IOException e1) {
            }
        }
    }

    @OnOpen
    public void openConn(Session session) throws IOException {
        session.getBasicRemote().sendText("hello web socket"); // means open it
    }

    @OnMessage
    public void echoBinaryMessage(Session session, ByteBuffer bb, boolean last) {
        System.out.println("send binary message...");
        try {
            if (session.isOpen()) {
                System.out.println("byte buffer lenghth : " + bb.array().length);
                System.out.println("byte buffer content: " + ((bb.array()[0]) & 0xff));
                System.out.println("byte buffer content: " + ((bb.array()[1]) & 0xff));
                System.out.println("byte buffer content: " + ((bb.array()[2]) & 0xff));
                session.getBasicRemote().sendBinary(bb, last);
            }
        } catch (IOException e) {
            try {
                session.close();
            } catch (IOException e1) {
                // Ignore
            }
        }
    }

}

web.xml配置

<listener>
        <listener-class>org.apache.tomcat.websocket.server.WsContextListener</listener-class>
</listener>

ServerApplicationConfig接口

package com.config.websocket.client;

import java.util.HashSet;
import java.util.Set;

import javax.websocket.Endpoint;
import javax.websocket.server.ServerApplicationConfig;
import javax.websocket.server.ServerEndpointConfig;

public class ScanWebSocketSeverConfig implements ServerApplicationConfig {

    @Override
    public Set<ServerEndpointConfig> getEndpointConfigs(Set<Class<? extends Endpoint>> scanned) {

        Set<ServerEndpointConfig> result = new HashSet<ServerEndpointConfig>();
/* if (scanned.contains(EchoWsChatSever.class)) { result.add(ServerEndpointConfig.Builder.create(EchoWsChatSever.class, "/echo").build()); }*/
        return result;
    }

    @Override
    public Set<Class<?>> getAnnotatedEndpointClasses(Set<Class<?>> scanned) {
        Set<Class<?>> results = new HashSet<Class<?>>();
        for (Class<?> clazz : scanned) {
            if (clazz.getPackage().getName().startsWith("com.websocket.")) {
                System.out.println("find end point : " + clazz.getName());
                results.add(clazz);
            }
        }
        return results;
    }
}

echo.html

<html>
<head>
<title>Web Socket Echo Test</title>
<script> var ws = null; var count = 0; function setConnected(connected) { document.getElementById('connect').disabled = connected; document.getElementById('disconnect').disabled = !connected; document.getElementById('echo').disabled = !connected; } function connect() { var target = document.getElementById('target').value; if (target == '') { alert('Please select server side connection implementation.'); return; } if ('WebSocket' in window) { ws = new WebSocket(target); } else if ('MozWebSocket' in window) { ws = new MozWebSocket(target); } else { alert('WebSocket is not supported by this browser.'); return; } ws.onopen = function () { setConnected(true); log('Info: WebSocket connection opened.'); }; ws.onmessage = function (event) { log('Received: ' + event.data); if(event.data instanceof ArrayBuffer) { var bytes = new Uint8Array(event.data); alert(bytes.length + " : " + bytes[0]); } }; ws.onclose = function (event) { setConnected(false); log('Info: WebSocket connection closed, Code: ' + event.code + (event.reason == "" ? "" : ", Reason: " + event.reason)); }; } function disconnect() { if (ws != null) { ws.doClose(); ws = null; } setConnected(false); } function echo() { if (ws != null) { var message = document.getElementById('message').value; log('Sent: ' + message); ws.send(JSON.stringify({'textMessage': message})); count++ } else { alert('WebSocket connection not established, please connect.'); } } function log(message) { var echomsg = document.getElementById('echomsg'); var p = document.createElement('p'); p.style.wordWrap = 'break-word'; p.appendChild(document.createTextNode(message)); echomsg.appendChild(p); while (echomsg.childNodes.length > 25) { echomsg.removeChild(console.firstChild); } echomsg.scrollTop = console.scrollHeight; } document.addEventListener("DOMContentLoaded", function() { // Remove elements with "noscript" class - <noscript> is not allowed in XHTML var noscripts = document.getElementsByClassName("noscript"); for (var i = 0; i < noscripts.length; i++) { noscripts[i].parentNode.removeChild(noscripts[i]); } }, false); </script>
</head>
<body>
    <div>
        <h4>URL - ws://localhost:8080/websocket/echo</h4>
        <input id="target" type="text" size="40" style="width: 350px" />
    </div>
    <div>
        <button id="connect" onclick="connect();">Connect</button>
        <button id="disconnect" disabled="disabled" onclick="disconnect();">Disconnect</button>
    </div>
    <div>
        <textarea id="message" style="width: 350px">Here is a message!</textarea>
    </div>
    <div>
        <button id="echo" onclick="echo();" disabled="disabled">Echo message</button>
    </div>
    <div id="echomsg">
    </div>
</body>
</html>

運行

打包部署到tomcat以後,啓動chrom瀏覽器,輸入地址:http://localhost:8080/websocket/echo.html


運行截圖


項目類圖

Auction WebSocket


系統順序圖

WebSocketDeviceServlet 類

買入類或者 拍賣類發起 WebSocket 長鏈接後,服務端接受請求的是 WebSocketDeviceServlet 類,跟傳統 HttpServlet 不一樣的是,WebSocketDeviceServlet 類實現 createWebSocketInbound 方法,相似 SocketServer 的 accept 方法,新生產的 WebSocketInbound 實例對應客戶端 HTTP 長鏈接,處理與客戶端交互功能。
WebSocketDeviceServlet 服務端代碼示例以下:

public class WebSocketDeviceServlet extends org.apache.catalina.websocket.WebSocketServlet {

    private static final long serialVersionUID = 1L;

    @Override
    protected StreamInbound createWebSocketInbound(String subProtocol, HttpServletRequest request)
    {

        WebSocketDeviceInbound newClientConn = new WebSocketDeviceInbound(request);
        WebSocketDeviceInboundPool.addMessageInbound(newClientConn);
        return newClientConn;

    }

}

WebSocketServlet 是 WebSocket 協議的後臺監聽進程,和傳統 HTTP 請求同樣,WebSocketServlet 相似 Spring/Struct 中的 Servlet 監聽進程,只不過經過客戶端 ws 的前綴指定了其監聽的協議爲 WebSocket。
WebSocketDeviceInboundPool 實現了相似 JDBC 數據庫鏈接池的客戶端 WebSocket 鏈接池功能,並統一處理 WebSocket 服務端對單個客戶端/多個客戶端(同組 買家類拍賣物品)的消息推送,詳見 WebSocketDeviceInboundPool 代碼類解釋。

WebSocketDeviceInboundl 類

WebSocketDeviceInbound 類爲每一個 買家類和 賣家類拍賣物品驗證登陸後,客戶端創建的 HTTP 長鏈接的對應後臺服務類,相似 Socket 編程中的 SocketServer accept 後的 Socket 進程,在 WebSocketInbound 中接收客戶端發送的實時位置信息等消息,並向客戶端(賣家類拍賣物品)發送下屬 買家類拍賣物品實時位置信息及位置分析結果數據,輸入流和輸出流都是 WebSocket 協議定製的。WsOutbound 負責輸出結果,StreamInbound 和 WsInputStream 負責接收數據:

public class WebSocketDeviceInbound extends MessageInbound {
    private final HttpServletRequest request;
    private DeviceAccount connectedDevice;

    public DeviceAccount getConnectedDevice()
    {
        return connectedDevice;
    }


    public void setConnectedDevice(DeviceAccount connectedDevice)
    {
        this.connectedDevice = connectedDevice;
    }


    public HttpServletRequest getRequest()
    {
        return request;
    }


    public WebSocketDeviceInbound(HttpServletRequest request)
    {
        this.request = request;
        DeviceAccount connectedDa = (DeviceAccount)request.getSession(true).getAttribute("connectedDevice");
        if(connectedDa == null)
        {
            String deviceId = request.getParameter("id");
            DeviceAccountDao deviceDao = new DeviceAccountDao();
            connectedDa = deviceDao.getDaById(Integer.parseInt(deviceId));
        }
        this.setConnectedDevice(connectedDa);
    }


    @Override
    protected void onOpen(WsOutbound outbound)
    {
        /

    }

    @Override
    protected void onClose(int status)
    {
        WebSocketDeviceInboundPool.removeMessageInbound(this);

    }

    @Override
    protected void onBinaryMessage(ByteBuffer message) throws IOException
    {
        throw new UnsupportedOperationException("Binary message not supported.");
    }

    @Override
    protected void onTextMessage(CharBuffer message) throws IOException
    {
        WebSocketDeviceInboundPool.processTextMessage(this, message.toString());

    }


    public void sendMessage(BaseEvent event)
    {
        String eventStr = JSON.toJSONString(event);
        try
        {
            this.getWsOutbound().writeTextMessage(CharBuffer.wrap(eventStr));
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
    }
}

connectedDevice 是當前鏈接的 A/賣家類客戶端拍賣物品類實例,在這裏作爲成員變量以便後續處理交互。

sendMessage 函數向客戶端發送數據,使用 Websocket WsOutbound 輸出流向客戶端推送數據,數據格式統一爲 JSON。

onTextMessage 函數爲客戶端發送消息到服務器時觸發事件,調用 WebSocketDeviceInboundPool 的 processTextMessage 統一處理 買家類拍賣物品的登入,更新位置,離線等消息。

onClose 函數觸發關閉事件,在鏈接池中移除鏈接。

WebSocketDeviceInbound 構造函數爲客戶端創建鏈接後,WebSocketServlet 的 createWebSocketInbound 函數觸發,查詢 買家類/賣家類拍賣物品在後臺數據庫的詳細數據並實例化 connectedDevice 作爲 WebSocketDeviceInbound 的成員變量,WebSocketServlet 類此時將新的 WebSocketInbound 實例加入自定義的 WebSocketDeviceInboundPool 鏈接池中,以便統一處理 A/B 拍賣物品組員關係及位置分佈信息計算等業務邏輯。

WebSocketDeviceInboundl 類

WebSocketInboundPool 類: 因爲須要處理大量 買家類 賣家類拍賣物品的實時消息,服務端會同時存在大量 HTTP 長鏈接,爲統一管理和有效利用 HTTP 長鏈接資源,項目中使用了簡單的 HashMap 實現內存鏈接池機制,每次拍賣物品登入新建的 WebSocketInbound 都放入 WebSocketInbound 實例的鏈接池中,當拍賣物品登出時,從鏈接池中 remove 對應的 WebSocketInbound 實例。

此外,WebSocketInboundPool 類還承擔 WebSocket 客戶端處理 買家類和 賣家類拍賣物品間消息傳遞的做用,在客戶端發送 買家類拍賣物品登入、登出及位置更新消息的時候,服務端 WebSocketInboundPool 進行位置分佈信息的計算,並將計算完的結果向同時在線的 賣家類拍賣物品推送。

代碼以下:

public class WebSocketDeviceInboundPool
{

    private static final ArrayList<WebSocketDeviceInbound> connections =
        new ArrayList<WebSocketDeviceInbound>();

    public static void addMessageInbound(WebSocketDeviceInbound inbound)
    {
        //添加鏈接
        DeviceAccount da = inbound.getConnectedDevice();
        System.out.println("新上線拍賣物品 : " + da.getDeviceNm());
        connections.add(inbound);
    }

    public static ArrayList<DeviceAccount> getOnlineDevices()
    {
        ArrayList<DeviceAccount> onlineDevices = new ArrayList<DeviceAccount>();
        for(WebSocketDeviceInbound webClient: connections)
        {
            onlineDevices.add(webClient.getConnectedDevice());
        }
        return onlineDevices;
    }

    public static WebSocketDeviceInbound getGroupBDevices(String group)
    {
        WebSocketDeviceInbound retWebClient = null;
        for(WebSocketDeviceInbound webClient: connections)
        {
            if(webClient.getConnectedDevice().getDeviceGroup().equals(group) &&
                    webClient.getConnectedDevice().getType().equals("B"))
            {
                retWebClient = webClient;
            }
        }
        return retWebClient;
    }
    public static void removeMessageInbound(WebSocketDeviceInbound inbound)
    {
        //移除鏈接
        System.out.println("拍賣物品離線 : " + inbound.getConnectedDevice());
        connections.remove(inbound);
    }

    public static void processTextMessage(WebSocketDeviceInbound inbound, String message)
    {


        BaseEvent receiveEvent = (BaseEvent)JSON.parseObject(message.toString(), BaseEvent.class);
        DBEventHandleImpl dbEventHandle = new DBEventHandleImpl();
        dbEventHandle.setReceiveEvent(receiveEvent);
        dbEventHandle.HandleEvent();
        if(receiveEvent.getEventType() == EventConst.EVENT_MATCHMATIC_RESULT ||
                receiveEvent.getEventType() == EventConst.EVENT_GROUP_DEVICES_RESULT ||
                receiveEvent.getEventType() == EventConst.EVENT_A_REPAIRE)
        {
            String clientDeviceGroup = ((ArrayList<DeviceAccount>)
                                        receiveEvent.getEventObjs()).get(0).getDeviceGroup();
            WebSocketDeviceInbound bClient = getGroupBDevices(clientDeviceGroup);
            if(bClient != null)
            {
                sendMessageToSingleClient(bClient, dbEventHandle.getReceiveEvent());
            }
        }
    }
}
public static void sendMessageToAllDevices(BaseEvent event)
{
    try
    {
        for (WebSocketDeviceInbound webClient : connections)
        {
            webClient.sendMessage(event);
        }
    }
    catch (Exception e)
    {
        e.printStackTrace();
    }
}
public static void sendMessageToSingleClient(WebSocketDeviceInbound webClient, BaseEvent event)
{

    try
    {
        webClient.sendMessage(event);

    }
    catch (Exception e)
    {
        e.printStackTrace();
    }
}
}

addMessageInbound 函數向鏈接池中添加客戶端創建好的鏈接。

getOnlineDevices 函數獲取全部的連線的 A/賣家類拍賣物品。

removeMessageInbound 函數實現 買家類拍賣物品或者 賣家類拍賣物品離線退出(服務端收到客戶端關閉 WebSocket 鏈接事件,觸發 WebSocketInbound 中的 onClose 方法),從鏈接池中刪除鏈接拍賣物品客戶端的鏈接實例。

processTextMessage 完成處理客戶端消息,這裏使用了消息處理的機制,包括解碼客戶端消息,根據消息構造 Event 事件,經過 EventHandle 多線程處理,處理完後向客戶端返回,能夠向該組 B 拍賣物品推送消息,也能夠向發送消息的客戶端推送消息。

sendMessageToAllDevices 函數實現發送數據給全部在線 A/賣家類拍賣物品客戶端。sendMessageToSingleClient 函數實現向某一 A/賣家類拍賣物品客戶端發送數據。

websocket.js客戶端

客戶端代碼 websocket.js,客戶端使用標準 HTML5 定義的 WebSocket API,從而保證支持 IE9+,Chrome,FireFox 等多種瀏覽器,並結合 jQueryJS 庫 API 處理 JSON 數據的處理及發送。代碼以下:

var websocket=window.WebSocket || window.MozWebSocket; 
var isConnected = false;

function doOpen(){
 isConnected = true;
if(deviceType=='B'){
 mapArea='mapB';
 doLoginB(mapArea);
 }
 else{
 mapArea='mapA';
 doLoginA(mapArea);
 }

}

function doClose(){
showDiagMsg("infoField","已經斷開鏈接", "infoDialog");
isConnected = false;
}

function doError() {
showDiagMsg("infoField","鏈接異常!", "infoDialog");
isConnected = false;

}

function doMessage(message){
var event = $.parseJSON(message.data);
doReciveEvent(event);
}

function doSend(message) {
if (websocket != null) {
websocket.send(JSON.stringify(message));
} else {
showDiagMsg("infoField","您已經掉線,沒法與服務器通訊!", "infoDialog");
}
}

//初始話 WebSocket
function initWebSocket(wcUrl) {
if (window.WebSocket) {
websocket = new WebSocket(encodeURI(wcUrl));
websocket.onopen = doOpen;
websocket.onerror = doError;
websocket.onclose = doClose;
websocket.onmessage = doMessage;
}
else{
showDiagMsg("infoField","您的拍賣物品不支持 webSocket!", "infoDialog");

}
};

function doReciveEvent(event){
//拍賣物品不存在,客戶端斷開鏈接
if(event.eventType==101){
showDiagMsg("infoField","拍賣物品不存在或拍賣物品號密碼錯!", "infoDialog");
websocket.close();
}
//返回組拍賣物品及計算目標位置信息,更新地圖
else if(event.eventType==104||event.eventType==103){
clearGMapOverlays(mapB); 
 $.each(event.eventObjs,function(idx,item){
 var deviceNm = item.deviceNm;
 //google api
// var deviceLocale = new google.maps.LatLng(item.lag,item.lat);
//baidu api
 var deviceLocale = new BMap.Point(item.lng,item.lat);
 var newMarker;
 if(item.status=='target'){
 newMarker = addMarkToMap(mapB,deviceLocale,deviceNm,true);
 }
 else{
 newMarker = addMarkToMap(mapB,deviceLocale,deviceNm);
 } 
 markArray.push(newMarker);
 });
 showDiagMsg("infoField","有新報修拍賣物品或拍賣物品離線, 地圖已更新!", "infoDialog");
}

}

oOpen 回調函數處理打開 WebSocket,買家類拍賣物品或者賣家類拍賣物品鏈接上 WebSocket 服務端後,將初始化地圖並顯示默認位置,而後向服務端發送拍賣物品登入的消息。

doReciveEvent 函數處理關閉 WebSocket,買家類/賣家類拍賣物品離線(退出移動終端上的應用)時,服務端關閉 HTTP 長鏈接,客戶端 WebSocket 對象執行 onclose 回調句柄。

initWebSocket 初始化 WebSocket,鏈接 WebSocket 服務端,並設置處理回調句柄,若是瀏覽器版本太低而不支持 HTML5,提示客戶拍賣物品不支持 WebSocket。

doSend 函數處理客戶端向服務端發送消息,注意 message 是 JSON OBJ 對象,經過 JSON 標準 API 格式化字符串。

doMessage 函數處理 WebSocket 服務端返回的消息,後臺返回的 message 爲 JSON 字符串,經過 jQuery 的 parseJSON API 格式化爲 JSON Object 以便客戶端處理 doReciveEvent 函數時客戶端收到服務端返回消息的具體處理,因爲涉及大量業務邏輯在此再也不贅述。

項目類圖


 項目類圖

結果分析

Java EE 7 新特性中增強了對 HTML 5 動態可伸縮應用程序的支持、提升了開發人員的生產力和進一步知足了企業的苛刻需求。Java EE 7 使得開發人員可使用依賴注入和默認資源的樣本文件來減小代碼的編寫;更好地支持最新的 Web 應用和框架,擁有更好的擴展性和更豐富的功能;使得企業從便捷式批處理等新功能中獲益。

(1)提升開發人員的生產力

從 Java EE 5 開始,重心就一直放在提升開發人員的生產力上。這對於 Java 開發者來講很是重要,由於這使得使用 Java EE 進行開發更加便捷,更重要的是可以知足快速管理和生產的需求。鑑於此,Java EE 7 大大提升了開發人員的生產力。首先,減小了編寫大量核心業務邏輯所須要的樣板代碼。其次,該平臺引入更多的註釋 POJOS 來下降 XML 配置的複雜性。最後,Java EE 7 使用更緊密集成的技術,提供一個更加無縫的開發體驗。

(2)減小冗餘代碼

Java EE 7 一直在致力於減小在覈心業務邏輯代碼運行前必須執行的樣板代碼。減小樣板代碼的三大核心區域是默認資源、JMS 2.0 和 JAX-RS 客戶端 API。默認資源是一個新的功能,要求平臺提供商預配置一個默認的數據源和一個默認的 JMS 鏈接工廠。這可讓開發人員直接使用默認的資源而無需進行額外的定義。JMS2.0 在可伸縮性和可移植性上經歷了重大的改進,減小了冗餘代碼,已運用在無數的產品部署上,事實證實它是一個良好的規範,可以較好地知足企業的需求。

(3)更多帶註釋的POJO

經過註釋 Java EE 使開發人員更專一於 Java 對象的編程而無需關注繁瑣的配置。

CDI 如今默認狀況下已不須要使用 beans.xml 文件就能夠直接使用。開發人員能夠不須要任何配置而是簡單的使用 @Inject 來注入任何 Java 對象。包括新的資源註釋 @JMSDestinationDefinition 和 @MailSessionDefinition ,使得開發人員在源代碼中就能夠指定元數據資源,簡化了 DevOps 體驗。

(4)更緊密集成的平臺

Java EE 6 引入了 Managed Beans 1.0 做爲第一步來朝着 EJBs、JSF Managed Beans 和 CDI beans 發展。Java EE 7 繼承了這一點,例如,對 JSF Managed Beans 進行了改進來更好支持 CDI Beans。Java EE 7 爲平臺引入了易用的 EJB 容器管理事物,使用基於 CDI 攔截器的解決方案來保證事務可用在 CDI managed beans 和其它 Java EE 組件中,把註釋 @Transactional 應用到任何 CDI bean 或者任何支持事務的方法中。

Bean Validation 在 Java EE 7 中使用普遍,如今能夠用於方法級別的驗證,包括內置和自定義的約束。約束可被應用於方法的參數以及返回值。約束也可使用靈活渲染和違反約束的字符串格式的 Java EE 的表達語言。

Bean Validation 也延伸到 JAX-RS 2.0。註釋約束能夠應用到公共構造函數的參數、方法參數、字段和 bean 的屬性。此外,他們還能夠修飾資源類、實體參數和資源的方法。例如,約束能夠經過 @ POST 和 @ PUT 應用於 JAX-RS 方法參數來驗證表單提交的數據。

(5)經過精簡現有技術來簡化Java EE

Java EE 7 中新增長了許多新的特性,有些老的特性和功能已經被更簡單的特性所替代或直接棄用。Java EE 6 爲過期技術的棄用和功能的修剪引入了一個正式的流程,如下的 API 在 Java EE 7 中已成可選,但在 Java EE 8 中將會被移除:

Java EE Management (JSR-77),本來是用於爲應用 服務器建立監控管理的 API,可各大供應商對此 API 熱情並不高漲;

Java EE Application Deployment (JSR-88),JSR 88 是當初用於 J2EE 應用程序在應用 服務器上進行配置和部署的標準 API 。但是該 API 始終沒有獲得衆供應商的支持;

JAX-RPC,是早期經過 RPC 調用和 SOAP web services 進行交互的編程模型。因爲 Web services 成熟後從 RPC 模型中分離出來,被更加健壯和具有更多特性的 JAX-WS API 所替代;

EJB 2.x Entity Beans CMP,複雜、笨重、過分複雜的 EJB2.* 的 Entity Bean 模型已經被 Java EE 5 的基於 POJO 的流行輕量級 JPA 持久層模型所代替。

(6)對 HTML 5 動態可伸縮應用程序的支持

HTML5 是包括 HTML、JavaScript 和 CSS3 在內的一套技術組合,它加快了開發人員建立高度互動的應用程序的步伐。開發出的應用程序都是以高度互動的方式提供實時的數據,如聊天應用程序,比賽實況報 導等,而且這些應用程序只須要編寫一次,就能夠應用在桌面、移動客戶端等不一樣設備上,具備很是好的跨平臺性。這些高度動態的應用程序,使得用戶能夠在任何 地點任什麼時候間進行訪問,從而對服務器端向客戶端傳送數據的規模提出了更高的要求。Java EE 7 在更新現有技術如 JAX-RS 2.0、Java Server Faces 2.二、和 Servlet 3.1 NIO 基礎上,又藉助新的應用技術 WebSockets 和 JSON 處理爲支持動態應用程序 HTML5 奠基了堅實的基礎。

(7)低延遲數據交換:Java API for WebSocket 1.0

愈來愈多的 web 應用程序依賴於從中央服務器及時獲取並更新數據。基於 HTTP 的 WebSockets 爲解決低延遲和雙向通訊提供了一種解決方案。在 WebSocket API 的最基層是一個帶註釋的 Java 對象(POJO),以下邊代碼所示:
帶註釋的 Java 對象(POJO)

@ServerEndpoint("/test") 
 public class TestEndpoint{ 
 @OnOpen 
     public void onOpen(...){ } 
 @OnClose 
     public void onClose(...){ } 
     @OnError 
 public void onError(...){ } 
 @OnMessage 
 public void testMessage(String message,...){ } 
 }

經過註釋 @ServerEndpoint 來指定一個 URI。諸如客戶端鏈接、接收消息和客戶端斷開這樣的回調函數均可以用註釋來指定。WebSocket API 的最基層支持發送和接收簡單文本和二進制信息。API 的簡單性也使得開發人員能夠快速入門。

固然,功能豐富的應用擁有更復雜的需求,所以須要支持對最基礎的網絡協議進行控制和自定義,而 WebSocket API 正好可以知足以上需求。另外,WebSocket 利用現有 Web 容器的安全特性,開發人員只需付出較少的代價就能夠創建良好的保密通訊。

(8)簡化應用數據分析和處理:Java API for JSON Processing 1.0
JSON 做爲一個輕量級的數據交換格式被應用在許多流行的 Web 服務中用來調用和返回數據。許多流行的在線服務都是使用基於 JSON 的 RESTful 服務。在 Java EE 7 以前,Java 應用程序使用了不一樣的類庫去生成和解析 RESTful 服務中的 JSON 對象。然而,如今這個功能已被標準化。

經過 Java API 中的 JSON Processing 1.0,JSON 處理過程標準化爲一個單一的 API,應用程序不須要使用第三方的類庫。這樣使得應用程序更小更簡便。同時 API 包括了支持任何轉換器和生成器實現的插件,使得開發人員能夠選擇最好的實現方式去完成工做。

(9)可擴展的RESTful服務:JAX-RS 2.0
JAX-RS 2.0 增長了異步響應處理,這對於支持對數據有着高要求的 HTML5 客戶端的擴展是相當重要的。異步處理是一種更好更有效利用線程處理的技術。在服務器端,處理請求的線程在等待外部任務去完成時應該避免阻塞,從而保證在這 一時間段內到達的其餘請求可以獲得響應。

一樣的,在客戶端,一個發出請求的線程在等待響應的時候也會發生阻塞,這影響了應用程序的性能。新 的 JAX-RS 2.0 異步客戶端 API 使得客戶端調用 RESTful 能夠和其餘客戶端活動並行執行。異步的好處是使得一個客戶端能夠同時調用多個後臺服務,對於一個使用者來講減小了整體的延遲時間。

同時爲了加強 RESTful 服務,JAX-RS 2.0 開發人員可使用過濾器和實體攔截器。這樣開發人員就可使用標準的 API 來實現過濾和攔截功能,使開發過程變得更加便捷和高效。

(10)加強開發的易用性:JSF 2.2

JavaServer Faces (JSF) 是一種用於構建 Web 應用程序的 Java 新標準框架。它提供了一種以組件爲中心來開發 Java Web 用戶界面的方法,從而簡化了開發。在這個版本中,JSF 增長了對 HTML5 的支持。JSF 2.2 增長了一個叫「pass-through elements」的新功能。併爲現有的元素增長了一系列的新屬性,如輸入元素「tel」、「range」和「date」等。

不幸的是,現有的 JSF 組件不能識別這些新的屬性,所以 JSF 應用程序會忽略這些屬性不能進行使用,直到建立專有的解決方案。對於「pass-through elements」,JSF 渲染器將會忽略這些元素,只是把它們傳給支持 HTML5 的瀏覽器,這使得能夠利用現有的 JSF 組件來利用 HTML5 的特性來正常渲染。

JSF 引入了一個新的 pass-through 命名空間 http://xmlns.jcp.org/jsf/passthrough 映射到「p:」,任何組件的 name/value 對均可以以「p:」 開始,而後傳給瀏覽器。如清單 2 所示,HTML 5 「type=color」不須要 JSF 組件的任何解析就能夠傳遞給瀏覽器。

<h:inputText Value=」#{bean.color}」 P:type=」color」 />

HTML5 的動態性使得服務器端處理信息更新的請求不斷增多。在 Java EE 6,Servlet 異步 I/O 經過移除「一個請求須要一個線程」的限制,使一個線程能夠處理多個併發請求。這可使 HTML5 客戶端快速獲得響應。可是,若是服務器端讀取數據的速度比客戶端發送的速度要快,那麼可能會因爲緩慢的客戶端鏈接而不能提供更多的數據致使線程阻塞,這樣 就限制了擴展性。在 Java EE 7 中使用新的事件驅動 API Servlet 3.1 從客戶端讀取數據將不會形成阻塞。若是有數據可用時,Servlet 線程將會讀取和處理這些數據,不然就去處理其餘請求。

(11)知足苛刻的企業需求

Java EE 十幾年來一直努力知足企業的需求,使用 Java 鏈接器鏈接到企業服務端、使用 Java 事務支持事務處理、使用 Java 消息服務讓系統間能夠進行相互通訊。如今企業但願利用開發人員的 Java 技能編寫基於標準的 API 並可以跨平臺運行的批處理應用程序。企業也需構建高度可擴展的應用來知足更高的服務要求並提升現有資產的利用率。Concurrency Utilities 使得 Java EE 開發人員編寫可擴展的應用程序成爲可能。

(12)在Java平臺中,提升批處理應用程序的效率使開發過程變得更加便捷和高效

絕 大部分的 Java EE 應用都是在線用戶驅動的系統,但同時有一些須要進行批處理的服務器端應用程序,尤爲是離線分析和 ETL 等。這些面向批處理的應用程序是非交互式的、須要長時間運行,這些任務一般須要大量計算,同時能夠按順序或者並行執行,並能夠經過特定的事件啓動或者定時 調度。批處理較適合選擇閒置的時間進行處理,這樣能夠有效利用計算機資源。

之前,對於批處理程序沒有標準的 Java 編程模型。如今,批處理應用程序爲 Java 平臺提供瞭如圖4.1 很是容易理解的模型。批處理過程包括任務、步驟、存儲庫、讀取 - 處理 - 寫入模式和工做流等。

如圖4.1 所示,一個任務 job 表明一系列執行離散業務流程但又密切相關的步驟。步驟能夠按順序或者並行執行。同時,在同一個工做流,當前步驟是可選的,基於前一步驟的運行結果決定當前 步驟將被執行或者跳過。另外,步驟能夠根據實際的須要被從新執行。存儲庫 (repository) 存儲了當前任務的信息,好比任務的最後執行時間。
經過操做員 (operator) 能夠對任務進行排序、開始、中止、暫停和取消操做。


 用步驟描述工做
相關文章
相關標籤/搜索