1. 程式人生 > >Java之JSP和Servlet基礎知識。

Java之JSP和Servlet基礎知識。

JSP基礎

JSP起源

JSP,Java Server Pager的簡稱。由SUN倡導並聯合其它公司建立。

JSP是一門指令碼語言

JSP可以嵌入到HTML中

JSP擁有Java語言的所有特性

面向物件、健壯、多執行緒、安全、可移植、高效能

JSP運行於JSP容器中

Tomcat、Jetty等。

JSP會被轉換成Servlet

JSP->Servlet->Class檔案。

Mac下常用shell命令:

pwd 列印當前目錄
ls 列出當前目錄下的所有檔案目錄
cd 更改目錄
.代表當前工作目錄
..代表上一級目錄
~代表使用者根目錄
chmod更改許可權

JSP基本語法

<% %> JSP程式碼段,可以輸入任意的Java語言片段。
<%! %> JSP宣告,在JSP頁面範圍宣告變數、函式和類。
<%= %> JSP表示式
JSP中HTML註釋:

<!-- -->
<!-- <% %>--> 

<%-- --%> JSP註釋,不會被客戶端瀏覽器看到。

JSP編譯指令

通知Servlet引擎處理訊息,它只在JSP程式被轉化成Servlet的過程中起作用。

Page指令

在JSP最上方,用於指定語言,MIME,編碼,錯誤跳轉頁面等等。
1. contentType指定MIME和網頁編碼,無859.
2. import匯入多個java類,也可以使用多個page匯入多個java類。
3. errorPage表示JSP頁面發生執行時錯誤跳轉的頁面。

<%@ page contentType="text/html;charset=UTF-8" language="java" import="java.util.ArrayList,java.util.LinkedList"
errorPage="err.jsp"%>

include指令

靜態包含指令,可將外部檔案包含進來。包含JSP檔案時,不能有重複的變數宣告。
被匯入的JSP編譯指令會起作用。

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

taglib指令

JSP動作指令

客戶端請求時動態執行的指令

forward指令

重定向指令,它下面的程式碼不會被執行,不會被返回到客戶端。
可以通過jsp:param指令向跳轉的JSP頁面傳遞引數。在目標JSP頁面中通過request.getParameter()方法接收引數。

<jsp:forward page="err.jsp">
  <jsp:param name="name" value="bendeng"></jsp:param>
  <jsp:param name="pass" value="8765432"></jsp:param>
</jsp:forward>

include指令

動態包含指令,包含靜態HTML和動態JSP檔案。
被匯入的JSP編譯指令不會起作用,並可以額外加引數。

<jsp:include page="body.jsp">
    <jsp:param name="bgcolor" value="red"></jsp:param>
</jsp:include>

在被包含的JSP檔案中通過request物件將body背景色修改為傳過去的值。

<body bgcolor="<%=request.getParameter("bgcolor")%></body>

useBean指令

JavaBean:一個公開的建構函式、屬性有get、set方法、可序列化。
例如:我們在接收提交的頁面的,使用User這個bean,然後使用useBean指令配合setProperty、getProperty指令來接收顯示提交內容:
提交頁面如下:

<form action="regist.jsp" method="post">
使用者名稱:<input type="text" name="name"/><br>
密碼:<input type="password" name="pass"/><br>
<input type="submit" value="提交"/>

接受頁面如下:

<jsp:useBean id="user" class="com.ben.bean.User"></jsp:useBean>
<jsp:setProperty property="name" name="user"></jsp:setProperty>
<jsp:setProperty property="pass" name="user"></jsp:setProperty>

<jsp:getProperty property="name" name="user"></jsp:getProperty>
<jsp:getProperty property="pass" name="user"></jsp:getProperty>

JSP內建物件

out物件

向客戶端瀏覽器輸出資訊

<%
out.println("out物件");
out.newLine();
out.println("<br>");
out.flush();
out.clearBuffer();//清空緩衝區的資料
//out.clear();//也是清空緩衝區資料,不同的是如果之前呼叫flush,則會丟擲異常。
out.println("緩衝區大小:" + out.getBufferSize());//預設8K,可通過編譯指令修改,如:<%@ page buffer="16kb" %>
out.println("<br>");
out.println("當前緩衝區剩餘大小:" + out.getRemaining());
%>

request物件

request物件封裝了客戶端提交到伺服器的請求資訊,表單、cookie等資訊。

請求的方法名:<%=request.getMethod() %><br/>//GET
請求的資源:<%=request.getRequestURI() %><br/>
請求使用的協議:<%=request.getProtocol() %><br/>//HTTP/1.1
請求的伺服器IP:<%=request.getServerName() %><br/>
請求的伺服器埠:<%=request.getServerPort() %><br/>
客戶端的IP地址:<%=request.getRemoteAddr() %><br/>
客戶端的主機名:<%=request.getRemoteHost() %><br/>
getRequestURL:<%=request.getRequestURL() %><br/>
getScheme:<%=request.getScheme() %>//Http

//request.getParameter()  用於從客戶端請求獲取表單資訊

request.setAttribute();
request.getAttribute();
這兩個方法用於在web元件間共享資訊,比如JSP之間。

請求頭資訊;
<% 
Enumeration<String> e = request.getHeaderNames();
while(e.hasMoreElements()){
    String headerName = e.nextElement();
    out.println(headerName+":"+request.getHeader(headerName)+"<br/>");
}
%>

response物件

對客戶端請求封裝回覆信息。

response.setHeader("Cache-Control", "no-cache");//瀏覽器讀到這個頭資訊,就不會將網頁資訊存到快取。還有public,private等
response.setIntHeader("Refresh", 2);//每隔2秒重新整理一次
//response.sendRedirect("index.jsp");//重定向到另一個JSP網頁
//設定Cookie
Cookie cookie = new Cookie("ben","deng");
cookie.setMaxAge(3600);//單位為s
response.addCookie(cookie);

session物件

HTTP是無狀態的。伺服器使用session可以儲存客戶端瀏覽器的資訊。

<%! SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); %>
sessionId= <%=session.getId() %></br>
session建立時間= <%=sdf.format(new Date(session.getCreationTime())) %></br>
session最後訪問時間= <%=sdf.format(new Date(session.getLastAccessedTime())) %></br>
session失效時間: <%= session.getMaxInactiveInterval()%>//預設1800s

session失效時間可以通過修改部署描述符來實現。

<session-config>
    <session-timeout>10</session-timeout>
</session-config>

如果不生效,需要刪掉work目錄的檔案。

application物件

應用一啟動就會生成application物件。web應用不關閉,application就一直存在。
servlet的環境通過呼叫getServletConfig().getContext()方法獲得。作用域是application(整個程式執行期)。它提供了關於伺服器版本,應用級初始化引數和應用內資源絕對路徑,註冊資訊的方式。

伺服器資訊:<%=application.getServerInfo() %>
應用資訊:<%=application.getServletContextName() %>
主機資訊:<%=application.getVirtualServerName() %>

由於Application物件是隨應用的生命週期存在,所以通過它可以對一些配置進行全域性載入和儲存。

config物件

代表當前JSP程式的配置資訊。一般JSP在應用中作為View層使用,一般使用較少,在Servlet中用的比較多。讀取的配置資訊來自web.xml。

<%=config.getInitParameter("name") %>

在web.xml中配置一個servlet:

<servlet>
    <servlet-name>config</servlet-name>
    <jsp-file>/jsp/config.jsp</jsp-file>
    <init-param>
        <param-name>name</param-name>
        <param-value>name</param-value>
    </init-param>
    <init-param>
        <param-name>pass</param-name>
        <param-value>pass</param-value>
    </init-param>
  </servlet>
  <servlet-mapping>
    <servlet-name>config</servlet-name>
    <url-pattern>/*</url-pattern><!--這個樣式所有請求都會返回config.jsp的內容 -->
  </servlet-mapping>

在config.jsp中,使用

String name = config.getInitParameter("name"); 
String pass = config.getInitParameter("pass"); 

取出配置在web.xml中的資訊。

exception物件

exception物件只有在page指令中具有屬性isErrorPage="true"時才有效。它就是Java中普通的Throwable物件。
通過JSP錯誤頁面中一個catch塊已經益出但沒有捕獲的java.lang.Throwable的任意例項,傳向了errorPage的URI。
比如在indx.jsp中執行如下程式碼

<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8"
 errorPage="exception.jsp"%>
try{
        int c = 3/0;
    }catch(Exception e){

    }

在exception.jsp中:

<% 
    out.print(exception.getLocalizedMessage());
%>

頁面輸出/ by zero

page物件

繼承自Object物件,代表當前JSP頁面。

pageContext物件。

pageContext物件提供所有四個作用域層次的屬性查詢和修改能力,它也提供了轉發請求到其它資源和包含其他資源的方法。該物件的方法都是抽象方法。

JSP作用域

作用域規定的是變數的有效期限。JSP有四大作用域:Request、Page、Session、Application。
我們使用public Object getAttribute(String name)獲得變數值,
使用public void setAttribute(String name, Object value)將變數值儲存到對應作用域中。

物件 說明 型別 作用域
request 請求物件 javax.servlet.ServletRequest Request
response 響應物件 javax.servlet.SrvletResponse Page
pageContext 頁面上下文物件 javax.servlet.jsp.PageContext Page
session 會話物件 javax.servlet.http.HttpSession Session
application 應用程式物件 javax.servlet.ServletContext Application
out 輸出物件 javax.servlet.jsp.JspWriter Page
config 配置物件 javax.servlet.ServletConfig Page
page 頁面物件 javax.lang.Object Page
exception 異常物件 javax.lang.Throwable page

如果把變數放到pageContext裡,就說明它的作用域是page,它的有效範圍只在當前jsp頁面裡。
從把變數放到pageContext開始,到jsp頁面結束,你都可以使用這個變數。
  
如果把變數放到request裡,就說明它的作用域是request,它的有效範圍是當前請求週期。
所謂請求週期,就是指從http請求發起,到伺服器處理結束,返回響應的整個過程。在這個過程中可能使用forward的方式跳轉了多個jsp頁面,在這些頁面裡你都可以使用這個變數。

如果把變數放到session裡,就說明它的作用域是session,它的有效範圍是當前會話。
所謂當前會話,就是指從使用者開啟瀏覽器開始,到使用者關閉瀏覽器這中間的過程。這個過程可能包含多個請求響應。也就是說,只要使用者不關瀏覽器,伺服器就有辦法知道這些請求是一個人發起的,整個過程被稱為一個會話(session),而放到會話中的變數,就可以在當前會話的所有請求裡使用。

如果把變數放到application裡,就說明它的作用域是application,它的有效範圍是整個應用。
整個應用是指從應用啟動,到應用結束。我們沒有說“從伺服器啟動,到伺服器關閉”,是因為一個伺服器可能部署多個應用,當然你關閉了伺服器,就會把上面所有的應用都關閉了。

application作用域裡的變數,它們的存活時間是最長的,如果不進行手工刪除,它們就一直可以使用。
與上述三個不同的是,application裡的變數可以被所有使用者共用。如果使用者甲的操作修改了application中的變數,使用者乙訪問時得到的是修改後的值。這在其他scope中都是不會發生的,page, request,session都是完全隔離的,無論如何修改都不會影響其他人的資料。

初識Servlet

Servlet = Server + Applet。它是一個特殊的Java類。
下面新建一個Servlet:

public class Hello extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //super.doGet(req, resp);
        PrintWriter pw = resp.getWriter();
        pw.println("Hello Servlet");
        pw.close();
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //super.doPost(req, resp);
        doGet(req,resp);
    }

    @Override
    public void init() throws ServletException {
        System.out.println("hello init()");
        super.init();
    }

    @Override
    public void destroy() {
        System.out.println("hello destroy()");
        super.destroy();
    }
}

在web.xml中進行配置:

<servlet>
        <servlet-name>hello</servlet-name>
        <servlet-class>com.ben.servlet.Hello</servlet-class>
</servlet>
<servlet-mapping>
        <servlet-name>hello</servlet-name>
        <url-pattern>/hello</url-pattern>
</servlet-mapping>

使用tomcat,進行執行。localhost:8080/hello,即可顯示Hello Servlet。我是在IDEAIU中進行的,如果在Eclipse中路徑可能有變化,需要加工程名。

如果瀏覽器出現HTTP Status 405 - HTTP method GET is not supported by this URL,註釋掉doGet和doPost的父類呼叫即可。

Servlet處理流程

Servlet-Process
客戶端瀏覽器發出一個請求,tomcat內建web Server接收到請求,將請求轉達給Servlet容器,Servlet容器會載入Servlet例項。Servlet使用HttpServletRequest接收請求例項,Servlet可能會將請求轉發給其它的Servlet處理。全部處理完,請求結果通過HttpServletResponse物件返回給瀏覽器。

當Servlet裝載和例項化以後,Servlet會呼叫init()方法,進行初始化。Servlet處於服務狀態。在整個生命週期中,init()執行一次。接收請求時,會呼叫service()方法,當Servlet不再使用,容器銷燬Servlet之前會呼叫destroy()方法。destroy()方法中做一些資源釋放和日誌輸出的操作。
init()不需要過載,只在需要初始化時做一些操作時才需要過載,建議過載無參的init()方法,不用呼叫super.init()方法,有參的init()必須呼叫父類的init()方法。

使用Servlet實現簡單的登陸

login.jsp如下:

<form method="post" action="<%=request.getContextPath()%>/login">
    <input name="username" type="text" placeholder="使用者名稱"/><br/>
    <input name="password" type="password" placeholder="密碼"/><br/>
    <input type="submit" value="登陸">
    <input type="reset" value="重置">
</form>

新建一個LoginServlet:

public class LoginServlet extends HttpServlet {

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String username = req.getParameter("username");
        String password = req.getParameter("password");

        //System.out.println("使用者名稱=" + username);
        //System.out.println("密碼=" + password);

        String forward = null;
        //這裡簡單判斷使用者名稱來跳轉成功和失敗頁面
       if("ben.deng".equals(username)){
           forward = "success.jsp";
           //這種跳轉不會帶請求的引數資訊,只在響應頭中的location中指定了跳轉頁面。但可以進入其他應用的頁面
           //resp.sendRedirect("success.jsp");
       }else{
           forward = "fail.jsp";
           //resp.sendRedirect("fail.jsp");
       }
        //請求轉發,可以請求引數帶給轉發的頁面。但只限於應用內的頁面。
        RequestDispatcher dispatcher = req.getRequestDispatcher(forward);
        dispatcher.forward(req,resp);
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
       doPost(req,resp);
    }

    @Override
    public void init() throws ServletException {
        System.out.println("login init()");
    }
}

web.xml配置:

<servlet>
        <servlet-name>login</servlet-name>
        <servlet-class>com.ben.servlet.LoginServlet</servlet-class>
</servlet>
<servlet-mapping>
        <servlet-name>login</servlet-name>
        <url-pattern>/login</url-pattern>
</servlet-mapping>

Servlet過濾器

建立一個過濾器:

public class MyFilter implements Filter {

    public void destroy() {
    }

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        System.out.println("doFilter...");
        chain.doFilter(req, resp);
    }

    public void init(FilterConfig config) throws ServletException {
        System.out.println("filter init():" + config.getInitParameter("paramKey"));
    }

}

和Servlet一樣,需要在web.xml中配置:

<filter>
        <filter-name>MyFilter</filter-name>
        <filter-class>com.ben.filter.MyFilter</filter-class>
        <init-param>
            <param-name>paramKey</param-name>
            <param-value>paramValue</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>MyFilter</filter-name>
        <url-pattern>/*</url-pattern>
</filter-mapping>

在一個web應用中,可以有多個Filter,多個Filter的執行順序是和web.xml中配置的順序是一致的。

過濾器之編碼轉換

在doGet或doPost方法中返回一串有中文的字串,瀏覽器很多時候會顯示亂碼。這時我們可以在過濾器中進行處理。

 String charEncoding = "UTF-8";
 public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        if(!charEncoding.equals(req.getCharacterEncoding())){
            req.setCharacterEncoding(charEncoding);
        }
        resp.setCharacterEncoding(charEncoding);
        chain.doFilter(req, resp);
    }

過濾器之許可權校驗

新建一個許可權校驗過濾器:

public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        HttpServletRequest request = (HttpServletRequest) req;
        String loginStatus = (String) request.getSession().getAttribute("login");
        if (loginStatus != null && "success".equals(loginStatus)) {
            chain.doFilter(req, resp);
        } else {
            String reqPath = request.getServletPath();
            if (!reqPath.contains("login")) {
                //System.out.println("您尚未登陸");
                RequestDispatcher rd = request.getRequestDispatcher("login.jsp");
                rd.forward(req, resp);
            }else{
                chain.doFilter(req, resp);
            }
        }
    }

在部署描述符中註冊過濾器:

<filter>
        <filter-name>AuthFilter</filter-name>
        <filter-class>com.ben.filter.AuthFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>AuthFilter</filter-name>
        <url-pattern>/*</url-pattern>
</filter-mapping>

上述程式碼示例中是將登陸成功後再session中設定一個login屬性為success,在其他非login/login.jsp等頁面中判斷是否有這個session,沒有或值不對(比如登入失敗設定的其他值),則要求跳轉到登入介面。從而實現一個簡單的許可權校驗過濾。