JSP: Giới thiệu JSP
JSP (viết tắt của JavaServer Pages) còn được biết đến với một cái tên khác là Java Scripting Preprocessor - tạm dịch là "Bộ tiền xử lý văn lệnh Java" - là một công nghệ Java cho phép các nhà phát triển tạo nội dung HTML, XML hay một số định dạng khác của trang web một cách năng động, trong khi hồi âm yêu cầu của trình khách. Công nghệ này cho phép người ta nhúng mã Java và một số hành động xử lý đã được định trước (pre-defined actions) vào trong nội dung tĩnh của trang.
Cú pháp của JSP cho thêm các thẻ XML mới, gọi là JSP actions - hành động JSP. Những "hành động JSP" này được dùng để khởi động chức năng sẵn có, là những chức năng đã được xây dựng trước. Cộng thêm vào đó, công nghệ còn cho phép chúng ta tạo ra các thư viện thẻ JSP (JSP tag libraries), là những cái đóng vai trò vào việc mở rộng các thẻ HTML hay XML tiêu chuẩn. Thư viện thẻ (Tag libraries) là phương pháp mở rộng khả năng của một máy chủ web trong khi những mở rộng đó không phụ thuộc vào hệ nền (về cả cấu trúc máy cũng như hệ điều hành được dùng).
Trước khi hiển thị ra trình duyệt, tập tin JSP phải được biên dịch thành Servlet, dùng bộ biên dịch JSP (JSP compiler). Bộ biên dịch JSP có thể tạo servlet thành mã nguồn Java trước, rồi biên dịch mã nguồn ra tập tin .class dùng bộ biên dịch Java, hoặc có thể trực tiếp tạo mã byte code cho servlet từ trang JSP.
JSP và Servlet
Về mặt kiến trúc mà nói, ta có thể xem JSP là một trừu tượng ở mức độ cao của servlet, một phần mở rộng thực thi của Servlet 2.1 API. Cả hai, servlet và, đều nguyên do công ty Sun Microsystems tạo dựng. Bắt đầu từ phiên bản 1.2 đặc tả của JSP, JavaServer Pages đã được xây dựng do Tiến trình cộng đồng Java (Java Community Process). JSR 53 - Java Specification Requests hay "Yêu cầu đặc tả cho Java" - định nghĩa bản đặc tả của cả hai, JSP 1.2 và Servlet 2.4, và JSR 152 định nghĩa bản đặc tả JSP 2.0. Tại thời điểm năm 2006 bản đặc tả JSP 2.1 được xây dựng trên nền của yêu cầu JSR 245 từ trang JSP.
Cú pháp JSP
Một trang JSP có thể được phân thành từng phần nhỏ như sau:
- dữ liệu tĩnh, như HTML
- các chỉ thị JSP (JSP directives) như chỉ thị include:
<jsp:include>
- các phần tử kịch bản JSP (scripting elements) và các biến (variables), ví dụ:
<% scriptlet %>
<%= expression %>
<%! declaration %>
- hành động JSP (JSP actions)
- thẻ tùy biến (custom tags)
Dữ liệu tĩnh
Vì dữ liệu tĩnh được viết ra trong phần hồi âm HTTP (HTTP response) cũng giống như nó được viết trong tập tin nhận vào (input file), cho nên dữ liệu nhập hợp lệ của JSP thường là một trang HTML bình thường, không có mã java hoặc hành động JSP nhúng trong đó. Với dữ liệu tĩnh này, bất cứ lúc nào trình chủ web hồi âm, trình chủ web cũng sẽ gửi cùng một dữ liệu sang trang trình khách.
Chỉ thị JSP
Chỉ thị JSP (JSP directives) điều khiển cách mà trình biên dịch JSP khởi tạo một servlet. Các chỉ thị hiện có được liệt kê như sau:
include
– Chỉ thịinclude
thông báo cho bộ biên dịch JSP cho nhập toàn bộ nội dung của một tập tin vào trong nội dung hiện có. Việc làm này tương tự như việc chúng ta lấy nội dung của tập tin đó dán trực tiếp vào trong nội dung của tập tin đang dùng. Tính năng này cũng tương tự như tính năng "#include" của Bộ tiền xử lý C (C preprocessor). Tập tin được nhập thường có đuôi mở rộng (extension) là "jspf" (do JSP Fragment (mảnh, đoạn) mà ra):
<%@ include file="somefile.ext" %>
page (Trang)
– Có nhiều tùy chọn với chỉ thịpage
.
import | Kết quả là câu lệnh import của Java được chèn vào trong tập tin. |
contentType | Chỉ định nội dụng của tập tin được khởi tạo. Chúng ta nên dùng chỉ thị này nếu chúng ta không dùng HTML, hoặc bộ ký tự (character set) được dùng không phải là bộ ký tự mặc định. |
errorPage | Chỉ định rằng trang web sẽ được hiển thị nếu có một ngoại lệ (exception) xảy ra trong quá trình xử lý yêu cầu HTTP. |
isErrorPage | Nếu định giá trị là true thì nó nói rằng trang này là trang báo lỗi (error page). |
isThreadSafe | Chỉ định cho biết servlet tạo ra có tính an toàn về luồng xử lý (threadsafe) hay không. |
<%@ page import="java.util.*" %> //example import <%@ page contentType="text/html" %> //example contentType <%@ page isErrorPage=false %> //example for non error page <%@ page isThreadSafe=true %> //example for a thread safe JSP
Chú ý: Chỉ có chỉ thị trang "import" là có thể được dùng nhiều lần trong cùng một trang JSP.
- taglib – Chỉ thị taglib thông báo rằng chúng ta sẽ sử dụng một thư viện thẻ JSP (JSP tag library). Chỉ thị này đòi hỏi một tiền tố (prefix) (tương tự như namespace trong C++), cũng như kết nối URI của phần mô tả thư viện thẻ (URI for the tag library description) phải được xác định.
<%@ taglib prefix="myprefix" uri="taglib/mytag.tld" %>
Các thành phần trong kịch bản JSP và các biến
Các biến kịch bản tiêu chuẩn
Các biến kịch bản (scripting variables) sau có thể dùng lúc nào cũng được:
- out (xuất) – JSPWriter sử dụng nó để ghi dữ liệu vào luồng dữ liệu hồi âm (response stream).
- page (trang) – Bản thân servlet.
- pageContext (ngữ cảnh của trang) – Một PageContext là một thực thể có chứa dữ liệu liên kết với toàn bộ trang web. Một trang HTML nào đấy có thể được truyền qua nhiều trang JSP.
- request (yêu cầu) – Đối tượng yêu cầu HTTP.
- response (hồi âm) – Đối tượng hồi âm HTTP.
- session (phiên giao dịch) – Đối tượng phiên giao dịch HTTP. Cái này có thể được dùng để theo dõi tin tức về một người dùng, từ yêu cầu này đến yêu cầu khác.
Các thành phần trong kịch bản
Có 3 thành phần chính trong kịch bản. Chúng cho phép mã Java được chèn (insert) trực tiếp vào trong servlet.
- Một thẻ khai báo (declaration tag) đặt định nghĩa của một biến vào trong lòng phần thân lớp servlet của Java (java servlet class). Những thành viên dữ liệu tĩnh (Static data members) cũng có thể được định nghĩa ở đây nữa.
<%! int serverInstanceVariable = 1; %>
- Một thẻ kịch bản con (scriptlet tag) đặt những câu lệnh đã được bao gọn (contained statements) vào trong phương thức _jspService() của lớp servlet của java.
<% int localStackBasedVariable = 1;
out.println(localStackBasedVariable); %>
- Một thẻ biểu thức (expression tag) đặt một biểu thức cần phải được tính toán (evaluate) vào trong lớp servlet của java. Không nên kết thúc biểu thức bằng một dấu chấm phẩy (semi-colon - ";").
<%= "expanded inline data " + 1 %>
Các hành động của JSP
Hành động JSP là các thẻ XML được dùng để khởi động chức năng mà máy chủ web vốn có. Những hành động sau đây được cung cấp:
jsp:include | Tương tự như mục đích sử dụng một thủ tục con (subroutine), Java servlet tạm thời trao nhiệm vụ đặt yêu cầu và hồi âm cho trang JavaServer (JavaServer Page) chỉ định nào đấy. Quyền khống chế sẽ được hoàn lại cho trang JSP hiện tại, một khi trang JSP kia hoàn thành nhiệm vụ của nó. Dùng phương pháp này, mã trang JSP được chia sẻ với nhiều trang JSP, thay vì phải sao mã. |
jsp:param | Có thể được dùng bên trong khối jsp:include , khối jsp:forward hoặc khối jsp:params . Nó được dùng để xác định một thông số, và thông số này sẽ được cộng thêm vào chuỗi các thông số hiện có của yêu cầu. |
jsp:forward | Được dùng để trao nhiệm vụ đặt yêu cầu và hồi âm sang cho một trang JSP khác, hoặc cho một servlet khác. Quyền khống chế sẽ được hoàn trả lại cho trang JSP hiện tại. |
jsp:plugin | Những phiên bản cũ của Netscape Navigator và Internet Explorer dùng những thẻ khác để nhúng một chương trình nhỏ (applet). Hành động này tạo nên thẻ thuộc trình duyệt web cần thiết để bao gồm một chương trình nhỏ (to include an applet). |
jsp:fallback | Phần nội dung sẽ được hiển thị nếu trình duyệt web không hỗ trợ chương trình nhỏ (applets). |
jsp:getProperty | get of property của một JavaBean chỉ định. |
jsp:setProperty | set Property value sở hữu của một JavaBean chỉ định. |
jsp:useBean | Kiến tạo hoặc dùng lại một JavaBean, là cái có thể dùng được trong trang JSP. |
Ví dụ về các thẻ
jsp:include
<html> <head></head> <body> <jsp:include page="mycommon.jsp" > <jsp:param name="extraparam" value="myvalue" /> </jsp:include> name:<%=request.getParameter("extraparam")%> </body> </html>
jsp:forward
<jsp:forward page="subpage.jsp" > <jsp:param name="forwardedFrom" value="this.jsp" /> </jsp:forward>
Trong ví dụ tiếp tới này, yêu cầu được chuyển về phía trước tới "subpage.jsp". Thao tác xử lý yêu cầu không quay trở lại trang này.
jsp:plugin
<jsp:plugin type=applet height="100%" width="100%" archive="myjarfile.jar,myotherjar.jar" codebase="/applets" code="com.foo.MyApplet" > <jsp:params> <jsp:param name="enableDebug" value="true" /> </jsp:params> <jsp:fallback> Trình duyệt web của bạn không hỗ trợ những applet này. </jsp:fallback> </jsp:plugin>
Ví dụ plugin trên minh họa một phương pháp đồng bộ để nhúng applet trong một trang web. Trước khi thẻ <OBJECT> được tạo ra, chưa có một phương pháp chung nào cho phép người ta nhúng các applet cả. Thiết kế của thẻ này tương đối nghèo nàn. Hy vọng những đặc tả của nó trong tương lai sẽ cho phép những cái như đặc tính năng động (dynamic attributes) (height="${param.height}"
, code="${chart}"
v.v.) và thông số năng động (dynamic parameters). Hiện này, thẻ jsp:plugin không cho phép dùng những applet khởi động một cách năng động. Chẳng hạn, nếu chúng ta có một applet biểu hiện đồ thị và nó cần có các dữ liệu về tọa độ được truyền cho nó thông qua các thông số, chúng ta không thể dùng jsp:params, trừ phi số điểm tọa độ là một số cố định. Chúng ta không thể, ví dụ, luân chuyển qua danh sách loạt kết quả (ResultSet) để kiến tạo các thẻ jsp:param
. Chúng ta phải tự tính toán cho từng cái thẻ jsp:param
một. Đương nhiên, mỗi một thẻ jsp:param
ấy có thể có một cái tên và một giá trị tùy định.
jsp:useBean
<jsp:useBean id="myBean" class="com.foo.MyBean" scope="request" /> <jsp:getProperty name="myBean" property="lastChanged" /> <jsp:setProperty name="myBean" property="lastChanged" value="<%= new Date()%>" />
Những thuộc tính về phạm vi ảnh hưởng (scope attribute) có thể là yêu cầu (request), trang (page), phiên giao dịch (session), hoặc trình ứng dụng (application). Ý nghĩa của các thuộc tính như sau:
- yêu cầu (request) — thuộc tính này kéo dài trong toàn bộ thời gian tồn tại của yêu cầu (lifetime of the request). Một khi yêu cầu đã được tất cả cácc trang JSP xử lý, thuộc tính sẽ mất điểm tham chiếu (de-referenced).
- trang (page) — thuộc tính chỉ được dùng trong trang hiện tại mà thôi.
- phiên giao dịch (session) — thuộc tính được dùng trong toàn bộ thời gian tồn tại phiên giao dịch của người dùng.
- trình ứng dụng (application) — thuộc tính có thể được dùng bởi tất cả các thực thể (instance) và sẽ không bao giờ mất điểm tham chiếu. Tương tự như một biến toàn cục (global variable).
Ví dụ trên sử dụng Chương trình quản lý Bean (Bean Manager) để tạo một thực thể (instance) của lớp com.foo.MyBean
và lưu trữ thực thể trong thuộc tính (attribute) có tên là "myBean". Thuộc tính tồn tại trong toàn bộ thời gian của yêu cầu. Nó có thể được chia sẻ giữa tất cả các trang JSP đã được bao gồm hay những trang được chuyển về phía trước từ trang JSP nhận yêu cầu đầu tiên.
Thư viện thẻ JSP
Cùng với các hành động JSP định sẵn, các nhà phát triển có thể thêm vào các hành động tùy biến của họ dùng các hàm API của JSP hỗ trợ cho việc mở rộng thẻ (JSP Tag Extension API). Các nhà phát triển sẽ viết một lớp Java (class) để thực thi một trong các thẻ giao diện (Tag interfaces) và cung cấp một tập tin chứa mô tả XML về thư viện thẻ. Tập tin này được dùng để xác định các thẻ và các lớp Java (java classes) thực hiện các thẻ đó.
Xem xét ví dụ sau:
<%@ taglib uri="mytaglib.tld" prefix="myprefix" %>
...
<myprefix:myaction> <%-- thẻ mở đầu %>
...
</myprefix:myaction> <%-- thẻ kết thúc %>
...
Bộ biên dịch JSP sẽ đọc tập tin XML mytaglib.tld
và phát hiện rằng thẻ myaction
được thực hiện bởi lớp Java (java class) tên MyActionTag
(xem ở dưới). Lần đầu tiên, khi thẻ này được dùng trong tập tin JSP, nó sẽ tạo nên một thực thể (instance) của MyActionTag
. Sau đó (và mỗi lần thẻ này được dùng lại), nó sẽ khởi động phương thức doStartTag()
khi nó gặp thẻ "mở đầu" (start). Nó thẩm tra kết quả của thẻ "mở đầu", và quyết định phương pháp để xử lý phần thân của thẻ. Thân của thẻ là phần văn bản nằm giữa thẻ "mở đầu" và thẻ "kết thúc" (end). Phương thức doStartTag()
có thể trả về một trong những hằng sau:
- SKIP_BODY - Phần thân giữa thẻ không được xử lý (bỏ qua)
- EVAL_BODY_INCLUDE - định giá trị phần thân của thẻ
- EVAL_BODY_TAG - định giá trị phần thân của thẻ và đẩn kết quả vào luồng dữ liệu (được lưu trong thuộc tính về nội dung thân của thẻ).
Chú ý: Nếu thẻ là kế thừa (extends) của lớp BodyTagSupport, thì phương thức doAfterBody() sẽ được khởi động khi phần thân được xử lý, trước khi khởi động doEndTag(). Phương thức này dùng để thực hiện cấu trúc lặp vòng.
Khi gặp thẻ "kết thúc", nó gọi phương thức doEndTag(). Phương thức này có thể trả lại một trong hai giá trị:
- EVAL_PAGE - ám chỉ rằng phần còn lại của tập tin JSP cần phải được xử lý.
- SKIP_PAGE - ám chỉ rằng không còn phần xử lý nào cần phải làm nữa. Quyền khống chế sẽ rời khỏi trang JSP. Phương pháp này được dùng trong hành động chuyển trang (forwarding action).
Thẻ myaction
cần phải có một lớp (class) thực thi nó, tương tự như phần sau đây:
public class MyActionTag extends TagSupport { //Giải phóng mọi thực thể của biến (instance variable) - trong bộ nhớ. public void release() {...} public MyActionTag() {... } //gọi khi gặp thẻ "mở đầu" public int doStartTag() {... } //gọi khi gặp thẻ "kết thúc" public int doEndTag() {... } }
Quốc tế hóa
Quốc tế hóa trong JSP có thể được thực hiện tương tự như phương pháp làm trong các trình ứng dụng Java thông thường, nghĩa là bằng cách dùng gói tài nguyên (resource bundle). Việc thay đổi ngôn ngữ thể hiện trên các trang web được thực hiện một cách đơn giản thông qua việc thay đổi đơn vị locale. Bằng cách đó, lập trình viên có thể thực hiện thay đổi nội dung, ngôn ngữ... thông qua thay đổi các resource bundle này chứ không cần trực tiếp tác động lên code của trang JSP hay code của taglib.
JSP 2.0
Phiên bản đặc tả mới của JSP bao gồm những đặc trưng mới nhằm nâng cao hiệu năng của nhà lập trình (hầu cho họ làm việc được nhanh và có kết quả hơn), gọi là:
- Ngôn ngữ biểu thức (JSP Expression Language - viết tắt là JSP EL) nhằm cho phép các nhà phát triển tạo ra mẫu (templates) theo phong cách Tốc lực (Velocity) (và những cái khác nữa).
- Một phương thức nhanh gọn / dễ dàng để tạo ra các thẻ mới.
Hello, ${param.visitor} <%-- giống như: Hello, <%=request.getParameter("visitor")%> --%>
Mẫu hình MVC
Sun khuyến cáo việc sử dụng mẫu hình MVC (Model-View-Controller - tạm dịch là "Mô hình dữ liệu - giao diện người dùng - lôgic điều khiển", có thể gọi ngắn là "mô hình - giao diện - điều khiển") cho các tập tin JSP để có thể tách biệt phần trình bày (presentation) khỏi phần xử lý yêu cầu (request processing) và lưu trữ dữ liệu (data storage). Hoặc là các servlets
thông thường hoặc là các tập tin JSP phải được sử dụng để xử lý yêu cầu. Sau khi đã hoàn thành việc xử lý yêu cầu, quyền điều khiển được chuyển sang cho một trang JSP khác. Trang này chỉ được dùng để tạo xuất liệu, tức là hiển thị ở đầu ra. Hiện nay có một vài hệ nền (platforms) được tạo dựa trên nền của nguyên lý mẫu hình MVC cho các tầng máy web (web tiers) (chẳng hạn như Struts và cơ cấu Spring (Spring framework))
Ví dụ
Mặc dầu bộ biên dịch JSP kiến tạo mã nguồn Java cho servlet hoặc phát ra mã byte của Java (byte code) trực tiếp, chúng ta cũng nên hiểu quá trình bộ biên dịch JSP biến đổi một trang JSP trở thành một servlet của Java như thế nào. Chẳng hạn, chúng ta thử cân nhắc những dữ liệu nhập của JSP sau đây, và kết quả Java Servlet mà nó sinh tạo.
Trang JSP nhập vào
<%@ page errorPage="myerror.jsp" %> <%@ page import="com.foo.bar" %> <html> <head> <%! int serverInstanceVariable = 1;%> ... <% int localStackBasedVariable = 1; %> <table> <tr><td><%= toStringOrBlank("expanded inline data" + 1) %></td></tr> ...
Kết quả servlet được sinh tạo
package jsp_servlet; import java.util.*; import java.io.*; import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.jsp.*; import javax.servlet.jsp.tagext.*; import com.foo.bar; //nhập nội do kết quả của dòng <%@ page import="com.foo.bar" %> import... class _myserlvet implements javax.servlet.Servlet, javax.servlet.jsp.HttpJspPage { //được chèn thêm vào //do kết quả của dòng <%! int serverInstanceVariable = 1;%> int serverInstanceVariable = 1; ... public void _jspService(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, java.io.IOException { javax.servlet.ServletConfig config =...;//lấy cấu hình của servlet vào Object page = this; PageContext pageContext =...;//lấy ngữ cảnh của trang đối với yêu cầu này javax.servlet.jsp.JspWriter out = pageContext.getOut(); HttpSession session = request.getSession(true); try { out.print("<html>\r\n"); out.print("<head>\r\n"); ... //sinh ra từ dòng <% int localStackBasedVariable = 1; %> int localStackBasedVariable = 1; ... out.print("<table>\r\n"); out.print(" <tr><td>"); //để ý, toStringOrBlank() đổi biểu thức thành chuỗi ký tự hoặc // nếu biểu thức không tồn tại (null), dùng chuỗi ký tự trống rỗng (empty string) thay thế. //từ dòng <%= "expanded inline data " + 1 %> out.print(toStringOrBlank("expanded inline data " + 1)); out.print(" </td></tr>\r\n"); ... } catch (Exception _exception) { //dọn dẹp sạch sẽ và chuyển hướng điều khiển tới trang báo lỗi như trong dòng <%@ page errorPage="myerror.jsp" %> } } }
Nguồn: https://vi.wikipedia.org/wiki/JSP