【Head First Servlets and JSP】實踐記錄01:從有腳本到無腳本
試圖章節式的閱讀《Head First Servlets and JSP》總感覺不對勁,這本書前後的聯系性很強,有點類似於“連續劇”,而不是通常的“知識清單”。
-
可以建立多態的bean引用嗎
-
使用type,但沒有class
-
scope屬性默認為“page”
從有腳本到無腳本
1、快速搭建一個測試環境:輸入用戶名,返回“Hello, 用戶名”
index.html
<!DOCTYPE html> <html lang="en"><head> <meta charset="UTF-8"> <title>Title</title> <style> body { font-family:‘comic sans ms‘,sans-serif; } </style> </head> <body> <form action="checking" method="post"> <p>Name:</p> <p><input type="text" name="name" value="admin"></p> <p>Comments: </p> <p><textarea name="comments" rows="7" cols="30">Your comments</textarea></p> <p><input type="submit"></p> </form> </body> </html>
web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1"> <servlet> <servlet-name>checking</servlet-name> <servlet-class>com.demo.checking</servlet-class> </servlet> <servlet-mapping> <servlet-name>checking</servlet-name> <url-pattern>/checking</url-pattern> </servlet-mapping> </web-app>
com.demo.checking
package com.demo; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; public class checking extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doPost(req, resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String name = req.getParameter("name"); req.setAttribute("name", name); RequestDispatcher view = req.getRequestDispatcher("/index.jsp"); view.forward(req, resp); } }
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>$Title$</title> <style> body { font-family:‘comic sans ms‘,sans-serif; } </style> </head> <body> <p>Hello, <%=request.getAttribute("name")%></p> <%--<p>Hello, <%=request.getParameter("name")%></p>--%> </body> </html>
2、把傳遞“值”改為傳遞“對象”
com.demo.Person
package com.demo; public class Person implements java.io.Serializable { private String name; public Person() { } public String getName() { return name; } public void setName(String name) { this.name = name; } }
com.demo.checking
package com.demo; import javax.servlet.*; import javax.servlet.http.*; import java.io.IOException; public class checking extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doPost(req, resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Person person = new Person(); person.setName(req.getParameter("name")); req.setAttribute("person", person); RequestDispatcher view = req.getRequestDispatcher("/index.jsp"); view.forward(req, resp); } }
index.jsp
<%@ page import="com.demo.Person" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>$Title$</title> <style> body { font-family:‘comic sans ms‘,sans-serif; } </style> </head> <body> <p>Hello, <%=((Person)request.getAttribute("person")).getName()%></p> </body> </html>
3、不過,還記得那個備忘錄嗎?可以用一句話來總結:“使用腳本則死”。所以我們需要另一種方法。
Person是一個JavaBean,所以我們使用與bean相關的標準動作
<%@ page import="com.demo.Person" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>$Title$</title> <style> body { font-family:‘comic sans ms‘,sans-serif; } </style> </head> <body> <jsp:useBean id="person" class="com.demo.Person" scope="request" /> <p>Person created by servlet: <jsp:getProperty name="person" property="name" /></p> </body> </html>
<jsp:useBean>與<jsp:getProperty>的關系
<jsp:useBean>可以用來聲明和初始化你在<jsp:getProperty>中使用的具體bean對象。
<jsp:getProperty>中的“name”值與<jsp:useBean>中的“id”值相對應;<jsp:useBean>中的“id”值與requst中的person屬性對應。// req.setAttribute("person", person);
事實上,上述代碼在_jspService()中將會變成這樣
com.demo.Person person = null; person = (com.demo.Person) _jspx_page_context.getAttribute("person", javax.servlet.jsp.PageContext.REQUEST_SCOPE); if (person == null){ person = new com.demo.Person(); _jspx_page_context.setAttribute("person", person, javax.servlet.jsp.PageContext.REQUEST_SCOPE); }
關於PageContext可以參考這裏。
4、使用JavaBean標準動作設置對象的成員值。
<%@ page import="com.demo.Person" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>$Title$</title> <style> body { font-family:‘comic sans ms‘,sans-serif; } </style> </head> <body> <%-- id值對應request的實例域中的person --%> <jsp:useBean id="person" class="com.demo.Person" scope="request" /> <jsp:setProperty name="person" property="name" value="fuck_admin" /> <p>Person created by servlet: <jsp:getProperty name="person" property="name" /></p> </body> </html>
5、但是,上述代碼有一個問題:覆蓋了用戶輸入的name。而我們只想在查找不到用戶輸入的時候,自行創建bean並設置實例域的值。
也就是說,我們希望_jspService()中是這樣的:
對應的JavaBean標準動作就是:
<body> <%-- id值對應request的實例域中的person成員 --%> <jsp:useBean id="person" class="com.demo.Person" scope="request"> <jsp:setProperty name="person" property="name" value="fuck_admin" /> </jsp:useBean> <p>Person created by servlet: <jsp:getProperty name="person" property="name" /></p> </body>
可以建立多態的bean引用嗎?——為<jsp:useBean>增加一個type屬性
也就是說,我們希望用一個父類引用持有子類對象。類似於:
Person p = new Employee();
首先,我們把Person改為抽象類(這樣就無法創建Person類對象了),然後實現一個Employee類:
package com.demo; public class Employee extends Person { private int empID; public int getEmpID() { return empID; } public void setEmpID(int empID) { this.empID = empID; } }
接著修改servlet和JSP,如下所示:
@Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Person person = new Employee(); person.setName(req.getParameter("name")); req.setAttribute("person", person); RequestDispatcher view = req.getRequestDispatcher("/index.jsp"); view.forward(req, resp); }
<<<<——IDE報錯,修改:
<body>
<%-- id值對應request的實例域中的person成員 --%>
<jsp:useBean id="person" type="com.demo.Person" class="com.demo.Employee" scope="request">
<jsp:setProperty name="person" property="name" value="fuck_admin" />
</jsp:useBean>
<p>Person created by servlet: <jsp:getProperty name="person" property="name" /></p>
</body>
type不僅可以是抽象類,也可以是接口類型、普通類。(共同點是都可以作為實現類or子類的持有者)
使用type,但沒有class
試一下就知道了,修改一下JSP;
<body> <%-- id值對應request的實例域中的person成員 --%> <jsp:useBean id="person" type="com.demo.Person" scope="request"> <jsp:setProperty name="person" property="name" value="fuck_admin" /> <%-- 通過這個體內語句我們可以判斷bean是不是新創建的 --%> </jsp:useBean> <p>Person created by servlet: <jsp:getProperty name="person" property="name" /></p> </body>
完全修改servlet;
@Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Employee employee = new Employee(); employee.setName(req.getParameter("name")); req.setAttribute("person", employee); RequestDispatcher view = req.getRequestDispatcher("/index.jsp"); view.forward(req, resp); }
重新部署,輸出結果仍是admin,證明Person引用正確持有了Employee對象。因此“使用type,但沒有class”是可行的。這個時候如果我把
上面的一句代碼註釋掉;
// req.setAttribute("person", employee);
將無法正常運行(並不會輸出fuck_admin),所以,只有type的前提是bean已經在指定作用域存在。
scope屬性默認為page
【Head First Servlets and JSP】實踐記錄01:從有腳本到無腳本