整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          Java Web輕松學40 - JSP初步使用

          本系列文章旨在記錄和總結自己在Java Web開發之路上的知識點、經驗、問題和思考,希望能幫助更多(Java)碼農和想成為(Java)碼農的人。
          

          1. 介紹
          2. 租房網現狀
          3. 思路
          4. 共享模擬數據的Filter
          5. 房源列表頁面 - houses.jsp
          6. 驗證
          7. 房源詳情頁面 - house-details.jsp
          8. 房源編輯表單頁面 - house-form.jsp
          9. 處理房源編輯表單的提交 - HouseFormServlet
          10. 進一步改進 - include.jsp
          11. 總結

          介紹

          上篇文章介紹了JSP的核心原理,本篇文章準備介紹JSP的初步使用。

          大家一定要自己動手編寫代碼,運行驗證,只有這樣才能理解的更加透徹,印象也更加深刻。不知道大家有沒有這樣一種體會,就是明明自己看了不少技術相關的書籍,但是每到用的時候就忘記了相關技術如何使用,還是需要不斷的上網搜索。

          這還是說明我們動手實踐比較少。在我看來,任何一門科學、技術、學問、專業都離不開以下四個環節:

          • 看資料、跟師傅,學習前人發現/發明的正確理論、總結的正確經驗,系統化地和碎片化地(比如上課、報班、看書、互聯網上的資訊和專欄等等)。
          • 身體力行,動手實踐。
          • 經常思考和總結,多問幾個為什么,多想想如何能更快更好的解決問題,想想自己是否能發明或發現新概念、新理論、新模型、新體系、新方法等等,可以利用發散思維、逆向思維等等。
          • 不斷記錄自己的學習體會、實踐的過程和結論、思考和總結的過程和成果,可以簡單的記錄,也可以系統的記錄,甚至可以寫成書。

          任何一門科學、技術、學問、專業都不是一日之功,需要長期的堅持這四個環節,所以必須從小就灌輸,養成學、做、思、記的習慣。這四個環節是互相影響、互相交叉、互相促進、需要同時進行,不可偏廢的。正所謂“紙上得來終覺淺,絕知此事要躬行”、“學而不思則罔,思而不學則殆”、“好記性不如爛筆頭”等等。

          雖說這四個環節不可偏廢,但各門科學、技術、學問、專業還是有所側重的,比如科學這個詞感覺就偏理論學習和思考多一些,動手實踐少一些;而技術這個詞就相反。再比如工科偏動手實踐多一些,理科偏理論研究多一些等等。

          不好意思,又跑題。說那么多無非就是想說我們需要多敲敲代碼。軟件/編程這一行業被劃分到工科,那就說明我們更要多動手實踐。話說這應該是實踐成本最低的工科行業了,只要一臺個人電腦即可,還有各種開源的軟件可用,要是建筑、土木、工業制造、各種勘探、化工、電力等等,要么需要去實地、要么需要建個實驗室、要么需要買個機床。。。。。。

          好了,不說廢話了。我們仍然以前面開發的租房網應用(可以參考這篇文章和這篇文章)為基礎,使用JSP技術來改造,至少需要達到的一個目標是讓代碼看起來更加清爽。

          租房網現狀

          工程結構:

          靜態資源 - 登錄頁面login.html:

          <!DOCTYPE html>
          <html>
          <head>
          <meta charset="UTF-8">
          <title>租房網 - 登錄</title>
          </head>
          <body>
          	<form action="login.servlet" method="post">
          		<label for="user_name">用戶名:</label><input type="text" id="user_name" name="userName" />
          		<label for="password">密碼:</label><input type="password" id="password" name="password" />
          		<input type="submit" value="登錄" />
          	</form>
          </body>
          </html>
          

          房源實體類 - House.java:

          package houserenter.entity;
          public class House {
          	private String id;
          	private String name;
          	private String detail;
          	public House(String id, String name, String detail) {
          		super();
          		this.id = id;
          		this.name = name;
          		this.detail = detail;
          	}
          	public String getId() {
          		return id;
          	}
          	public void setId(String id) {
          		this.id = id;
          	}
          	public String getName() {
          		return name;
          	}
          	public void setName(String name) {
          		this.name = name;
          	}
          	public String getDetail() {
          		return detail;
          	}
          	public void setDetail(String detail) {
          		this.detail = detail;
          	}
          	@Override
          	public String toString() {
          		return "House [id=" + id + ", name=" + name + ", detail=" + detail + "]";
          	}
          }
          

          處理登錄請求的Servlet - LoginServlet.java:

          package houserenter.servlet;
          import java.io.IOException;
          import javax.servlet.ServletException;
          import javax.servlet.annotation.WebServlet;
          import javax.servlet.http.HttpServlet;
          import javax.servlet.http.HttpServletRequest;
          import javax.servlet.http.HttpServletResponse;
          @WebServlet("/login.servlet")
          public class LoginServlet extends HttpServlet {
          	private static final long serialVersionUID = 1L;
          	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
          		String userName = request.getParameter("userName");
          		String password = request.getParameter("password");
          		
          		//這里需要驗證用戶是否已經注冊,省略
          		System.out.println("userName: " + userName + ", password: " + password);
          		
          		//用戶登錄成功,重定向到房源列表頁面
          		response.sendRedirect("house.html?userName=" + userName);
          	}
          }
          

          處理房源相關請求(房源查找、房源詳情、房源編輯)的Servlet - HouseServlet.java:

          package houserenter.servlet;
          import java.io.IOException;
          import java.io.PrintWriter;
          import java.util.ArrayList;
          import java.util.List;
          import javax.servlet.ServletException;
          import javax.servlet.annotation.WebServlet;
          import javax.servlet.http.HttpServlet;
          import javax.servlet.http.HttpServletRequest;
          import javax.servlet.http.HttpServletResponse;
          import houserenter.entity.House;
          @WebServlet("/house.html")
          public class HouseServlet extends HttpServlet {
          	private static final long serialVersionUID = 1L;
          	
          	private List<House> mockHouses;
          	@Override
          	public void init() {
          		mockHouses = new ArrayList<House>();
          		mockHouses.add(new House("1", "金科嘉苑3-2-1201", "詳細信息"));
          		mockHouses.add(new House("2", "萬科橙9-1-501", "詳細信息"));
          	}
          	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
          		String userName = request.getParameter("userName");
          		PrintWriter writer = response.getWriter();
          		
          		writer.println("<!DOCTYPE html>");
          		writer.println("<html>");
          		writer.println("<head>");
          		writer.println("<meta charset=\"UTF-8\">");
          		writer.println("<title>租房網</title>");
          		writer.println("</head>");
          		writer.println("<body>");
          		
          		writer.println("<h1>你好,"+userName+"!歡迎來到租房網! <a href=\"login.html\">退出</a></h1>");
          		writer.println("<br><br>");
          		
          		String houseId = request.getParameter("houseId");
          		String editHouse = request.getParameter("editHouse");
          		
          		if (houseId == null || houseId.isEmpty()) {
          			//查找該用戶感興趣的房源,這里省略
          			System.out.println("userName: " + userName + " access house.html!");
          			writer.println("<h6>共找到你感興趣的房源 "+mockHouses.size()+" 條</h6>");
          			writer.println("<ul>");
          			for (House house : mockHouses) {
          				writer.println("<li><h2><a href=\"house.html?userName="+userName+"&houseId="+house.getId()+"\">"+house.getName()+"</a></h2></li>");
          			}
          			writer.println("</ul>");
          		} else if (editHouse == null) {
          			//根據houseId查找該房源的詳細信息
          			System.out.println("userName: " + userName + " access house.html for house detail!");
          			House target = null;
          			for (House house : mockHouses) {
          				if (houseId.equals(house.getId())) {
          					target = house;
          					break;
          				}
          			}
          			
          			writer.println("<h2>"+target.getName()+"<a href=\"house.html?userName="+userName+"&houseId="+houseId+"&editHouse=true\">編輯</a></h2>");
          			writer.println("<h3>"+target.getDetail()+"</h3>");
          			writer.println("<h4><a href=\"house.html?userName="+userName+"\">回到列表</a></h4>");
          		} else {
          			//存在editHouse參數,返回指定房源的編輯頁面
          			System.out.println("userName: " + userName + " access house.html to edit house!");
          			House target = null;
          			for (House house : mockHouses) {
          				if (houseId.equals(house.getId())) {
          					target = house;
          					break;
          				}
          			}
          			//writer.println("<form action=\"house.html?userName="+userName+"&houseId="+houseId+"\" method=\"post\">");
          			writer.println("<form action=\"house.html\" method=\"post\">");
          			writer.println("<input type=\"hidden\" name=\"userName\" value=\""+userName+"\"/>");
          			writer.println("<input type=\"hidden\" name=\"houseId\" value=\""+houseId+"\"/>");
          			writer.println("<label for=\"house_name\">房源名字:</label><input type=\"text\" id=\"house_name\" name=\"houseName\" value=\""+target.getName()+"\" />");
          			writer.println("<label for=\"house_detail\">房源詳細信息:</label><input type=\"text\" id=\"house_detail\" name=\"houseDetail\" value=\""+target.getDetail()+"\" />");
          			writer.println("<input type=\"submit\" value=\"提交\" />");
          			writer.println("</form>");
          		}
          		
          		writer.println("</body>");
          		writer.println("</html>");
          	}
          	
          	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
          		String userName = request.getParameter("userName");
          		String houseId = request.getParameter("houseId");
          		
          		//獲取提交的房源信息,并保存
          		System.out.println("userName: " + userName + " access house.html to save house detail!");
          		String houseName = request.getParameter("houseName");
          		String houseDetail = request.getParameter("houseDetail");
          		for (House house : mockHouses) {
          			if (houseId.equals(house.getId())) {
          				house.setName(houseName);
          				house.setDetail(houseDetail);
          				break;
          			}
          		}
          		
          		response.sendRedirect("house.html?userName="+userName+"&houseId="+houseId);
          	}
          }
          

          設置請求響應編碼、登錄驗證的Filter - MyFirstFilter.java:

          package houserenter.filter;
          import java.io.IOException;
          import javax.servlet.Filter;
          import javax.servlet.FilterChain;
          import javax.servlet.ServletException;
          import javax.servlet.ServletRequest;
          import javax.servlet.ServletResponse;
          import javax.servlet.annotation.WebFilter;
          import javax.servlet.http.HttpServletRequest;
          import javax.servlet.http.HttpServletResponse;
          @WebFilter("/house.html")
          public class MyFirstFilter implements Filter {
          	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
          		request.setCharacterEncoding("UTF-8");
          		response.setCharacterEncoding("UTF-8");
          		
          		HttpServletRequest httpServletRequest = (HttpServletRequest) request;
          		HttpServletResponse httpServletResponse = (HttpServletResponse) response;
          		
          		String userName = httpServletRequest.getParameter("userName");
          		
          		if (userName == null || userName.isEmpty()) {
          			System.out.println("invalid user!");
          			httpServletResponse.sendRedirect("login.html");
          		} else {
          			chain.doFilter(request, response);
          		}
          	}
          }
          

          思路

          登錄頁面和LoginServlet目前還算簡單,暫時可以不用改造。

          HouseServlet的代碼量稍微有點多(也不過一百多行),且夾雜著輸出HTML內容,看著比較亂。從功能上來說雖然都與房源有關,但實際上動態生成的呈現給瀏覽器的是三個頁面(房源列表、房源詳情、房源編輯),所以可以考慮用三個JSP頁面代替,咱就命名為:

          • houses.jsp
          • house-details.jsp
          • house-form.jsp

          另外,因為涉及到一個房源編輯的表單,所以需要一個Servlet來處理表單的提交,就叫HouseFormServlet吧。

          還有一個要提的是HouseServlet初始化了一些模擬數據,那這部分應該放在哪呢?因為一個JSP頁面就相當于一個Servlet,所以原來的HouseServlet現在要改造成三個JSP頁面,感覺模擬數據放在哪個JSP頁面都不太合適,因為三個JSP頁面都要訪問同一份模擬數據才行。

          于是問題變為:如何在多個JSP頁面之間共享訪問同一份數據?我先想到的是能不能在一個Filter中初始化模擬數據,然后讓它攔截到這三個JSP頁面的請求,然后在請求中使用setAttribute()方法將模擬數據掛上去,最后在JSP頁面中使用請求的getAttribute()方法取出來。

          好了,既然思路有了,那我們就大刀闊斧的干吧。

          不過我們可以把之前的代碼都保留著,另外編寫新添加的JSP頁面和Filter即可,當然也可以把之前的都刪掉。

          共享模擬數據的Filter

          這次我們先編寫我們的Filter。

          在Filter中初始化模擬數據跟之前在HouseServlet中是類似的。Filter也有一個相同的生命周期方法init()(另一個相同的是destroy()方法),大家可以直接看看Filter的源碼(如何查看源碼可以參考這篇文章)。

          不過Filter的這個init()方法是Servlet容器初始化Filter的時候就調用,與Servlet的init()方法在第一個請求到來時才調用是不同的。

          Filter的doFilter()方法還是可以跟之前的Filter一樣設置請求響應的編碼以及進行登錄驗證,但最主要的是調用請求的setAttribute()方法把模擬數據掛載到該請求中。

          在Eclipse中創建Filter還是可以使用New工具(可以參考這篇文章中的新建Java類部分),大家應該很容易知道該怎么填寫相關信息,比如Filter的類名、初始化參數、映射(即攔截何種請求)。當然,你也可以新建空白的文件手動敲寫所有代碼。

          package houserenter.filter;
          import java.io.IOException;
          import java.util.ArrayList;
          import java.util.List;
          import javax.servlet.Filter;
          import javax.servlet.FilterChain;
          import javax.servlet.FilterConfig;
          import javax.servlet.ServletException;
          import javax.servlet.ServletRequest;
          import javax.servlet.ServletResponse;
          import javax.servlet.annotation.WebFilter;
          import javax.servlet.http.HttpServletRequest;
          import javax.servlet.http.HttpServletResponse;
          import houserenter.entity.House;
          @WebFilter(
          		urlPatterns = { 
          				"/houses.jsp", 
          				"/house-details.jsp", 
          				"/house-form.jsp",
          				"house-form.servlet"
          		})
          public class MySecondFilter implements Filter {
          	private List<House> mockHouses;
          	@Override
          	public void init(FilterConfig filterConfig) {
          		System.out.println("AAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
          		mockHouses = new ArrayList<House>();
          		mockHouses.add(new House("1", "金科嘉苑3-2-1201", "詳細信息"));
          		mockHouses.add(new House("2", "萬科橙9-1-501", "詳細信息"));
          	}
          	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
          		System.out.println("BBBBBBBBBBBBBBBBBBBBBBBBBBBB");
          		request.setCharacterEncoding("UTF-8");
          		response.setCharacterEncoding("UTF-8");
          		
          		HttpServletRequest httpServletRequest = (HttpServletRequest) request;
          		HttpServletResponse httpServletResponse = (HttpServletResponse) response;
          		
          		Object obj = httpServletRequest.getAttribute("mockHouses");
          		if (obj == null) {
          			httpServletRequest.setAttribute("mockHouses", mockHouses);
          		}
          		
          		String userName = httpServletRequest.getParameter("userName");
          		
          		if (userName == null || userName.isEmpty()) {
          			System.out.println("invalid user!");
          			httpServletResponse.sendRedirect("login.html");
          		} else {
          			chain.doFilter(request, response);
          		}
          	}
          }
          

          上面的代碼是我用New工具生成之后,把無用的注釋、成員方法刪掉之后,再加上初始化模擬數據的邏輯、doFilter的邏輯。

          因為Filter接口的init()方法和destroy()方法是default的(JDK8才支持),因此可以不必實現。

          注意,此Filter攔截的有哪些資源的訪問請求!

          doFilter的邏輯跟之前的相比,多了一個把mockHouses掛載到請求上去的部分。經過測試,不用強制轉換成HttpServletRequest也是可以的。

          對了,我在init()方法和doFilter()方法中都加了打印日志的部分,便于調試。

          房源列表頁面 - houses.jsp

          現在開始編寫我們的JSP頁面,第一個是房源列表。

          編寫房源列表頁面,實際上就是編寫Servlet,只不過它們的HTML和Java內容是反著來的。所以你可以參照原來的HouseServlet中是怎樣輸出HTML的,你就可以把它們拷貝過來,然后把writer.println("")這種去掉,把HTML標簽拿出來,而需要Java代碼的地方就用JSP語法中的<%和%>括起來即可。

          即編寫JSP頁面時,你就可以想象著這是在編寫Servlet的service()方法(除去JSP的聲明,即<%!%>括起來的,和page指令中的import部分以外),只不過HTML內容直接寫,而Java代碼需要<%(或<%=)和%>括起來。

          但實際上,我們仍然可以使用New工具,從而生成通用的JSP模板頁面(工具中可以選擇是HTML 5、HTML 4.01、XHTML等等,我選的是HTML5)。

          我們把這幾個JSP頁面都直接放在WebContent節點下,這樣跟MySecondFilter的映射配置是一致的。

          這里有一個小訣竅,就是寫一部分代碼驗證一部分代碼,不必整個頁面編寫完畢才驗證,當然我們這幾個頁面都比較簡單,也可以全部編寫完畢再驗證。

          <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
          <%@ page import="java.util.List" %>
          <%@ page import="houserenter.entity.House" %>
          <!DOCTYPE html>
          <html>
          <head>
          <meta charset="UTF-8">
          <title>租房網</title>
          </head>
          <body>
          <h1>你好,${param.userName}!歡迎來到租房網! <a href="login.html">退出</a></h1>
          <br><br>
          <%
          	List<House> mockHouses = (List<House>) request.getAttribute("mockHouses");
          	System.out.println(mockHouses);
          %>
          <h6>共找到你感興趣的房源 <%=mockHouses.size() %> 條</h6>
          <ul>
          <%for (House house : mockHouses) { 
          	System.out.println(house); %>
          	<li><h2><a href="house-details.jsp?userName=${param.userName}&houseId=<%=house.getId() %>"><%=house.getName() %></a></h2></li>
          <%} %>
          </ul>
          </body>
          </html>
          

          需要提一下:

          • 我把編碼格式都改成了UTF-8,此文件一共三處地方。
          • page指令中導入Java類的部分可以在需要的時候編寫,因為開始你也不知道需要什么類啊,后面不能解析類型的變量Eclipse會有提示。
          • 可以看到我用了三種元素:JSP腳本<% %>)、JSP表達式<%= %>)、EL表達式${ }),這回總算清楚它們的基本用法了。
          • 比較奇葩的是for循環部分,盡然可以將前半部分用<% %>括起來、中間部分直接寫HTML代碼、最后一個花括弧也用<% %>括起來。不過也容易理解,這些都要被Servlet容器轉換成Java代碼的。可能看起來有點怪,但你要適應這種風格。
          • JSP表達式可以用在HTML標簽的內容上,還可以用在HTML標簽屬性值的雙引號中。EL表達式也是如此。
          • EL表達式的基本語法是用 ${} 把內容括起來,內容一般是訪問某個對象的屬性,使用點表達式方括號表達式均可,也支持一般的算術運算符、關系運算符、邏輯運算符等等。
          • JSP腳本中的request是JSP的隱式對象,EL表達式中的param是EL中的隱式對象,不過你都可以理解為Servelt容器在轉換成Servlet代碼時幫你定義的對象。param實際上就等價于request.getParameter()方法,也可以理解為param就是一個包括請求中所有參數的Map。
          • 加了一些打印日志的代碼,便于調試。
          • 最后,<a>標簽的跳轉頁面是房源詳情頁面house.jsp,URL中也可以攜帶參數。
          • URL中攜帶的參數與表單中提交的參數,都是使用request.getParameter()來訪問的。
          • request.getAttribute()只能訪問服務端添加的數據,而不是瀏覽器端用戶發送過來的數據。

          驗證

          我們可以一邊開發JSP頁面一邊進行驗證了,Eclispe中啟動Tomcat之后也不用關閉,它會自動檢測JSP頁面和Java代碼的變化,進行重新編譯和發布,不過有時候修改代碼后還是重新發布應用比較踏實。

          先別忘了修改我們的LoginServlet重定向的地址:

          response.sendRedirect("houses.jsp?userName=" + userName);
          

          OK,從瀏覽器中訪問租房網的登錄頁面login.html:

          隨便輸入用戶名和密碼,點擊登錄:

          耶,完全沒有問題!繼續編寫其他兩個JSP頁面。

          房源詳情頁面 - house-details.jsp

          有了前面的分析,相信大家都能夠理解一般的JSP代碼了,那就直接上代碼吧。

          <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
          <%@ page import="java.util.List" %>
          <%@ page import="houserenter.entity.House" %>
          <!DOCTYPE html>
          <html>
          <head>
          <meta charset="UTF-8">
          <title>租房網</title>
          </head>
          <body>
          <h1>你好,${param.userName}!歡迎來到租房網! <a href="login.html">退出</a></h1>
          <br><br>
          <%
          	List<House> mockHouses = (List<House>) request.getAttribute("mockHouses");
          	String houseId = request.getParameter("houseId");
          	House target = null;
          	for (House house : mockHouses) {
          		if (houseId.equals(house.getId())) {
          			target = house;
          			break;
          		}
          	}
          %>	
          <h2><%=target.getName() %><a href="house-form.jsp?userName=${param.userName }&houseId=<%=target.getId() %>">編輯</a></h2>
          <h3><%=target.getDetail() %></h3>
          <h4><a href="houses.jsp?userName=${param.userName }">回到列表</a></h4>
          </body>
          </html>
          

          還是主要用了JSP腳本、JSP表達式和EL表達式三個技術。

          不過,JSP腳本看著似乎有點長。

          房源編輯表單頁面 - house-form.jsp

          還是直接上代碼。

          <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
          <%@ page import="java.util.List" %>
          <%@ page import="houserenter.entity.House" %>
          <!DOCTYPE html>
          <html>
          <head>
          <meta charset="UTF-8">
          <title>租房網</title>
          </head>
          <body>
          <h1>你好,${param.userName}!歡迎來到租房網! <a href="login.html">退出</a></h1>
          <br><br>
          <%
          	List<House> mockHouses = (List<House>) request.getAttribute("mockHouses");
          	String houseId = request.getParameter("houseId");
          	House target = null;
          	for (House house : mockHouses) {
          		if (houseId.equals(house.getId())) {
          			target = house;
          			break;
          		}
          	}
          %>
          <form action="house-form.servlet" method="post">
          <input type="hidden" name="userName" value="${param.userName}"/>
          <input type="hidden" name="houseId" value="<%=target.getId() %>"/>
          <label for="house_name">房源名字:</label><input type="text" id="house_name" name="houseName" value="<%=target.getName() %>" />
          <label for="house_detail">房源詳細信息:</label><input type="text" id="house_detail" name="houseDetail" value="<%=target.getDetail() %>" />
          <input type="submit" value="提交" />
          </form>
          </body>
          </html>
          

          注意,此時表單的提交路徑是:house-form.servlet

          所以,后面編寫HouseFormServlet時,配置其URL映射模式要與此一致。

          處理房源編輯表單的提交 - HouseFormServlet

          無非就是提取表單提交的數據,然后保存到我們的模擬數據中,最后重定向回該房源的詳情頁面。

          package houserenter.servlet;
          import java.io.IOException;
          import java.util.List;
          import javax.servlet.ServletException;
          import javax.servlet.annotation.WebServlet;
          import javax.servlet.http.HttpServlet;
          import javax.servlet.http.HttpServletRequest;
          import javax.servlet.http.HttpServletResponse;
          import houserenter.entity.House;
          @WebServlet("/house-form.servlet")
          public class HouseFormServlet extends HttpServlet {
          	private static final long serialVersionUID = 1L;
          	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
          		List<House> mockHouses = (List<House>) request.getAttribute("mockHouses");
          		String houseId = request.getParameter("houseId");
          		House target = null;
          		for (House house : mockHouses) {
          			if (houseId.equals(house.getId())) {
          				target = house;
          				break;
          			}
          		}
          		
          		String houseName = request.getParameter("houseName");
          		target.setName(houseName);
          		
          		String houseDetail = request.getParameter("houseDetail");
          		target.setDetail(houseDetail);
          		
          		String userName = request.getParameter("userName");
          		response.sendRedirect("house-details.jsp?userName=" + userName + "&houseId=" + houseId);
          	}
          }
          

          至此,全部JSP頁面和Servlet代碼已經編寫完畢,大家可以自行驗證,應該沒有問題。

          進一步改進 - include.jsp

          雖然已經完成了全部工作,運行也沒有問題,但是很明顯,三個JSP頁面中開頭的很大一部分都是重復的,這時候JSP中的include指令就排上用場了。

          我們把這部分重復的提取出來,形成一個獨立的JSP文件:

          <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
          <%@ page import="java.util.List" %>
          <%@ page import="houserenter.entity.House" %>
          <!DOCTYPE html>
          <html>
          <head>
          <meta charset="UTF-8">
          <title>租房網</title>
          </head>
          <body>
          <h1>你好,${param.userName}!歡迎來到租房網! <a href="login.html">退出</a></h1>
          <br><br>
          

          然后改造三個JSP頁面,比如房源列表頁面houses.jsp

          <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
          <%@ include file="include.jsp"%>
          <%
          	List<House> mockHouses = (List<House>) request.getAttribute("mockHouses");
          	System.out.println(mockHouses);
          %>
          <h6>共找到你感興趣的房源 <%=mockHouses.size() %> 條</h6>
          <ul>
          <%for (House house : mockHouses) { 
          	System.out.println(house); %>
          	<li><h2><a href="house-details.jsp?userName=${param.userName}&houseId=<%=house.getId() %>"><%=house.getName() %></a></h2></li>
          <%} %>
          </ul>
          </body>
          </html>
          

          直接使用include指令:

          <%@ include file="include.jsp"%>
          

          但是,要注意,房源列表頁面houses.jsp中仍然需要下面的page指令,否則會有中文亂碼:

          <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
          

          實際上,include指令也是由Servlet容器在將JSP頁面生成Servlet代碼的過程中解析,并用包含的JSP頁面的內容替換該指令,完全是解析階段發生的故事。

          最后,我們的租房網的工程結構變成:

          總結

          雖然我們的租房網還不夠完美,但總算比之前純粹使用Servlet的時候干凈清爽多了,頁面結構也比較清晰合理。

          當然,還有很多需要改進的地方,讀者朋友們自己可以思考思考。

          • 養成學、做、思、記的習慣;我寫這些文章,也就是記,鼓勵大家也多記;
          • JSP的本質就是由Servlet/JSP容器轉換成Servlet代碼;
          • Servlet代碼中HTML內容需要writer括起來;而JSP頁面中Java代碼需要<%%>括起來;
          • 加打印日志的代碼,便于調試;
          • 一邊開發、一邊測試/驗證;
          • EL表達式的基本語法是用 ${} 把內容括起來;
          • JSP和EL都有隱式對象,實際上是Servlet/JSP容器生成并傳進來的;
          • 要有演化思維,或迭代思維,或最小可用產品思維;
          • 再次運用消除重復思維,重復的地方總是可以改進的。

          TML5是移動互聯網的未來嗎?

          自2010年喬布斯公開支持并在iOS禁止Flash后,在多數開發者心中,這開始變成一個肯定的答案。

          2010年到2011年,HTML5概念被熱炒,受到追捧,甚至不少人預言HTML5應用將會替代原生app。但或許是當初被捧得太高,而不論是生態環境還是技術支持都遠遠算不上成熟,HTML5游戲在短暫熱捧之后遭遇諸多問題,隨后開始陷入冰谷,當初使用HTML5開發游戲的團隊紛紛轉型——HTML5或許將是未來,但現在談論還為時過早。

          在被遺忘一段時間之后,現在,一些專注于游戲領域的HTML5引擎服務提供商正在重整旗鼓(詳見今日推送的第二篇文章),這讓我們不由再次回頭審視HTML5游戲走過的整個路程,并開始思考一個問題:在HTML5那條通往“未來”的遙遠道路上,它正站在一個什么樣的位置上?

          ■過去


          2010年4月,蘋果公司宣布禁止FlashPlayer登陸iOS系統,鼓勵開發者使用HTML5技術,這一舉動引起了軒然大波,喬布斯甚至為此撰寫了一篇長文《關于Flash的幾點思考》進行回應。當年十月,Zynga收購了HTML5游戲引擎開發商Dextrose,并在隨后發布了第一款HTML5游戲《MafiaWarsAtlanticCity》。

          從2011年開始,HTML5的概念開始火爆,在這一年,諸多大廠紛紛出擊這一領域進行布局,MOTO投資了HTML5游戲公司Moblyng,迪士尼收購了HTML5游戲引擎公司RocketPack。

          Facebook社交游戲開發商Wooga也在當年宣布進軍HTML5和iOS游戲領域。

          在同一年,Unity確認支持HTML5,Facebook收購HTML5技術團隊Strobe,將自身在移動端的發力重心放在HTML5上。同年,手游大廠EA、Popcap、Gameloft也相繼發布了自己的HTML5游戲。

          在2011年底,還有一件重要的事情,Adobe宣布停止在Android系統更新FlashPlayer,并推薦開發者使用HTML5技術開發移動Web應用。

          就在看起來形勢一片大好HTML5概念火熱的時候,哀歌從2012年開始唱響。

          在2012年開年之際,此前MOTO投資的HTML5游戲開發商Moblyng倒閉是第一個音符,隨后在六月,Wooga宣布停止開發HTML5游戲,他們曾經推出一款HTML5游戲《MagicLand:Island》,但是玩家玩這個游戲的總次數只有130萬次,留存率僅5%,相比之下,他們在iOS平臺推出的《DiamondDash》則獲得了1800萬次的下載。

          為這支哀歌譜下強音的是Facebook的失敗。在2012年9月,扎克伯格在接受采訪時表示:“Facebook曾經錯誤地將賭注押在了HTML5上,這是我們最大的戰略錯誤,致使我們錯失了移動市場的發展良機。”

          在那之后,曾經被捧上高位打上未來標簽的HTML5技術在移動端狠狠摔落,HTML5開始淡出視野。

          總結下來,HTML5在移動游戲領域所遭遇的困境,主要是由于四個原因:

          1、技術不成熟,開發生態不完整

          2、沒有合適的載體,瀏覽器渲染性能低下

          3、沒有成熟的生態環境(渠道,運營商)

          4、受制于網絡環境

          這些原因最終造成了游戲功能和表現受限,體驗大打折扣。

          在2013年底,下一代JavaScript標準規范ES6草案鎖定并正式發布。

          ■現在

          目前HTML5的框架和庫都普遍偏于Web應用的制作,專注于游戲的偏少,比較流行的HTML5框架包括CreateJS,JQuery,AngularJS和Node.js,專注于HTML5游戲的引擎有Impact、Phaser、Pixi、Createjs、EaselJSPhaser、Turbulenz、GameClosure、Coco2d-HTML5和Egret等。

          現在主流網站幾乎全部支持HTML5標準,幾乎所有流行的網站都采用了HTML5技術。但是在移動設備上,還尚未出現非常成熟的HTML5應用或游戲,這一市場還在剛剛起步。游戲方面,此前有一二三國、修仙三國、三國喵喵傳等游戲,而目前,墨麟、游戲谷、光年互動等開發商正在開發HTML5游戲。

          正在發生的另外一些事情:

          1.各瀏覽器對HTML5標準化的支持正在慢慢趨于一致

          2.硬件的變革正在不斷推動采用HTML5技術制作的復雜應用和游戲的用戶體驗的快速提升

          3.Web游戲類型在3G/4G網絡下的數據發送接收速度正在變得相對高效,但目前國內總體網絡質量仍不樂觀

          4.逐漸涌現出的HTML5游戲引擎和制作工具,開始降低H5游戲制作成本,并提高游戲開發效率

          5.混生應用出現,上層使用h5開發,底層使用c++渲染,性能得到很大提升,這種方式是當前階段的主流

          6.一些巨頭正在嘗試引領這個市場,騰訊的手機QQ空間的安卓版應用,在前段時間將“玩吧”菜單放置在了底部菜單欄的一級入口;在玩吧中,現在已經上線了不少HTML5游戲,不僅有休閑游戲,也有一些卡牌類的中重度游戲,同時在安裝QQ空間首次登錄時,會自動進入一個名為“讓童年飛”的HTML5休閑游戲,騰訊正在嘗試引導用戶嘗試這些HTML5游戲,不過總體來說,內容還處于匱乏階段,玩吧目前僅提供13款游戲;此外還有百度輕應用等。

          看起來一切正在往前有序推進,不過站在游戲開發者角度來說又是什么看法?

          在一些開發者看來,HTML5游戲開發快、易調試、跨平臺、推廣成本更低的特點,或許會是其優勢所在,但是問題的核心在于如果不能在游戲體驗上給予玩家更多好處,那么就沒有太多的理由去看好,基于這點才會有市場,別的都是業內臆想。

          而對于游戲玩家來說,內容才是永遠的核心。

          ■未來

          HTML5的成熟條件是什么?

          sp之大文件分段上傳、斷點續傳,jsp之大文件分段上傳,jsp之大文件分塊上傳,jsp之大文件分片上傳,jsp之大文件切片上傳,jsp之大文件加密上傳,jsp之大文件斷點續傳,jsp之大文件批量上傳,jsp之文件夾上傳,jsp之文件夾批量上傳,


          文件比較大,有20G左右,用戶希望能夠在網頁里面直接上傳,一期的時候我們是直接用的HTML5的API,也就是chrome提供的API來做的,但是上線后用戶反饋不是特別的好用,用戶那邊有些電腦用的是WIN7+IE9,chrome的API在ie9里面不支持。但是用戶系統是支持的,這就把人整的有點不會了。

          前端用了JSP,VUE2,VUE3,后端用了JSP,SpringBoot,IDE用了Eclipse,MyEclipse,因為新項目和老項目都用了兩種IDE。

          用戶要求能夠在網頁上面上傳文件夾,文件夾里面大約有1萬多個文件,有大有小,大的有1G~10G,小的有幾MB,文件夾上傳的時候需要保存層級結構,同時能夠將層級結構信息保存到數據庫中,同時還需要支持文件夾下載,下載下來的文件夾要和上傳的文件夾層級結構一模一樣。

          要求支持斷點續傳,支持進度信息離線存儲,用戶可能傳一半沒有傳完,下班了,明天上班后繼續上傳,電腦晚上到點需要關機,支持加密傳輸,支持國密加密算法SM4,要求支持下載,支持非打包方式下載,瀏覽器要求支持包含IE在內的所有瀏覽器,

          斷點續傳,就是在文件上傳的過程中發生了中斷,人為因素(暫停)或者不可抗力(斷網或者網絡差)導致了文件上傳到一半失敗了。然后在環境恢復的時候,重新上傳該文件,而不至于是從新開始上傳的。

          斷點續傳的功能是基于分塊上傳來實現的,把一個大文件分成很多個小塊,服務端能夠把每個上傳成功的分塊都落地下來,客戶端在上傳文件開始時調用接口快速驗證,條件選擇跳過某個分塊。

          實現原理,就是在每個文件上傳前,就獲取到文件MD5取值,在上傳文件前調用接口,如果獲取的文件狀態是未完成,則返回所有的還沒上傳的分塊的編號,然后前端進行條件篩算出哪些沒上傳的分塊,然后進行上傳。

          當接收到文件塊后就可以直接寫入到服務器的文件中。


          導入項目:
          導入到Eclipse:
          http://www.ncmem.com/doc/view.aspx?id=9da9c7c2b91b40b7b09768eeb282e647
          導入到IDEA:http://www.ncmem.com/doc/view.aspx?id=9fee385dfc0742448b56679420f22162
          springboot統一配置:http://www.ncmem.com/doc/view.aspx?id=7768eec9284b48e3abe08f032f554ea2



          下載示例:

          https://gitee.com/xproer/up6-jsp-eclipse/tree/6.5.40/




          工程



          NOSQL

          NOSQL示例不需要任何配置,可以直接訪問測試



          創建數據表

          選擇對應的數據表腳本,這里以SQL為例




          修改數據庫連接信息


          訪問頁面進行測試



          文件存儲路徑

          up6/upload/年/月/日/guid/filename






          相關問題:
          1.javax.servlet.http.HttpServlet錯誤
          2.項目無法發布到tomcat
          3.md5計算完畢后卡住
          4.服務器找不到config.json文件

          相關參考:

          文件保存位置


          源碼工程文檔:https://drive.weixin.qq.com/s?k=ACoAYgezAAw1dWofra

          源碼報價單:https://drive.weixin.qq.com/s?k=ACoAYgezAAwoiul8gl



          OEM版報價單:https://drive.weixin.qq.com/s?k=ACoAYgezAAwuzp4W0a

          產品源代碼:https://drive.weixin.qq.com/s?k=ACoAYgezAAwbdKCskc
          授權生成器:https://drive.weixin.qq.com/s?k=ACoAYgezAAwTIcFph1


          主站蜘蛛池模板: 亚洲一区二区免费视频| 中文字幕一区二区三区精彩视频| 无码一区二区三区中文字幕| 精品一区二区三区中文字幕| 2014AV天堂无码一区| 亚洲一区日韩高清中文字幕亚洲| 日韩一区二区三区视频久久| 国产精品一区二区久久精品无码| 精品无码人妻一区二区三区不卡| 国产av成人一区二区三区| 高清国产精品人妻一区二区| 亚洲天堂一区二区三区四区| 成人免费一区二区无码视频| 免费一区二区三区四区五区| 人妻少妇精品一区二区三区| 99久久人妻精品免费一区| 国产精品日韩欧美一区二区三区| 日韩一区二区三区在线精品| 成人精品视频一区二区三区不卡| 亚洲av成人一区二区三区| 国产在线精品一区在线观看| 无人码一区二区三区视频| 国产福利91精品一区二区三区| 国产av夜夜欢一区二区三区| 三上悠亚亚洲一区高清| 精品一区二区三区东京热| 日本一区二区三区精品视频| 日本成人一区二区三区| 国产一区二区三区播放心情潘金莲| 亚洲日韩国产一区二区三区| 91精品一区二区| 中日av乱码一区二区三区乱码| 国产一区二区久久久| V一区无码内射国产| 性色AV一区二区三区天美传媒| 成人精品视频一区二区三区| 免费高清在线影片一区| 无码AV天堂一区二区三区| 一区二区在线视频免费观看| 国产成人综合一区精品| 国产在线一区二区三区|