3atv精品不卡视频,97人人超碰国产精品最新,中文字幕av一区二区三区人妻少妇,久久久精品波多野结衣,日韩一区二区三区精品

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

26Play框架教程2学习笔记

發布時間:2023/12/14 编程问答 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 26Play框架教程2学习笔记 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
Play框架教程2學習筆記

文章目錄

  • 1 play框架01
    • 1.1 概述
    • 1.2 特性
      • 1.2.1 無縫集成現有開發環境
      • 1.2.2 熱重載和修改Bug
      • 1.2.3 簡單的無狀態MVC架構
      • 1.2.4 HTTP到代碼的映射
      • 1.2.5 高效的模板引擎
      • 1.2.6 內置JPA支持
      • 1.2.7 Full Stack應用框架
      • 1.2.8 Play的特性總結
  • 2 play框架02
    • 2.1 app目錄
    • 2.2 conf目錄
    • 2.3 public 的目錄
    • 2.4 lib目錄
    • 2.5 project目錄
    • 2.6 target目錄
  • 3 play框架03
    • 3.1 安裝Play
    • 3.2 創建項目
    • 3.3 運行程序
    • 3.4 配置數據庫
    • 3.5 補充
    • 3.6 將play項目導入myeclipse
  • 4 play框架04
    • 4.1 路由
      • 4.1.1 HTTP方法
      • 4.1.2 URI表達式
      • 4.1.3 定義Java調用
      • 4.1.4 404作為Action
      • 4.1.5 指定靜態參數
      • 4.1.6 變量和腳本
      • 4.1.7 staticDir:mapping
      • 4.1.8 staticFile:mapping
      • 4.1.9 虛擬主機
    • 4.2 逆向生成URL
    • 4.3 設置content type
    • 4.4 HTTP內容協商
      • 4.3.1 在HTTP頭中設置content type
      • 4.3.2 自定義格式
    • 4.5 關于REST
  • 5 play框架05
    • 5.1 概述
    • 5.2 獲取HTTP參數
      • 5.2.1 使用Map參數
      • 5.2.2 高級HTTP綁定
        • 5.2.2.1 簡單類型
        • 5.2.2.2 日歷類型
        • 5.2.2.3 文件類型
        • 5.2.2.4 數組和集合類型
        • 5.2.2.5 POJO對象綁定
      • 5.2.3 JPA對象綁定
      • 5.2.4 自定義綁定
    • 5.3 結果返回
      • 5.3.1 返回文本內容
      • 5.3.2 返回JSON字符串
      • 5.3.3 返回XML字符串
      • 5.3.4 返回二進制內容
      • 5.3.5 下載附件功能
      • 5.3.6 執行模板
      • 5.2.7 為模板作用域添加數據
      • 5.3.8 更簡單方式
      • 5.3.9 指定其他模板進行渲染
      • 5.3.10 重定向URL
      • 5.3.11 自定義Web編碼
    • 5.4 Action鏈
    • 5.5 攔截器
      • 5.5.1 @Before
      • 5.5.2 @After
      • 5.5.3 @Catch
      • 5.5.4 @Finally
      • 5.5.5 使用@with注解增加更多攔截器
    • 5.6 Session和Flash作用域
  • 6 play框架06
    • 6.1 模板語法
      • 6.1.1 表達式(expression):${…}
      • 6.1.2 標簽(tag): #{tagName /}
      • 6.1.3 引用(action):@{…}或者@@{…}
      • 6.1.4 國際化(messages):&{…}
      • 6.1.5 注釋(comment):
      • 6.1.6 腳本(script): %{…}%
    • 6.2 模板繼承
  • 7 play框架07
    • 7.1 屬性模擬
    • 7.2 數據庫配置
    • 7.3 數據持久化
    • 7.4 無狀態模型
  • 8 play框架08
    • 8.1 Job實現
    • 8.2 Bootstrap Job
      • 8.2.1 應用啟動
      • 8.2.2 應用停止
    • 8.3 Scheduled Job
    • 8.4 Job的直接調用
  • 9 play框架的請求處理流程
    • 9.1 PlayHandler
    • 9.2 Invoker與Invocation
    • 9.3 Result類
    • 9.4 Template類
  • 10 play框架的攔截器
  • 11 play源碼分析
    • 11.1總體流程
    • 11.2 Start流程
    • 11.3 啟動HTTP服務

1 play框架01

? play框架01–介紹

1.1 概述

Play框架顛覆了臃腫的企業級Java EE規范,以Restful為目標并專注于開發效率,是Java敏捷開發的最佳參考方案。

開發者只要具備Java以及數據庫的相關基礎知識就可以輕松上手,從而讓Web應用開發變得更加容易,提高項目催化速度。

作為Full Stack的Java Web應用框架,Play包括了所有開發中涉及的領域:NIO應用容器,無狀態MVC模型,Hibernate數據持久化,

Groovy模板引擎,以及建立Web應用所需要的各種工具類。需要注意的是,這里雖然使用了Groovy,但只是將其作為頁面模板語言,

和Freemaker、Velocity使用自己定義的語言是同樣的道理。

Groovy的成熟以及它和Java的相似性決定了采用Groovy遠遠好于定義自己的模板語言。

1.2 特性

1.2.1 無縫集成現有開發環境

Play1.x是基于Java的Web開發框架,允許開發者使用自己常用的集成開發工具(如Eclipse)和類庫。

如果讀者已經以Java作為開發方向,那么無須進行開發語言、IDE或者類庫的切換,要做的就是在更加高效的Java環境中開發Web應用。

1.2.2 熱重載和修改Bug

Java在過去因為開發效率低下而臭名昭著,主要是因為其重復和乏味的編譯-打包-部署周期。因此在設計框架的時候對這些因素都進行了重新考量,目標是讓Play應用的開發過程變得更加高效。

Play框架會自動編譯Java源文件,而不用重新啟動Web服務器將代碼熱加載至JVM。這樣做的好處是:當代碼修改完保存后,框架自動編譯并重載修改后的類,只需刷新瀏覽器就可以查看更改的結果,就像在LAMP或者Rails環境中開發一樣。另外一個好處是:開發的時候甚至可以只用簡單的文本編輯器,而不使用功能完備的Java IDE進行開發。

1.2.3 簡單的無狀態MVC架構

一端是數據庫,另一端是Web瀏覽器,為什么我們需要在這兩者之間保存狀態?

有狀態并且基于組件的Java Web框架能夠更加容易地保存頁面狀態,但這同樣帶來了很多其他的問題:如果用戶在新的瀏覽器窗口中重新打開應用會發生什么?用戶按了后退按鈕又會是什么結果?

無共享架構是很多Web應用框架所提倡的(ROR,Django等)。由于瀏覽器變得越來越強大,我們并不需要技巧性地構建HTTP模型來創建偽造的狀態,只需在客戶端使用Ajax或者離線存儲技術就可以很容易地解決狀態問題。無共享架構的另一優勢是使頁面的呈現更加平滑,更容易地實現局部頁面更新(或者漸進式的頁面處理流程)。

1.2.4 HTTP到代碼的映射

如果讀者使用過其他的Java Web框架(比如說Struts)可能會發現,這些框架的底層實現其實是對HTTP協議做了進一步封裝,

所以它們提供的Java API和自身的理念會讓人覺得很不自然。Play框架在設計過程中換了一種思維方式,

即Web應用框架也應該提供完整、直接的方式去訪問HTTP————這也是Play框架和其他Java Web框架最根本的差異。

HTTP,Request/Response模式,Rest架構風格,HTTP內容協商(Content–type negotiation),URI等等,

所有這些都是Play框架的主要概念。如果用戶需要將URI綁定到指定的Java方法調用,只需要在路由文件中以如下方式進行配置:

GET /clients/{id} Clients.show

如果Ajax,REST以及管理頁面之間的“前進/后退”操作是日常開發中需要頻繁考慮的需求,

那么Play框架無疑是最佳的選擇,因為針對這些問題它都提供了非常優秀的解決方案。

Play是一個完全無狀態的,只面向請求/響應的框架,所有HTTP請求都具有相同的處理流程:

? 1.框架接收HTTP請求。

? 2.路由組件找到最匹配的規則,接受并處理請求,隨后調用相應的Action方法。

? 3.執行Action中的應用代碼。

? 4.如果需要生成復雜的視圖,使用模板文件進行渲染。

? 5.Action方法的返回結果(HTTP響應代碼以及內容)被轉換為HTTP響應。

1.2.5 高效的模板引擎

? 也許讀者已經深深地感受到了JSP和表達式語言背后的理念,但是為什么在創建標簽庫的時候需要如此多的配置文件?為什么不能直接訪問底層的模型對象?JSP中太多的限制確實讓開發者感到失望,受JSP啟發又不被其約束,Play框架提供了自定義的模板引擎機制。

開發者再也不需要編寫這些令人厭倦的代碼了:

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %><c:choose><c:when test="${emails.unread != null && fn:size(emails.unread)}">You have ${fn:size(emails.unread)} unread email(s)!</c:when><c:otherwise>You have no unread emails!</c:otherwise> </c:choose>

相信開發者更傾向于用以下方式來書寫模板代碼:

You have ${emails.unread ?: 'no'} ${emails.unread?.pluralize('email')} !

Play模板引擎使用的表達式語言為Groovy,它提供了與Java一致的語法。

Play主要使用模板機制來渲染HTML,當然也可以生成其他的文檔格式,比如e-mail messages,JSON等等。

1.2.6 內置JPA支持

? JPA(Java Persistence API)是Java中最簡潔的對象關系映射(object-relational mapping即ORM)API。

如果讀者以前了解或者使用過JPA,就會發現與其他框架相比,在Play中使用會更加方便。這是因為Play框架對其做了進一步封裝,

不需要任何配置,Play會自動開啟JPA實體管理器(EM),一旦代碼被調用就自動進行持久化操作。

此外,實體如果繼承Play提供的play.db.jpa.Model類,操作代碼將會更加簡潔,更加美觀:

public static void messages(int page) {User connectedUser = User.find("byEmail", connected()).first();List<Message> messages = Message.find("user = ? and read = false order by date desc",connectedUser).from(page * 10).fetch(10);render(connectedUser, messages); }

1.2.7 Full Stack應用框架

Play框架的最初設計受到實際Java Web開發的啟發,包含了所有創建主流Web應用所需要的工具:

  • 通過JDBC提供關系數據庫支持。

  • 使用Hibernate進行對象關系映射(JPA)。

  • 使用分布式Memcached集成緩存支持。

  • 以JSON或者XML的形式提供web service支持。

  • 基于OpenID的分布式用戶信息驗證。

  • Web應用可以部署在任何應用服務器上(Tomcat,Jboss,GAE,Cloud等)。

  • 圖像處理API(驗證碼)。

    此外Play還提供了很多實用的模塊。開發者可以結合這些模塊構建Web應用,

? 這使得我們可以以更加簡單,更加直接的方式重用Java代碼、模板以及靜態資源(比如JavaScript和CSS文件)。

1.2.8 Play的特性總結

  • 自動編譯和重載:當編輯Java文件并保存后,刷新瀏覽器就能立即查看結果。

    使用Play開發不需要手動編譯、部署以及重新啟動Web服務器等操作。

  • 無狀態模型:Play是真正的無共享框架,為REST而準備。它可以將同一個應用的多個實例分別部署在多臺服務器上,因而擴展性非常強。

  • 高效的模板引擎:基于表達式語言Groovy的清晰模板引擎,提供了模板的繼承、導入以及標簽自定義等功能。

  • 快速解決錯誤:當錯誤發生時,Play會在瀏覽器中顯示出錯代碼塊并提示問題發生的確切位置。

  • Full Stack:提供創建Web應用所需的全部功能,集成了Hibernate、OpenID、Memcached等第三方類庫。

  • 純Java:Play采用Java編寫代碼,可以方便地使用任何Java類庫,并且能夠非常好地和Eclipse、Netbeans等IDE集成,只需通過命令生成匹配的項目文件即可。

  • 基于非阻塞的IO模型:允許創建基于長輪詢和WebSocket的主流Web應用。

  • 有趣并且高效:省去了Java應用重啟的時間,提高了應用的開發效率。

  • 2 play框架02

    ? play框架02–細說目錄結構

    play的目錄結構制作的相當精簡,以下是從play官網截下的圖片:

    2.1 app目錄

    app目錄是代碼目錄,包含了所有的Java或者Scala的源碼,一般的“hello-world”sample程序都含有controllers、models、和views三個目錄,分別對應MVC三層結構中的:C、M和V;我想這大家都和清楚,大家還可以根據自己的項目需要創建其他的目錄,

    例如utils、dao等等。例如以下:

    如果有需要,你還可以建一個名為“assets”的目錄,里面可以放LESS或者CoffeeScript源文件。

    注意:這些controllers、models和views等目錄可以隨著你項目的需要而改變,

    例如:你可以寫成com.yourcompany.controllers、com.yourcompnay.model和com.yourcompany.views而不必非得寫成controllers、models和views。

    2.2 conf目錄

    在這個目錄里,放置的都是這個應用的一些配置文件信息,有兩個主要的文件:

    一個是application.conf:意思很明顯,就是整個應用的配置信息,里面會有一些配置的參數。

    包括數據庫鏈接中數據源的信息填寫,日志打印的級別等信息等等,還可以自定義一些參數。

    注意:在conf中,play默認定義的有:數據庫信息、應用信息(名字、 Secret key、語言等)、日志;

    這三塊兒的信息,在conf中直接改后,效果會在應用程序中直接出現。

    假如你想一用conf中自定義的配置參數:例如上圖中的阿里云相關的信息,你需要在application.conf中定義之后,在程序中使用

    Play.configuration.getString("oss.access_id").getOrElse("diSnug5q4zb9y2mq")

    來調用。實際上某人的那三塊信息也是這么來調用的。

    假如你在application.conf中不想定義過多的自定義信息,你也可以寫一個自定義的conf文件,然后在application.conf中引用(include “fileName.conf”)如下:

    routes:路由。非常重要的部分!使用方法非常簡單,在這里定義你需要的rest接口,然后接口后面對應的處理函數。如下圖:

    2.3 public 的目錄

    這里放置的都是前端頁面相關的信息,例如js、css、json文件、圖片等等。

    這些目錄文件的名字是可以改的,但是引用的時候需要注意目錄名字。包括public的名字也是可以改的。前端頁面中需要其中的靜態文件的話,需要再routes中添加:

    然后在前端需要靜態文件的地方這么引用:

    這里就是用的public目錄下images目錄中的靜態文件。

    2.4 lib目錄

    如果之前你是做J2EE項目的,這個目錄你一定清楚,這就是放置其他依賴包的地方。

    (當然如果Maven有依賴鏈接,盡量用Maven的依賴鏈接)

    build.sbt file:

    這個文件是整個項目添加依賴包的地方,所有的依賴都寫在這里。

    如果你是J2EE開發者的話,你一定知道Maven的pom.xml文件,在這里,build.sbt文件就相當于pom.xml的文件。

    2.5 project目錄

    這個目錄包含了sbt構建之后的東西:

    1、pulgins.sbt:插件sbt

    2、build.properties:包含了sbt的版本。

    2.6 target目錄

    target目錄包含了應用編譯之后的東西,就是編譯后的可執行文件。

    3 play框架03

    ? play框架03–創建項目

    3.1 安裝Play

    從下載頁面下載最新的二進制包,然后在你喜歡的地方解壓它。

    ? 如果你用的是Windows,最好避免在路徑中混入空格。比如c:\play就是個比c:\Documents And Settings\user\play更好的選擇。

    為了方便操作,你需要添加Play文件夾到你的系統路徑中。這樣你就不需要在play命令前面敲一大通路徑名了。

    要想檢查安裝是否成功,打開一個新的命令行窗口,敲下play;應該會出來play的基本使用幫助。

    3.2 創建項目

    現在Play已經安好了,是時候開始寫博客應用。創建一個Play應用非常簡單,僅需要play命令行工具。之后會生成Play應用的基本架構。

    打開一個新的命令行并敲入:

    ~$ play new yabe

    它會提醒輸入應用的全名。輸入yabe
    play new命令創建了一個新的文件夾yabe/外加一系列文件和文件夾。其中包括下面各部分:

    ? app/ 包括應用的核心,劃分為models,controllers和views文件夾。它也可以包括其他Java的包。這是.java源代碼文件所在之處。

    ? conf/ 包括所有的應用配置文件,特別是主application.conf文件,路由定義文件和用于國際化的信息文件。

    ? lib/ 包括所有可選的Java庫,比如標準的.jar。

    ? public/ 包括所有可以公開的資源,比如Javascript文件,樣式表和圖片。

    ? test/ 包括所有的應用測試。測試可以是Java的JUnit測試或者Selenium測試。

    因為Play只使用UTF-8編碼,故所有的文本文件都需要使用UTF-8編碼。確保你的文本編輯器已經做了相應的配置。

    如果你開發過Java應用,你可能會奇怪.class文件到哪兒去了。答案是……沒有.class文件了:Play并不使用任何class文件;相反它直接處理Java源代碼。實際上我們使用Eclipse的編譯器來即時編譯Java源代碼
    這導致了開發過程中的兩點重要的改進。

    ? 第一個,Play會自動監測Java源代碼的改變并在運行時自動重載。

    ? 第二個,當一個Java異常發生時,Play能向你展示更好的錯誤報告 - 帶對應的源代碼的哦~

    事實上Play在應用的tmp/文件夾下有字節碼的緩存,但只用于加速重新啟動項目的過程。

    如果需要,你可以用play clean清空緩存。

    3.3 運行程序

    現在可以測試一下新建立的程序了。回到命令行窗口,在yabe/目錄下輸入play run命令。

    Play框架將載入程序,啟動Web服務器并監聽9000端口。

    打開瀏覽器鍵入http://localhost:9000,程序顯示了一個缺省的歡迎頁。

    現在我們來看看這個頁面是怎樣顯示的。

    程序的主入口配置在conf/routes文件里。這個文件定義了程序所有可訪問的URL。

    打開routes文件,會看到第一個“route”:

    GET / Application.index

    它告訴Play,當服務器收到來自于/路徑的GET請求時要調用Application.index的方法。

    在這個程序中,Application.index是controllers.Application.index簡寫,因為controllers包是隱式附加的。

    在創建一個標準Java程序時,通常會定義一個入口方法,比如:

    public static void main(String[] args) { ... }

    Play程序則有多個入口方法,每個URL就有一個。這些方法稱為action方法。定義Action方法的類稱為controller。

    看看什么是controller。打開yabe/app/controllers/Application.java源文件:

    package controllers; import play.mvc.*; public class Application extends Controller { public static void index() { render(); } }

    controller 類繼承于play.mvc.Controller類,這個類提供了許多controller需要的方法,比如在index action中的render方法。

    index action定義為public static void,因為controller類不需要實例化和返回值。

    index action很簡單,只是調用了render()方法來通知Play渲染模板。

    使用模板是返回HTTP響應的一個最通用的方式。

    模板是在/app/views 目錄下的簡單文本文件。

    因為這里沒有指定一個模板,index action會使用一個默認的模板:Application/index.html。

    打開/yabe/app/views/Application/index.html:

    #{extends 'main.html' /}#{set title:'Home' /} #{welcome /}

    在這個模板中,只有Play tag,與JSP tag類似,#{welcome /} tag會在瀏覽器中生成歡迎信息。

    #{extends /} tag 表示這個模板繼承于main.html這個模板。模板繼承可用來創建復雜的web也并重用公共部分。

    打開/yabe/app/views/main.html模板:

    <!DOCTYPE html> <html> <head> <title>#{get 'title' /}</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <link rel="stylesheet" type="text/css" media="screen" href="@{'/public/stylesheets/main.css'}" /> <link rel="shortcut icon" type="image/png" href="@{'/public/images/favicon.png'}" /> </head> <body> #{doLayout /} </body> </html>

    #{doLayout /}tag表示index.html插入內容的地方。

    試著編輯controller類來看看Play怎么自動加載它。

    打開yabe/app/controllers/Application.java,刪除render()后的分號,讓它出錯,就像這樣:

    public static void index() { render() }

    然后到瀏覽器刷新這個頁面,Play會檢測源文件變更并試著加載程序controller,

    但是因為controller有錯誤,所以在瀏覽器中顯示一個編譯錯誤。

    把剛才的錯誤修改正確,在編輯模板,打開yabe/app/views/Application/index.html覆蓋歡迎消息。

    #{extends 'main.html' /} #{set title:'Home' /} <h1>A blog will be here</h1>

    在瀏覽器刷新這個頁面。

    3.4 配置數據庫

    在開始寫代碼之前還要多做一件事。作為博客引擎,我們需要一個數據庫。為了便于與開發,Play內置了一個叫做H2的數據庫。

    當然如果需要,我們也可以切換到一個更加健壯的數據庫。

    你可以選擇設置數據時存儲在內存中,還是在文件系統中(這樣即使你重新啟動,你的數據也會保留)。
    在一開始,我們將對應用模型做許多測試和改動。因此,最好選擇存儲在內存中,這樣每次啟動,都不會跟舊數據有任何牽連。
    打開yabe/app/application.conf,解除這一行的注釋:

    db=em

    正如你在注釋中看到的一樣,你可以冗余的配置任何JDBC數據庫,甚至配置鏈接池。

    現在回到瀏覽器并刷新歡迎頁面。Play將自動啟動數據庫。

    檢查下面一行是否出現在應用日志中:

    INFO ~ Connected to jdbc:h2:mem:play

    3.5 補充

    如果運行play run 命令出現下面提示:

    解決辦法:
    找到play\framework\build.bat 修改

    java -Xms512M -Xmx1024M -Xss1M -XX:+CMSClassUnloadingEnabled -XX:MaxPermSize=256M %DEBUG_PARAM% %JAVA_OPTS% -Dfile.encoding=UTF-8 -Dinput.encoding=Cp1252 -Dplay.version="%PLAY_VERSION%" -Dsbt.ivy.home="%~dp0..\repository" -Dplay.home="%~dp0." -Dsbt.boot.properties="%fp%sbt/sbt.boot.properties" %PLAY_OPTS% -jar "%~dp0sbt\sbt-launch.jar" %*

    java -XX:+CMSClassUnloadingEnabled %DEBUG_PARAM% -Dfile.encoding=UTF8 -Dplay.version="%PLAY_VERSION%" -Dsbt.ivy.home="%~dp0..\repository" -Dplay.home="%~dp0." -Dsbt.boot.properties="file:///%p%sbt/sbt.boot.properties" -jar "%~dp0sbt\sbt-launch.jar" %*

    3.6 將play項目導入myeclipse

    前提:已安裝play并配置了環境變量

    第一步 :打開你的項目將下面幾項刪除(沒有就跳過)

    第二步:在cmd中來到項目的路徑下(cd,不是來到項目里,而是項目名前一級目錄),

    ? 然后輸入play eclipsify +項目名

    第三步:導入

    4 play框架04

    4.1 路由

    Play框架中的路由器是負責將傳入的HTTP請求映射為Action調用(即控制器中被聲明為public static void的方法)的組件。

    HTTP請求被MVC框架視為事件,其主要包括以下兩塊內容:

    ? 。請求路徑(比如/clients/1542,/photos/list),其中可以包含查詢字符串。
    ? 。HTTP方法(GET,POST,PUT,DELETE)。

    Play路由器使用的配置文件為conf/routes,該文件列出了應用需要的所有路由規則。

    每條路由由HTTP方法和與Java調用相關聯的URI組成。以下是路由配置的例子:

    GET /clients/{id} Clients.show

    ? 路由配置總是從HTTP方法開始,URI作為中間部分,最后的元素是Java調用。

    在路由文件中可以使用#進行注釋:

    # Display a client GET /clients/{id} Clients.show

    4.1.1 HTTP方法

    ? HTTP協議支持以下所列的方法,用于指定客戶請求服務器的動作,其中GET和POST是最為常用的兩種方法:

    ? 。GET
    ? 。POST
    ? 。PUT
    ? 。DELETE
    ? 。HEAD

    Play同時也支持以WebSocket的方式來調用服務器端的Action方法

    如果在路由文件中指定*作為HTTP方法,那么這個路由會匹配任何HTTP請求:

    * /clients/{id} Clients.show

    使用上述的路由配置,以下兩個HTTP請求都會被框架接受:

    GET /clients/1541 PUT /clients/1212

    4.1.2 URI表達式

    ? URI表達式定義了路由規則需要的請求路徑,請求路徑中允許存在動態內容,但必須被聲明在{}中。

    /clients/all

    ? 以上的路由配置只能精確匹配到:

    /clients/all

    但是如果以包含動態部分配置路由規則:

    /clients/{id}

    ? 則可以分別匹配:

    /clients/12121

    ? 和

    /clients/toto

    如果某條路由配置的URI中需要包含多個動態部分,可以采用下例方法進行配置:

    /clients/{id}/accounts/{accountId}

    ? 默認情況下,動態部分的匹配策略采用的是正則表達式/[^/]+/。

    也可以為動態部分定義自己的正則表達式,以下是使用正則表達式的例子。

    路由規則只允許接受id為數字的值:

    /clients/{<[0-9]+>id}

    ? 路由規則確保id是長度為4到10字符的小寫單詞:

    /clients/{<[a-z]{4,10}>id}

    ? 正則表達式的使用非常靈活,還可以定義更多的路由規則,本節就不做贅述了。

    注意:
    動態部分指定后,控制器可以在HTTP參數map中獲取該值。

    默認情況下,Play將URI尾部的斜線(“/”)作為重要的組成部分,因為有無“/”將會出現不同的結果。比如:

    GET /clients Clients.index

    ? 該路由規則會匹配/clients,而不是/clinets/(注意這里的區別),但可以通過在斜線后面增加問號來同時匹配兩個URI:

    GET /clients/? Clients.index

    注意:
    URI除了尾斜線不允許有其他可選的部分。

    4.1.3 定義Java調用

    ? 路由定義的最后部分為需要調用的Java方法:控制器中必須定義指定的Action方法,否則會提示找不到控制器方法的錯誤信息;

    必須聲明為public static void方法;控制器需作為play.mvc.Controller的子類定義在controllers包中。

    ? 如果控制器沒有在controllers包中定義,在配置路由規則時可以在其名稱之前增加Java包(比如admin.Dashboard.index)的說明。

    由于controllers包本身被Play默認包含,所以用戶在配置路由時不需要顯式地指定。

    GET /admin admin.Dashboard.index

    4.1.4 404作為Action

    ? 可以直接使用404作為路由配置中的Action部分。如果這樣進行配置,對應的URL路徑就會被Play應用所忽略。

    比如:

    # 忽略favicon請求 GET /favicon.ico 404

    4.1.5 指定靜態參數

    ? 在某些情況下,可能會需要基于不同的參數值定義特殊路由。以下是預先定義好的Action:

    public static void page(String id) {Page page = Page.findById(id);render(page); }

    ? 針對該Action,常規的路由配置為:

    GET /pages/{id} Application.page

    ? 現在給參數id=home的頁面指定一條特殊的URL,需要通過設置靜態參數來實現:

    GET /home Application.page(id:'home') GET /pages/{id} Application.page

    ? 當參數id=home時,兩條路由配置等價,但是由于前者具有較高的優先級,

    ? 所以被作為默認的URL來調用Application.page。

    4.1.6 變量和腳本

    ? 與模板中的使用方法類似,在routes文件中可以使用${…}作為變量表達式,使用%{…}作為腳本表達式,

    比如:

    %{ context = play.configuration.getProperty('context', '') }%# 主頁 GET ${context} Secure.login GET ${context}/ Secure.login

    ? 在路由文件中定義變量和腳本的典型例子是CRUD模塊的routes文件。

    該文件中使用crud.types標簽對model類型進行迭代,為每種類型生成控制器路由定義。

    以后文章會詳細介紹CRUD模塊的使用。

    #{crud.types} GET /? ${type.controllerClass.name.substring(12).replace('$','')}.index GET /${type.controllerName} ${type.controllerClass.name.substring(12).replace('$','')}.list GET /${type.controllerName}/new ${type.controllerClass.name.substring(12).replace('$','')}.blank GET /${type.controllerName}/{id} ${type.controllerClass.name.substring(12).replace('$','')}.show GET /${type.controllerName}/{id}/{field} ${type.controllerClass.name.substring(12).replace('$','')}.attachment GET /${type.controllerName}/{id}/edit ${type.controllerClass.name.substring(12).replace('$','')}.edit POST /${type.controllerName} ${type.controllerClass.name.substring(12).replace('$','')}.create POST /${type.controllerName}/{id} ${type.controllerClass.name.substring(12).replace('$','')}.save DELETE /${type.controllerName}/{id} ${type.controllerClass.name.substring(12).replace('$','')}.delete #{/crud.types}

    Play會按照聲明的順序,優先選擇最先聲明的路由,比如:

    GET /clients/all Clients.listAll GET /clinets/{id} Clients.show

    ? 在上例的路由配置中,雖然請求/clients/all可以同時匹配這兩條路由配置,

    但按照聲明的優先順序會被第一條路由攔截,并調用相應的Clients.listAll方法。

    如果id參數需要匹配5個數字,在不使用重復規則的前提下,只能連續使用五個\d元字符,而使用重復規則后,規則的如下:

    GET /clinets/{<\d{5}>id} Clients.index

    ? 以下路由規則匹配2個大寫字母以及3-4個數字:

    GET /clinets/{<[A-Z]{2}[0-9]{3,4}>id} Clients.index

    4.1.7 staticDir:mapping

    ? Play的路由配置使用特殊的Action(staticDir)將存放靜態資源的public目錄開放。

    該目錄里包含的資源可以是圖片,Javascript,Stylesheet等,這些資源將直接響應給客戶端,并不需要服務器做進一步加工處理:

    GET /public/ staticDir:public
    當客戶端請求/public/*路徑時,Play會從應用的public文件夾中獲取相應的靜態資源。這里的優先級與標準路由配置一樣適用。

    4.1.8 staticFile:mapping

    ? 還可以直接將URL路徑映射為靜態文件:

    GET /home staticFile:/public/html/index.html

    ? 當客戶端通過GET方法請求/home時,服務器將不做任何處理直接把/public/html目錄下面的index.html文件返回給客戶端。

    4.1.9 虛擬主機

    Play的路由器具有主機匹配功能,當Action的變量需要從主機參數(指子域名,而不是子目錄)中獲取時,就顯得特別有用。

    比如SAAS應用可以使用如下方式配置路由規則:

    GET {client}.mysoftware.com/ Application.index

    ? 根據以上配置,框架會自動獲取client的值作為請求的參數:

    public static void index(String client) {... }

    ? 如果在模板中使用@@{…}標簽,那么框架會根據指定的條件來選擇對應的路由,這種方式在很多場合下都非常實用。比如,需要在產品中使用額外的服務器來提供靜態資源,則可以采用如下方式進行路由配置:

    #{if play.Play.mode.isDev()}GET /public/ staticDir:public #{/} #{else}GET assets.myapp.com/ staticDir:public #{/}

    ? 對應模板中的代碼如下:

    <img src="@@{'/public/images/logo.png'}">

    ? 當應用在DEV模式下運行時,靜態資源的URL為http://locahost:9000/public/images/logo.png;

    ? 如果運行在PROD模式下,URL為http://assets.myapp.com/images/logo.png。

    4.2 逆向生成URL

    Play路由器是按照Java調用生成URL的,所以可以將URI表達式都集中到同個配置文件中,使得重構應用變得更加便捷。

    比如,為conf/routes文件添加如下路由配置:

    GET /clients/{id} Clients.show

    ? 之后在Java代碼中,就可以調用Client.show來生成URL:

    map.put("id", 1541); String url = Router.reverse("Clients.show", map).url; // GET /clients/1541

    注意:
    URL的生成已經集成到框架的大部分組件當中,一般我們不需要直接調用Router.reverse方法。

    如果增加的參數不包含在URI表達式中,這些參數會被添加到查詢字符串中:

    map.put("id", 1541); map.put("display", "full"); String url = Router.reverse("Clients.show", map).url; // GET /clients/1541?display=full

    ? 同樣地,路由器會根據優先順序匹配最適的URL。

    4.3 設置content type

    ? Play會根據request.format設定的值,選擇指定的media類型來響應HTTP請求。

    該值通過文件擴展名來決定使用何種視圖模板進行渲染,并且通過Play框架中的mime-types.properties文件進行映射處理(映射關系詳見play\framework\src\play\libs\mime-types.properties文件),為media類型設定Content-type響應。

    ? Play請求的默認格式為html,因此index()控制器方法默認的渲染模板文件為index.html。

    如果需要指定其他的格式,有以下四種方式:

    (1)可以在程序代碼調用render()方法之前進行格式設置。比如將media類型設置為text/css,就可以使用CSS文件進行渲染:

    public static void index() {request.format = "css"; render(); }

    (2)推薦一種更直接的做法,直接在routes文件中使用URL來指定格式。以下列路由配置為例:首先客戶端通過index.xml請求服務器,服務器端將響應格式設置為xml,最后使用index.xml模版進行渲染。

    GET /index.xml Application.index(format:'xml')

    ? 同樣地,我們也可以使用CSS進行渲染:

    GET /stylesheets/dynamic_css css.SiteCSS(format:'css')

    (3)Play還可以直接從URL中獲取請求格式,動態指定渲染的模板類型。參考如下路由配置:

    GET /index.{format} Application.index

    ? 當請求為/index.xml時,服務器會將返回格式設置為xml并使用相應的XMl文件進行渲染;

    ? 請求為/index.txt時,則會使用文本進行渲染。

    (4)使用Play中的HTTP內容協商進行格式設置,詳見以后更新的內容。

    4.4 HTTP內容協商

    ? Play與其他REST架構的框架一樣,直接使用HTTP方法,而不是試圖隱藏HTTP或者在上面構建抽象層。

    內容協商是HTTP的特性,它允許HTTP服務器根據客戶端的請求類型,實現同個URL提供不同的media類型響應。

    客戶端可以在Accept header中設置media屬性,指定可接收的響應類型。如果用戶需要XML響應,則進行如下設置:

    Accept:application/xml

    ? 客戶端可以指定多種media類型,或使用cacth-all通配符(/)來指定任何media類型。

    Accept:application/xml,image/png,*/*

    ? 常規的Web瀏覽器總是在Accept header中包含了通配符的值,這樣瀏覽器便會接受任何media類型。

    Play將HTML作為默認格式進行渲染,因此在客戶端使用HTTP內容協商就顯得特別有用:通過Ajax請求返回JSON格式,

    或是使文檔以PDF和EPUB形式顯示等

    4.3.1 在HTTP頭中設置content type

    ? 如果Accept header中包含了text/html,application/xhtml或者通配符 /,Play會選擇使用其默認的請求格式(即HTML)。

    只有當通配符的值被顯式指定時,Play才會選擇其默認的請求格式。

    Play內置了一些常規格式支持:html、txt、json、xml。

    下例代碼定義了控制器方法(Action)進行數據渲染:

    public static void index() { final String name = "Peter Hilton"; final String organisation = "Lunatech Research"; final String url = "http://www.lunatech-research.com/"; render(name, organisation, url); }

    ? 如果在瀏覽器中訪問http://localhost:9000,Play默認會使用index.html模板進行渲染,因為瀏覽器發送了包含text/html的Accept header。

    通過將請求的格式設置為xml,可以使用index.xml模板響應標識為Accept: text/xml的請求:

    <?xml version="1.0"?> <contact> <name>${name}</name> <organisation>${organisation}</organisation> <url>${url}</url> </contact>

    ? 下表針對index()控制器方法給出了Play內置的Accept header請求格式映射:

    Accept header包含了Play能夠映射成的所有格式(最后轉化為相應的模板文件),如表3.1:

    ? (表3.1 Play內置的Accept header請求格式映射)

    Accept headerFormatTemplate file namemapping
    nullnullindex.htmlnull格式請求提供默認模版擴展
    image/pngnullindex.htmlmedia類型沒有映射為指定格式
    /, image/pnghtmlindex.html默認將media類型映射為html格式
    text/htmlhtmlindex.html內置映射
    application/xhtmlhtmlindex.html內置映射
    text/xmlxmlindex.xml內置映射
    application/xmlxmlindex.xml內置映射
    text/plaintxtindex.txt內置映射
    text/javascriptjsonindex.json內置映射
    application/json, /jsonindex.json內置映射, 忽略默認media類型

    4.3.2 自定義格式

    ? 在Play中可以通過檢查HTTP請求頭,為應用選擇相應的media類型來實現自定義格式。

    比如使用@Before標簽攔截該控制器下的所有Action,檢查請求的media類型是否為text/x-vcard:

    @Before static void setFormat() { if (request.headers.get("accept").value().equals("text/x-vcard")) { request.format = "vcf"; } }

    ? 如果檢查后發現請求頭中media類型為text/x-vcard時,將調用index.vcf模板渲染:

    BEGIN:VCARD VERSION:3.0 N:${name} FN:${name} ORG:${organisation} URL:${url} END:VCARD

    4.5 關于REST

    REST全稱為Representational State Transfer,即表述性狀態傳輸。

    它是一種為分布式超媒體系統(比如萬維網)而設計的軟件架構方式。REST定義了一些關鍵的規則:

    ? 。應用的所有功能都被劃分為資源。
    ? 。每個資源都使用URI來唯一訪問。
    ? 。所有資源共享統一的接口用于客戶端與資源之間進行狀態傳輸。

    如果應用使用的是HTTP協議,那么這些接口是通過一系列可用的HTTP方法來定義的。

    HTTP協議往往通過以下方法來使用資源的狀態:

    ? 。客戶端-服務器模式。
    ? 。無狀態模式。
    ? 。緩存模式。
    ? 。分層模式。

    如果應用遵循了REST設計規則,那么該應用就可以被稱為RESTful了。

    Play框架可以很容易地構建RESTful應用:

    ? 。Play的路由器通過解析URI和HTTP方法,將請求路由至Action方法,基于正則表達形式的URI為開發提供了更好的靈活性。
    ? 。協議是無狀態的,這意味著在兩次成功的請求之間不會把任何狀態保存在服務器中。
    ? 。Play將HTTP作為關鍵的特性,因此框架提供了對HTTP信息的完全訪問。

    5 play框架05

    ? play框架05–控制層

    5.1 概述

    Play的控制層位于應用的controllers包中,其中的Java類即為控制器(Controller)。

    如圖4.1所示,Application.java和MyController.java都屬于控制層。

    ? (圖4.1 控制器為controllers包中的Java類)

    控制器需要繼承play.mvc.Controller:

    package controllers;import models.Client; import play.mvc.Controller;public class Clients extends Controller {public static void show(Long id) {Client client = Client.findById(id);render(client);}public static void delete(Long id) {Client client = Client.findById(id);client.delete();}}

    在控制器中,每個以public static聲明,返回值為void的方法稱為Action。

    Action的方法聲明如下:

    public static void action_name(params…);

    Play會自動將HTTP請求參數轉化為與之相匹配的Action方法參數,這部分內容會在后面的獲取HTTP參數小節進行詳細講解。

    通常情況下,Action方法無需返回任何值,以調用結果方法來終止執行。

    在上述例子中,render(…)方法就是用來渲染模板的結果方法。

    HTTP請求中往往包含各種參數,這些參數的傳遞形式如下:

    • URI路徑:在路徑/clients/1541中,1541是URI的動態部分。

    • 查詢字符串:clients?id=1541。

    • 請求體:如果請求是來自HTML的表單提交(GET或者POST),

      那么請求體包含的是表單數據(采用x-www-urlform-encoded作為編碼格式)。

    針對以上幾種情況,Play會自動提取這些HTTP參數并將他們保存在Map<String,String>類型的變量中,以參數名作為Map的key。

    這些參數名分別來自于:

    • URI中動態部分的名稱(在routes文件中定義)。
    • 查詢字符串中“名稱/值”對中的名稱部分 。
    • 采用x-www-urlform-encoded編碼的表單數據的參數名。

    5.2 獲取HTTP參數

    5.2.1 使用Map參數

    HTTP請求中參數對象(params)在任何控制器中都是可訪問的(該實現在play.mvc.Controller超類中定義),

    它包含了當前所有HTTP請求的參數,并且可以通過get()方法得到,具體如下:

    public static void show(){String id=params.get("id");String[] names=params.getAll("name"); }

    這些參數也可以進行類型轉換:

    public static void show(){Long id=params.get("id",Long.class); }

    本節將推薦一種更好的解決方案。Play框架提供了自動將Action聲明的參數與HTTP參數自動匹配的功能(只需要保持Action方法的參數名和HTTP參數名一致即可):

    /clients?id=1541

    Action方法可以在聲明中以id作為參數,以此匹配HTTP中變量名為id的參數:

    public static void show(String id){System.out.println(id); }

    當然,也可以使用其他Java參數類型,而不僅僅是String。

    在下面的例子中框架會自動將參數轉換為正確的數據類型:

    public static void show(Long id){System.out.println(id); }

    如果參數含有多個值,那么可以定義數組參數,具體如下:

    public static void show(Long[] id){for(Long anId:id){System.out.println(anId);} }

    參數甚至可以是List類型:

    public static void show(List<Long> id){for(Long anId:id){System.out.println(anId);} }

    注意:

    如果Action與HTTP之間的參數無法匹配,Play會將該參數設置為默認值(通常情況下對象類型為null,原始數據類型為0)。

    如果參數可以匹配但不能正確進行數據轉換,那么Play會先生成錯誤并添加到驗證器的error對象集合中,然后將參數設置為默認值。

    5.2.2 高級HTTP綁定

    5.2.2.1 簡單類型

    Play可以實現所有Java原生的簡單數據類型的自動轉換,

    主要包括:int,long,boolean,char,byte,float,double,Integer,Long,Boolean,Char,String,Float,Double。

    日期類型

    如果HTTP參數字符串符合以下幾種數據格式,框架能夠自動將其轉換為日期類型:

    yyyy-MM-dd'T'hh:mm:ss’Z' // ISO8601 + timezone yyyy-MM-dd'T'hh:mm:ss" // ISO8601 yyyy-MM-dd yyyyMMdd'T'hhmmss yyyyMMddhhmmss dd'/'MM'/'yyyy dd-MM-yyyy ddMMyyyy MMddyy MM-dd-yy MM'/'dd'/'yy

    而且還能通過@As注解,指定特定格式的日期,例如:

    archives?from=21/12/1980 public static void articlesSince(@As("dd/MM/yyyy") Date from) {List<Article> articles = Article.findBy("date >= ?", from);render(articles); }

    也可以根據不同地區的語言習慣對日期的格式做進一步的優化,具體如下:

    public static void articlesSince(@As(lang={"fr,de","*"}, value={"dd-MM-yyyy","MM-dd-yyyy"}) Date from) {List<Article> articles = Article.findBy("date >= ?", from);render(articles); }

    在這個例子中,對于法語和德語的日期格式是dd-MM-yyyy,其他語言的日期格式是MM-dd-yyyy。

    語言值可以通過逗號隔開,且需要與參數的個數相匹配。

    如果沒有使用@As注解來指定,Play會采用框架默認的日期格式。為了使默認的日期格式能夠正常工作,

    按照以下方式編輯application.conf文件:

    date.format=yyyy-MM-dd

    在application.conf文件中設置默認的日期格式之后,就可以通過${date.format()}方法對模板中的日期進行格式化操作了。

    5.2.2.2 日歷類型

    日歷類型和日期類型非常相像,當然Play會根據本地化選擇默認的日歷類型。

    讀者也可以通過@Bind注解來使用自定義的日歷類型。

    5.2.2.3 文件類型

    在Play中處理文件上傳是件非常容易的事情,首先通過multipart/form-data編碼的請求將文件發送到服務器,

    然后使用java.io.File類型提取文件對象:

    public static void create(String comment, File attachment) {String s3Key = S3.post(attachment);Document doc = new Document(comment, s3Key);doc.save();show(doc.id); }

    新創建文件的名稱與原始文件一致,保存在應用的臨時文件下(Application_name/tmp)。

    在實際開發中,需要將其拷貝到安全的目錄,否則在請求結束后會丟失。

    5.2.2.4 數組和集合類型

    所有Java支持的數據類型都可以通過數組或者集合的形式來獲取。

    數組形式:

    public static void show(Long[] id){... }

    List形式:

    public staic void show(List<Long> id){... }

    集合形式:

    public static void show(Set<Long> id){... }

    Play還可以處理Map<String, String>映射形式:

    public static void show(Map<String, String> client) {... }

    例如下面的查詢字符串會轉化為帶有兩個元素的map類型,

    第一個元素key值為name,value為John;

    第二個元素key值為phone,value為111-1111, 222-2222。:

    ?user.name=John&user.phone=111-1111&user.phone=222-2222

    5.2.2.5 POJO對象綁定

    Play使用同名約束規則(即HTTP參數名必須與模型類中的屬性名一致),自動綁定模型類:

    public static void create(Client client){client.save();show(client); }

    以下的查詢字符串可以通過上例的Action創建client:

    ?client.name=Zenexity&client.email=contact@zenexity.fr

    框架通過Action創建Client的實例,并將HTTP參數解析為該實例的屬性。如果出現參數無法解析或者類型不匹配的情況,會自動忽略。

    參數綁定是遞歸執行的,這意味著可以深入到關聯對象:

    ?client.name=Zenexity &client.address.street=64+rue+taitbout &client.address.zip=75009 &client.address.country=France

    Play的參數綁定提供數組的支持,可以將對象id作為映射規則,更新一組模型對象。

    假設Client模型有一組聲明為List的customers屬性,那么更新該屬性需要使用如下查詢字符串:

    ?client.customers[0].id=123 &client.customers[1].id=456 &client.customers[2].id=789

    5.2.3 JPA對象綁定

    通過HTTP參數還可以實現JPA對象的自動綁定。Play會識別HTTP請求中提供的參數user.id,自動與數據庫中User實例的id進行匹配。

    一旦匹配成功,HTTP請求中的其他User屬性參數可以直接更新到數據庫相應的User記錄中:

    public static void save(User user){user.save(); }

    和POJO映射類似,可以使用JPA綁定來更改對象,但需要注意的是必須為每個需要更改的對象提供id:

    user.id = 1 &user.name=morten &user.address.id=34 &user.address.street=MyStreet

    5.2.4 自定義綁定

    綁定機制支持自定義功能,可以按照讀者的需求,自定義參數綁定的規則。

    ? @play.data.binding.As

    @play.data.binding.As注解可以依據配置提供綁定的支持。下例使用DateBinder指定日期的數據格式:

    public static void update(@As("dd/MM/yyyy") Date updatedAt) {... }

    @As注解還具有國際化支持,可以為每個本地化提供專門的注解:

    public static void update(@As(lang={"fr,de","en","*"},value={"dd/MM/yyyy","dd-MM-yyyy","MM-dd-yy"})Date updatedAt) {... }

    @As注解可以和所有支持它的綁定一起工作,包括用戶自定義的綁定。以下是使用ListBinder的例子:

    public static void update(@As(",") List<String> items) {... }

    上例中的綁定使用逗號將字符串分隔成List。

    ? @play.data.binding.NoBinding

    @play.data.binding.NoBinding注解允許對不需要綁定的屬性進行標記,以此來解決潛在的安全問題。

    比如:

    //User為Model類 public class User extends Model {@NoBinding("profile") public boolean isAdmin;@As("dd, MM yyyy") Date birthDate;public String name; }//editProfile為Action方法 public static void editProfile(@As("profile") User user) {... }

    在上述例子中,user對象的isAdmin屬性始終不會被editProfile方法(Action)所修改,

    即使有惡意用戶偽造POST表單提交user.isAdmin=true信息,也不能修改user的isAdmin權限。

    play.data.binding.TypeBinder

    @As注解還提供完全自定義綁定的功能。自定義綁定必須是TypeBinder類的實現:

    public class MyCustomStringBinder implements TypeBinder<String> {public Object bind(String name, Annotation[] anns, String value, Class clazz) {return "!!" + value + "!!";} }

    定義完成后,就可以在任何Action中使用它:

    public static void anyAction(@As(binder=MyCustomStringBinder.class) String name) {... }

    @play.data.binding.Global

    Play中還可以自定義全局Global綁定。以下是為java.awt.Point類定義綁定的例子:

    @Global public class PointBinder implements TypeBinder<Point> {public Object bind(String name, Annotation[] anns, String value, Class class) {String[] values = value.split(",");return new Point(Integer.parseInt(values[0]),Integer.parseInt(values[1]));} }

    因此外部模塊很容易通過自定義綁定來提供可重用的類型轉換組件。

    5.3 結果返回

    Action方法需要對客戶端作出HTTP響應,最簡單的方法就是發送結果對象。當對象發送后,常規的執行流程就會中斷。

    以下面這段代碼為例,最后一句System.out.println的輸出不會被執行:

    public static void show(Long id) {Client client = Client.findById(id);render(client);System.out.println("This message will never be displayed !"); }

    render(…)方法向模板發送client對象,之后的其他語句將不會執行,所以在控制臺中,

    并不會打印出“This message will never be displayed !”。

    5.3.1 返回文本內容

    renderText(…)方法直接將文本內容寫到底層HTTP響應中:

    public static void countUnreadMessages(){Integer unreadMessages=MessagesBos.countUnreadMessage();renderText(unreadMessages); }

    也可以通過Java標準的格式化語法對輸出的文本進行處理:

    public static void countUnreadMessages(){Integer unreadMessages=MessagesBox.countUnreadMessages();renderText("There are %s unread messages",unreadMessages); }

    5.3.2 返回JSON字符串

    越來越多的應用使用JSON作為數據格式進行交互,Play對此進行了很好的封裝,

    只需要使用renderJSON(…)方法就可以輕松地返回JSON字符串。

    在使用renderJSON(…)方法時,Play會自動將服務器返回的響應的content type值設置為application/json,

    并且將renderJSON(…)方法中的參數以JSON格式返回。

    在使用renderJSON(…)方法時,可以輸入字符串格式的參數,自行指定JSON返回的內容。

    public static void countUnreadMessages() {Integer unreadMessages = MessagesBox.countUnreadMessages();renderJSON("{\"messages\": " + unreadMessages +"}"); }

    以上范例在使用renderJSON(…)方法時,傳入了拼接成JSON格式的字符串參數。

    Play框架會對其進行自動設置,改變content type的值為application/json。

    當然,renderJSON(…)方法的功能并不只有這些。因為大部分的應用需求,都會要求服務端返回比較復雜的JSON格式,如果都采用字符串拼接的方式組成JSON內容,就太不人性化了。renderJSON(…)的輸入參數還可以是復雜的對象,如果采用這種方式使用renderJSON(…)方法,Play在執行renderJSON(…)時,底層會先調用GsonBuilder將對象參數進行序列化,之后再將復雜的對象以JSON的格式返回給請求。這樣開發者就可以完全透明地使用renderJSON(…)方法,不需要做其他的任何操作了,以下代碼范例將會展示renderJSON(…)的這個功能。

    public static void getUnreadMessages() {List<Message> unreadMessages = MessagesBox.unreadMessages();renderJSON(unreadMessages); }

    5.3.3 返回XML字符串

    與使用renderJSON(…)方法返回JSON內容類似,如果用戶希望以XML格式對內容進行渲染,

    可以在Controller控制器中直接使用renderXml(…)方法。

    使用renderXml(…)方法時,Play會自動將服務器返回的響應的content type值設置為application/xml。

    在使用renderXml(…)方法時,可以輸入字符串格式的參數,自行指定XML返回的內容。

    public static void countUnreadMessages() {Integer unreadMessages = MessagesBox.countUnreadMessages();renderXml("<unreadmessages>"+unreadMessages+"</unreadmessages>"); }

    如果希望將復雜的對象以XML格式進行渲染,可以在使用renderXml(…)方法時輸入org.w3c.dom.Document格式的對象,

    或者直接輸入POJO對象。以POJO對象作為參數使用renderXml(…)方法時,Play會使用XStream將其進行序列化操作。

    同樣的,這些序列化操作都不需要由開發者去做,全部交給Play就行,開發者需要做的就是按照規范簡單地調用renderXml(…)方法即可。

    public static void getUnreadMessages() {Document unreadMessages = MessagesBox.unreadMessagesXML();renderXml(unreadMessages); }

    5.3.4 返回二進制內容

    Play為開發者提供了renderBinary(…)方法,可以非常方便的返回二進制數據(如存儲在服務器里的文件、圖片等)給客戶端。

    以下代碼范例將會展示如何使用renderBinary(…)方法進行二進制圖片的渲染。

    public static void userPhoto(long id) { final User user = User.findById(id); response.setContentTypeIfNotSet(user.photo.type());java.io.InputStream binaryData = user.photo.get();renderBinary(binaryData); }

    首先,開發者需要建立用于持久化的域模型User,該User模型具有play.db.jpa.Blob類型的屬性photo。

    play.db.jpa.Blob是經過Play封裝的特有的屬性類型,可以很方便的處理二進制數據。之后,在Controller控制器中使用時,

    需要調用域模型的findById(…)方法加載持久化的數據,并將圖片以二進制數據流InputStream的形式進行渲染。

    5.3.5 下載附件功能

    如果開發者希望將存儲在服務器端的文件,采用下載的形式渲染給客戶端用戶,需要對HTTP的header進行設置。

    通常的做法是通知Web瀏覽器將二進制響應數據以附件的形式,下載至用戶的本地電腦上。

    在Play中完成這個功能非常簡單,只需要在使用renderBinary(…)方法時多傳入一個文件名的參數即可。

    這樣做會觸發renderBinary(…)的額外功能,提供文件名并設置響應頭的Content-Disposition屬性。

    之后二進制文件(包括圖片)將會以附件下載的形式,渲染給用戶。

    public static void userPhoto(long id) { final User user = User.findById(id); response.setContentTypeIfNotSet(user.photo.type());java.io.InputStream binaryData = user.photo.get();renderBinary(binaryData, user.photoFileName); }

    5.3.6 執行模板

    如果需要響應的內容比較復雜,那么就應該使用模板來進行處理:

    public class Clients extends Controller{public static void index(){render();} }

    模板的名稱遵從Play的約束規則,默認的模板路徑采用控制器和Action的名稱相結合的方式來定義,

    比如在上述例子中,模板對應的路徑為:app/views/Clients/index.html。

    5.2.7 為模板作用域添加數據

    通常情況下模板文件都需要數據進行顯示,可以使用renderArg()方法為模板注入數據:

    public class Clients extends Controller {public static void show(Long id) {Client client = Client.findById(id);renderArgs.put("client", client);render(); } }

    在模板執行過程當中,client變量可以被使用:

    <h1>Client ${client.name}</h1>

    5.3.8 更簡單方式

    這里介紹一種更簡單的方式向模板傳遞數據。

    直接使用render(…)方法注入模板數據:

    public static void show(Long id){Client client=Client.findById(id);render(client); }

    以該方式進行數據傳遞,模板中可訪問的變量與Java本地變量的名稱(也就是render()方法中的參數名)一致。

    當然也可以同時傳遞多個參數:

    public static void show(Long id){Client client=Client.findById(id);render(id,client); }

    注意:

    render()方法只允許傳遞本地變量。

    5.3.9 指定其他模板進行渲染

    如果讀者不希望使用默認的模板進行渲染,那么可以在renderTemplate(…)方法的第一個參數中指定其他自定義的模板路徑,

    例如:

    public static void show(Long id) {Client client = Client.findById(id);renderTemplate("Clients/showClient.html", id, client); }

    5.3.10 重定向URL

    redirect(…)方法產生HTTP重定向響應,可以將請求轉發到其他URL:

    public static void index(){redirect("http://www.oopsplay.org"); }

    5.3.11 自定義Web編碼

    Play推薦開發者使用UTF-8作為應用開發的編碼格式,如果不進行任何設置,Play框架默認使用的也就是UTF-8格式。

    但是具體情況并不總是這么理想,有些特殊的需求可能要求某些響應(response)的格式為ISO-8859-1,

    或者要求整個應用都必須保持ISO-8859-1編碼。

    為當前響應設置編碼格式

    如果需要改變某一個響應(response)的編碼格式,可以直接在Controller控制器中進行修改,

    具體做法如下所示:

    response.encoding = "ISO-8859-1";

    ? 當開發表單提交功能時,如果開發者希望某一表單提交的內容采用非框架默認使用的編碼(即Play框架采用默認的編碼格式UTF-8,

    而該form表單提交的內容希望采用ISO-8859-1編碼格式),Play的做法有一些特殊。在書寫form表單的HTML代碼時,

    需要對采用何種編碼格式進行兩次標識。首先需要在標簽中添加accept-charset屬性(如:accept-charset=“ISO-8859-1”),

    accept-charset屬性會通知瀏覽器當form表單提交的時候,采用何種編碼格式;

    其次,需要在form表單中添加hidden隱藏域,name屬性規定為“charset”,value屬性為具體需要的編碼格式,

    這樣做的目的是當form提交的時候,可以通知服務端的Play采用何種編碼方式,

    具體范例如下:

    <form action="@{application.index}" method="POST" accept-charset="ISO-8859-1"><input type="hidden" name="_charset_" value="ISO-8859-1"> </form>

    定義全局編碼格式

    通常情況下,整個應用應該保持統一的編碼格式。

    如果開發者需要設置應用全局的編碼格式,可以在application.conf配置文件中修改application.web_encoding屬性,配置相應的編碼。

    5.4 Action鏈

    Play中的Action鏈與Servlet API中的forward不盡相同。Play的每次HTTP請求只能調用一個Action,

    如果需要調用其他的Action,那么必須將瀏覽器重定向到相應的URL。

    在這種情況下,瀏覽器的URL始終與正在執行的Action保持對應關系,使得后退、前進、刷新操作更加清晰。

    調用控制器中其他Action方法也可以實現重定向,框架會攔截該調用并生成正確的HTTP重定向。

    具體實現如下:

    public class Clients extends Controller {public static void show(Long id) {Client client = Client.findById(id);render(client);}public static void create(String name) {Client client = new Client(name);client.save();show(client.id);} }

    相應的路由規則定義如下:

    GET /clients/{id} Clients.show POST /clients Clients.create

    按照定義,Action鏈的生命周期為:

    • 瀏覽器向/clients發送POST請求;

    • 路由器調用Clients控制器中的create方法;

    • create方法直接訪問show方法;

    • Java調用被攔截,路由器逆向生成帶有id參數的URL來調用Clients.show;

    • HTTP響應重定向為:/clients/3132;

    • 瀏覽器地址欄中URL展現為:/clients/3132;

    5.5 攔截器

    控制器中可以定義攔截方法(也可稱之為攔截器),為控制器及其子類的所有Action提供服務。

    當所有的Action都需要進行通用的處理時,該功能就顯得非常有用:比如驗證用戶的合法性,加載請求范圍內的信息等。

    讀者在使用時需要注意的是,這些攔截器方法不能定義為public,但必須是static,并通過有效的攔截標記進行注解。

    5.5.1 @Before

    使用@Before注解的方法會在每個Action調用之前執行。如創建具有用戶合法性檢查的攔截器:

    public class Admin extends Application {@Beforestatic void checkAuthentification() {if(session.get("user") == null) login();}public static void index() {List<User> users = User.findAll();render(users);}...}

    如果不希望@Before注解攔截所有的Action方法,那么可以使用unless參數列出需要排除的方法:

    public class Admin extends Application {@Before(unless="login")static void checkAuthentification() {if(session.get("user") == null) login();}public static void index() {List<User> users = User.findAll();render(users);}...}

    或者直接使用only參數把需要攔截的方法列舉出來:

    public class Admin extends Application {@Before(only={"login","logout"})static void doSomething() { ...}... }

    unless和only參數對@After,@Before以及@Finally注解都適用。

    5.5.2 @After

    使用@After注解的方法會在每個Action調用之后執行:

    public class Admin extends Application {@Afterstatic void log() {Logger.info("Action executed ...");}public static void index() {List<User> users = User.findAll();render(users);}...}

    5.5.3 @Catch

    如果有Action方法拋出了異常,那么使用@Catch注解的方法就會執行,且拋出的異常會以參數的形式傳遞到@Catch注解的方法中。

    具體實現如下:

    public class Admin extends Application {@Catch(IllegalStateException.class)public static void logIllegalState(Throwable throwable) {Logger.error("Illegal state %s…", throwable);}public static void index() {List<User> users = User.findAll();if (users.size() == 0) {throw new IllegalStateException("Invalid database - 0 users");}render(users);} }

    使用@Catch注解和普通的Java異常處理程序一樣,捕獲父類往往可以獲得更多的異常類型。

    如果擁有多個需要捕獲的方法,可以通過指定優先級來確定他們的執行順序。具體實現如下:

    public class Admin extends Application {@Catch(value = Throwable.class, priority = 1)public static void logThrowable(Throwable throwable) {// Custom error logging…Logger.error("EXCEPTION %s", throwable);}@Catch(value = IllegalStateException.class, priority = 2)public static void logIllegalState(Throwable throwable) {Logger.error("Illegal state %s…", throwable);}public static void index() {List<User> users = User.findAll();if(users.size() == 0) {throw new IllegalStateException("Invalid database - 0 users");}render(users);} }

    5.5.4 @Finally

    @Finally注解的方法總是在每個Action調用之后執行(無論Action是否成功執行):

    public class Admin extends Application {@Finallystatic void log() {Logger.info("Response contains : " + response.out);}public static void index() {List<User> users = User.findAll();render(users);}...}

    如果@Finally注解的方法中包含的參數是可拋出的異常,其方法中的內容還是可以繼續執行的,具體如下:

    public class Admin extends Application {@Finallystatic void log(Throwable e) {if( e == null ){Logger.info("action call was successful");} else{Logger.info("action call failed", e);}}public static void index() {List<User> users = User.findAll();render(users);}... }

    5.5.5 使用@with注解增加更多攔截器

    如果某個控制器是其他一些類的父類,那么該控制器中定義的所有攔截器會影響到所有子類。

    由于Java不允許多重繼承,對單純通過繼承來使用攔截器造成了一定的局限性。

    Play可以通過@With注解,調用其他控制器中已經定義好的攔截方法,從而突破這一局限。

    比如創建Secure控制器,定義checkAuthenticated()攔截方法驗證用戶合法性:

    public class Secure extends Controller {@Beforestatic void checkAuthenticated() {if(!session.containsKey("user")) {unAuthorized();}} }

    在其他的控制器中,可以通過@With(Secure.class)注解將其包含進來:

    @With(Secure.class) public class Admin extends Application {... }

    5.6 Session和Flash作用域

    在Play開發中,如果需要在HTTP請求之間保存數據,可以將數據保存在Session或者Flash內。

    保存在Session中的數據在整個用戶會話中都是有效的,而保存在Flash的數據只對下一次請求有效。

    特別需要注意的是,Session和Flash作用域中的數據都是采用Cookie機制添加到隨后的HTTP響應中的(并沒有存儲在服務器上的),

    所以數據大小非常有限(不能超過4K),而且只能存儲字符串類型的數據。

    由于Cookie是使用密鑰簽名過的,所以客戶端不能輕易修改Cookie的數據(否則會失效)。

    不要將Play的Session當作緩存來使用,如果需要在特定的會話中緩存一些數據,那么可以使用Play內置的緩存機制,

    并將session.getId()作為緩存的key進行儲存。

    public static void index() {List messages = Cache.get(session.getId() + "-messages", List.class);if(messages == null) { // 處理緩存失效messages = Message.findByUser(session.get("user"));Cache.set(session.getId() + "-messages", messages, "30mn");}render(messages); }

    Session在用戶關閉瀏覽器后就會失效,除非修改配置文件中的application.session.maxAge屬性。

    設置方法如下:

    application.session.maxAge=7d # Remember for one week.

    使用Play內置的Cache緩存時需要注意,Cache與傳統Servlet的HTTP Session對象是不同的。

    框架無法保證這些緩存對象會一直存在,所以在業務代碼中必須處理緩存失效的問題,以便保持應用完全無狀態化。

    6 play框架06

    ? play框架06–模板語法、模板繼承

    Play具有高效的模板體系,采用Groovy作為其表達式語言,允許動態生成HTML、XML、JSON或者任何基于文本格式的文檔,

    并且具有創建可重用標簽(tag)的功能。

    模板儲存在Play應用的app/views目錄下。

    6.1 模板語法

    與其他的語言一樣,Play的模板也具有嚴格定義的語法。模板語法被劃分為多種元素,用于完成不同類型的任務。

    Play模板的本質是普通的文本文件,其中帶有占位符的部分可以生成動態內容。

    模板的動態部分采用Groovy語言編寫,其語法與Java非常類似。

    框架可以將需要渲染的結果追加至HTTP響應的數據部分,并發送至模板。

    所有的動態部分將會在模板的執行期間被解析。

    ? (表1 模板語法)

    元素描述語法
    表達式用于輸出表達式的值。如 ${note.title} 的作用是將域對象note的屬性title的值輸出。${…}
    標簽用于調用Play框架內置的或是開發人員自定義的標簽。如#{get ‘title’ /}:獲取變量 title 的值,該值僅在模板頁面中有效。#{…}
    引用用于生成調用控制器中Action方法的URL,在頁面鏈接中使用的最為頻繁。@{…} 和 @@{…} 的區別在于生成的URL分別是相對路徑還是絕對路徑。如: 1.首頁:生成指向首頁的鏈接。 2.@{’/public/stylesheets/main.css’}:引入CSS靜態資源文件。@{…} 和@@{…}
    國際化用于顯示經過國際化處理后的消息內容。&{…}
    注釋用于在模板中添加注釋。如:{ 這是注釋 }。{…}
    腳本用于添加復雜的 Groovy 腳本,可以聲明變量和執行業務邏輯。%{…}%

    6.1.1 表達式(expression)😒{…}

    構建動態部分最簡單的方法就是聲明表達式。表達式需要以 ${ 開頭, 并以 } 結尾,作為占位符使用。

    具體例子如下:

    <h1>Client ${client.name}</h1>

    以上是輸出客戶姓名的表達式例子。在該例子中,將客戶類定義為Client。

    經過控制器中Action的業務邏輯執行,首先需要向模板注入對象client,之后就可以在模板中使用${…}表達式語法輸出client對象的name屬性。

    如果不能確定向模板注入的client對象是否為null,可以使用如下Groovy快捷語法:

    <h1>Client ${client?.name}</h1>

    此時,只有client不為null的情況下,才進行client.name的輸出。

    6.1.2 標簽(tag): #{tagName /}

    標簽是能夠附帶參數調用的模板片段,如果標簽只有一個參數,按照約定,參數的名稱為arg,并且該參數名是可以省略的。

    例如,可以使用#{script}標簽加載JavaScript文件:

    #{script 'jquery.js' /}

    Play模板中的標簽必須是閉合的,可以通過兩種方式閉合標簽。采用直接閉合的形式:

    #{script 'jquery.js'/}

    或者起始標簽和結束標簽成對地使用:

    #{script 'jquery.js'}#{/script}

    #{script}是Play模板的內置標簽,由框架實現,可以直接使用。后面會進一步介紹如何自定義標簽來滿足開發中一些特定的需求。

    #{list}標簽可以對集合類進行迭代操作,使用時需要注意,必須帶有兩個參數(items以及as):

    <h1>Client ${client.name}</h1> <ul>#{list items:client.accounts, as:'account' }<li>${account}</li>#{/list} </ul>

    上例中使用#{list}標簽對client.accounts集合進行迭代,并將集合中的每一條數據作為account在頁面中輸出。

    在應用中,模板引擎默認對所有的動態表達式進行轉義,以此來避免XSS的安全問題。

    如果模板中變量${title}的內容為

    Title

    ,在頁面輸出時會自動進行轉義:

    ${title} --> &lt;h1&gt;Title&lt;/h1&gt;

    也可以通過調用擴展方法raw(),以非轉義的形式在頁面中輸出,具體使用方法如下:

    ${title.raw()} --> <h1>Title</h1>

    如果需要顯示大量的非轉義HTML內容,可以使用#{verbatim /}標簽:

    #{verbatim}${title} --> <h1>Title</h1> #{/verbatim}

    6.1.3 引用(action)😡{…}或者@@{…}

    在前面的章節已經有過一些介紹,Play通過路由器可以(逆向)生成URL,匹配指定的路由。

    在模板中使用@{…}引用可以達到相同的目的:

    <h1>Client ${client.name}</h1> <p><a href="@{Clients.showAccounts(client.id)}">All accounts</a> </p> <hr /><a href="@{Clients.index()}">Back</a>

    該實例中,@{Clients.showAccounts(client.id)}調用了Clients控制器中的showAccounts Action方法,并傳遞了client.id參數。

    @@{…}引用的使用語法與@{…}相同,只不過生成的是絕對URL(尤其適用于郵箱)。

    6.1.4 國際化(messages):&{…}

    如果應用需要進行國際化操作,那么可以在模板中使用&{…}顯示國際化信息。

    需要進行國際化的應用首先需要在conf/messages文件中進行國際化定義:

    clientName=The client name is %s

    之后在模板中就可以通過&{…}顯示該國際化信息了:

    <h1>&{'clientName',client.name}</h1>

    6.1.5 注釋(comment):

    *{…}*使用*{…}*標記的內容會被模板引擎忽略,起到注釋作用:*{**** Display the user name ****}* <div class="name">${user.name} </div>

    6.1.6 腳本(script): %{…}%

    腳本是更加復雜的表達式集合,能夠聲明一些變量或者定義一些語句。

    Play的模板中使用%{…}%插入腳本:

    %{fullName = client.name.toUpperCase()+' '+client.forname; }%<h1>Client ${fullName}</h1>

    也可以直接使用out內置對象輸出動態內容:

    %{fullName = client.name.toUpperCase()+' '+client.forname;out.print('<h1>'+fullName+'</h1>'); }%

    在模板中還可以使用腳本編寫結構化語句,執行一些邏輯操作,比如迭代:

    <h1>Client ${client.name}</h1> <ul> %{for(account in client.accounts) { }%<li>${account}</li> %{} }% </ul>

    使用模板時切記:模板不適合處理復雜的業務邏輯,所以在模板中請盡量使用標簽,或者直接將處理交給控制器或模型對象。

    6.2 模板繼承

    Play提供繼承機制為多模板之間進行頁面布局或設計提供服務。

    #{extends /} 和 #{doLayout /}可以使頁面的共享和重用變得更加方便簡潔,

    同時也能夠幫助開發人員實現頁面中動態內容和靜態外觀裝飾的分離。

    在模板與裝飾之間可以使用#{get}和#{set}標簽進行參數傳遞。

    將simpledesign.html定義為父模板,也就是裝飾模板。

    可以將頁面中重用的靜態內容定義在裝飾模板中,如導航條、頁面頂端的圖片banner、頁面底端的footer說明等。

    <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head><title>#{get 'title' /}</title><link rel="stylesheet" type="text/css" href="@{'/public/stylesheets/main.css'}" /> </head><body><h1>#{get 'title' /}</h1>#{doLayout /}<div class="footer">Built with the play! framework</div> </body> </html>

    在simpledesign.html中將、

    、 中的內容定義為公用元素,

    所有繼承于該模板的頁面都會包含這些內容。其中#{doLayout /}標簽起到占位的作用,包含其子模板的頁面內容。

    其他所有繼承于simpledesign.html模板的頁面內容都將顯示在#{doLayout /}所占的頁面區塊。

    其他頁面使用#{extends}標簽可以非常簡單地植入該裝飾模板,

    具體使用方法如下:

    #{extends 'simpledesign.html' /}#{set title:'A decorated page' /} This content will be decorated.

    該子模板使用#{extends ‘simpledesign.html’ /}標簽來繼承simpledesign.html,

    使用#{set title:‘A decorated page’ /}標簽傳遞頁面的title變量,

    最后在頁面中輸出This content will be decorated。

    7 play框架07

    ? play框架07–域模型

    7.1 屬性模擬

    查看Play提供的示例應用,模型類里面會頻繁地使用聲明為public的變量。即使是經驗尚淺的Java開發者,也懂得慎用public類型的變量。

    在Java開發中(當然還有其他的面向對象語言),實踐經驗是這樣告訴我們的:將所有的成員變量聲明為私有,只提供獲取與修改的方法。

    這樣做的目的在于增強程序的封裝性,而“封裝”在面向對象設計中恰恰是非常關鍵的概念。

    Java沒有真正的內置屬性定義機制,而是使用Java Bean來進行約束:Java對象的屬性通過一對getXxx/setXxx的方法來修改,

    如果對象是只讀的那么只需要提供getXxx方法。

    在過去的開發中我們一直這樣做,但是編碼過程就顯得有些乏味了。

    每個屬性必須聲明為private,同時還有相應的getXxx/setXxx方法,

    而且大多數情況下,getXxx和setXxx方法的實現都是類似的。

    private String name; public String getName() {return name; } public void setName(String value) {name = value; }

    Play框架的模型部分會自動生成getXxx/setXxx方法,保持代碼的簡潔。

    也就是說,在Play中開發者可以直接把屬性變量聲明為public,運行時Play會自動生成相應的getXxx/setXxx方法(在這里我們將聲明為public的字段都視為屬性)。

    public class Product {public String name;public Integer price; }

    上述代碼被框架載入后就會轉換成如下形式:

    public class Product {public String name;public Integer price;public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getPrice() {return price;}public void setPrice(Integer price) {this.price = price;} }

    因為變量被聲明為public,可以使用如下方式對屬性進行操作:

    product.name = "My product"; product.price = 58;

    程序在加載時會自動地轉換成:

    product.setName("My product"); product.setPrice(58);

    注意:

    因為這些getXxx/setXxx方法是在運行時動態生成的,所以不能直接調用。

    如果在編碼階段使用他們,編譯器會因為找不到該方法而報錯誤。

    當然也可以自己定義相應的getXxx/setXxx方法,Play會優先選擇手動編寫的方法。

    如果需要保護Product類的price屬性就可以定義setPrice()方法:

    public class Product {public String name;public Integer price;public void setPrice(Integer price) {if (price < 0) {throw new IllegalArgumentException("Price can’t be negative!");}this.price = price;} }

    如果為Product類的price屬性賦負值就會拋出異常:

    product.price = -10: // Oops! IllegalArgumentException

    Play總會優先使用已經定義的getXxx/setXxx方法:

    @Entity public class Data extends Model {@Requiredpublic String value;public Integer anotherValue;public Integer getAnotherValue() {if(anotherValue == null) {return 0;}return anotherValue;}public void setAnotherValue(Integer value) {if(value == null) {this.anotherValue = null;} else {this.anotherValue = value * 2;}} public String toString() {return value + " - " + anotherValue;} }

    補充:

    @Entity注解的作用是通知Play自動開啟JPA實體管理器,@Required是對該屬性的約束。

    該類繼承于play.db.jpa.Model,Model提供了非常簡單的對象處理方式,在后面章節會做詳細介紹。

    針對以上例子可以進行如下測試斷言:

    Data data = new Data(); data.anotherValue = null; assert data.anotherValue == 0; data.anotherValue = 4 assert data.anotherValue == 8;

    以上的斷言都會執行通過,而且因為這種改進的類遵從JavaBean規范,可以滿足開發中的更復雜需求。

    7.2 數據庫配置

    通常情況下,開發者需要將模型對象持久化。最常用的方法是把這些數據保存到數據庫中。

    在Play應用的開發過程中,開發者可以迅速配置嵌入式內存數據庫或者直接將數據保存到文件系統中。

    開啟內存數據庫H2,只需要在conf/application.conf文件中進行如下配置:

    db=mem

    補充:

    H2是開放源代碼的Java數據庫,其具有標準的SQL語法和Java接口,可以自由使用和分發,且非常簡潔和快速。

    將數據保存在內存中相比從磁盤上訪問能夠極大地提高應用的性能,

    但由于內存容量的限制,內存數據庫適用于開發階段,或者原型示例開發。

    如果需要將數據保存在文件系統中,則使用如下配置:

    db=fs

    如果需要連接到MySQL服務器,則使用如下配置:

    db=mysql:user:pwd@database_name

    Play框架集成了H2數據庫和MySQL數據庫的驅動程序,存放在$PLAY_HOME/framework/lib/目錄下。

    如果需要使用PostgreSQL,Oracle或者其他數據庫,需要在該目錄(或者應用程序的lib/目錄)下添加相應的數據庫驅動。

    Play可以連接任何JDBC兼容的數據庫,只需要將相應的驅動類庫添加到/lib目錄中,并在conf/application.conf文件中定義JDBC配置:

    db.url=jdbc:mysql://localhost/test db.driver=com.mysql.jdbc.Driver db.user=root db.pass=123456

    還可以在conf/application.conf文件中用配置選項指定JPA方言:

    jpa.dialect=<dialect>

    補充:

    由于不同的數據庫產品支持不同的ANSI SQL標準,所以Hibernate必須要使用“方言”才能與各種數據庫成功的進行通信。

    在Play中,大多數情況下會自動根據配置信息識別特定數據庫方言,但是存在某些數據庫,Play無法判斷其使用的方言。

    這時就需要開發者顯式地在Play配置文件中指定。

    除了使用Hibernate外,在編碼時還可以直接從play.db.DB中獲得java.sql.Connection,然后使用標準SQL語句來執行數據庫操作。

    Connection conn = DB.getConnection(); conn.createStatement().execute("select * from products");

    7.3 數據持久化

    Play的持久層框架采用的是Hibernate,使用Hibernate(通過JPA)自動地將Java對象持久化到數據庫。

    當在任意的實體類上增加@javax.persistence.Entity注解后,Play會自動為其開啟JPA實體管理器。

    @Entity public class Product {public String name;public Integer price; }

    注意:

    Play應用開發者一開始可能經常會犯的錯誤是使用Hibernate的@Entity注解來取代JPA。

    這里請讀者注意,Play是直接調用JPA的API來使用Hibernate。

    也可以直接從play.db.jpa.JPA對象中得到實體管理器,通過實體管理器可以將Model持久化到數據庫或者執行HQL語句,

    例如:

    EntityManager em = JPA.em(); em.persist(product); em.createQuery("from Product where price > 50").getResultList();

    Play為JPA的使用提供了非常好的支持,只需要繼承Play提供的play.db.jpa.Model類:

    @Entity public class Product extends Model {public String name;public Integer price; }

    接著就可以執行Product實例中CRUD操作進行對象持久化:

    Product.find("price > ?", 50).fetch(); Product product = Product.findById(2L); product.save(); product.delete();

    補充:ActiveRecord模式

    ActiveRecord也屬于ORM層,由Rails最早提出,遵循標準的ORM模型:表映射到記錄,記錄映射到對象,字段映射到對象屬性。

    配合遵循的命名和配置慣例,能夠很大程度的快速實現模型的操作,而且簡潔易懂。

    Play也提倡使用ActiveRecord模式進行快速開發,其主要思想是:

  • 每一個數據庫表對應創建一個類,類的每一個對象實例對應于數據庫中表的一行記錄;通常表的每個字段在類中都有相應的Field;
  • ActiveRecord同時負責把自己持久化,在ActiveRecord中封裝了對數據庫的訪問,即CURD;
  • ActiveRecord是一種領域模型(Domain Model),封裝了部分業務邏輯。
  • 7.4 無狀態模型

    Play被設計成為“無共享”的架構,目的就是為了保持應用的完全無狀態化。

    這樣做的好處在于可以讓一個應用同一時刻在多個服務器節點上運行。

    Play為了保持模型無狀態化,需要避免一些常見的陷阱,最重要的就是不要因為多請求而將對象保存到Java堆中。

    Play應用中多請求之間保存數據有以下幾種解決方案:

    1.如果數據很小而且非常簡單,那么可以將其存儲在Session或者Flash作用域,

    但是這些作用域最大只允許存放4K的內容,并且存儲的數據只能為字符串類型。

    2.將數據保存到持久化存儲中(數據庫或者文件系統)。

    比如用戶創建了需要跨越多個請求的對象,就可以按照以下步驟對其進行操作:

    • 在第一次請求時初始化對象并將它保存到數據庫中。

    • 將創建的對象的id保存在Flash作用域中。

    • 在以后不停的請求鏈執行過程中,使用id從數據庫中獲取對象,更新并重新保存它。

      3.將數據保存在瞬時存儲中(比如Cache):

    • 在第一次請求時初始化對象并將它保存在緩存中。

    • 將創建的對象的id保存在Flash作用域中。

    • 在請求鏈的執行過程中,從Cache里獲取對象(通過保存在Flash作用域中的對象id),更新后并將它再次保存回Cache。

    • 當請求鏈結束后,將對象進行持久化操作(數據庫或者文件系統)。

    根據具體應用的需求,第三種解決方案使用緩存可以是一種非常好的選擇,也是Java Servlet Session的良好的替代方案。

    但緩存并不是可靠的數據存儲方式,因此如果選擇將對象保存到緩存中,就必須確保能夠將它重新讀取回來。

    8 play框架08

    ? play框架08–Job異步處理

    8.1 Job實現

    在Play中建立Job只需要繼承play.jobs.Job類:

    package jobs; import play.jobs.*; public class MyJob extends Job {public void doJob() {// 執行一些業務邏輯} }

    如果希望創建具有返回值的Job,那么需要覆蓋doJobWithResult()方法:

    package jobs; import play.jobs.*; public class MyJob extends Job<String> {public String doJobWithResult() {// 執行一些業務邏輯return result;} }

    上例自定義的Job覆蓋了doJobWithResult()方法,并且方法的返回類型為String,事實上Job可以返回任何類型的值。

    8.2 Bootstrap Job

    8.2.1 應用啟動

    Bootstrap Job,顧名思義就是在應用開始時運行的任務,

    只需要添加@OnApplicationStart注解就可以把當前Job設置為Bootstrap Job:

    import play.jobs.*;@OnApplicationStart public class Bootstrap extends Job {public void doJob() {if(Page.count() == 0) {new Page("root").save();Logger.info("A root page has been created.");}}}

    需要注意的是,Bootstrap Job不需要任何返回值。

    如果有多個帶有@OnApplicationStart注解的Bootstrap Job,那么默認情況下這些Job會按照定義的先后順序執行。

    當所有的Bootstrap Job執行完成之后,Web應用就處于等待階段,等待處理那些即將到來的請求。

    如果希望Web應用啟動后,能夠在執行Bootstrap Job的同時,又能很快地處理到來的請求,

    可以為@OnApplicationStart注解添加async=true屬性:@OnApplicationStart(async=true)。

    這樣應用程序開啟后,Bootstrap Job就會作為后臺程序異步執行了。

    不僅如此,所有的異步Job(async=true)也會在Web應用開啟之后同時運行。

    注意:

    Play具有兩種不同的工作模式:開發模式(DEV)和產品模式(PROD),因此Job的啟動時間也有略微差異。

    在DEV模式下,直到第一個HTTP請求到達時才會開啟應用,且不會預先編譯Java文件。

    如果在該模式下更改Java源文件可以立即生效,刷新瀏覽器即可查看修改后的結果。

    此外,應用還會在需要的時候自動重啟;而在PROD模式下,應用會在服務器啟動時同步開啟,一旦應用開啟就會自動編譯所有的Java文件,之后不再重載任何文件(包括模板文件和配置文件),所以如果有文件修改必須重啟應用才能生效。

    所以DEV模式下Job會延遲啟動。

    8.2.2 應用停止

    Web應用停止或關閉的時候,常常也需要進行一些額外的操作,如進行數據的清理、日志的打印等。

    如果開發者需要這類任務調度操作,可以使用Play提供的@OnApplicationStop注解。

    import play.jobs.*;@OnApplicationStop public class Bootstrap extends Job {public void doJob() {Fixture.deleteAll();} }

    用法非常簡單,繼承Job類之后,重寫doJob()方法即可。

    8.3 Scheduled Job

    Scheduled Job是指可以被框架周期性執行的任務,可以使用@Every注解指定時間間隔控制Scheduled Job運行,

    例如:

    import play.jobs.*;@Every("1h") public class Bootstrap extends Job {public void doJob() {List<User> newUsers = User.find("newAccount = true").fetch();for(User user : newUsers) {Notifier.sayWelcome(user);}}}

    在實際開發中,@Every注解并不能夠完全滿足開發需求,比如有時候需要指定Scheduled Job在具體的某個時間點執行。

    這時候可以使用@On注解指定時間點來執行Job,例如:

    import play.jobs.*;/** Fire at 12pm (noon) every day **/ @On("0 0 12 * * ?") public class Bootstrap extends Job {public void doJob() {Logger.info("Maintenance job ...");...}}

    與Bootstrap Job一樣,Scheduled Job也是不需要任何返回值的,即使返回了也會丟失。

    補充:

    @On標簽中使用的是Quartz庫的CRON表達式。CRON表達式是由7個子表達式組成的字符串,每個子表達式都描述了單獨的日程細節。

    這些子表達式用空格分隔,分別表示:

    Seconds 秒 Minutes 分鐘 Hours 小時 Day-of-Month 一個月中的某一天 Month 月 Day-of-Week 一周中的某一天 Year 年(可選) 具體CRON表達式的例子:"0 0 12 ? * WED",表示“每周三的中午12:00”。

    8.4 Job的直接調用

    Play的Job除了被框架自動調用外,也可以通過now()方法手動調用Job對象的實例,隨時觸發Job來執行指定任務。

    使用now()方法調用Job后,任務會立即執行:

    public static void encodeVideo(Long videoId) {new VideoEncoder(videoId).now();renderText("Encoding started"); }

    now()方法的返回值是Promise對象,通過該值可以在結束后獲得Job的執行結果。

    9 play框架的請求處理流程

    ? Play框架的請求處理流程

    Play框架使用事件驅動模型,以提供在不可預知的使用環境下的靈活的處理能力。

    在一個web應用中,事件主要指用戶向服務器發起一次HTTP請求。對于Play框架,此類事件定義在routes文件中,play根據routes文件的內容以及用戶的請求,確定應該調用哪些過程。Play框架使用了Netty服務器,該服務器使用管道(pipeline),提供了在高并發情況下的優秀的異步處理能力。

    當服務器接收到一個用戶請求的時候,將獲取一個管道,將請求相關信息傳入,之后交由Play框架處理。Play框架會根據該請求的內容,查找相應的路由規則,根據路由規則調用相應的事件處理流程,并(一般來說會)最終向用戶返回結果,完成一次事件處理:

    ? 圖1. 事件流向

    用戶請求處理流程相關類

    作為一個web應用框架,Play框架的最基本的功能就是響應用戶請求。

    在本小節中,將概要講述當一個用戶請求(request)到來時,play將啟動怎樣的流程來對該請求進行處理,并最終返回相應給用戶。

    本小節的重點在于闡明流程,由于這一流程涉及到M-V-C三個方面,對于其具體實現細節,將在后文敘述。

    相關類介紹

    在介紹處理流程之前,需要介紹在這里流程中涉及到的一些類。本小節將重點介紹這些類的類結構,以及它們在這個流程中發揮的主要作用。

    9.1 PlayHandler

    PlayHandler繼承了org.jboss.netty.channel.SimpleChannelUpstreamHandler,用于在管道中處理服務器監聽到的用戶請求。

    類圖如下:

    圖2 PlayHandler類圖

    其中比較重要的就是

    messageReceived(final ChannelHandlerContext ctx,final MessageEvent messageEvent)

    方法。該方法為對父類同名函數的重寫(Override)。父類提供這個函數,由子類提供各自的具體實現。

    當有消息到來的時候,服務器調用handler類的messageReceived函數,利用多態,將執行不同的實現。

    在HttpServerPipelineFactory.getPipeline()中,當每次需要獲得pipeline時,新建一個PlayHandler的實例,注冊到pipeline中。

    因此,每次的請求都會對應一個新的PlayHandler實例,接著PlayHandler.messageReceiveed()方法被調用以處理用戶請求。PlayHandler.messageReceived()方法執行過程將在后文敘述。

    9.2 Invoker與Invocation

    Play框架采用了命令模式(Command pattern),用于在多線程多任務情況下調度任務,

    命令模式的原型如下:

    ? 圖3 命令模式原型

    在命令模式中,Client調用Invoker,往其中添加命令。

    Command代表了一個命令,開發者繼承Command并實現一個具體的命令,提交給Invoker進行執行。

    Invoker負責管理這些命令,在合適的時候執行它們,并提供處理結果。

    命令模式把請求一個操作的對象與知道怎么執行一個操作的對象分割開

    如此一來,開發者只需要關注命令的實現,而不需要關注何時、如何執行該命令。

    在Play框架中,Invoker及其內部類Invocation實現了命令模式,Invocation相當于Command類,

    由子類實現其execute方法(在這里表現為run()方法)。它們的類圖如下:

    圖4 Invoker類圖

    圖5 Invocation及DirectInvocation類圖

    在Invoker中,主要是invoke方法與invokeInThread方法。

    前者使用(ScheduledThreadPoolExecutor) executor調度線程執行Invocation;

    后者直接在當前線程中執行任務,當執行不成功時,等待一段時間之后重新執行。

    Invoke方法還提供了另一個版本的重載函數,可以在等待一段時間之后再執行當前任務:invoke(final Invocation invocation, longmillis)。

    關于java.util.concurrent.ScheduledThreadPoolExecutor:繼承自java.util.concurrent.ThreadPoolExecutor,用于在給定的延遲后執行命令,或者定期執行命令,當需要多個輔助線程,或者要求ThreadPoolExecutor具有額外的靈活性或功能時,此類要優于Timer。

    Invoker.invoke(Invocation)的代碼如下,可以看到executor是如何調度Invocation的:

    publicstatic Future<?> invoke(final Invocation invocation) {Monitor monitor = MonitorFactory.getMonitor("Invokerqueue size", "elmts.");monitor.add(executor.getQueue().size());invocation.waitInQueue = MonitorFactory.start("Waiting forexecution");returnexecutor.submit(invocation);} publicvoid run() {if (waitInQueue != null) {waitInQueue.stop();}try {preInit();if (init()) {before();execute();after();onSuccess();}} catch (Suspend e) {suspend(e);after();} catch (Throwable e) {onException(e);} finally {_finally();}}

    在Invocation中實現了模板方法模式(Template method pattern),定義了run()方法的執行步驟,

    而將各個步驟的實現方式交由其子類實現,以此方式來完成命令模式中的自定義命令。

    模板方法模式原型如下:

    圖3-6 模板方法模式原型

    在Play框架的模板方法模式具體實現中,Invocation實現了Runnable接口,實現了run()方法,

    代碼如下:

    run()方法即為模板方法,在run()方法中,按順序調用了init(),before(),execute(),after(),onSuccess()等方法,

    這些方法需要由Invocation的子類實現,從而實現不同的功能。通過這樣的設計,將執行過程分解為多個部分,

    每個部分由子類實現,而各部分的執行順序由父類規定。

    在下文中,將提到PlayHandler.NettyInvocation,即為Invocation的一個子類。

    9.3 Result類

    Result繼承自RuntimeException,封裝了視圖層渲染結果(可能是HTML文件,也可能是XML文件或者二進制文件等),

    類繼承關系如下:

    圖3-7 Result類繼承結構圖

    FastRunTimeException是在Play框架中定義的一個可以快速實例化并拋異常類(Exception),

    主要是修改了fillInStackTrace()方法,使其直接返回null,從而實現快速實例化:

    public Throwable fillInStackTrace() {returnnull;}

    在Result中,提供了apply()方法,該方法需要其子類重寫,實現將相應的內容輸出的功能。

    在Result之下,play提供了多個Result的子類,用于執行對不同的相應的操作,其中比較常見的是RenderTemplate類,實現了對模板文件進行最后輸出的功能。

    將Result作為一個Exception并以try/catch的形式來捕獲Result,而不是用返回值的方式,這是Play框架比較奇特的一點。

    這不符合通常對“異常(Exception)”的看法——一般來說,只有程序出現不可預知的情況的時候,才會使用try/catch代碼塊來捕獲Exception。然而,將渲染結果當做異常拋出并捕捉,將簡化代碼,由Java自身決定執行過程,提高了開發者的開發效率;

    另外,在處理用戶請求過程中,多處地方可能直接返回結果(如直接返回404頁面),

    框架在處理過程中將所有的處理結果歸到同一個地方并統一作判斷和處理,

    如果將結果作為返回值,則需要更繁復的方法來對結果進行收集和處理。

    9.4 Template類

    Template類提供了對模板文件的封裝,并實現了對模板文件的編譯與執行。

    類繼承關系如下:

    圖3-8 Template類繼承結構圖

    最終實際使用的是GroovyTemplate類,該類代表一個模板文件,

    提供對頁面進行渲染(將template文件轉換為html)的方法(render(Map<String, Object>))。

    在前面討論的RenderTemplate中,在構造函數中傳入一個(Template)template(即一個RenderTemplate“擁有”一個Template實例),

    并執行

    this.content = template.render(args);

    調用Template.render(args)將渲染結果保存在content中。

    在一個請求處理流程中,依次會經歷PlayHandler->Invoker->Invocation->Result->Template

    10 play框架的攔截器

    ? Play框架的攔截器

    在控制器里,定義攔截器方法。攔截器將被控制器類及后代的所有action調用。

    這些方法必須是static的,但不能是public的,并使用有效的攔截注釋。

    @Before

    用@Before注釋的方法將在控制器的每個action前被調用執行

    public class weixinIntercept extends Controller{@Before(unless="login") //""中寫的是方法的具體位置,例:wechat.account.wechatInformationstatic void check(){if(session.get("user") == null)login();} } /*可以使用unless和only,還可以使用@After,@Before,@Finally注釋 */

    控制器繼承

    如果一個控制器類是其他控制器類的子類,那么會按照繼承順序應用于相應的子類。

    使用@With注釋添加更多的攔截器

    public class Security extends Controller{@Beforeprotected static void checkAuthentic(){if(!session.containsKey("user"){unAuthen();}} }//另一個控制器 @With(Security.class) public class Admin extends Controller{...... }

    11 play源碼分析

    ==Play源代碼分析1—Server啟動過程==

    ? Play是個Rails風格的Java Web框架,需要了解背景請看:

  • Play Framework介紹1–主要概念
  • Play Framework介紹2—Helloworld
  • 如何調試請看此處。以下進入正題_

    Server啟動過程主要涉及三個地方:

  • play.Play類:代表Play本身業務模型。
  • play.server.Server類:負責服務器啟動。
  • play.classloading包:負責.java文件讀取、編譯和加載。
  • 11.1總體流程

    Server.main為入口方法:

    public static void main(String[] args) throws Exception {Play.init(root, System.getProperty("play.id", ""));if (System.getProperty("precompile") == null) {new Server();} else {Logger.info("Done.");} }

    做兩件事:

  • Play.init
  • 然后創建Server對象。
  • Play.init

    public static void init(File root, String id) {readConfiguration();Play.classes = new ApplicationClasses();// Build basic java source pathVirtualFile appRoot = VirtualFile.open(applicationPath);roots.add(appRoot);javaPath = new ArrayList<VirtualFile>(2);javaPath.add(appRoot.child("app"));javaPath.add(appRoot.child("conf"));// Build basic templates pathtemplatesPath = new ArrayList<VirtualFile>(2);templatesPath.add(appRoot.child("app/views"));// Main route fileroutes = appRoot.child("conf/routes");// Load modulesloadModules();// Enable a first classloaderclassloader = new ApplicationClassloader();// PluginsloadPlugins();// Done !if (mode == Mode.PROD ||preCompile() ) {start();}}

    主要做:

  • 加載配置
  • new ApplicationClasses();加載app、views和conf路徑到VirtualFile中,VirtualFile是Play內部的統一文件訪問接口,方便后續讀取文件
  • 加載route
  • 加載Module,Play的應用擴展組件。
  • 加載Plugin,Play框架自身的擴展組件。
  • 工作在產品模式則啟動Play.
  • 關鍵步驟為new ApplicationClasses(),執行computeCodeHashe(),后者觸發目錄掃描,搜索.java文件。

    相關過程簡化代碼如下:

    public ApplicationClassloader() { super(ApplicationClassloader.class.getClassLoader()); // Clean the existing classes for (ApplicationClass applicationClass : Play.classes.all()) {applicationClass.uncompile(); } pathHash = computePathHash();}int computePathHash() { StringBuffer buf = new StringBuffer(); for (VirtualFile virtualFile : Play.javaPath) {scan(buf, virtualFile); } return buf.toString().hashCode(); }void scan(StringBuffer buf, VirtualFile current) {if (!current.isDirectory()) {if (current.getName().endsWith(".java")) {Matcher matcher = Pattern.compile("\\s+class\\s([a-zA-Z0-9_]+)\\s+").matcher(current.contentAsString());buf.append(current.getName());buf.append("(");while (matcher.find()) {buf.append(matcher.group(1));buf.append(",");}buf.append(")");}} else if (!current.getName().startsWith(".")) {for (VirtualFile virtualFile : current.list()) {scan(buf, virtualFile);}} }

    11.2 Start流程

    簡化代碼如下:

    public static synchronized void start() { try { ... // Reload configuration readConfiguration();...// Try to load all classes Play.classloader.getAllClasses();// Routes Router.detectChanges(ctxPath);// Cache Cache.init();// Plugins for (PlayPlugin plugin : plugins) {try {plugin.onApplicationStart();} catch(Exception e) {if(Play.mode.isProd()) {Logger.error(e, "Can't start in PROD mode with errors");}if(e instanceof RuntimeException) {throw (RuntimeException)e;}throw new UnexpectedException(e);} }...// Plugins for (PlayPlugin plugin : plugins) {plugin.afterApplicationStart(); }} catch (PlayException e) {started = false;throw e; } catch (Exception e) {started = false;throw new UnexpectedException(e); } }

    關鍵步驟為執行Play.classloader.getAllClasses()加載app目錄中的類型。

    簡化代碼如下:

    public List<Class> getAllClasses() { if (allClasses == null) { allClasses = new ArrayList<Class>();if (Play.usePrecompiled) { ... } else { List<ApplicationClass> all = new ArrayList<ApplicationClass>();// Let's plugins play for (PlayPlugin plugin : Play.plugins) { plugin.compileAll(all); }for (VirtualFile virtualFile : Play.javaPath) { all.addAll(getAllClasses(virtualFile)); } List<String> classNames = new ArrayList<String>(); for (int i = 0; i < all.size(); i++) { if (all.get(i) != null && !all.get(i).compiled) { classNames.add(all.get(i).name); } }Play.classes.compiler.compile(classNames.toArray(new String[classNames.size()]));for (ApplicationClass applicationClass : Play.classes.all()) { Class clazz = loadApplicationClass(applicationClass.name); if (clazz != null) { allClasses.add(clazz); } } ... } } return allClasses; }

    主要步驟:

  • plugin.compileAll,給所有plugin一次機會進行自定義編譯。
  • Play.classes.compiler.compile(classNames.toArray(new String[classNames.size()]));編譯所有.java文件。編譯后的.class存儲在ApplicationClass中。內部使用了eclipse的JDT編譯器。
  • loadApplicationClass,取出ApplicationClass中的.class加入List中返回。
  • **到此完成.java的加載。**相關對象關系如下圖:

    11.3 啟動HTTP服務

    ? 接著new Server()啟動HTTP服務,監聽請求

    簡化代碼如下:

    public Server() { ... if (httpPort == -1 && httpsPort == -1) {httpPort = 9000; } ... InetAddress address = null; try {if (p.getProperty("http.address") != null) {address = InetAddress.getByName(p.getProperty("http.address"));} else if (System.getProperties().containsKey("http.address")) {address = InetAddress.getByName(System.getProperty("http.address"));}} catch (Exception e) {Logger.error(e, "Could not understand http.address");System.exit(-1); }ServerBootstrap bootstrap = new ServerBootstrap(new NioServerSocketChannelFactory(Executors.newCachedThreadPool(), Executors.newCachedThreadPool()) ); try { if (httpPort != -1) {bootstrap.setPipelineFactory(new HttpServerPipelineFactory());bootstrap.bind(new InetSocketAddress(address, httpPort));bootstrap.setOption("child.tcpNoDelay", true);if (Play.mode == Mode.DEV) {if (address == null) {Logger.info("Listening for HTTP on port %s (Waiting a first request to start) ...", httpPort);} else {Logger.info("Listening for HTTP at %2$s:%1$s (Waiting a first request to start) ...", httpPort, address);} } else {if (address == null) {Logger.info("Listening for HTTP on port %s ...", httpPort);} else {Logger.info("Listening for HTTP at %2$s:%1$s ...", httpPort, address);} }}} catch (ChannelException e) {Logger.error("Could not bind on port " + httpPort, e);System.exit(-1); } ... }

    主要步驟:

  • 設置端口,地址
  • new ServerBootstrap,創建jboss netty服務器。Play1.1.1使用了netty作為底層通訊服務器。
  • new HttpServerPipelineFactory(),設置netty所需的請求處理管道工廠。它負責當請求到達時提供處理者。
  • bootstrap.bind(new InetSocketAddress(address, httpPort),綁定地址,端口。
  • 總結

    以上是生活随笔為你收集整理的26Play框架教程2学习笔记的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

    午夜成人1000部免费视频 | 麻豆国产人妻欲求不满谁演的 | 国产亚洲人成在线播放 | 妺妺窝人体色www婷婷 | 少妇厨房愉情理9仑片视频 | 97精品国产97久久久久久免费 | 高清无码午夜福利视频 | 国产精品亚洲а∨无码播放麻豆 | 色偷偷人人澡人人爽人人模 | 麻豆国产人妻欲求不满 | 中文字幕av无码一区二区三区电影 | 小泽玛莉亚一区二区视频在线 | 丰满人妻被黑人猛烈进入 | 理论片87福利理论电影 | 日日碰狠狠丁香久燥 | 中文字幕无码人妻少妇免费 | 国产欧美精品一区二区三区 | 国产又爽又猛又粗的视频a片 | 男女作爱免费网站 | 欧美一区二区三区 | 无码午夜成人1000部免费视频 | 蜜臀aⅴ国产精品久久久国产老师 | 国产sm调教视频在线观看 | 领导边摸边吃奶边做爽在线观看 | 亚洲国产高清在线观看视频 | 成人免费视频视频在线观看 免费 | 色婷婷欧美在线播放内射 | 色综合久久久无码网中文 | 欧美35页视频在线观看 | 亚洲啪av永久无码精品放毛片 | 中文字幕无线码 | 中文字幕+乱码+中文字幕一区 | 丰满少妇熟乱xxxxx视频 | 天堂а√在线地址中文在线 | 亚洲日本一区二区三区在线 | 六十路熟妇乱子伦 | 国产内射老熟女aaaa | 少妇的肉体aa片免费 | 最近免费中文字幕中文高清百度 | 欧美 日韩 亚洲 在线 | 亚洲综合无码一区二区三区 | 国产成人精品久久亚洲高清不卡 | 999久久久国产精品消防器材 | 丰满少妇人妻久久久久久 | 亚洲精品中文字幕乱码 | 自拍偷自拍亚洲精品10p | 白嫩日本少妇做爰 | 亚洲人成网站在线播放942 | 2020最新国产自产精品 | 欧美国产日韩亚洲中文 | 亚洲人交乣女bbw | 精品久久久久久人妻无码中文字幕 | 丰满人妻一区二区三区免费视频 | 亚洲中文字幕在线无码一区二区 | 精品一区二区不卡无码av | 天堂а√在线中文在线 | 福利一区二区三区视频在线观看 | 日本成熟视频免费视频 | 国产99久久精品一区二区 | 无码福利日韩神码福利片 | а天堂中文在线官网 | 国产香蕉97碰碰久久人人 | 日日躁夜夜躁狠狠躁 | 国产一区二区三区四区五区加勒比 | 国语精品一区二区三区 | 97人妻精品一区二区三区 | 亚洲综合久久一区二区 | 精品无码一区二区三区爱欲 | 久久久精品国产sm最大网站 | 女人和拘做爰正片视频 | 久久精品人人做人人综合 | 熟女少妇人妻中文字幕 | 永久免费观看国产裸体美女 | 在线 国产 欧美 亚洲 天堂 | 熟女少妇在线视频播放 | 国内少妇偷人精品视频 | 精品国产青草久久久久福利 | 国产乱人偷精品人妻a片 | 亚洲日韩av一区二区三区四区 | 最新版天堂资源中文官网 | 亚拍精品一区二区三区探花 | 亚洲欧美日韩国产精品一区二区 | 在线播放亚洲第一字幕 | 99在线 | 亚洲 | 丰满肥臀大屁股熟妇激情视频 | 亚洲乱亚洲乱妇50p | 又色又爽又黄的美女裸体网站 | 国精产品一品二品国精品69xx | 国产一区二区三区四区五区加勒比 | 男女猛烈xx00免费视频试看 | 国产sm调教视频在线观看 | 欧美老妇交乱视频在线观看 | 一个人看的www免费视频在线观看 | 4hu四虎永久在线观看 | 人人爽人人爽人人片av亚洲 | 丰满人妻精品国产99aⅴ | 亚洲精品一区二区三区在线观看 | 帮老师解开蕾丝奶罩吸乳网站 | 久久99久久99精品中文字幕 | 丰满肥臀大屁股熟妇激情视频 | 免费无码av一区二区 | 亚洲热妇无码av在线播放 | 男人和女人高潮免费网站 | 性欧美牲交xxxxx视频 | 久久99热只有频精品8 | 中文字幕无码免费久久99 | 偷窥日本少妇撒尿chinese | 国产成人精品三级麻豆 | 免费网站看v片在线18禁无码 | 理论片87福利理论电影 | 日韩少妇内射免费播放 | 色婷婷av一区二区三区之红樱桃 | 无码吃奶揉捏奶头高潮视频 | 又大又黄又粗又爽的免费视频 | 精品成在人线av无码免费看 | 99精品无人区乱码1区2区3区 | 无码av最新清无码专区吞精 | 欧美激情一区二区三区成人 | 欧美猛少妇色xxxxx | 日韩精品a片一区二区三区妖精 | 午夜无码人妻av大片色欲 | 无码精品人妻一区二区三区av | 免费中文字幕日韩欧美 | 老熟妇乱子伦牲交视频 | 人人超人人超碰超国产 | 亚洲热妇无码av在线播放 | 久久综合给合久久狠狠狠97色 | 少妇人妻av毛片在线看 | 俄罗斯老熟妇色xxxx | 久久久精品456亚洲影院 | 成人欧美一区二区三区 | 狂野欧美性猛xxxx乱大交 | 男女猛烈xx00免费视频试看 | 亚洲人成人无码网www国产 | 色爱情人网站 | 2020久久超碰国产精品最新 | 高潮喷水的毛片 | 桃花色综合影院 | 国精品人妻无码一区二区三区蜜柚 | 乱人伦人妻中文字幕无码 | 色婷婷综合中文久久一本 | 搡女人真爽免费视频大全 | 18无码粉嫩小泬无套在线观看 | 国产精品久久国产三级国 | 激情爆乳一区二区三区 | 久久精品人妻少妇一区二区三区 | 久久久久se色偷偷亚洲精品av | 中文字幕亚洲情99在线 | 2020久久超碰国产精品最新 | 男人的天堂av网站 | 水蜜桃亚洲一二三四在线 | 国产精品久久久久9999小说 | 一本无码人妻在中文字幕免费 | 高潮喷水的毛片 | 国产另类ts人妖一区二区 | 欧美成人午夜精品久久久 | 久久久成人毛片无码 | 少妇的肉体aa片免费 | 无码毛片视频一区二区本码 | 小鲜肉自慰网站xnxx | 中文字幕无码av波多野吉衣 | 国内综合精品午夜久久资源 | 动漫av网站免费观看 | 日日橹狠狠爱欧美视频 | 日日鲁鲁鲁夜夜爽爽狠狠 | 一本久道久久综合婷婷五月 | 亚洲综合伊人久久大杳蕉 | 午夜理论片yy44880影院 | 伊人久久婷婷五月综合97色 | 国产午夜亚洲精品不卡下载 | 欧美高清在线精品一区 | 无码av岛国片在线播放 | 无码一区二区三区在线观看 | 亚洲区欧美区综合区自拍区 | 老太婆性杂交欧美肥老太 | 青青青爽视频在线观看 | 色综合久久中文娱乐网 | 国产av剧情md精品麻豆 | 少妇性荡欲午夜性开放视频剧场 | 亚洲乱码日产精品bd | 久久无码专区国产精品s | 99久久人妻精品免费一区 | 黑人大群体交免费视频 | 亚洲熟妇色xxxxx欧美老妇y | 亚洲大尺度无码无码专区 | 99久久亚洲精品无码毛片 | 在线精品国产一区二区三区 | 成人免费无码大片a毛片 | 日韩亚洲欧美中文高清在线 | 亚洲精品一区二区三区婷婷月 | 国产亚洲人成在线播放 | 久久天天躁夜夜躁狠狠 | 扒开双腿疯狂进出爽爽爽视频 | 97无码免费人妻超级碰碰夜夜 | 中文字幕乱妇无码av在线 | 久久人人爽人人爽人人片av高清 | 黑人粗大猛烈进出高潮视频 | 亚洲精品午夜国产va久久成人 | 亚洲成色www久久网站 | а√资源新版在线天堂 | 少妇人妻av毛片在线看 | 中文字幕乱码亚洲无线三区 | 日本www一道久久久免费榴莲 | 欧美人与牲动交xxxx | 亚洲 激情 小说 另类 欧美 | 亚洲精品午夜无码电影网 | 乱人伦人妻中文字幕无码久久网 | 三上悠亚人妻中文字幕在线 | 国产人妖乱国产精品人妖 | 九月婷婷人人澡人人添人人爽 | 国产午夜精品一区二区三区嫩草 | 天天摸天天碰天天添 | 精品无码一区二区三区爱欲 | 好屌草这里只有精品 | 天下第一社区视频www日本 | 日韩人妻少妇一区二区三区 | 亚洲国产精品毛片av不卡在线 | 国产精品久久久av久久久 | 国产日产欧产精品精品app | 国语自产偷拍精品视频偷 | 国产办公室秘书无码精品99 | 国产精品丝袜黑色高跟鞋 | 亚洲精品久久久久中文第一幕 | 人妻aⅴ无码一区二区三区 | 久久 国产 尿 小便 嘘嘘 | 亚洲欧美色中文字幕在线 | 欧洲精品码一区二区三区免费看 | 日本丰满护士爆乳xxxx | 亚洲 激情 小说 另类 欧美 | 丰满少妇人妻久久久久久 | 色狠狠av一区二区三区 | 18禁黄网站男男禁片免费观看 | 正在播放东北夫妻内射 | 欧美日韩视频无码一区二区三 | 久久99精品国产麻豆 | 无套内射视频囯产 | 无码人妻精品一区二区三区下载 | 亚洲春色在线视频 | 无码成人精品区在线观看 | 熟妇人妻无乱码中文字幕 | 无码国产激情在线观看 | 国产成人无码av片在线观看不卡 | 亚洲精品国产精品乱码不卡 | 大地资源网第二页免费观看 | 成人无码视频在线观看网站 | 亚洲精品一区三区三区在线观看 | 伊人色综合久久天天小片 | 亚洲毛片av日韩av无码 | www成人国产高清内射 | 5858s亚洲色大成网站www | 亚洲色偷偷偷综合网 | 无码纯肉视频在线观看 | 狠狠躁日日躁夜夜躁2020 | 曰韩无码二三区中文字幕 | 中文字幕人妻无码一区二区三区 | 久久zyz资源站无码中文动漫 | 国产免费观看黄av片 | 成年美女黄网站色大免费视频 | 天天av天天av天天透 | 国产无套粉嫩白浆在线 | 特黄特色大片免费播放器图片 | 国产亚洲精品久久久久久久久动漫 | 在线播放无码字幕亚洲 | 天天av天天av天天透 | 午夜性刺激在线视频免费 | 真人与拘做受免费视频一 | 精品久久久无码中文字幕 | 美女黄网站人色视频免费国产 | 台湾无码一区二区 | 日本高清一区免费中文视频 | 日本精品久久久久中文字幕 | 97精品国产97久久久久久免费 | 少女韩国电视剧在线观看完整 | 天天做天天爱天天爽综合网 | 未满成年国产在线观看 | 伊人久久婷婷五月综合97色 | 天海翼激烈高潮到腰振不止 | 亚洲欧美色中文字幕在线 | 久久99热只有频精品8 | 国产激情无码一区二区app | 最近免费中文字幕中文高清百度 | 久久aⅴ免费观看 | 夜夜影院未满十八勿进 | 人妻少妇精品无码专区动漫 | 激情内射日本一区二区三区 | 日韩精品无码一本二本三本色 | 97精品人妻一区二区三区香蕉 | 亚洲一区二区三区国产精华液 | 女人色极品影院 | 午夜精品久久久内射近拍高清 | 亚洲中文字幕乱码av波多ji | 欧美性黑人极品hd | 九九久久精品国产免费看小说 | 无码国内精品人妻少妇 | 狠狠噜狠狠狠狠丁香五月 | 国产精品资源一区二区 | 无码av免费一区二区三区试看 | 亚洲一区二区三区在线观看网站 | 国产成人无码午夜视频在线观看 | 国产美女极度色诱视频www | 国产精品久久久久7777 | 丰满人妻一区二区三区免费视频 | 亚洲人成人无码网www国产 | 日产精品高潮呻吟av久久 | 成人精品视频一区二区 | 久久精品人人做人人综合试看 | 成人aaa片一区国产精品 | 国产suv精品一区二区五 | 亚洲中文无码av永久不收费 | 色欲久久久天天天综合网精品 | 国产麻豆精品一区二区三区v视界 | 亚洲国精产品一二二线 | 色婷婷av一区二区三区之红樱桃 | 少妇高潮喷潮久久久影院 | 亚洲熟妇色xxxxx欧美老妇y | 精品一区二区不卡无码av | 男人的天堂2018无码 | 好男人www社区 | 中文毛片无遮挡高清免费 | 成人片黄网站色大片免费观看 | 无遮挡国产高潮视频免费观看 | 人人爽人人爽人人片av亚洲 | 天海翼激烈高潮到腰振不止 | 亚洲色www成人永久网址 | 国产激情艳情在线看视频 | 欧美丰满熟妇xxxx | 日韩欧美中文字幕在线三区 | 亚洲国产精品久久人人爱 | 好爽又高潮了毛片免费下载 | 麻豆国产人妻欲求不满 | 国产suv精品一区二区五 | 九九综合va免费看 | 国产人妖乱国产精品人妖 | 少妇的肉体aa片免费 | 男女下面进入的视频免费午夜 | 大胆欧美熟妇xx | 日韩欧美中文字幕在线三区 | 精品成在人线av无码免费看 | 无码帝国www无码专区色综合 | 婷婷色婷婷开心五月四房播播 | 97夜夜澡人人双人人人喊 | 亚洲人成影院在线无码按摩店 | 一本色道久久综合狠狠躁 | 六月丁香婷婷色狠狠久久 | 人妻尝试又大又粗久久 | 国产乱人无码伦av在线a | 国产又粗又硬又大爽黄老大爷视 | 日本一区二区三区免费高清 | 精品无人区无码乱码毛片国产 | 人人澡人摸人人添 | 伦伦影院午夜理论片 | www国产亚洲精品久久久日本 | 天天做天天爱天天爽综合网 | 国产suv精品一区二区五 | 亚洲熟熟妇xxxx | 亚洲国产欧美日韩精品一区二区三区 | 国产在线精品一区二区三区直播 | 中文字幕无码日韩专区 | 欧美丰满熟妇xxxx | 日日噜噜噜噜夜夜爽亚洲精品 | 欧美国产日产一区二区 | 国产av一区二区精品久久凹凸 | 色五月五月丁香亚洲综合网 | 熟妇女人妻丰满少妇中文字幕 | 精品少妇爆乳无码av无码专区 | 1000部夫妻午夜免费 | 国产精品多人p群无码 | 欧美乱妇无乱码大黄a片 | 亚洲国产日韩a在线播放 | 免费无码一区二区三区蜜桃大 | 玩弄人妻少妇500系列视频 | 欧美人与物videos另类 | 亚洲中文字幕久久无码 | 一本加勒比波多野结衣 | 欧美国产日韩久久mv | 日日碰狠狠丁香久燥 | 亚洲精品鲁一鲁一区二区三区 | 久久99精品久久久久婷婷 | 高中生自慰www网站 | 在线播放亚洲第一字幕 | 曰韩无码二三区中文字幕 | 国产精品久久久久久亚洲影视内衣 | 国产精品久久久久久久9999 | 国产乱子伦视频在线播放 | 丰满肥臀大屁股熟妇激情视频 | 亚洲娇小与黑人巨大交 | 日本精品人妻无码77777 天堂一区人妻无码 | 国产成人人人97超碰超爽8 | 亚洲成av人综合在线观看 | 精品亚洲韩国一区二区三区 | 超碰97人人做人人爱少妇 | 无码一区二区三区在线 | √天堂资源地址中文在线 | 无码人妻少妇伦在线电影 | 免费播放一区二区三区 | 欧美精品无码一区二区三区 | 国产香蕉尹人综合在线观看 | 大乳丰满人妻中文字幕日本 | 国产99久久精品一区二区 | 久久精品丝袜高跟鞋 | 久久国产36精品色熟妇 | 免费网站看v片在线18禁无码 | 中文字幕人妻无码一夲道 | 无码成人精品区在线观看 | 久久人人爽人人人人片 | 国产精品无码永久免费888 | 亚洲一区二区三区在线观看网站 | 精品一区二区不卡无码av | 亚洲欧洲日本综合aⅴ在线 | 高清无码午夜福利视频 | 国产一区二区三区精品视频 | 亚无码乱人伦一区二区 | 久久国产精品萌白酱免费 | 好屌草这里只有精品 | 99久久人妻精品免费二区 | 亚洲欧美综合区丁香五月小说 | 亚洲熟悉妇女xxx妇女av | 国产成人精品三级麻豆 | 国产成人精品三级麻豆 | 熟女少妇人妻中文字幕 | 中文久久乱码一区二区 | 中文精品久久久久人妻不卡 | 亚洲熟妇色xxxxx欧美老妇 | 熟女体下毛毛黑森林 | 乱码av麻豆丝袜熟女系列 | 无码帝国www无码专区色综合 | 伊人久久大香线蕉av一区二区 | 久久精品丝袜高跟鞋 | 精品一区二区三区无码免费视频 | 牲欲强的熟妇农村老妇女 | 亚洲精品一区二区三区在线 | 久久久www成人免费毛片 | 大肉大捧一进一出视频出来呀 | 久久精品无码一区二区三区 | 欧美国产亚洲日韩在线二区 | 亚洲中文字幕在线观看 | 午夜福利试看120秒体验区 | 中文字幕无码日韩欧毛 | 色欲综合久久中文字幕网 | 精品国产福利一区二区 | 99精品国产综合久久久久五月天 | 日本丰满熟妇videos | 在线视频网站www色 | 欧美日韩一区二区综合 | 丰满诱人的人妻3 | 亚洲人成网站色7799 | 两性色午夜视频免费播放 | 精品国精品国产自在久国产87 | 中文字幕人成乱码熟女app | 免费乱码人妻系列无码专区 | 欧美人与善在线com | 97精品人妻一区二区三区香蕉 | 丁香花在线影院观看在线播放 | 东京无码熟妇人妻av在线网址 | av香港经典三级级 在线 | 小泽玛莉亚一区二区视频在线 | 性史性农村dvd毛片 | 东京热一精品无码av | av无码不卡在线观看免费 | 99麻豆久久久国产精品免费 | 亚洲热妇无码av在线播放 | 日本护士毛茸茸高潮 | 国产精品久久久久久亚洲毛片 | 欧洲欧美人成视频在线 | 欧美喷潮久久久xxxxx | 免费观看黄网站 | 成人精品一区二区三区中文字幕 | 国产亚洲tv在线观看 | 窝窝午夜理论片影院 | 小鲜肉自慰网站xnxx | 国产肉丝袜在线观看 | 国产精品久久久久久无码 | 国产精品亚洲专区无码不卡 | 国产特级毛片aaaaaaa高清 | 亚洲理论电影在线观看 | 欧美喷潮久久久xxxxx | 中文字幕人妻无码一区二区三区 | 亚洲精品成人福利网站 | 国产麻豆精品精东影业av网站 | 在线欧美精品一区二区三区 | 熟妇人妻无码xxx视频 | 国产精品亚洲а∨无码播放麻豆 | 清纯唯美经典一区二区 | 亚洲 另类 在线 欧美 制服 | 精品人妻人人做人人爽 | 亚洲精品无码人妻无码 | 高清无码午夜福利视频 | 激情内射亚州一区二区三区爱妻 | 性啪啪chinese东北女人 | 性史性农村dvd毛片 | 99久久精品午夜一区二区 | 久久久www成人免费毛片 | 久久久精品国产sm最大网站 | 日本爽爽爽爽爽爽在线观看免 | 国产片av国语在线观看 | 色老头在线一区二区三区 | 理论片87福利理论电影 | 国产精品久久久久无码av色戒 | 捆绑白丝粉色jk震动捧喷白浆 | 美女毛片一区二区三区四区 | 国产成人无码av片在线观看不卡 | 亚洲一区二区三区播放 | 日韩在线不卡免费视频一区 | 国产 精品 自在自线 | 成熟女人特级毛片www免费 | 人人妻人人澡人人爽人人精品浪潮 | 国产精品丝袜黑色高跟鞋 | 天天摸天天碰天天添 | 欧美黑人巨大xxxxx | 一本精品99久久精品77 | 亚洲精品久久久久中文第一幕 | 丰满人妻一区二区三区免费视频 | 亚洲日本一区二区三区在线 | 国产成人综合美国十次 | 国产真实夫妇视频 | 美女极度色诱视频国产 | 亚洲精品午夜无码电影网 | 国产va免费精品观看 | 成年美女黄网站色大免费视频 | 无码帝国www无码专区色综合 | 亚洲精品国产精品乱码不卡 | 国产电影无码午夜在线播放 | 成人精品视频一区二区三区尤物 | 国产偷国产偷精品高清尤物 | 性色欲情网站iwww九文堂 | 丰满诱人的人妻3 | 久久精品国产99精品亚洲 | 男人的天堂2018无码 | 亚洲成av人片在线观看无码不卡 | 国产免费观看黄av片 | 亚洲色www成人永久网址 | 兔费看少妇性l交大片免费 | 国产97在线 | 亚洲 | 色欲av亚洲一区无码少妇 | 极品尤物被啪到呻吟喷水 | 国产午夜亚洲精品不卡 | 18黄暴禁片在线观看 | 日本大香伊一区二区三区 | 永久免费精品精品永久-夜色 | 夜夜夜高潮夜夜爽夜夜爰爰 | 性欧美疯狂xxxxbbbb | 2019nv天堂香蕉在线观看 | 美女黄网站人色视频免费国产 | 久久午夜无码鲁丝片 | 中文字幕 亚洲精品 第1页 | 久久亚洲中文字幕精品一区 | 未满小14洗澡无码视频网站 | 精品成人av一区二区三区 | 天海翼激烈高潮到腰振不止 | 精品久久久无码中文字幕 | 人人妻人人澡人人爽人人精品 | 大胆欧美熟妇xx | 亚洲国产精品成人久久蜜臀 | 国产肉丝袜在线观看 | 蜜桃av蜜臀av色欲av麻 999久久久国产精品消防器材 | 久久久久se色偷偷亚洲精品av | 人人妻人人澡人人爽欧美一区 | 欧美高清在线精品一区 | 四十如虎的丰满熟妇啪啪 | 免费无码午夜福利片69 | 国产在线一区二区三区四区五区 | 天堂无码人妻精品一区二区三区 | 在教室伦流澡到高潮hnp视频 | 欧美精品一区二区精品久久 | 国产另类ts人妖一区二区 | 99久久人妻精品免费二区 | √8天堂资源地址中文在线 | 无码福利日韩神码福利片 | 成熟女人特级毛片www免费 | 欧美freesex黑人又粗又大 | 精品久久久久久人妻无码中文字幕 | 亚洲国产精品成人久久蜜臀 | 乱人伦中文视频在线观看 | 丰满少妇人妻久久久久久 | 99久久久无码国产aaa精品 | 中文字幕无码免费久久9一区9 | 国产婷婷色一区二区三区在线 | 人人澡人人妻人人爽人人蜜桃 | 伊人久久大香线焦av综合影院 | 成人无码精品1区2区3区免费看 | 久久综合狠狠综合久久综合88 | 国产在线精品一区二区高清不卡 | 香港三级日本三级妇三级 | 精品久久久无码人妻字幂 | 丝袜美腿亚洲一区二区 | 国产人妖乱国产精品人妖 | 国产人妻人伦精品 | 人妻少妇精品无码专区二区 | 人妻aⅴ无码一区二区三区 | 暴力强奷在线播放无码 | 日韩欧美成人免费观看 | 无码人妻丰满熟妇区毛片18 | 国产精品亚洲一区二区三区喷水 | 又大又硬又爽免费视频 | 欧美野外疯狂做受xxxx高潮 | 国产人妻大战黑人第1集 | 青青草原综合久久大伊人精品 | 色综合久久中文娱乐网 | 亚洲最大成人网站 | 少妇被粗大的猛进出69影院 | 亚洲国产成人av在线观看 | 成人无码精品一区二区三区 | 一个人看的视频www在线 | 亚洲综合精品香蕉久久网 | 亚洲色成人中文字幕网站 | 久久久久久久久蜜桃 | 国产国语老龄妇女a片 | 久久亚洲精品中文字幕无男同 | 波多野结衣aⅴ在线 | 亚洲 日韩 欧美 成人 在线观看 | 一本加勒比波多野结衣 | 三级4级全黄60分钟 | 久久久久亚洲精品男人的天堂 | 色老头在线一区二区三区 | 亚洲国产精品久久久久久 | 中文字幕乱码人妻无码久久 | 捆绑白丝粉色jk震动捧喷白浆 | 色老头在线一区二区三区 | 51国偷自产一区二区三区 | 亚洲性无码av中文字幕 | 免费看男女做好爽好硬视频 | 国产在热线精品视频 | 午夜时刻免费入口 | 亚洲最大成人网站 | 香蕉久久久久久av成人 | 中文字幕乱码人妻二区三区 | 免费网站看v片在线18禁无码 | 成人一在线视频日韩国产 | 少妇高潮一区二区三区99 | 国精产品一品二品国精品69xx | 欧美日韩一区二区免费视频 | 乌克兰少妇xxxx做受 | 在线观看免费人成视频 | 色老头在线一区二区三区 | 2019午夜福利不卡片在线 | 水蜜桃色314在线观看 | 一本大道伊人av久久综合 | 天天做天天爱天天爽综合网 | www国产亚洲精品久久久日本 | 熟妇激情内射com | 日本乱人伦片中文三区 | 亚洲第一无码av无码专区 | 国产做国产爱免费视频 | 国产午夜视频在线观看 | 爆乳一区二区三区无码 | 国产色精品久久人妻 | 国产两女互慰高潮视频在线观看 | 少妇无码一区二区二三区 | 久久久精品国产sm最大网站 | 免费观看的无遮挡av | 国产特级毛片aaaaaa高潮流水 | 男女下面进入的视频免费午夜 | 少妇无码av无码专区在线观看 | 午夜精品久久久内射近拍高清 | 性欧美牲交在线视频 | 国产精品久免费的黄网站 | 国产av一区二区精品久久凹凸 | 亚洲中文字幕无码中文字在线 | 国产真人无遮挡作爱免费视频 | 久久久精品欧美一区二区免费 | 国产又爽又猛又粗的视频a片 | 亚洲欧洲日本无在线码 | 激情人妻另类人妻伦 | 欧美国产日韩久久mv | 久久99热只有频精品8 | 男女猛烈xx00免费视频试看 | 高潮毛片无遮挡高清免费 | 任你躁在线精品免费 | 亚洲人亚洲人成电影网站色 | 色婷婷综合激情综在线播放 | 国产性生大片免费观看性 | 乱中年女人伦av三区 | 日日鲁鲁鲁夜夜爽爽狠狠 | 国产午夜手机精彩视频 | 精品久久久久久亚洲精品 | 蜜臀av无码人妻精品 | 国产精品久久久久久亚洲影视内衣 | 在线播放亚洲第一字幕 | 国产精品美女久久久网av | 日本www一道久久久免费榴莲 | 成人免费视频在线观看 | 欧美35页视频在线观看 | 亚洲精品久久久久avwww潮水 | 波多野结衣一区二区三区av免费 | 国产亚洲人成a在线v网站 | 一个人免费观看的www视频 | 久青草影院在线观看国产 | 成人av无码一区二区三区 | 免费中文字幕日韩欧美 | 久久久www成人免费毛片 | 亚洲伊人久久精品影院 | 亚洲国产成人av在线观看 | 精品厕所偷拍各类美女tp嘘嘘 | 国产 精品 自在自线 | 国产黑色丝袜在线播放 | 国产内射爽爽大片视频社区在线 | 男女爱爱好爽视频免费看 | 国产av剧情md精品麻豆 | 九九热爱视频精品 | 亚洲 欧美 激情 小说 另类 | 午夜精品一区二区三区的区别 | 国产内射爽爽大片视频社区在线 | 中文字幕无码免费久久9一区9 | 鲁大师影院在线观看 | 欧洲精品码一区二区三区免费看 | 国产手机在线αⅴ片无码观看 | 久久人人97超碰a片精品 | 天天爽夜夜爽夜夜爽 | 丰满肥臀大屁股熟妇激情视频 | 澳门永久av免费网站 | 中文字幕亚洲情99在线 | 中文字幕av无码一区二区三区电影 | av无码久久久久不卡免费网站 | 青青草原综合久久大伊人精品 | 亚洲国精产品一二二线 | 国产av无码专区亚洲a∨毛片 | 青春草在线视频免费观看 | 中文无码精品a∨在线观看不卡 | 成人免费无码大片a毛片 | 爱做久久久久久 | 大地资源网第二页免费观看 | 中文字幕无码免费久久99 | 久久久久久久久888 | 亚洲自偷自偷在线制服 | 樱花草在线播放免费中文 | 亚洲 欧美 激情 小说 另类 | 国产成人无码午夜视频在线观看 | 伊人久久大香线蕉午夜 | 午夜肉伦伦影院 | 国产精品嫩草久久久久 | 青青青爽视频在线观看 | 日本精品久久久久中文字幕 | 7777奇米四色成人眼影 | 性欧美疯狂xxxxbbbb | 色一情一乱一伦 | 少妇被粗大的猛进出69影院 | 亚洲爆乳精品无码一区二区三区 | 久久无码专区国产精品s | 久久精品99久久香蕉国产色戒 | 国产精华av午夜在线观看 | 亚洲精品久久久久久一区二区 | 亚洲中文字幕无码中文字在线 | 精品偷拍一区二区三区在线看 | 波多野42部无码喷潮在线 | 日本熟妇大屁股人妻 | 永久免费观看国产裸体美女 | 蜜桃无码一区二区三区 | 国内老熟妇对白xxxxhd | 国产精品毛多多水多 | 国产一区二区不卡老阿姨 | 色欲av亚洲一区无码少妇 | 日本一区二区三区免费高清 | www国产精品内射老师 | 性史性农村dvd毛片 | 精品aⅴ一区二区三区 | 欧洲精品码一区二区三区免费看 | 无码人妻黑人中文字幕 | 99麻豆久久久国产精品免费 | 国产午夜亚洲精品不卡下载 | 国产精品成人av在线观看 | 色欲久久久天天天综合网精品 | 欧美一区二区三区 | 亚洲日韩精品欧美一区二区 | 国产特级毛片aaaaaa高潮流水 | 欧美野外疯狂做受xxxx高潮 | 成人一在线视频日韩国产 | 欧美性猛交xxxx富婆 | 国产成人综合色在线观看网站 | 欧美日韩在线亚洲综合国产人 | 无码乱肉视频免费大全合集 | 国产国语老龄妇女a片 | 97色伦图片97综合影院 | 波多野结衣一区二区三区av免费 | 对白脏话肉麻粗话av | 极品尤物被啪到呻吟喷水 | 日韩无套无码精品 | 国产成人一区二区三区别 | 激情综合激情五月俺也去 | 无码帝国www无码专区色综合 | 亚洲乱码日产精品bd | 久久久久国色av免费观看性色 | 国产疯狂伦交大片 | 97久久国产亚洲精品超碰热 | 亚洲一区二区三区无码久久 | 牛和人交xxxx欧美 | 人人妻人人澡人人爽欧美一区九九 | 强伦人妻一区二区三区视频18 | 娇妻被黑人粗大高潮白浆 | 成人无码精品1区2区3区免费看 | 青青青手机频在线观看 | 色偷偷人人澡人人爽人人模 | 色欲综合久久中文字幕网 | 亚洲精品久久久久中文第一幕 | 亚洲人成网站在线播放942 | 欧美一区二区三区 | 国产精品毛片一区二区 | 亚洲热妇无码av在线播放 | 国产精品久久久久无码av色戒 | 无码午夜成人1000部免费视频 | 国产亚洲精品精品国产亚洲综合 | 无码精品国产va在线观看dvd | 国产97人人超碰caoprom | 激情综合激情五月俺也去 | 无码人妻丰满熟妇区五十路百度 | 天堂无码人妻精品一区二区三区 | 麻豆果冻传媒2021精品传媒一区下载 | 久久婷婷五月综合色国产香蕉 | 熟妇人妻中文av无码 | 中文字幕乱码亚洲无线三区 | 国产精品a成v人在线播放 | 日日躁夜夜躁狠狠躁 | 国产亲子乱弄免费视频 | 国产在线一区二区三区四区五区 | 无码吃奶揉捏奶头高潮视频 | 极品尤物被啪到呻吟喷水 | 极品嫩模高潮叫床 | 色综合久久久无码中文字幕 | 真人与拘做受免费视频 | 伊人久久大香线焦av综合影院 | 国产午夜福利100集发布 | 日日摸天天摸爽爽狠狠97 | 欧美真人作爱免费视频 | 亚洲国产欧美日韩精品一区二区三区 | 乱码av麻豆丝袜熟女系列 | 内射老妇bbwx0c0ck | 妺妺窝人体色www在线小说 | 亚洲精品欧美二区三区中文字幕 | 亚洲小说图区综合在线 | 国产无套内射久久久国产 | 一本色道久久综合狠狠躁 | 日产精品99久久久久久 | 欧美丰满熟妇xxxx | 蜜臀av在线观看 在线欧美精品一区二区三区 | 蜜桃视频插满18在线观看 | 色噜噜亚洲男人的天堂 | 水蜜桃亚洲一二三四在线 | 大地资源网第二页免费观看 | 亚洲乱码中文字幕在线 | 国内精品久久毛片一区二区 | 天海翼激烈高潮到腰振不止 | 国产艳妇av在线观看果冻传媒 | 波多野结衣av一区二区全免费观看 | 久久午夜夜伦鲁鲁片无码免费 | 国产色精品久久人妻 | 中文字幕无码av激情不卡 | 亲嘴扒胸摸屁股激烈网站 | 日产精品高潮呻吟av久久 | 国产后入清纯学生妹 | 久久99精品久久久久久 | 免费无码一区二区三区蜜桃大 | 久久亚洲中文字幕精品一区 | 无码国模国产在线观看 | 中文字幕色婷婷在线视频 | 自拍偷自拍亚洲精品被多人伦好爽 | 动漫av一区二区在线观看 | 久久精品人妻少妇一区二区三区 | 日本一卡二卡不卡视频查询 | 国产成人一区二区三区在线观看 | 国产偷抇久久精品a片69 | 人人爽人人澡人人高潮 | 国产乱人伦app精品久久 国产在线无码精品电影网 国产国产精品人在线视 | 蜜桃av抽搐高潮一区二区 | 色综合久久久无码中文字幕 | 4hu四虎永久在线观看 | 麻豆国产人妻欲求不满谁演的 | 又大又紧又粉嫩18p少妇 | 国产精品永久免费视频 | 美女黄网站人色视频免费国产 | 国精品人妻无码一区二区三区蜜柚 | 成人女人看片免费视频放人 | 亚洲精品一区二区三区大桥未久 | 精品久久综合1区2区3区激情 | 国产成人无码a区在线观看视频app | 中文字幕无码免费久久9一区9 | 国产偷自视频区视频 | 蜜桃臀无码内射一区二区三区 | 97精品人妻一区二区三区香蕉 | v一区无码内射国产 | 永久黄网站色视频免费直播 | 麻豆成人精品国产免费 | 曰本女人与公拘交酡免费视频 | 免费网站看v片在线18禁无码 | 国内精品人妻无码久久久影院蜜桃 | 亚洲乱码国产乱码精品精 | 精品国产福利一区二区 | 国产亚洲精品久久久久久久久动漫 | 亚洲中文字幕无码中文字在线 | 色欲久久久天天天综合网精品 | 日韩视频 中文字幕 视频一区 | 日本熟妇乱子伦xxxx | 久久综合网欧美色妞网 | 色妞www精品免费视频 | 青草青草久热国产精品 | 国产精品欧美成人 | 亚洲精品一区二区三区在线观看 | 欧美 日韩 人妻 高清 中文 | 国产偷抇久久精品a片69 | 国产做国产爱免费视频 | 久激情内射婷内射蜜桃人妖 | 久久精品国产一区二区三区肥胖 | 夫妻免费无码v看片 | 大色综合色综合网站 | 色综合天天综合狠狠爱 | 久久久精品人妻久久影视 | 亚洲中文字幕无码中文字在线 | 精品成人av一区二区三区 | 亚洲 另类 在线 欧美 制服 | 精品熟女少妇av免费观看 | 国产日产欧产精品精品app | 国产午夜手机精彩视频 | 久在线观看福利视频 | 动漫av网站免费观看 | 99久久精品国产一区二区蜜芽 | 福利一区二区三区视频在线观看 | 最新国产乱人伦偷精品免费网站 | 2019nv天堂香蕉在线观看 | 久久久精品国产sm最大网站 | 久久精品国产大片免费观看 | 成人无码影片精品久久久 | 色一情一乱一伦一区二区三欧美 | 99精品无人区乱码1区2区3区 | 夜夜高潮次次欢爽av女 | 国产高清av在线播放 | 中文无码成人免费视频在线观看 | 国产一区二区三区精品视频 | 精品午夜福利在线观看 | 乱码av麻豆丝袜熟女系列 | 久久99精品久久久久久 | 人妻与老人中文字幕 | 亚洲人亚洲人成电影网站色 | 久久久久亚洲精品中文字幕 | 国产凸凹视频一区二区 | 东京无码熟妇人妻av在线网址 | 婷婷五月综合缴情在线视频 | 亚无码乱人伦一区二区 | 人人澡人摸人人添 | 99久久精品午夜一区二区 | 特级做a爰片毛片免费69 | 成人动漫在线观看 | 色一情一乱一伦一视频免费看 | 久久午夜夜伦鲁鲁片无码免费 | 成人动漫在线观看 | 亚洲欧美日韩国产精品一区二区 | 亚洲 高清 成人 动漫 | 野外少妇愉情中文字幕 | 国产精品人人妻人人爽 | 一本无码人妻在中文字幕免费 | 好爽又高潮了毛片免费下载 | 久久这里只有精品视频9 | 国色天香社区在线视频 | 水蜜桃亚洲一二三四在线 | 日本欧美一区二区三区乱码 | 久久亚洲精品成人无码 | 国产熟女一区二区三区四区五区 | 特级做a爰片毛片免费69 | 精品国产一区av天美传媒 | 国产一区二区三区精品视频 | 无码精品国产va在线观看dvd | 水蜜桃av无码 | 国产精品第一国产精品 | 欧美兽交xxxx×视频 | 国产莉萝无码av在线播放 | 欧美亚洲国产一区二区三区 | 最新版天堂资源中文官网 | 夜夜夜高潮夜夜爽夜夜爰爰 | 国产激情艳情在线看视频 | 激情内射亚州一区二区三区爱妻 | 欧美老人巨大xxxx做受 | 人妻少妇被猛烈进入中文字幕 | 性欧美疯狂xxxxbbbb | 国产两女互慰高潮视频在线观看 | 日韩在线不卡免费视频一区 | 婷婷丁香六月激情综合啪 | 中文字幕乱码人妻无码久久 | 免费观看激色视频网站 | 日本xxxx色视频在线观看免费 | 无码国内精品人妻少妇 | 国产明星裸体无码xxxx视频 | 少女韩国电视剧在线观看完整 | 久久久无码中文字幕久... | 久久综合香蕉国产蜜臀av | 午夜福利不卡在线视频 | 女人被爽到呻吟gif动态图视看 | 激情综合激情五月俺也去 | 欧美freesex黑人又粗又大 | 久久久久se色偷偷亚洲精品av | 国产乱人伦偷精品视频 | 精品国产福利一区二区 | 荡女精品导航 | 99久久人妻精品免费二区 | 性生交大片免费看l | av无码久久久久不卡免费网站 | 天天综合网天天综合色 | 国产免费无码一区二区视频 | 欧美性色19p | 国内老熟妇对白xxxxhd | 久久久久免费看成人影片 | 国产精品国产自线拍免费软件 | 老司机亚洲精品影院无码 | 亚洲午夜福利在线观看 | 婷婷色婷婷开心五月四房播播 | 自拍偷自拍亚洲精品被多人伦好爽 | 熟妇人妻无乱码中文字幕 | 欧美日本精品一区二区三区 | 亚洲色www成人永久网址 | 亚洲色欲久久久综合网东京热 | 极品嫩模高潮叫床 | 久久亚洲中文字幕精品一区 | 欧美丰满熟妇xxxx | 麻豆精品国产精华精华液好用吗 | 欧美猛少妇色xxxxx | 成人无码视频在线观看网站 | 丰满肥臀大屁股熟妇激情视频 | 中文字幕精品av一区二区五区 | 亚洲区小说区激情区图片区 | 欧美激情内射喷水高潮 | 丝袜足控一区二区三区 | 亚洲中文字幕久久无码 | 嫩b人妻精品一区二区三区 | 欧美一区二区三区 | 老子影院午夜精品无码 | 精品国产成人一区二区三区 | 亚洲春色在线视频 | 初尝人妻少妇中文字幕 | 中文精品无码中文字幕无码专区 | 成人精品视频一区二区三区尤物 | aa片在线观看视频在线播放 | 伊人久久大香线蕉亚洲 | 欧美三级不卡在线观看 | 丰满人妻被黑人猛烈进入 | 亚洲一区二区三区国产精华液 | 99精品无人区乱码1区2区3区 | 亚洲男人av天堂午夜在 | 国产免费久久精品国产传媒 | 国产av人人夜夜澡人人爽麻豆 | 国产午夜亚洲精品不卡下载 | 亚洲精品成a人在线观看 | 综合人妻久久一区二区精品 | 精品熟女少妇av免费观看 | 久久99精品久久久久久 | 久久精品丝袜高跟鞋 | 国产成人无码av一区二区 | 亚洲日韩av一区二区三区四区 | 精品乱码久久久久久久 | 欧美日韩人成综合在线播放 | 丁香花在线影院观看在线播放 | 欧美自拍另类欧美综合图片区 | √天堂中文官网8在线 | 精品人妻人人做人人爽 | 欧美xxxx黑人又粗又长 | 娇妻被黑人粗大高潮白浆 | 在线观看欧美一区二区三区 | 夜精品a片一区二区三区无码白浆 | 熟妇人妻无乱码中文字幕 | 国产成人综合在线女婷五月99播放 | 国产无套内射久久久国产 | 97无码免费人妻超级碰碰夜夜 | 131美女爱做视频 | 成人一区二区免费视频 | 丰满妇女强制高潮18xxxx | 成人一在线视频日韩国产 | 久久久久久a亚洲欧洲av冫 | 欧美黑人性暴力猛交喷水 | 牲交欧美兽交欧美 | 国产疯狂伦交大片 | 亚洲国产精品成人久久蜜臀 | 国产农村乱对白刺激视频 | 亚洲人成影院在线无码按摩店 | 国产乱子伦视频在线播放 | 亚洲欧美日韩综合久久久 | 西西人体www44rt大胆高清 | 亚洲日韩av一区二区三区四区 | 蜜臀aⅴ国产精品久久久国产老师 | 久久精品国产一区二区三区肥胖 | 日产精品99久久久久久 | 亚洲色偷偷男人的天堂 | www国产精品内射老师 | 一本精品99久久精品77 | 牲交欧美兽交欧美 | 国产午夜无码视频在线观看 | 国产精品高潮呻吟av久久 | 亚洲熟妇色xxxxx欧美老妇 | 久久精品国产精品国产精品污 | 人妻少妇精品无码专区二区 | www国产亚洲精品久久网站 | 日本护士毛茸茸高潮 | 亚洲 另类 在线 欧美 制服 | 成 人影片 免费观看 | 国产色xx群视频射精 | 午夜福利一区二区三区在线观看 | 一区二区传媒有限公司 | 夜夜躁日日躁狠狠久久av | 未满小14洗澡无码视频网站 | 国产亚洲美女精品久久久2020 | 亚洲天堂2017无码中文 | 无套内射视频囯产 | 大肉大捧一进一出好爽视频 | 亚洲人成人无码网www国产 | 日本免费一区二区三区最新 | 国产av一区二区三区最新精品 | 国产一精品一av一免费 | 人妻少妇精品久久 | 亚洲精品国产精品乱码不卡 | 自拍偷自拍亚洲精品10p | 成人精品一区二区三区中文字幕 | 在线精品国产一区二区三区 | 国产精品久久久久7777 | 免费无码的av片在线观看 | 亚洲aⅴ无码成人网站国产app | 宝宝好涨水快流出来免费视频 | 鲁鲁鲁爽爽爽在线视频观看 | 学生妹亚洲一区二区 | 人妻aⅴ无码一区二区三区 | 人妻有码中文字幕在线 | 国产精品成人av在线观看 | 中文字幕色婷婷在线视频 | 六十路熟妇乱子伦 | 亚洲人成影院在线无码按摩店 | aⅴ在线视频男人的天堂 | 日韩人妻少妇一区二区三区 | 国产偷抇久久精品a片69 | 亚洲成色在线综合网站 | 色婷婷综合激情综在线播放 | 国内揄拍国内精品人妻 | 熟妇人妻无乱码中文字幕 | 奇米影视888欧美在线观看 | 西西人体www44rt大胆高清 | 无码人妻黑人中文字幕 | 亚洲精品一区二区三区四区五区 | 国产片av国语在线观看 | 婷婷丁香六月激情综合啪 | 亚洲国产欧美日韩精品一区二区三区 | 久久亚洲中文字幕精品一区 | 97久久精品无码一区二区 | 国产精品亚洲五月天高清 | 久精品国产欧美亚洲色aⅴ大片 | 色综合久久中文娱乐网 | 亚洲色偷偷偷综合网 | 久久精品国产大片免费观看 | 日韩av无码一区二区三区 | 人人妻人人藻人人爽欧美一区 | 一区二区三区乱码在线 | 欧洲 | 亚洲春色在线视频 | 老司机亚洲精品影院 | 性色欲网站人妻丰满中文久久不卡 | 国产精品欧美成人 | 特级做a爰片毛片免费69 | 精品国产一区二区三区四区在线看 | 无码播放一区二区三区 | 大胆欧美熟妇xx | 中国大陆精品视频xxxx | 日韩少妇内射免费播放 | 精品日本一区二区三区在线观看 | www国产亚洲精品久久网站 | 日韩精品a片一区二区三区妖精 | 婷婷五月综合激情中文字幕 | 一本一道久久综合久久 | 国产亚洲欧美日韩亚洲中文色 | 人妻与老人中文字幕 | 波多野结衣一区二区三区av免费 | 国产后入清纯学生妹 | 亚洲日韩av一区二区三区四区 | 熟妇人妻无码xxx视频 | 激情五月综合色婷婷一区二区 | 欧美性黑人极品hd | 亚洲午夜福利在线观看 | 激情爆乳一区二区三区 | av无码久久久久不卡免费网站 | 国产精品内射视频免费 | 装睡被陌生人摸出水好爽 | 亚洲中文字幕乱码av波多ji | 又黄又爽又色的视频 | 在线视频网站www色 | 美女极度色诱视频国产 | 任你躁国产自任一区二区三区 | 亚洲人成网站在线播放942 | 国产真实夫妇视频 | 99久久人妻精品免费二区 | 麻豆果冻传媒2021精品传媒一区下载 | 国产精品资源一区二区 | 成人亚洲精品久久久久 | 激情综合激情五月俺也去 | 又紧又大又爽精品一区二区 | 少妇无码一区二区二三区 | 国产精品人人爽人人做我的可爱 | 中文字幕无码免费久久9一区9 | 四虎国产精品一区二区 | 少妇无套内谢久久久久 | 国产成人av免费观看 | 东京热一精品无码av | 女人高潮内射99精品 | 国内少妇偷人精品视频免费 | 在线 国产 欧美 亚洲 天堂 | 国产精品美女久久久网av | 亚洲s码欧洲m码国产av | 国产超级va在线观看视频 | 欧美日韩一区二区免费视频 | 亚洲国精产品一二二线 | 亚洲 高清 成人 动漫 | 九九久久精品国产免费看小说 | 国产热a欧美热a在线视频 | 精品久久久久久人妻无码中文字幕 | 中文字幕中文有码在线 | 伊人久久婷婷五月综合97色 | 奇米影视7777久久精品人人爽 | 国产美女极度色诱视频www | 人人妻人人澡人人爽欧美一区九九 | 日产精品高潮呻吟av久久 | 在线视频网站www色 | 噜噜噜亚洲色成人网站 | 国产亚洲人成在线播放 | 中文字幕乱妇无码av在线 | 日本丰满护士爆乳xxxx | 亚洲综合另类小说色区 | 激情内射亚州一区二区三区爱妻 | 熟女少妇在线视频播放 | 欧美人与牲动交xxxx | 精品少妇爆乳无码av无码专区 | 丰满少妇女裸体bbw | 亚洲国产精品无码久久久久高潮 | www国产精品内射老师 | 成人精品天堂一区二区三区 | 日韩av无码一区二区三区不卡 | 真人与拘做受免费视频一 | 狂野欧美性猛交免费视频 | 亚洲精品国产a久久久久久 | 亚洲色偷偷偷综合网 | 欧美放荡的少妇 | 夜精品a片一区二区三区无码白浆 | 亚洲呦女专区 | 东京无码熟妇人妻av在线网址 | 日本又色又爽又黄的a片18禁 | 伊在人天堂亚洲香蕉精品区 | 国产精品毛片一区二区 | 亚洲aⅴ无码成人网站国产app | 欧美野外疯狂做受xxxx高潮 | 欧美喷潮久久久xxxxx | 在线天堂新版最新版在线8 | 日本va欧美va欧美va精品 | 中文毛片无遮挡高清免费 | 国产黄在线观看免费观看不卡 | 亚洲熟女一区二区三区 | 亚洲阿v天堂在线 | 亚洲综合在线一区二区三区 | 欧美日韩视频无码一区二区三 | 丰满人妻一区二区三区免费视频 | 一区二区三区乱码在线 | 欧洲 | 国精产品一品二品国精品69xx | 亚洲欧美日韩成人高清在线一区 | 最近免费中文字幕中文高清百度 | 欧美精品免费观看二区 | 永久免费观看国产裸体美女 | 色婷婷综合激情综在线播放 | 亚洲精品鲁一鲁一区二区三区 | 精品国产一区二区三区av 性色 | 无码人妻久久一区二区三区不卡 | 99久久精品国产一区二区蜜芽 | 亚洲国产精品久久久久久 | 成人三级无码视频在线观看 | 国产香蕉尹人综合在线观看 | 人人澡人人妻人人爽人人蜜桃 | 亚洲精品国偷拍自产在线观看蜜桃 | 美女极度色诱视频国产 | 少妇太爽了在线观看 | 国产超级va在线观看视频 | 福利一区二区三区视频在线观看 | 国产无遮挡吃胸膜奶免费看 | a在线观看免费网站大全 | 午夜福利试看120秒体验区 | 亚洲成a人片在线观看无码 | 在线观看免费人成视频 | 色偷偷人人澡人人爽人人模 | 久久久亚洲欧洲日产国码αv | 亲嘴扒胸摸屁股激烈网站 | 久久久久av无码免费网 | 高清国产亚洲精品自在久久 | 人妻少妇被猛烈进入中文字幕 | 99精品国产综合久久久久五月天 | 亚洲一区二区三区国产精华液 | 久久国产精品偷任你爽任你 | 人人澡人摸人人添 | 曰韩少妇内射免费播放 | 偷窥日本少妇撒尿chinese | 国产午夜福利亚洲第一 | 成人无码影片精品久久久 | 国产精品久久久久久亚洲毛片 | 妺妺窝人体色www婷婷 | 久久精品国产一区二区三区 | 国产区女主播在线观看 | 亚洲va欧美va天堂v国产综合 | 又大又硬又爽免费视频 | 日日天干夜夜狠狠爱 | 亚洲中文字幕无码一久久区 | 国产免费久久精品国产传媒 | 国产麻豆精品一区二区三区v视界 | 亚洲精品久久久久中文第一幕 | 亚洲精品一区二区三区大桥未久 | 久久精品国产一区二区三区肥胖 | 十八禁真人啪啪免费网站 | 美女极度色诱视频国产 | 欧美变态另类xxxx | 偷窥村妇洗澡毛毛多 | 久久午夜无码鲁丝片午夜精品 | 国产在线精品一区二区三区直播 | 无套内谢的新婚少妇国语播放 | 午夜成人1000部免费视频 | 狠狠cao日日穞夜夜穞av | 永久免费精品精品永久-夜色 | 久久伊人色av天堂九九小黄鸭 | 国产成人人人97超碰超爽8 | 一区二区三区高清视频一 | 丰满人妻被黑人猛烈进入 | 国产麻豆精品一区二区三区v视界 | 无码人妻出轨黑人中文字幕 | 无码av中文字幕免费放 | 久久国产精品萌白酱免费 | 少妇无码一区二区二三区 | 欧美熟妇另类久久久久久多毛 | 亚洲成a人一区二区三区 | 成熟人妻av无码专区 | 特大黑人娇小亚洲女 | 牛和人交xxxx欧美 | 波多野结衣aⅴ在线 | 四虎国产精品免费久久 | 国产av一区二区精品久久凹凸 | 蜜桃视频插满18在线观看 | 人人澡人摸人人添 | 双乳奶水饱满少妇呻吟 | 中国女人内谢69xxxxxa片 | 乱码av麻豆丝袜熟女系列 | 丰满人妻精品国产99aⅴ | 无码国产色欲xxxxx视频 | 欧美成人午夜精品久久久 | 国产精品久久精品三级 | 色偷偷人人澡人人爽人人模 | 天天爽夜夜爽夜夜爽 | 欧美 日韩 人妻 高清 中文 | 奇米影视7777久久精品人人爽 | 国产精品99久久精品爆乳 | 日本一卡2卡3卡4卡无卡免费网站 国产一区二区三区影院 | 亚洲高清偷拍一区二区三区 | 国产精品人人爽人人做我的可爱 | 国产精品欧美成人 | 无码帝国www无码专区色综合 | 国产手机在线αⅴ片无码观看 | 免费观看激色视频网站 | 成人一在线视频日韩国产 | 免费看少妇作爱视频 | 国产真实乱对白精彩久久 | 初尝人妻少妇中文字幕 | 亚洲精品一区二区三区婷婷月 | 欧美zoozzooz性欧美 | 精品乱码久久久久久久 | 久久精品国产一区二区三区 | 亚洲成a人片在线观看无码3d | 亚洲中文字幕无码中文字在线 | 亚洲午夜福利在线观看 | 性欧美熟妇videofreesex | 成人一区二区免费视频 | 六月丁香婷婷色狠狠久久 | 乱人伦中文视频在线观看 | 成年美女黄网站色大免费视频 | 欧美第一黄网免费网站 | 俄罗斯老熟妇色xxxx | 久久aⅴ免费观看 | 国产激情精品一区二区三区 | 亚洲小说春色综合另类 | 久久综合狠狠综合久久综合88 | 亚洲日本一区二区三区在线 | 亚洲国产精品成人久久蜜臀 | 国产日产欧产精品精品app | 女人色极品影院 | 人人妻人人藻人人爽欧美一区 | 成在人线av无码免费 | а√资源新版在线天堂 | 国产午夜福利100集发布 | 日日麻批免费40分钟无码 | 夜先锋av资源网站 | 国产高清不卡无码视频 | 中文字幕中文有码在线 | 亚洲国产午夜精品理论片 | 99er热精品视频 | 中文字幕无码热在线视频 | 又大又黄又粗又爽的免费视频 | 国产性生交xxxxx无码 | 欧美一区二区三区 | 人人澡人摸人人添 | 亚洲中文字幕成人无码 | 久久国产精品二国产精品 | 久久精品国产大片免费观看 | 亚洲狠狠色丁香婷婷综合 | 国产猛烈高潮尖叫视频免费 | 特级做a爰片毛片免费69 | 久久久中文字幕日本无吗 | 国产精品毛片一区二区 | 麻豆国产丝袜白领秘书在线观看 | 日本www一道久久久免费榴莲 | 国产精品高潮呻吟av久久4虎 | 久久伊人色av天堂九九小黄鸭 | 久久精品国产大片免费观看 | 中文亚洲成a人片在线观看 | 两性色午夜视频免费播放 | 亚洲欧洲日本综合aⅴ在线 | 婷婷五月综合激情中文字幕 | 久久久久久亚洲精品a片成人 | 欧美性猛交内射兽交老熟妇 | 国产精品自产拍在线观看 | 亚洲 a v无 码免 费 成 人 a v | 亚洲经典千人经典日产 | 国产成人综合在线女婷五月99播放 | 国产偷抇久久精品a片69 | 亚洲精品一区二区三区大桥未久 | 久久久久久久久888 | 国产精品毛多多水多 | 真人与拘做受免费视频一 | 午夜精品一区二区三区的区别 | 最近中文2019字幕第二页 | 免费国产成人高清在线观看网站 | 欧美 丝袜 自拍 制服 另类 | 久久久久久av无码免费看大片 | 精品乱码久久久久久久 | 少妇激情av一区二区 | 少妇激情av一区二区 | 久久久www成人免费毛片 | 中文精品久久久久人妻不卡 | 永久免费观看美女裸体的网站 | 无码人妻出轨黑人中文字幕 | 欧洲精品码一区二区三区免费看 | 日产国产精品亚洲系列 | 亚洲人成人无码网www国产 | 日韩亚洲欧美中文高清在线 | 女高中生第一次破苞av | 亚洲综合久久一区二区 | 中文无码伦av中文字幕 | 色婷婷久久一区二区三区麻豆 | 久久久久亚洲精品男人的天堂 | 人妻无码αv中文字幕久久琪琪布 | 99视频精品全部免费免费观看 | 蜜臀aⅴ国产精品久久久国产老师 | 内射后入在线观看一区 | 夜夜影院未满十八勿进 | 无码成人精品区在线观看 | 97色伦图片97综合影院 | 日韩精品乱码av一区二区 | 久久无码中文字幕免费影院蜜桃 | 国产香蕉尹人视频在线 | 精品成人av一区二区三区 | 天堂а√在线地址中文在线 | 国模大胆一区二区三区 | 久久精品中文闷骚内射 | 国产精品久久久av久久久 | 性欧美熟妇videofreesex | 无码免费一区二区三区 | 老熟妇乱子伦牲交视频 | 天天做天天爱天天爽综合网 | 日韩少妇内射免费播放 | 美女毛片一区二区三区四区 | 久久99久久99精品中文字幕 | 欧美日韩亚洲国产精品 | 亚洲人成网站免费播放 | 精品国产一区二区三区四区 | 国产sm调教视频在线观看 | 蜜臀av在线观看 在线欧美精品一区二区三区 | 国产精品久久国产三级国 | 精品无人区无码乱码毛片国产 | 久久国语露脸国产精品电影 | 精品无人国产偷自产在线 | 人人妻人人澡人人爽欧美一区 | 色五月五月丁香亚洲综合网 | 中国女人内谢69xxxxxa片 | 久久婷婷五月综合色国产香蕉 | 亚洲の无码国产の无码步美 | 亚洲精品综合一区二区三区在线 | 女人被男人躁得好爽免费视频 | 日本熟妇大屁股人妻 | 日本成熟视频免费视频 | 久久亚洲日韩精品一区二区三区 | 又粗又大又硬毛片免费看 | av在线亚洲欧洲日产一区二区 | 高中生自慰www网站 | 欧美国产日韩亚洲中文 | 成人无码视频免费播放 | 蜜桃视频插满18在线观看 | 国产偷国产偷精品高清尤物 | 国产性生交xxxxx无码 | 成人免费视频视频在线观看 免费 | 婷婷色婷婷开心五月四房播播 | 永久黄网站色视频免费直播 | a片免费视频在线观看 | 亚洲欧洲中文日韩av乱码 | 中文字幕无码免费久久99 | 亚洲s色大片在线观看 | 色综合天天综合狠狠爱 | 粉嫩少妇内射浓精videos | 国产 浪潮av性色四虎 | 色综合天天综合狠狠爱 | 日韩av无码一区二区三区 | 狠狠色噜噜狠狠狠7777奇米 | 久久国产劲爆∧v内射 | 夜夜高潮次次欢爽av女 | 中国女人内谢69xxxx | 欧美人与禽猛交狂配 | 国产卡一卡二卡三 | 亚洲一区二区三区四区 | 图片小说视频一区二区 | 亚洲精品成a人在线观看 | 乱人伦中文视频在线观看 | 久久精品国产大片免费观看 | 一区二区三区乱码在线 | 欧洲 | 国产免费久久久久久无码 | 亚洲人成网站色7799 | 国产xxx69麻豆国语对白 | 国产精品久久久av久久久 | 俺去俺来也www色官网 | 国产黄在线观看免费观看不卡 | 免费看男女做好爽好硬视频 | 国产成人人人97超碰超爽8 | 亚洲成av人影院在线观看 | 无套内谢老熟女 | 最近免费中文字幕中文高清百度 | 亚洲精品国产精品乱码不卡 | 大肉大捧一进一出好爽视频 | 国内丰满熟女出轨videos | 国产成人精品必看 | 3d动漫精品啪啪一区二区中 | 狠狠色欧美亚洲狠狠色www | 男人扒开女人内裤强吻桶进去 | 99久久人妻精品免费二区 | 亚洲伊人久久精品影院 | 一个人看的www免费视频在线观看 | 精品厕所偷拍各类美女tp嘘嘘 | 久久精品无码一区二区三区 | 日本va欧美va欧美va精品 | 久久久亚洲欧洲日产国码αv | 蜜臀av无码人妻精品 | 亚洲精品午夜无码电影网 | 宝宝好涨水快流出来免费视频 | 18黄暴禁片在线观看 | 中文字幕 亚洲精品 第1页 | 少妇性l交大片欧洲热妇乱xxx | 日韩精品无码免费一区二区三区 | 3d动漫精品啪啪一区二区中 | 装睡被陌生人摸出水好爽 | 日日麻批免费40分钟无码 | 乱码av麻豆丝袜熟女系列 | 中文字幕无码乱人伦 | 亚洲熟妇色xxxxx欧美老妇 | 少妇一晚三次一区二区三区 | www国产精品内射老师 | 正在播放东北夫妻内射 | 久久久久成人精品免费播放动漫 | 97久久超碰中文字幕 | 亚洲综合精品香蕉久久网 | 欧美性黑人极品hd | 麻豆成人精品国产免费 | 内射欧美老妇wbb | 国产av久久久久精东av | 亲嘴扒胸摸屁股激烈网站 | 无码av中文字幕免费放 | 国产9 9在线 | 中文 | 亚洲成av人片在线观看无码不卡 | 亚洲中文字幕av在天堂 | 亚洲天堂2017无码 | 欧美黑人性暴力猛交喷水 | 久久久久久亚洲精品a片成人 | 99精品国产综合久久久久五月天 | 成 人 免费观看网站 | 亚洲人成网站在线播放942 | 国产成人精品视频ⅴa片软件竹菊 | 成人精品视频一区二区三区尤物 | 欧美日韩在线亚洲综合国产人 | 久久午夜无码鲁丝片秋霞 | а√资源新版在线天堂 | 成人试看120秒体验区 | 成人精品一区二区三区中文字幕 | 国产亚洲欧美日韩亚洲中文色 | 麻豆成人精品国产免费 | 亚洲欧美精品伊人久久 | 国产成人一区二区三区在线观看 | 丰满人妻精品国产99aⅴ | 中文字幕无线码免费人妻 | 久久99精品国产麻豆蜜芽 | 国产极品美女高潮无套在线观看 | 中文字幕中文有码在线 | 亚洲中文字幕乱码av波多ji | 亚洲 a v无 码免 费 成 人 a v | 国产成人无码a区在线观看视频app | 亚洲欧美中文字幕5发布 | 亚洲人交乣女bbw | 国产在线精品一区二区高清不卡 | 男女下面进入的视频免费午夜 | 国产精品对白交换视频 | 少妇无码av无码专区在线观看 | 又粗又大又硬又长又爽 | 精品日本一区二区三区在线观看 | 男女爱爱好爽视频免费看 | 精品无码av一区二区三区 | 国产无套内射久久久国产 | 国产精品爱久久久久久久 | 亚洲精品一区三区三区在线观看 | 久久国产精品偷任你爽任你 | 久久综合激激的五月天 | 国产精品对白交换视频 | 国产亚洲精品久久久久久久 | 亚洲欧美日韩综合久久久 | 欧美熟妇另类久久久久久多毛 | 无人区乱码一区二区三区 | 亚洲成av人影院在线观看 | 亚洲娇小与黑人巨大交 | 日韩精品成人一区二区三区 | 久久99精品国产.久久久久 | 人人妻人人澡人人爽人人精品 | 亚洲人亚洲人成电影网站色 | 中文字幕精品av一区二区五区 | 激情五月综合色婷婷一区二区 | 欧美阿v高清资源不卡在线播放 | 欧美兽交xxxx×视频 | 国产精品久久久午夜夜伦鲁鲁 | 国产亚洲精品久久久久久久久动漫 | 久久久中文久久久无码 | 久久综合狠狠综合久久综合88 | 欧美日韩久久久精品a片 | 亚洲国产av精品一区二区蜜芽 | www成人国产高清内射 | 亚洲午夜福利在线观看 | 国产精品香蕉在线观看 | 亚洲s色大片在线观看 | 国产绳艺sm调教室论坛 | 天干天干啦夜天干天2017 | 欧美肥老太牲交大战 | 免费观看黄网站 | 免费人成在线视频无码 | 波多野42部无码喷潮在线 | 亚洲中文字幕无码一久久区 | 日本va欧美va欧美va精品 | 亚洲小说春色综合另类 | 精品亚洲韩国一区二区三区 | 澳门永久av免费网站 | 18精品久久久无码午夜福利 | 无码国模国产在线观看 | 欧美日韩一区二区综合 | 精品熟女少妇av免费观看 | 国产成人综合在线女婷五月99播放 | 国产亚洲精品久久久久久国模美 | 天天躁日日躁狠狠躁免费麻豆 | 欧美自拍另类欧美综合图片区 | 黑人玩弄人妻中文在线 | 无码帝国www无码专区色综合 | 亚洲精品国产品国语在线观看 | 国产97在线 | 亚洲 | 国产乱码精品一品二品 | 国产一区二区三区精品视频 | 亚洲中文字幕在线观看 | 无码乱肉视频免费大全合集 | 少妇人妻大乳在线视频 | 免费看少妇作爱视频 | 少妇性l交大片 | 性开放的女人aaa片 | 日本精品高清一区二区 | 特级做a爰片毛片免费69 | 无码人妻少妇伦在线电影 | 国内少妇偷人精品视频 | 色噜噜亚洲男人的天堂 | 国产精品久久国产精品99 | 全球成人中文在线 | 日日摸日日碰夜夜爽av | 欧美日韩一区二区免费视频 | 久久www免费人成人片 | 国产午夜福利100集发布 | 精品久久8x国产免费观看 | 亚洲精品国产精品乱码不卡 | 激情亚洲一区国产精品 | 国产9 9在线 | 中文 | 亚洲国产成人a精品不卡在线 | 亚洲热妇无码av在线播放 | 日本精品人妻无码免费大全 |