首页 > 代码库 > JSP

JSP

JSP

1、什么是jsp?

(java server page: java服务器端页面技术)

sun公司制订的一种服务器端动态页面生成技术规范。

    因为直接使用servlet虽然也可以生成动态页面,但是过于繁琐(需要使用out.println输出),并且难

以维护(如果要修改页面,就必须修改java源代码),所以,sun公司制订了一种新的规范专门用于生成动

态页面,即jsp。

    jsp其实就是一个以.jsp为后缀的文件,该文件当中主要包含html和java代码。容器会将.jsp文件

转换成.java文件(其实就是一个servlet),然后调用。

2、如何写一个jsp文件?

        写一个以.jsp为后缀的文件,然后在该文件中,添加html和java代码。编写完成之后不需要编译,

     当客户端请求访问某个.jsp文件,服务器会自动将.jsp文件转换成一个.java文件。(该.java文件其实

     就是一个servlet)。

3、jsp是如何执行的?

阶段一:容器要将jsp文件转换成对应的servlet类。(该类可在tomcat的work文件中查看)

             如何转换成servlet类?

             1) html(css,javascript)  ---->service方法里,使用out.write输出

             2)<% java代码片断 %>---->service方法里,照搬

             3)<%=java表达式  %> ---->service方法里,使用out.print输出

             4)<%! java声明 %>    ----->给servlet添加新的属性或者方法

             5)out.write方法会将null转换成””,而且write方法不能输出对象只能输出字符串和基本类型

 out.println()可以输出对象

阶段二:容器会将servlet类编译实例化,初始化,然后执行service方法。

   注意:第一次访问jsp文件时,会执行阶段一步骤,之后访问不再执行阶段一步骤。

         直接访问转换好的servlet类。

案例:hello.jsp

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

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=utf-8">

</head>

<body style="font-size:30px;">

                                    <%

                                             for(int i=0;i<100;i++){

                                    %>

                                                      helloWorld!<br/>

                                    <%

                                             }

                                   %>

</body>

</html>

转换成的java文件hello_jsp.java

public final class hello_jsp extends org.apache.jasper.runtime.HttpJspBase..{

public void _jspInit(){ .... }

public void _jspDestroy(){ ..... }

public void _jspService(HttpServletRequest request, HttpServletResponse response){ .... }

}

1)类HttpJspBase继承了HttpServlet。覆盖了HttpServlet的service方法,覆盖的service方法中

            调用了_jspService方法,HttpJsPBase中的_jspService方法是空的(钩子方法,要求被覆盖),

            在hello_jsp类中重写了_jspService方法。

     2)当有请求时,容器会去调用HttpServlet的service方法,但这个方法被HttpJspBase覆盖了,

        所以会去调用HttpJspBase类中的service方法,这个方法又去调用_jspService方法,而

        hello_jsp覆盖了_jspService方法,所以去调用hello_jsp类中的_jspService方法,完成业务逻

        辑。

4jsp文件的组成

   1)html(包括css、javascript)直接写在.jsp文件里即可。

(2)java代码

     第一种形式:java代码片断   语法:<% java代码 %>

     第二种形式:java表达式      语法:<%= java 表达式 %>

     第三种形式:java声明<%!  %>

  <body style="font-size:30px;font-style:italic;">

                                             <%!

                                                      int i = 100;

                                                      int sum(int a1,int a2){

                                                               return a1 + a2;

                                                      }

                                            %>

                                            <%=i%><br/>

                                            <%=sum(1,1)%>

            </body>

(3)指令

         a、什么是指令?    

       所谓指令,就是告诉jsp引擎(容器),在将.jsp文件转换成.java文件时,做一些额外的处理。

       比如导包。

       jsp引擎:容器当中负责将.jsp文件转换成.java文件。并在运行时为jsp提供一些辅助支持的

                模块。

    b、指令语法

  <% @指令名 属性名=属性值   %>

    c、主要的指令

      1page指令:

          1)import属性:用于导包。

             比如<%@page import="java.util.*,java.text.* "  %>

          2)contentType属性:等价于response.setContentType();

                 <%@page import="java.util.*,java.text.*" %>

                 3)pageEncoding属性

             告诉容器, jsp文件的编码格式是什么。因为容器需要读取jsp文件的内容

             (也就是解码: 本地编码格式 --->unicode),

             有些容器不能够正确识别jsp文件的编码格式,所以最好加上该属性。

             <%@page  pageEncoding="utf8" %>

 4)session属性

    true(缺省)/false,当值为false时,容器不再添加获得session对象

                  <%@page session=”false” %>

 5)errorPage属性

    指定一个错误处理页面<%@page errorPage="a4.jsp" %>

 6)isErrorPage属性

    true/false(缺省),如果值为true,表示这是一个错误处理页面。

    只有当isErrorPage属性等于true,才能使用exception隐含对象。

    <%@page isErrorPage="true" %>

    案例

    a3.jsp

//当访问a3.jsp页面报错时,跳到指定的错误页面a4.jsp

                                         <%@page errorPage="a4.jsp" pageEncoding="utf-8" %>

<html>

                                                      <body style="font-size:30px;font-style:italic;">

                                                      <%

                                                               String num = request.getParameter("num");

                                                               out.println(Integer.parseInt(num) + 100);

                                                     %>

                                             </body>

</html>

a4.jsp

<%@page isErrorPage="true" pageEncoding="utf-8" %>

<html>

                                                      <body style="font-size:30px;font-style:italic;">

                                                               发生了错误: <%=exception.getMessage()%>

                                                      </body>

</html>

     2include指令:

          对于页面的公共部分,我们可以使用相同的jsp文件,并使用include指令导入,

          如此可以实现代码的优化。

          1)file属性:告诉容器,在将.jsp文件转换成.java文件时,在指令所在的位置插入file指

                      定的文件的内容。

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

     3taglib属性

          用于导入书签

          1)prefix属性:命名空间的前缀

                     <%@taglib uri="http://java.sun.com/jsp/jstl/core"  prefix="c" %>

(4)隐含对象

a、什么是隐含对象?

        所谓隐含对象,指的是在.jsp文件当中,不用声明和创建该对象,就可以直接使用的对象。

b、为什么可以直接使用这些隐含对象?

   .jsp文件对应的.java文件当中,因为容器会自动添加创建或者获得这些对象的语句。  

          out  request  response   session    application

 c、九种隐含对象

out

request

response

session

application(就是servletContext,servlet上下文)

            1exception

               必须设置isErrorPage=”true”才能使用这个隐含对象。可以通过该对象获得jsp页面运行的错

               误信息。

                           <%@page isErrorPage="true"%>

             2config

               就是ServletConfig,可以读取jsp的配置参数。

 案例:为jsp配置初始化参数

 web.xml

                                   <servlet>

                                           <servlet-name>a7</servlet-name>

                                           <jsp-file>/a7.jsp</jsp-file>

                                           <init-param>

                                                    <param-name>company</param-name>

                                                    <param-value>北京达内</param-value>

                                           </init-param>

                                  </servlet>

                                  <servlet-mapping>

                                           <servlet-name>a7</servlet-name>

                                           <url-pattern>/abc.html</url-pattern>//可以随便写

</servlet-mapping>

a7.jsp

                                   <body style="font-size:30px;font-style:italic;">

                                             公司名称:<%=config.getInitParameter("company") %>

                                    </body>

发送请求:

http://localhost:8088/web10/abc.html

              3pageContext

              是PageContext类的实例,容器会为每一个jsp实例(指的是jsp对应的那个servlet对象)创建一

              个符合PageContext接口要求的对象,一般称之为page上下文。

两个特点:

1)唯一性

   一个jsp实例对应唯一一个page上下文

2)一直存在

   只要jsp实例没有被容器销毁,则page上下文就一直存在。

作用

1)绑定数据

   setAttribute、getAttribute、removeAttribute

2)提供了相应的方法来获得其他八个隐含对象。对象

           4page

               表示jsp实例本身 。相当于this。

               如下jsp隐含对象访问范围从小到大

?              pageContext     只有对应的JSP实例自己可以访问,生命周期从JSP对象创建到JSP对象消亡。

?        request              一次请求能访问,生命周期在一起请求和响应期间。

?        session              一次会话期间能访问,多次请求和响应期间都存在。

?        ServletContext 整个应用内部所有组件都能访问,除非服务器关闭,否则一直存在。

(5)注释

      a,  <!--  注释的内容 -->

        允许注释的内容是java代码,如果是java代码,容器会执行。

        但是执行的结果浏览器不会显示。

          b,  <%--   注释的内容 -->

        不允许出现java代码。如果是java代码,会被容器忽略。

a8.jsp

<html>

                           <body style="font-size:30px;font-style:italic;">

                                    当前系统时间:<!-- <%=new Date()%> -->

                                    当前系统时间:<%--<%=new Date()%>--%>

                           </body>

</html>

发送请求:http://localhost:8088/web10/a8.jsp,页面上都没有时间数据

查看页面源代码

<html>

         <head></head>

         <body style="font-size:30px;font-style:italic;">

                  当前系统时间:<!-- Sat Oct 26 15:11:33 CST 2013 -->

                  当前系统时间:

         </body>

</html>

5、案例empList.jsp

localhost:8080/emp/empList.jsp(empList.jsp是放在webRoot下面的)

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

<%@page import="dao.EmployeeDAO"%>

<%@page import="util.Factory"%>

<%@page import="java.util.List"%>

<%@page import="entity.Employee"%>

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=utf-8">

<style type="text/css">

         .row1{       background-color: red;           }

                                    .row2{       background-color: yellow;     }

</style>

</head>

<body style="font-size:30px;">

                           <table cellpadding="0" cellspacing="0" border="1" width="60%">

                                    <tr style="">

                                             <td>ID</td>

                                             <td>姓名</td>

                                             <td>薪水</td>

                                             <td>年龄</td>

                                             <td>操作</td>

                                    </tr>

                           <%

                                    EmployeeDAO dao=(EmployeeDAO)Factory.getInstance("EmployeeDAO");

                                    List<Employee> employees=dao.findAll();

                                    for(int i=0;i<employees.size();i++){

                                             Employee e=employees.get(i);

                           %>

                                             <tr class="row<%=(i%2+1) %>">

                                                      <td><%=e.getId() %></td>

                                                      <td><%=e.getName() %></td>

                                                      <td><%=e.getSalary() %></td>

                                                      <td><%=e.getAge() %></td>

                                                      <td><a href="">删除</a>&nbsp;&nbsp;<a href="">修改</a></td>

                                             </tr>

                           <%

                                    }

                           %>

                           </table>

</body>

</html>

6、转发

1) 什么是转发?

一个web组件(jsp/servlet)将未完成的处理通过容器转交给另一个web组件继续处理。转发的各个

         组件会共享request和response对象。

最常见的情况是:一个servlet 将数据转交给一个jsp,由该jsp生成相应的页面。

2) 如何转发?

   step1 先绑订数据 request.setAttribute(String name,Object obj);

               跟绑订相关的另外两个方法 (如果name对应的值不存在,返回null。)

               Object request.getAttribute(String name); 取出数据,然后进行下一步的处理。

                request.removeAttribute(String name);

   step2 获得转发器,转发

 RequestDispatcher rd = request.getRequestDispatcher(String uri);

 rd.forward(request,response);

3) 编程中要注意的问题

   转发之前,不能够执行out.close或者out.flush。(容器不允许发送两个数据包)

   转发之前,会将response中缓存的数据先清空。

4) 转发的特点

a. 转发的目的地只能是同一个应用内部的某个组件

b. 转发之后,浏览器地址栏的地址没有变化

c. 转发所涉及的各个组件可以共享同一个request,response对象

7、转发与重定向的区别

(1)转发所涉及的各个web组件(servlet和jsp)会共享request对象和response对象;重定向不行。

                 因为转发是一次请求,重定向是两次请求。

request和response对象的生存时间:

当请求到达容器,容器创建这两个对象,当响应发送完毕,容器会立即删除这两个对象。

(2)转发的地址必须是同一个应用内部的某个组件,重定向的地址是任意的。

(3)转发共享request,重定向不行。转发之后浏览器地址不变,重定向会变

(4)转发是一件事没做完,让给其他组件做。重定向是一件事已经做完。

 

8、表单中的中文问题

表单中文处理步骤 

step1、jsp文件,要添加

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

表单设置method="post"。

step2、在servlet类当中,添加request.setCharacterEncoding("utf-8");

step3 如果要访问数据库:

 a. 保证数据库能够正常地保存中文。

对于mysql数据库 create database dbname default character set utf8;

 b. 使用jdbc访问数据库时,必须要保证jdbc驱劢程序能够识别数据库中保存数据的编码。

                  jdbc:mysql://localhost:3306/jd1109db2? useUnicode=true&characterEncoding=utf8

9、如何处理servlet产生的系统异常?

方式1: 使用转发(更灵活一些,会绑定错误提示)

                  step1, 绑订错误提示信息。

                  step2, 转发到某个错误处理页面(比如,error.jsp)。

servlet

try {

                                                      dao.delete(id);

                                              } catch (Exception e) {

                                                      e.printStackTrace();

                                                      request.setAttribute("error", "系统异常,稍后重试!");

                                                      request.getRequestDispatcher("error.jsp").forward(request, response);

                   }

error.jsp

<body  style="font-size:30px;color:red;">

                                                               <%=request.getAttribute("error")%>

</body>

方式2: 让容器来处理

                  step1, 编写一个错误处理页面(error.jsp)

                  step2, 将异常抛出给容器   throw new ServletException(e)

                  step3, 配置错误处理页面,让容器知道出现异常后,应该调用哪一个页面。

servlet

try {

                                                      dao.modify(e);

                                                      response.sendRedirect("/emp/list.do");

                                             } catch (Exception e1) {

                                                      e1.printStackTrace();

                                                      //将异常抛给容器(容器调用这个servlet方法,有异常,容器解决)

                                                      throw new ServletException(e1);

}

web.xml

<web-app version="2.4" ....>

                                                    <servlet>

                                                             <servlet-name>action</servlet-name>

                                                             <servlet-class>web.ActionServlet</servlet-class>

                                                    </servlet>

<servlet-mapping>

                                                             <servlet-name>action</servlet-name>

                                                             <url-pattern>*.do</url-pattern>

                                                    </servlet-mapping>

<error-page>

                                                             <exception-type>javax.servlet.ServletException</exception-type>

                                                             <location>/error.jsp</location>

                                                    </error-page>

</web-app>

10、路径问题

   1) 链接地址、表单提交、重定向、转发的路径应该如何写?

a. 链接地址: <a href=""></a>

b. 表单提交: <form action="">

c. 重定向: response.sendRedirect("");

d. 转发: getRequestDispatcher("");

2) 相对路径不绝对路径

相对路径: 不以"/"开头的路径,比如:<a href="http://www.mamicode.com/del.do"></a> 在当前路径下进行跳转

绝对路径: 以"/"开头的路径,比如:<a href="http://www.mamicode.com/appname/del.do"></a>

建议:因为相对路径较易出错,建议在实际开发中尽量使用绝对路径

3) 怎样写绝对路径?

链接、表单提交、重定向,绝对路径要从应用名开始写。 转发要从应用名之后开始写。

获得实际部署时的应用名 /appname

String request.getContextPath()

response.sendRedirect(request.getContextPath() + "/app3/sub/some.jsp");

<body style="font-szie:30px;">

                           a1....<br>

                           <a href="http://www.mamicode.com//a2.jsp">a2.jsp</a>

</body>

<body style="font-size:30px;">

                           a2.jsp<br>

                           <a href="http://www.mamicode.com//app/a1.jsp">a1.jsp</a>

</body>

转发:request.getRequestDispatcher("/emplist.jsp").forward(request, response);

10、状态管理

   1)什么是状态管理?

 将客户端与服务器之间的多次交互当作一个整体来看待,并且将多次操作所涉及的数据记录下

 来。(状态管理就是这些数据的管理)

   2)怎样进行状态管理?

      第一种方式:将状态保存在客户端:cookie(在客户端管理用户的状态)

      第二种方式:将状态保存在服务器端:session(在服务器端管理用户的状态)

11Cookie

  1)什么是cookie

                浏览器在向服务器发送请求时,服务器将少量的数据以set-cookie消息头的形式发送给浏

       览器。浏览器会将这些数据(key-value形式)保存起来(可能保存在内存里,也可能保存在

       硬盘里)。当浏览器再次访问服务器时,会将这些数据以cookie消息头的形式发送给服务器。

       通过这种方式,可以管理用户的状态。

 服务器发送给浏览器端的形式:Set-Cookie: username=zhangsan

 浏览器发送给服务器的形式:Cookie: username=zhangsan; userpwd=1234

     (2)cookie适合放什么数据?

          这个取决于网站自身,有的说网站会存储一些重要的用户信息(什么用户名、密码、浏览记录、

          IP地址什么的)到Cookie里。事实上:

          普通网站都不会存重要的信息,它们仅仅存一个你的登陆状态,也就是你拿用户名密码换取的

          令牌,还有就是网站针对你的判定(比如你在这个网站上的唯一标识是什么,你访问的是我们的

          哪台服务器,你使用的是我们的哪个版本的产品),这些信息你都不需要关心,它和你的隐私一

          点关系都没有。

          文艺一点的网站会将这些信息进行加密,目的是防止别人伪造这些信息欺骗网站。

          在Cookie里存用户名、密码的,也许是央视网的做法在互联网上是极其极其少见的,可能只有

          外行或者刚学网络开发的学生会这么做,这种网站是极其不安全的,你的信息很容易就泄漏了,

          所以还是少去访问。

 网站的一些为你推荐的产品,您可能会喜欢的宝贝等等功能都是把用户经常浏览信息记录下

 来,用户再次登录根据信心返回相应的一些数据给客户。

 淘宝显示浏览过的商品的功能就是使用cookie技术:

 用户浏览淘宝页面时,服务器将items=1,3,4,5,6,7(商品的id值)这样的数据以cookie的形式

 发送给浏览器并且设置cookie的保存时间比较长,用户之后再访问淘宝页面时,将intems=1,2..

 发送给服务器,服务器根据这个id值查询出商品信息显示在页面上。

  2)创建cookie

       Cookie  cookie=new Cookie(String name,String value);

       response.addCookie(cookie);

  3)查询cookie(如果没有cookie,则返回null

       Cookie[ ]  cookies= request.getCookies( ); 注意:该方法有可能返回null

       String name=cookie.getName();

       String value=http://www.mamicode.com/cookie.getValue();

      案例:创建和查询cookie

 <web-app version="2.4" ...>

                           <servlet>

                                    <servlet-name>addCookie</servlet-name>

                                    <servlet-class>web.AddCookieServlet</servlet-class>

                           </servlet>

                           <servlet-mapping>

                                    <servlet-name>addCookie</servlet-name>

                                    <url-pattern>/addCookie</url-pattern>

                           </servlet-mapping>

                           <servlet>

                                    <servlet-name>getCookie</servlet-name>

                                    <servlet-class>web.GetCookieServlet</servlet-class>

                           </servlet>

                           <servlet-mapping>

                                    <servlet-name>getCookie</servlet-name>

                                    <url-pattern>/getCookie</url-pattern>

                           </servlet-mapping>

 </web-app>

 AddCookieServlet

public class AddCookieServlet extends HttpServlet{

                                    protected void service(HttpServletRequest request, HttpServletResponse response)

                                                      throws ServletException, IOException {

                                             Cookie cookie=new Cookie("username","zhangsan");

                                             Cookie cookie2=new Cookie("userpwd","1234");

                                             response.addCookie(cookie);

                                             response.addCookie(cookie2);

                                    }

}

 GetCookieServlet

public class GetCookieServlet extends HttpServlet{

                                    protected void service(HttpServletRequest request, HttpServletResponse response)

                                                               throws ServletException, IOException {

                                             Cookie[] cookies=request.getCookies();

                                             for(Cookie cookie:cookies){

                                                      System.out.println(cookie.getName());

                                                      System.out.println(cookie.getValue());

                                             }

                                    }

}

 在地址栏发送请求:http://localhost:8888/web/setCookie时,服务器向浏览器发送两个Cookie

 以Set-Cookie: username=zhangsan,Set-Cookie: userpwd=1234形式发送给浏览器。

 在地址栏发送请求:http://localhost:8888/web/getCookie时,浏览器将cookie发送给服务器,

 以Cookie: username=zhangsan; userpwd=1234的形式发送给服务器

 4cookie保存时的编码问题

      cookie的值只能是ascii字符,如果是中文,需要将中文转换成ascii字符串形式。

 可以使用URLEncoder.encode()方法和URLDecoder.decode()方法来进行这种转换。

 java.net.URLEncoder.encode()可以对要传递的中文进行编码

                  /**

                   * encode方法先把字符串按照指定的编码格式(比如:utf-8),

          * 然后将编码之后得到的字节数组转换成一个ascii字符串

                   */

                   String str=URLEncoder.encode("过儿","utf-8");

                   System.out.println(str);

                   String str2=URLDecoder.decode(str,"utf-8");

 System.out.println(str2);

                  案例:cookie中保存中文

  AddCookieServlet

public class AddCookieServlet extends HttpServlet{

                                    protected void service(HttpServletRequest request, HttpServletResponse response)

                                                      throws ServletException, IOException {

                                             Cookie cookie=new Cookie("username",URLEncoder.encode("张三丰","utf-8"));

                                             Cookie cookie2=new Cookie("userpwd","1234");

                                             response.addCookie(cookie);

                                             response.addCookie(cookie2);

                                    }

}

 GetCookieServlet

public class GetCookieServlet extends HttpServlet{

                                    protected void service(HttpServletRequest request, HttpServletResponse response)

                                                               throws ServletException, IOException {

                                             Cookie[] cookies=request.getCookies();

                                             for(Cookie cookie:cookies){

                                                      System.out.println(cookie.getName());

                                                      System.out.println(URLDecoder.decode(cookie.getValue(), "utf-8"));

                                             }

                                    }

}

         5cookie的保存时间

 cookie.setMaxAge(int seconds); 单位是秒

?              seconds > 0 :浏览器会将cookie以文件的方式保存在硬盘上。

                       在超过指定的时间以后,会删除该文件。

?              seconds < 0 :默认值,浏览器会将cookie保存在内存里面。只有当浏览器关闭之后才会删除。

?              seconds = 0 :立即删除该Cookie

 AddCookieServlet

public class AddCookieServlet extends HttpServlet{

                                    protected void service(HttpServletRequest request, HttpServletResponse response)

                                                      throws ServletException, IOException {

                                             Cookie cookie=new Cookie("username",URLEncoder.encode("张三丰","utf-8"));

                                             Cookie cookie2=new Cookie("userpwd","1234");

//cookie会在60秒之后被删除

cookie.setMaxAge(60);

//cookie会在浏览器关闭时被删除

                                             cookie2.setMaxAge(-1);

                                             response.addCookie(cookie);

                                             response.addCookie(cookie2);

                                    }

}

         6删除cookie

        比如要删除一个name为"username"的cookie。

        Cookie c = new Cookie("username","");

c.setMaxAge(0);

        response.addCookie(c);

  DelCookieServlet

protected void service(HttpServletRequest request, HttpServletResponse response)

                                                                                                   throws ServletException, IOException {

                                    Cookie cookie=new Cookie("username","");

                                    cookie.setMaxAge(0);

                                    response.addCookie(cookie);

                  }

(7)练习:查询名称为username的cookie,如果找不到,就添加名为username的cookie

              public class FindCookieServlet extends HttpServlet{

                           protected void service(HttpServletRequest request, HttpServletResponse response)

                                                                                                   throws ServletException, IOException {

                                    response.setContentType("text/html;charset=utf-8");

                                    PrintWriter out=response.getWriter();

                                    Cookie[] cookies=request.getCookies();

                                    if(cookies!=null){

                                             boolean flag=false;

                                             for(Cookie cookie:cookies){

                                                      if(cookie.getName().equals("username")){

                                                               out.println(URLDecoder.decode(cookie.getValue(),"utf-8"));

                                                               flag=true;

                                                               break;

                                                      }

                                             }

                                             if(!flag){

                                                      Cookie cookie=new Cookie("username",URLEncoder.encode("张三","utf-8"));

                                                      response.addCookie(cookie);

                                             }

                                    }else{

                                                      Cookie cookie=new Cookie("username",URLEncoder.encode("张三","utf-8"));

                                                      response.addCookie(cookie);

                                             }

                                    out.close();

                           }

}

7 cookie的路径问题

 1)什么是cookie的路径问题?

浏览器在向服务器上的某个地址发送请求时, 会先比较cookie的路径与向访问的路径

(地址)是否匹配,只有匹配的cookie,才会发送。

 2)cookie的默认路径

         cookie的路径默认等于创建这个cookie的组件的路径

比如:/web07/app01/addCookie.jsp创建了一个cookie,则该cookie的路径等于"/web07/app01"。

 3)匹配规则

    要访问的服务器的地址必须是cookie的路径或者是其子路径,浏览器才会发送该cookie。

    http://localhost:8080/web07/findCookie1.jsp                              error

    http://localhost:8080/web07/app01/findCookie2.jsp                    ok

    http://localhost:8080/web07/app01/sub/findCookie3.jsp    ok

 4)修改cookie的路径

cookie.setPath(String path);           比如:cookie.setPath("/web07");

经常将cookie的路径设置为应用名,这样可以保证该cookie可以被该应用的其它组件都能访

问到。

 5)要添加一个cookie,一般需要这样写

    Cookie cookie=new Cookie(“username”,URLEncoder.encode(“李白”,”utf-8”));

    cookie.setMaxAge(3600);

    cookie.setPath(“/web07”);

    response.addCookie(cookie);

(8) cookie的限制

 1)不安全

    因为cookie存放在浏览器端,可以很容器被查看到,所以,如果有敏感数据,一定需要加密。

 2)cookie可以被用户禁止

 3)cookie的大小有限制(大约只能存放4k左右的数据,具体大小跟浏览器有关系)

 4)cookie的个数也有限制(浏览器大约只能保存300个左右的cookie)。

 5)cookie只能保存字符串,并且,需要考虑编码问题。

(9)小知识

浏览器有保存密码的功能,存在安全隐患。密码信息将很容器被他人窃取。

安全建议:

OWASP建议网站设计者应该禁止浏览器保存用户登录密码,实现方法如下面的html标签。

<input type=”password” autocomplete=”offf”>      autocomplete属性是html5后新增的属性

有了autocomplete属性的输入框,浏览器就不会记录密码了,Yahoo登陆页面就是这样的。

12session

 1)什么是session?

 session是一种服务器端的状态管理技术。

 浏览器访问服务器时,服务器会创建一个session对象(该对象有一个id属性,其值是唯一的, 一

 般称为sessionId)。

 服务器在缺省情况下,会将sessionId以cookie机制(将sessionId放到set-cookie消息头)发送给浏

 览器(浏览器把sessionId放入内存中)。

 当浏览器再次访问服务器时,会将sessionId发送给服务器。服务器依据sessionId就可以找到对应

 的session对象。通过这种方式,就可以管理用户的状态。

 比如:

 jsp页面在转成java文件时有:HttpSession  session=pageContext.getSession()。

 这个方法里实际调用了request.getSession()方法。

 所以发送一个jsp的请求就会创建一个session对象。所以在jsp页面中可以直接使用session对象

 a、浏览器发送请求:http://localhost:8088/emp/list.do

    查询数据时还没有创建session对象,转发到emplist.jsp时,jsp对应的java文件中创建了一个

    session对象,发送响应时服务器将session对象的sessionId以cookie的形式发送给浏览器

    Set-Cookie: JSESSIONID=70C101358F84CEF4A63B3EAFCCF1BC52; Path=/emp

    该cookie的路径是/应用名

 b、浏览器再次发送请求:http://localhost:8088/emp/addEmp.jsp时,会把sessionId再以cookie的形

    式发送给浏览器

    Cookie: JSESSIONID=70C101358F84CEF4A63B3EAFCCF1BC52

 注意浏览器保存的cookie JSESSIONID在浏览器关闭时就被删除了。过期时间为浏览会话结束时

 也就是说,每次关闭浏览器后所有的session不可用。再次访问服务器都是重新创建session对象

2)如何获得session对象

方式一: HttpSession session = request.getSession(boolean flag);

       1)当flag = true:

          服务器会先查看请求中是否包含sessionId, 如果没有,则创建一个session对象。

          如果有,则依据sessionId去查找对应的session对象,如果找到,则返回。

          如果找不到(可能被容器删除了),则创建一个新的session对象。

       2)当flag = false:

          服务器会先查看请求中是否包含sessionId, 如果没有,返回null。

          如果有,则依据sessionId去查找对应的session对象,如果找到,则返回。

          如果找不到,返回null。

方式二:HttpSession session = request.getSession(); 与request.getSession(true)等价。

3HttpSession接口提供的一些方法

1)获得sessionId。

   String session.getId();

2)绑订数据

   session.setAttribute(String name,Object obj); 绑定数据

   Object session.getAttribute(String name); 如果name对应的值不存在,返回null。

   session.removeAttribute(String name); 解除绑定

3)案例:计算用户是第几次访问?

   web.xml

<servlet>

                                    <servlet-name>count</servlet-name>

                                    <servlet-class>session.CountServlet</servlet-class>

                           </servlet>

                           <servlet-mapping>

                                    <servlet-name>count</servlet-name>

                                    <url-pattern>/count</url-pattern>

</servlet-mapping>

   CountServlet

protected void service(HttpServletRequest request, HttpServletResponse response)

                                                                                                            throws ServletException, IOException {

                                    response.setContentType("text/html;charset=utf-8");

                                    PrintWriter out = response.getWriter();

                                    HttpSession session = request.getSession();

                                    //session.setMaxInactiveInterval(35);

                                    String sessionId = session.getId();

                                    System.out.println("sessionId:" + sessionId);

                                    Integer count = (Integer)session.getAttribute("count");

                                    if(count == null){

                                             //第一次访问

                                             count = 1;

                                    }else{

                                             //不是第一次,在原有值的基础上加1

                                             count ++;

                                    }

                                    session.setAttribute("count", count);

                                    out.println("你是第:" + count + " 次访问");

                                    //session.invalidate();把session对象内容清空

                           }

(4)session超时

1)什么是session的超时?

   session对象不是一直存在的。

   服务器会将超过指定时间的session对象删除(在指定的时间内,该session对象没有使用)。

   原因是,过多的session对象会占用服务器过多的内存空间。

   大部分服务器都会有一个缺省的超时限制,一般是30分钟。

2)修改服务器缺省的session超时限制。

   方式一: session.setMaxInactiveInterval(int seconds);单位是秒

   方式二: 服务器有一个缺省的超时限制,可以通过相应的配置文件来重新设置。

            比如可以修改tomcat的web.xml(tomcat_home/conf下面)。

            <session-config> <session-timeout>30</session-timeout> </session-config>

            修改完之后,需要重新启动服务器。

            另外,也可以将以上配置放在某个应用的web.xml文件当中。

           (这样修改的内容只针对当前应用)

5)立即删除session

  session.invalidate(); 把session对象内容清空

6session的优缺点

优点:

         session相对安全

         session能够保存的数据类型更丰富

         session能够保存的数据大小更大

缺点:

         session需要将所有数据写在服务器端,所以,服务器会占用过多的内存空间。

     可以考虑使用cookie来代替或者使用数据库来保存状态(即数据)。

注意:无论是cookie还是session都不能永久保存数据,要想永久保存数据就把数据插入到数据库里。

(7)session里通常存些什么值?

session通常用来保存与用户信息相关的:身份信息、登陆状态,用户的个性设置、权限列表,

其他的一些通用数据(比如购物车)

原则是把通用的,频繁存取的、小数据量的跟用户相关的数据放入session,视场景而定。

     session就相当于一个缓存,放在内存中,避免多次存取而效率低下。但是

     如果存放的数据比较多的话,会占用过多的内存

11、用户禁止cookie以后,如何继续使用session

  当用户禁止cookie以后,服务器仍然会发送sessionId(以set-cookie消息头的方式)。但是,浏览器

       会拒绝接受,这样,session机制会失效。

1)解决方式

   使用url重写机制

2)什么是url重写?

        如果要访问的web组件(jsp/servlet)需要session机制的支持,那么不能够直接输入该web组件

        的地址,而应该使用服务器生成的包含有的sessionId的地址。

       3)如何生成包含有sessionId的地址?

   方式1(适用于链接、表单提交)

response.encodeURL(String url); 等价于 response.encodeUrl(String url)(不常用)

   方式2(适用于重定向)

response.encodeRedirectURL(String url);

   转发不需要、转发和session无关,是服务器内部完成,没有和浏览器进行交互

session没有被禁止时

<a href="http://www.mamicode.com/count">vist countServlet</a>

访问countServlet步骤:

① 浏览器访问test.jsp

② 服务器为该浏览器用户创建一个Session对象,用于保存此次会话的数据

③ 服务器将sessionId返回给浏览器&&返回生成的显示给用户的页面

④ 浏览器将服务器传回的sessionId保存到内存中

⑤ 当用户点击“链接”<a href="http://www.mamicode.com/count">时,浏览器发送带sessionId的请求给服务器

⑥ 服务器中的CountServlet通过该sessionId找到该用户对应的Session并做计数操作

⑦ CountServlet将计数结果和页面返回给用户浏览器

session被禁止时:

<a href="http://www.mamicode.com/<%=response.encodeURL("count")%>">vist countServlet</a>

访问countServlet步骤:

① 浏览器访问test.jsp

② 服务器创建Session对象

③ test.jsp将页面和SessionId返回给浏览器

   因为浏览器禁用了Cookie,所以浏览器并不保存sessionId, 为了能继续使用Session,

   我们在test.jsp中重写了URL, 所以此时test.jsp返回给浏览器一个带有sessionId的地址:

                 http://localhost:8080/web08_session/count;jsessionid=596A1BC51F79553E341AF3B

④ 当用户点击“链接”,向服务器发送的请求中包含了sessionId

⑤ 服务器通过这个sessionId可以找到对应的Session

⑥ CountServlet将计数结果和页面返回给用户浏览器

12、过滤器

(1)什么是过滤器?

     servlet规范当中定义的一种特殊的类,用于对servlet容器的调用过程进行拦截。

     执行步骤:

1) 浏览器发送请求给服务器

2) 服务器的Servlet引擎创建Request对象&&Response对象

3) Servlet引擎先调用过滤器的doFilter方法,该方法有两个参数request和response,

(在过滤器中可以访问到Request对象&&Response对象)

4) 过滤器对拦截的内容进行处理

5) 之后调用SomeServlet的service方法

6) service方法执行

7) service方法执行结束后,将结果返回到过滤器

8) 过滤器将service方法返回的结果再次进行过滤

9) 最后,Servlet引擎将结果返回给浏览器

    (2)怎样写一个过滤器?

    step1、写一个java类,实现一个Filter接口

    step2、在doFilter方法里,实现过滤的逻辑

    step3、配置(web.xml)

    案例:对用户的评论过滤

                jsp页面

<body style="font-size:30px;">

                                    <form action="<%=request.getContextPath() %>/comment" method="post">

                                             评论:<input type="text" name="content"><br>

                                             <input type="submit" value="http://www.mamicode.com/发表">

                                    </form>

</body>

web.xml文件

<filter>

                                    <filter-name>filter1</filter-name>

                                    <filter-class>web.Filter1</filter-class>

                           </filter>

                           <filter-mapping>

                                    <filter-name>filter1</filter-name>

                                    <url-pattern>/comment</url-pattern>

</filter-mapping>

<servlet>

                                    <servlet-name>comment</servlet-name>

                                    <servlet-class>web.CommentServlet</servlet-class>

                           </servlet>

                           <servlet-mapping>

                                    <servlet-name>comment</servlet-name>

                                    <url-pattern>/comment</url-pattern>

</servlet-mapping>

Filter1

                      public class Filter1 implements Filter{

                       //容器再删除过滤器对象之前,会调用destory方法

                                    public void destroy() {

                                             System.out.println("过滤器被销毁");

                                    }

//类似于servlet的service方法,请求到达时容器会调用doFilter方法来处理请求

// servletrequest是容器创建的request对象,servletresponse是response对象

//容器会将request对象和response对象作为参数传给doFilter方法

//FilterChain:过滤器链

// FilterChain 对象有一个doFilter方法,如果该方法调用了,

//表示让容器继续向后调用过滤器或者servlet

public void doFilter(ServletRequest servletrequest,ServletResponse servletresponse,

FilterChain filterchain)throws IOException, ServletException {

//HttpServletRequest是sun公司过度设计的产物

                                             HttpServletRequest request=(HttpServletRequest) servletrequest;

                                             HttpServletResponse response=(HttpServletResponse) servletresponse;

                                             request.setCharacterEncoding("utf-8");

                                             response.setContentType("text/html;charset=utf-8");

                                             PrintWriter out=response.getWriter();

                                             String str=request.getParameter("content");

                                             if(str.lastIndexOf("dog")!=-1){

                                                      out.println("评论当中包含了敏感字!");

                                             }else{

                                                      filterchain.doFilter(servletrequest, servletresponse);

                                             }

                                   //容器启动之后,会立即创建过滤器对象

//接下来,会调用init方法.在调用init方法之前,容器会先创建好一个符合FilterConfig

//接口要求的对象。该对象可以用来访问过滤器的初始化参数

                 (getInitParamter(String paraname))

                                    public void init(FilterConfig filterconfig) throws ServletException {

                                             //FilterConfig相当于ServletConfig,用来读取初始化参数

                                             System.out.println("过滤器被初始化");

                                    }

                           }

CommentServlet

protected void service(HttpServletRequest request, HttpServletResponse response)

                                                                                                   throws ServletException, IOException {

                                    request.setCharacterEncoding("utf-8");

                                    response.setContentType("text/html;charset=utf-8");

                                    PrintWriter out=response.getWriter();

                                    String str=request.getParameter("content");

                                    out.println("您的评论是:"+str);

}

    (3)配置初始化参数

    step1、web.xml中,使用<init-para>元素来配置初始化参数

    step2、在Filter类中,使用FilterConfig.getInitParamter(String paraName);获得初始化参数

    案例:将敏感字写在配置文件里

web.xml文件

<filter>

                           <filter-name>filter1</filter-name>

                           <filter-class>web.Filter1</filter-class>

<!-- 初始化参数 -->

<init-param>

       <param-name>illegalStr</param-name>

       <param-value>cat</param-value>

</init-param>

                  </filter>

                  <filter-mapping>

                           <filter-name>filter1</filter-name>

                           <url-pattern>/comment</url-pattern>

</filter-mapping>

Filter1

public class  Filter1 implements Filter{

         private FilterConfig config;

         public  Filter1(){

                  System.out.println("Filter1‘s constructor...");

         }

         public void destroy() {

                  System.out.println("Filter1‘s destroy...");

         }

         public void doFilter(ServletRequest arg0,

                           ServletResponse arg1, FilterChain arg2) throws IOException, ServletException {

                  System.out.println("Filter1‘s doFilter begin...");

                  HttpServletRequest request = (HttpServletRequest)arg0;

                  HttpServletResponse response = (HttpServletResponse)arg1;

                  request.setCharacterEncoding("utf-8");

                  String content = request.getParameter("content");

                  response.setContentType("text/html;charset=utf-8");

                  PrintWriter out = response.getWriter();

                  //读过滤器的初始化参数

                  String illegalStr = config.getInitParameter("illegalStr");

                  if(content.indexOf(illegalStr) != -1){

                           out.println("评论当中包含了敏感字");

                  }else{

                           //调用后续的过滤器或者servlet

                           arg2.doFilter(arg0, arg1);

                  }

                  System.out.println("Filter1‘s doFilter end.");

         }

         public void init(FilterConfig arg0) throws ServletException {

                  System.out.println("Filter1‘s init...");

                  config = arg0;

         }

}

    (4)过滤器的优先级

         当有多个过滤器都满足过滤的条件时,依据<filter-mapping>的先后顺序依次执行。

    (5)过滤器的优点

    a、可以将多个web组件相同的处理逻辑集中写在一个过滤器中,方便代码的维护

    b、可实现代码的“可插拔性”

  给一个软件增加或者减少某个模块,不会影响程序的正常运行

13ServletContext接口servlet上下文)

    (1)什么是ServletContext(servlet上下文)

     context:环境 上下文 。

     web服务器在启动时,会为每一个已经部署的应用创建唯一的一个ServletContext实例。 该实例

     会一直存在,除非服务器关闭或者应用被删除。

     可以把ServletContext看成是一个web应用的服务端组件的一个共享内存,在ServletContext中可以

     存放共享数据。整个应用内部所有组件都能访问ServletContext的内容。

     servlet上下文有两个特点:

     1)唯一性

   一个应用对应一个servlet上下文(有且只有一个)

2)一直存在

   只要容器不关闭或者应用不卸载,servlet上下文就一直存在。

   (2)如何获得ServletContext实例。

         1)GenericServlet提供了getServletContext()方法

         2)ServletConfig提供了getServletContext()方法

         3)HttpSession提供了getServletContext()方法

         4)FilterConfig提供了getServletContext()方法

    以上几种方式都是得到同一个ServletContext对象。tomcat容器的ApplicationContext类实现了

    ServletContext接口。以上方法最终是

    ServletContext application = new ApplicationContext();

   (3)servlet上下文的作用

   1)绑定数据

 setAttribute(String name,Object obj);

 getAttribute(String name);

 removeAttribute(String name);

 注意:request对象、session对象、servlet上下文都有绑定数据的挡风区别如下:

 a、request对象的生存时间是一次请求与响应期间,

    session对象的生存时间是多次请求与响应期间(用户与服务器的一次会话)

    servlet上下文是一直存在的。

    所以,在满足使用条件的情况下,应该优先使用生命周期短的。

             比如转发,选择用request,转发结束后,绑定的数据就没有用了,随着request对象的销毁,

             数据也被销毁,这样会比较节省内存。如果转发使用servlet上下文时,转发结束后,数据还

             保留着,这样会浪费内存空间。

 b、request对象上面绑定的数据只能是同一个请求所涉及的组件可以访问,

    session对象上面绑定的数据是同一个会话当中所涉及的组件可以访问,

    servlet上下文上面绑定的数据是同一个应用当中的所有组件都可以访问。

?     2)配置全局的初始化参数

                  step1 在web.xml中,使用<context-param>配置的参数,可以被所有的servlet共享。

 step2 使用String ServletContext.getInitParameter(String paraName);

 web.xml配置时一般都有一定的顺序(约定俗成)

<web-app version="2.4" ...>

                 <!-- 全局的初始化参数 -->

<context-param>

                          <param-name>company</param-name>

                          <param-value>北京达内</param-value>

 </context-param>

                 <!-- 过滤器的配置 -->

                 <!-- 监听器的配置 -->

<!-- servlet的配置 -->

</web-app>

 Servlet代码

 ServletContext sctx = getServletContext();

 String company = sctx.getInitParameter("company");

 System.out.println("  " + company);

       3)依据逻辑路径获得实际部署时的物理路径。

 String ServletContext.getRealPath(String url);

14、监听器 **

1) 什么是监听器?

servlet规范当中定义的一种特殊的类,作用是监听容器当中产生的一些事件并进行相应的处理。

容器产生的事件指的是两大类事件:

1)生命周期相关的事件

   指的是当容器创建或者销毁request,session,ServletContext对象时产生的事件。

2)绑订事件

   指的是当调用request,session,ServletContext对象的setAttribute,removeAttribute时产生的事件。

2) 如何写监听器

step1 写一个java类,实现特定的监听器接口类(依据要监听的事件类型)。

step2 在接口声明的方法中,实现监听的逻辑。

step3 配置(web.xml)。

3) 案例

   a、统计在线人数

      CountListener

public class CountListener implements HttpSessionListener{

         private int count = 0;//计数器

         /**

          * session对象被创建后,容器会调用这个方法

          */

         public void sessionCreated(HttpSessionEvent httpsessionevent) {

                  System.out.println("session被创建");

                  count++;

                  //将在线人数绑定到servlet上下文对象上

                  HttpSession session=httpsessionevent.getSession();

                  ServletContext application=session.getServletContext();

                  application.setAttribute("count", count);        

         }

         /**

          * 当session对象被销毁后,容器会调用这个方法

          */

         public void sessionDestroyed(HttpSessionEvent httpsessionevent) {

                  System.out.println("session被销毁");

                  count--;

                  HttpSession session=httpsessionevent.getSession();

                  ServletContext application=session.getServletContext();

                  application.setAttribute("count", count);

         }

}

web.xml

<web-app version="2.4" ...>

                           <listener>

                                    <listener-class>web.CountListener</listener-class>

                           </listener>

//index.jsp首页的意思

//当发送请求时(只到应用名)

 如:http://localhost:8080/web09时,会自动定位到index.jsp

                           <welcome-file-list>

                                  <welcome-file>index.jsp</welcome-file>

                           </welcome-file-list>

</web-app>

index.jsp

<html>

                         <body>

                                  当前系统在线人数是:<!--application隐含对象就是ServletContext对象  -->

                                  <%=application.getAttribute("count") %>

                           </body>

</html>

 b、容器开启时,每隔5秒输出当前系统时间。容器关闭或应用卸载时,停止。

    TaskListneer类

             public class TaskListneer implements ServletContextListener{

                           private Timer timer=new Timer();

public void contextDestroyed(ServletContextEvent servletcontextevent) {

                                    System.out.println("任务停止");

                                    timer.cancel();

                           }

public void contextInitialized(ServletContextEvent servletcontextevent) {

                                    System.out.println("任务开始");

                                    timer.schedule(new TimerTask(){

                                             public void run() {

                                                      SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");

                                                      System.out.println(sdf.format(new Date()));

                                             }

                                    }, 0,5000);

                           }

}

web.xml

<listener>

                           <listener-class>web.CountListener</listener-class>

</listener>

15、上传文件(扩展)

  (1)在form中,设置method="post",设置enctype="multipart/form-data"。

       enctype属性用于设置表单的编码方式,按照http协议,

       对于文件上传,必须设置成"multipart/form-data"。

       比如:

                  <form enctype="multipart/form-data" method="post">

                  "multipart/form-data":用来设置浏览器上传文件的格式。

  (2)在服务器端读取文件的内容,并保存到相应的文件夹底下

  如果enctype=”multipart/form-data”,则服务器不再解析请求数据包,也就是说

  request.getParamter()方法返回null。

  要使用 InputStream request.getInputStream();获得一个输入流,

  然后分析InputStream流来获得参数值。

  直接分析InputStream比较复杂,一般使用一些封装好的工具

  (比如apache提供的 commons-fileupload.jar)来获得参数值。

16、servlet线程安全问题

1)servlet为什么会有线程安全问题?

a、servlet容器在默认情况下只会创建一个servlet实例

  (比如SomeServlet在容器里面,只有一个SomeServlet对象)

b、当容器收到请求之后,会启动一个线程来处理

c、如果有多个请求同时访问某个servlet实例,即有多个线程调用同一个servlet实例的方法,

   就有可能产生线程安全问题。(比如,这些线程同时去修改servlet属性)

2)如何解决

   a、使用synchronized对整个service方法或者代码块加锁(建议使用代码块加锁)

   b、让servlet实现SingleThreadModel接口

      容器会为每一个实现了该接口的servlet创建多个实例(一个线程创建一个servlet对象)。

 不建议使用,因为会创建过多的servlet对象。

 SingleThreadModel中没有任何的方法,称为“标识”接口(作用和序列化接口一样)。

 

JSP