servlet

一.Servlet

  1. Servlet 是 Server Applet 的缩写,译为“服务器端小程序”,是一种使用 Java 语言来开发动态网站的技术。
  2. Servlet 虽然被称作“小程序”,但是它的功能却异常强大,因为它是基于 Java 的,几乎可以使用所有的 Java API,Java 能做的事情,Servlet 也能做。
  3. Servlet只是一种规范,严格来说,Servlet 只是一套 Java Web 开发的规范,或者说是一套 Java Web 开发的技术标准。只有规范并不能做任何事情,必须要有人去实现它。所谓实现 Servlet 规范,就是真正编写代码去实现 Servlet 规范提到的各种功能,包括类、方法、属性等。

1.Servlet三种创建方式

​ Servlet 规范的最顶层是一个名为 javax.servlet.Servlet 的接口,所有的 Servlet 类都要直接或者间接地实现该接口。直接实现 Servlet 接口不太方便,所以 Servlet 又内置了两个 Servlet 接口的实现类(抽象类),分别为 GenericServlet 和 HttpServlet,

1.servlet之间的关系

Servlet
GenericServlet
nyServlet
HttpServlet

由上图可知:

  1. GenericServlet 是实现了 Servlet 接口的抽象类。
  2. HttpServlet 是 GenericServlet 的子类,具有 GenericServlet 的一切特性。
  3. Servlet 程序(MyServlet 类)是一个实现了 Servlet 接口的 Java 类。

2.Servlet接口

javax.servlet.Servlet 是 Servlet API 的核心接口,所有的 Servlet 类都直接或间接地实现了这一接口。

Servlet接口下有五个方法,下图如是:

返回值方法说明
voidinit(ServletConfig config)Servlet 实例化之后,由 Servlet 容器调用,用来初始化 Servlet 对象。该方法只能被调用一次。
参数 config 用来向 Servlet 传递配置信息。
voidservice(ServletRequest req,ServletResponse res)Servlet 容器调用该方法处理客户端请求。
voiddestroy()服务器关闭、重启或者 Servlet 对象被移除时,由 Servlet 容器调用,负责释放 Servlet 对象占用的资源。
ServletConfiggetServletConfig()该方法用来获取 ServletConfig 对象,该对象中包含了 Servlet 的初始化参数。
StringgetServletInfo()该方法用于获取 Servlet 的信息,例如作者、版本、版权等。

通过Servlet接口实现servlet,实例如下:

package net.biancheng.www;

import javax.servlet.*;
import java.io.IOException;
import java.io.PrintWriter;

public class MyServlet implements Servlet {

    //Servlet 实例被创建后,调用 init() 方法进行初始化,该方法只能被调用一次
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
    }

    //返回 ServletConfig 对象,该对象包含了 Servlet 的初始化参数
    @Override
    public ServletConfig getServletConfig() {
        return null;
    }

    //每次请求,都会调用一次 service() 方法
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        //设置字符集
        servletResponse.setContentType("text/html;charset=UTF-8");
        //使用PrintWriter.write()方法向前台页面输出内容
        PrintWriter writer = servletResponse.getWriter();
        writer.write("编程帮欢迎您的到来,网址: www.biancheng.net");
        writer.close();
    }

    //返回关于 Servlet 的信息,例如作者、版本、版权等
    @Override
    public String getServletInfo() {
        return null;
    }

    //Servelet 被销毁时调用
    @Override
    public void destroy() {
    }
}

3.GenericServlet 抽象类

javax.servlet.GenericServlet 实现了 Servlet 接口,并提供了除 service() 方法以外的其他四个方法的简单实现。通过继承 GenericServlet 类创建 Servlet ,只需要重写 service() 方法即可,大大减少了创建 Servlet 的工作量。

GenericServlet 类实现了一下几个方法,如下图所示:

返回值方法说明
StringgetInitParameter(String name)返回名字为 name 的初始化参数的值,初始化参数在 web.xml 中进行配置。如果参数不存在,则返回 null。
EnumerationgetInitParameterNames()返回 Servlet 所有初始化参数的名字的枚举集合,若 Servlet 没有初始化参数,返回一个空的枚举集合。
ServletContextgetServletContext()返回 Servlet 上下文对象的引用。
StringgetServletName()返回此 Servlet 实例的名称。

通过GenericServlet 抽象类创建servlet,示例如下:

public class myServlet extends GenericServlet {

	@Override
	public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
		//设置返回前台的字符集
		res.setContentType("text/html;charset=utf-8");
        //获取字符流对象
		PrintWriter writer = res.getWriter();
		writer.append("这是一个继承servlet接口的抽象类");
        //关闭字符流
		writer.close();
	}
}

4.HttpServlet抽象类

javax.servlet.http.HttpServlet 继承了 GenericServlet 抽象类,用于开发基于 HTTP 协议的 Servlet 程序。由于 Servlet 主要用来处理 HTTP 的请求和响应,所以通常情况下,编写的 Servlet 类都继承自 HttpServlet。

  1. 在 HTTP/1.1 协议中共定义了 7 种请求方式,即 GET、POST、HEAD、PUT、DELETE、TRACE 和 OPTIONS。
  2. HttpServlet 针对这 7 种请求方式分别定义了 7 种方法,即 doGet()、doPost()、doHead()、doPut()、doDelete()、doTrace() 和 doOptions()。
  3. HttpServlet 重写了 service() 方法,该方法会先获取客户端的请求方式,然后根据请求方式调用对应 doXxx 方法。

由于我们在请求方式上主要是get和post请求,所以在通过HttpServlet创建servlet时,只需要doGet和doPost方法。

public class myServlet extends HttpServlet {
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		// TODO Auto-generated method stub
		 resp.setContentType("text/html;charset=utf-8");
		 PrintWriter writer = resp.getWriter();
		 writer.println("这是doGet方法");
		 writer.close();
	}

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		// TODO Auto-generated method stub
		resp.setContentType("text/html;charset=utf-8");
		 PrintWriter writer = resp.getWriter();
		 writer.println("这是dopost方法");
		 writer.close();
	}
}

2.ServletConfig接口

​ Servlet 容器初始化 Servlet 时,会为这个 Servlet 创建一个 ServletConfig 对象,并将 ServletConfig 对象作为参数传递给 Servlet 。通过 ServletConfig 对象即可获得当前 Servlet 的初始化参数信息。

​ 一个 Web 应用中可以存在多个 ServletConfig 对象,一个 Servlet 只能对应一个 ServletConfig 对象,即 Servlet 的初始化参数仅对当前 Servlet 有效。

1.获得ServletConfig对象

获得ServletConfig对象一般有两种方法

1.从参数init()方法中提取

public class myServlet extends HttpServlet {

    //创建私有的config对象
	private ServletConfig config;
	
	@Override
	public void init(ServletConfig config) throws ServletException {
		// TODO Auto-generated method stub
		super.init(config); //父类的方法
		this.config=config; //将config对象传到私有属性上
	}

	@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
        
	}
    
	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		// TODO Auto-generated method stub
	    System.out.println("这是网站首页");
	}
}
  1. 调用GenericServlet提供的getServletConfig()方法获取
 ServletConfig servletConfig = this.getServletConfig();
/*
 我们继承HttpServlet时,其实也就是间接性的继承了servlet接口和GenericServlet抽象类,所以可以使用其中的方法
*/

2.ServletConfig接口的用途

该接口提供了以下方法

返回值方法说明
StringgetInitParameter(String name)根据初始化参数名 name,返回对应的初始化参数值。
EnumerationgetInitParameterNames()返回 Servlet 所有的初始化参数名的枚举集合,如果该 Servlet 没有初始化参数,则返回一个空的集合。
ServletContextgetServletContext()返回一个代表当前 Web 应用的 ServletContext 对象。
StringgetServletName()返回 Servlet 的名字,即 web.xml 中 元素的值。

配置Servlet初始化参数

配置 Servlet 的初始化参数有 2 种方式:

  1. 使用 web.xml 配置初始化参数;
  2. 使用 @WebServlet 配置初始化参数。(不做介绍,不常用)
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
  <display-name>demo01</display-name>
  <welcome-file-list>
    <welcome-file>login.html</welcome-file>
    <welcome-file>login.jsp</welcome-file>
  </welcome-file-list>
    
  <servlet>
    <servlet-name>servlet1</servlet-name>
    <servlet-class>com.yc.servlet1</servlet-class>
      <!--servlet初始化参数-->
    <init-param>
       <param-name>name</param-name>
       <param-value>user</param-value>
    </init-param>
  </servlet>
    
  <servlet-mapping>
    <servlet-name>servlet1</servlet-name>
    <url-pattern>/servlet1</url-pattern>
  </servlet-mapping>
</web-app>

以上配置说明如下:

  • 元素是 的子元素, 需要在 元素内使用,表示只对当前 Servlet 有效 。
  • 子元素表示参数的名称。
  • 子元素表示参数的值。
public class myServlet extends HttpServlet {

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		
	  response.setContentType("text/html;charset=utf-8");	
	   ServletConfig servletConfig = getServletConfig();
       response.getWriter().append(servletConfig.getInitParameter("name"));
	}


	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
          doGet(request, response);
	}

}

3.ServletContext接口

  1. Servlet 容器启动时,会为每个 Web 应用(webapps 下的每个目录都是一个 Web 应用)创建一个唯一的 ServletContext 对象,该对象一般被称为“Servlet 上下文”。

  2. ServletContext 对象的生命周期从 Servlet 容器启动时开始,到容器关闭或应用被卸载时结束。

  3. Web 应用中的所有 Servlet 共享同一个 ServletContext 对象,不同 Servlet 之间可以通过 ServletContext 对象实现数据通讯,因此 ServletContext 对象也被称为 Context 域对象。

1.获得ServletContext对象

​ 获得ServletCcontext对象有以下四种方式

  1. 通过GenericServlet提供的getServletContext()方法
ServletContext servletContext = getServletContext();
ServletContext servletContext2 = this.getServletContext();
  1. 通过ServletConfig提供的getServletContext()方法
ServletContext servletContext = this.getServletConfig().getServletContext();
ServletContext servletContext2 = getServletConfig().getServletContext();
  1. 通过HttpSession提供的getServletContext()方法
ServletContext servletContext = request.getSession().getServletContext();
  1. 通过HttpServletRequest提供的getServletContext()方法
ServletContext servletContext = request.getServletContext();

2.ServletContext的应用

ServletContext 的应用主要有以下 3 个:

  1. 获取上下文初始化参数
  2. 实现 Servlet 之间的数据通讯
  3. 读取 Web 应用下的资源文件
1.获取上下文初始化参数

使用 ServletContext 对象获取 Web 应用的上下文初始化参数,分为 2 步:

  1. 设置上下文初始化参数
  2. 调用接口中方法获取初始化参数

1.设置上下文初始化参数

​ 通过 web.xml 中的 元素可以为 Web 应用设置一些全局的初始化参数,这些参数被称为上下文初始化参数。

​ 与 Servlet 的初始化参数不同,应用中的所有 Servlet 都共享同一个上下文初始化参数。在 Web 应用的整个生命周期中,上下文初始化参数会一直存在,并且可以随时被任意一个 Servlet 访问。

在web.xml中配置上下文参数,代码如下

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
  <display-name>demo01</display-name>
  <welcome-file-list>
    <welcome-file>login.html</welcome-file>
    <welcome-file>login.jsp</welcome-file>
  </welcome-file-list>
  <servlet>
    <servlet-name>servlet1</servlet-name>
    <servlet-class>com.yc.servlet1</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>servlet1</servlet-name>
    <url-pattern>/servlet1</url-pattern>
  </servlet-mapping>
  <!--设置web应用的上下文参数-->
  <context-param>
     <param-name>name</param-name>
     <param-value>这是所有servlet都可以访问的上下文参数</param-value>
  </context-param>
</web-app>

对以上标签说明如下:

  • 元素用来声明上下文初始化参数,必须在根元素 内使用。
  • 子元素表示参数名,参数名在整个 Web 应用中必须是唯一的。
  • 子元素表示参数值。

2。调用接口中的方法获取初始化参数

​ Servlet 容器启动时,会为容器内每个 Web 应用创建一个 ServletContext 对象,并将 元素中的上下文初始化参数以键值对的形式存入该对象中,因此我们可以通过 ServletContext 的相关方法获取到这些初始化参数。

获取上下文参数的方法如下:

返回值方法说明
StringgetInitParameter(String name)根据初始化参数名 name,返回对应的初始化参数值。
EnumerationgetInitParameterNames()返回 Web 应用所有上下文初始化参数名的枚举集合,如果该 Web 应用没有上下文初始化参数,则返回一个空的枚举集合。

获取上下文参数的代码如下:

public class servlet1 extends HttpServlet {
    /**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // TODO Auto-generated method stub
        response.setContentType("text/html;charset=utf-8");
        Enumeration<String> init= getServletContext().getInitParameterNames();
        while(init.hasMoreElements()) {
            String name=init.nextElement();
            response.getWriter().println(getServletContext().getInitParameter(name));
        }
    }

    /**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
	 */
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // TODO Auto-generated method stub
        doGet(request, response);
    }

}
2.实现数据通信

​ 在 Servlet 中,调用 ServletContext 接口的 setAttribute() 方法可以创建一些属性,这些属性被存放在 ServletContext 对象中。应用中所有 Servlet 都可以对这些属性进行访问和操作,通过它们可以实现应用内不同 Servlet 之间的数据通讯。

下方是数据通信的方法:

返回值方法说明
voidsetAttribute(String name, Object object)把一个 Java 对象与一个属性名绑定,并将它作为一个属性存放到 ServletContext 中。
参数 name 为属性名,参数 object 为属性值。
voidremoveAttribute(String name)从 ServletContext 中移除属性名为 name 的属性。
ObjectgetAttribute(String name)根据指定的属性名 name,返回 ServletContext 中对应的属性值。

下面我们统计页面点击量的案例,代码如下:

public class servlet1 extends HttpServlet {

    public void init() throws ServletException {
        this.getServletContext().setAttribute("count", 0);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
        response.setContentType("text/html;charset=utf-8");
        ServletContext servletContext = this.getServletContext();
        Integer attribute = (Integer)servletContext.getAttribute("count");
        if(attribute==null) {
            servletContext.setAttribute("count",1);
        }else {
            attribute++;
            servletContext.setAttribute("count",attribute);
        }
        response.getWriter().println(servletContext.getAttribute("count"));

    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
        doGet(request, response);
    }

}

4.HttpServletRequest接口详解

一般情况下,浏览器(客户端)通过 HTTP 协议来访问服务器的资源,Servlet 主要用来处理 HTTP 请求。

  1. Servlet 容器接收到来自客户端的 HTTP 请求后,容器会针对该请求分别创建一个 HttpServletRequest 对象和 HttpServletReponse 对象。
  2. 容器将 HttpServletRequest 对象和 HttpServletReponse 对象以参数的形式传入 service() 方法内,并调用该方法。
  3. 在 service() 方法中 Servlet 通过 HttpServletRequest 对象获取客户端信息以及请求的相关信息。
  4. 对 HTTP 请求进行处理。
  5. 请求处理完成后,将响应信息封装到 HttpServletReponse 对象中。
  6. Servlet 容器将响应信息返回给客户端。
  7. 当 Servlet 容器将响应信息返回给客户端后,HttpServletRequest 对象和 HttpServletReponse 对象被销毁。

1.HttpServletRequest接口方法

在 Servlet API 中,定义了一个 HttpServletRequest 接口,它继承自 ServletRequest 接口。HttpServletRequest 对象专门用于封装 HTTP 请求消息,简称 request 对象。

HTTP 请求消息分为请求行、请求消息头和请求消息体三部分,所以 HttpServletRequest 接口中定义了获取请求行、请求头和请求消息体的相关方法。

获取请求行信息

​ HTTP 请求的请求行中包含请求方法、请求资源名、请求路径等信息,HttpServletRequest 接口定义了一系列获取请求行信息的方法,如下表。

返回类型方法说明
StringgetMethod()该方法用于获取 HTTP 请求方式(如 GET、POST 等)。
StringgetRequestURI()该方法用于获取请求行中的资源名称部分,即位于 URL 的主机和端口之后,参数部分之前的部分。
StringgetQueryString()该方法用于获取请求行中的参数部分,也就是 URL 中“?”以后的所有内容。
StringgetContextPath()返回当前 Servlet 所在的应用的名字(上下文)。对于默认(ROOT)上下文中的 Servlet,此方法返回空字符串""。
StringgetServletPath()该方法用于获取 Servlet 所映射的路径。
StringgetRemoteAddr()该方法用于获取客户端的 IP 地址。
StringgetRemoteHost()该方法用于获取客户端的完整主机名,如果无法解析出客户机的完整主机名,则该方法将会返回客户端的 IP 地址。

下面创建一个servlet类,地址栏显示:localhost:8080/demo01/servlet1?name=我的世界&age=12345代码如下:

public class servlet1 extends HttpServlet {

    public void init() throws ServletException {

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
    	  response.setContentType("text/html;charset=UTF-8");
          PrintWriter writer = response.getWriter();
          writer.println("请求方式:" + request.getMethod() + "<br/>" +
                  "客户端的 IP 地址:" + request.getRemoteAddr() + "<br/>" +
                  "应用名字(上下文):" + request.getContextPath() + "<br/>" +
                  "URI:" + request.getRequestURI() + "<br/>" +
                  "请求字符串:" + URLDecoder.decode(request.getQueryString(),"utf-8") + "<br/>" +
                  "Servlet所映射的路径:" + request.getServletPath() + "<br/>" +
                  "客户端的完整主机名:" + request.getRemoteHost() + "<br/>"
          );
    
          writer.close();
    	
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }

}

结果如下:

请求方式:GET
客户端的 IP 地址:0:0:0:0:0:0:0:1
应用名字(上下文):/demo01
URI:/demo01/servlet1
请求字符串:name=我的世界&age=12345
Servlet所映射的路径:/servlet1
客户端的完整主机名:0:0:0:0:0:0:0:1

获取请求头信息

​ 当浏览器发送请求时,需要通过请求头向服务器传递一些附加信息,例如客户端可以接收的数据类型、压缩方式、语言等。为了获取请求头中的信息, HttpServletRequest 接口定义了一系列用于获取 HTTP 请求头字段的方法,如下表所示。

返回值方法说明
StringgetHeader(String name)该方法用于获取一个指定头字段的值。 如果请求消息中包含多个指定名称的头字段,则该方法返回其中第一个头字段的值。
EnumerationgetHeaders(String name)该方法返回指定头字段的所有值的枚举集合, 在多数情况下,一个头字段名在请求消息中只出现一次,但有时可能会出现多次。
EnumerationgetHeaderNames()该方法返回请求头中所有头字段的枚举集合。
StringgetContentType()该方法用于获取 Content-Type 头字段的值。
intgetContentLength()该方法用于获取 Content-Length 头字段的值 。
StringgetCharacterEncoding()该方法用于返回请求消息的字符集编码 。

为了更好的显示这些方法的作用,给出以下Java代码:

public class servlet1 extends HttpServlet {

    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
    	  response.setContentType("text/html;charset=UTF-8");
        
          PrintWriter writer = response.getWriter();
          Enumeration<String> headerNames = request.getHeaderNames();
          while (headerNames.hasMoreElements()) {
			String string = (String) headerNames.nextElement();
			String header = request.getHeader(string);
			writer.println(string+"==  "+header+"<br>");
		}
        writer.println("Content-Type"+"==  "+request.getContentType()+"<br>");
        writer.println("字符编码"+"==  "+request.getCharacterEncoding());
          writer.close();
    	
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }

}

获取form表单数据

​ 在实际开发中,我们经常需要获取用户提交的表单数据,例如用户名和密码等。为了方便获取表单中的请求参数,ServletRequest 定义了一系列获取请求参数的方法,如下表所示。

返回类型方法说明
StringgetParameter(String name)返回指定参数名的参数值。
String[]getParameterValues (String name)以字符串数组的形式返回指定参数名的所有参数值(HTTP 请求中可以有多个相同参数名的参数)。
EnumerationgetParameterNames()以枚举集合的形式返回请求中所有参数名。
MapgetParameterMap()用于将请求中的所有参数名和参数值装入一个 Map 对象中返回。

在这里介绍 getParameterMap()这个方法,其他的方法很简单

public class servlet1 extends HttpServlet {

    protected void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");

        Map<String, String[]> parameterMap = request.getParameterMap();
        Iterator<Entry<String, String[]>> iterator = parameterMap.entrySet().iterator();
        while(iterator.hasNext()) {
            Entry<String, String[]> next = iterator.next();
            response.getWriter().println(next.getKey()+" == "+Arrays.toString(next.getValue())+"<br>");
        }

    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
        doGet(request, response);
    }

}

2.解决中文乱码

​ 根据请求方式的不同,请求一般可以被分为两种:GET 请求和 POST 请求。这两种请求方式都可能会产生中文乱码问题,下面我们分别对它们产生乱码的原因及其解决方案进行介绍。

POST请求

​ 乱码的原因:POST 提交的数据在请求体中,其所使用的编码格式时页面一致(即 utf-8)。request 对象接收到数据之后,会将数据放到 request 缓冲区,缓冲区的默认字符集是 ISO-8859-1(该字符集不支持中文),两者使用的字符集不一致导致乱码。

解决方案:在获取请求参数之前设置 request 缓冲区字符集为 utf-8 ,代码如下。

request.setCharacterEncoding("utf-8"); //设置request缓冲区的字符集为utf-8

GET请求

​ 乱码的原因:Get 请求将请求数据附加到 URL 后面作为参数,浏览器发送文字时采用的编码格式与页面编码保持一致(utf-8)。如果 Tomcat 没有设置字符集,接收 URL 时默认使用 ISO-8859-1 进行解码,ISO-8859-1 不兼容中文,无法正确解码,导致出现乱码。

需要注意的是,在 Tomcat 8 中已解决了 get 方式提交请求中文乱码的问题,使用 Tomcat 8 及以上版本的同学不必再考虑此问题了,如果您使用的是 Tomcat 7 或更早的版本,出现乱码问题可以使用如下的方案解决。

  1. 修改 tomcat/conf/server.xml 中的配置,代码如下。
<Connector port="80" protocol="HTTP/1.1"
           connectionTimeout="20000"
           redirectPort="8443" URIEncoding="UTF-8"/>
  1. 使用 URLEncoder 和 URLDecoder 进行编码和解码的操作(逆向编解码)。
//得到TOMCAT通过ISO8859-1解码的字符串
String username = request.getParameter("username");
//对字符串使用ISO8859-1进行编码,得到最初浏览器使用UTF-8编码的字符串
username = URLEncoder.encode(username, "ISO8859-1");
//将使用UTF-8编码的字符串使用UTF-8进行解码,得到正确的字符串
username = URLDecoder.decode(username, "UTF-8");
  1. 使用 String 的构造方法:String(byte[] bytes, String charset) ,对字节数组(bytes)按照指定的字符集(charset)进行解码,返回解码后的字符串,解决乱码问题(推荐使用)。
//获取姓名
String username = request.getParameter("username");
//使用String的构造方法解决乱码的问题
username = new String(username.getBytes("ISO-8859-1"),"UTF-8");

5.servlet请求转发

​ Web 应用在处理客户端的请求时,经常需要多个 Web 资源共同协作才能生成响应结果。但由于 Serlvet 对象无法直接调用其他 Servlet 的 service() 方法,所以 Servlet 规范提供了 2 种解决方案:

  1. 请求转发
  2. 请求包含(了解即可)

1. 请求转发

​ 请求转发属于服务器行为。容器接收请求后,Servlet 会先对请求做一些预处理,然后将请求传递给其他 Web 资源,来完成包括生成响应在内的后续工作。

RequestDispatcher 接口

​ javax.servlet 包中定义了一个 RequestDispatcher 接口,RequestDispatcher 对象由 Servlet 容器创建,用于封装由路径所标识的 Web 资源。利用 RequestDispatcher 对象可以把请求转发给其他的 Web 资源。

Servlet 可以通过 2 种方式获得 RequestDispatcher 对象:

  1. 调用 ServletContext 的 getRequestDispatcher(String path) 方法,参数 path 指定目标资源的路径,必须为绝对路径

  2. 调用 ServletRequest 的 getRequestDispatcher(String path) 方法,参数 path 指定目标资源的路径,可以为绝对路径,也可以为相对路径

**注意:**绝对路径是指以符号“/”开头的路径,“/”表示当前 Web 应用的根目录。相对路径是指相对当前 Web 资源的路径,不以符号“/”开头。

返回值方法说明
voidforward(ServletRequest request,ServletResponse response)用于将请求转发给另一个 Web 资源。该方法必须在响应提交给客户端之前被调用,否则将抛出 IllegalStateException 异常
voidinclude(ServletRequest request,ServletResponse response)用于将其他的资源作为当前响应内容包含进来

请求转发的工作原理:

请求转发的特点

  1. 请求转发不支持跨域访问,只能跳转到当前应用中的资源。
  2. 请求转发之后,浏览器地址栏中的 URL 不会发生变化,因此浏览器不知道在服务器内部发生了转发行为,更无法得知转发的次数。
  3. 参与请求转发的 Web 资源之间共享同一 request 对象和 response 对象。
  4. 由于 forward() 方法会先清空 response 缓冲区,因此只有转发到最后一个 Web 资源时,生成的响应才会被发送到客户端。

2.request域对象

​ request 是 Servlet 的三大域对象之一,它需要与请求转发配合使用,才可以实现动态资源间的数据传递,此域对象不是ServletContext中的全局域对象,只有对哪个servlet进行请求转发,那个servlet才能被使用,否则直接报错。

request域对象与ServletContext域对象比较

  1. 生命周期不同

Context 域对象的生命周期从容器启动开始,到容器关闭或者 Web 应用被移除时结束;

request 域对象的生命周期从客户端向容器发送请求开始,到对这次请求做出响应后结束。

  1. 作用域不同

Context 域对象对整个 Web 应用内的所有Servlet都有效;

request 域对象只对本次请求涉及的 Servlet 有效。

  1. web应用中数量的不同

整个 Web 应用中只有一个 Context 域对象;

由于 Servlet 能处理多个请求,因此 Web 应用中的每个 Servlet 实例都可以有多个 request 域对象。

  1. 实现数据共享的方式不同

Context 域对象可以独立完成动态资源之间的数据共享;

Request 域对象需要与请求转发配合使用才能实现动态资源之间的数据共享。

示例

下面通过一个案例实现servlet的请求转发以及对request域对象的理解,也可以在请求转发后的servlet中获取form表单数据:

  1. 首先创建一个名字为servlet1的servlet,这个servlet中主要获取兴趣,性别;并且通过request域对象传入到请求转发中。
public class servlet1 extends HttpServlet {

    protected void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
        //设置字符集
        response.setContentType("text/html;charset=utf-8");
        //获取兴趣的复选
        String[] language=request.getParameterValues("language");
        //获取性别
        String sex=request.getParameter("sex");
        //将性别放到域对象中
        request.setAttribute("sex", sex);
        //将兴趣放到域对象中
        request.setAttribute("language",Arrays.toString(language));
        //请求转发到other路径的servlet
        this.getServletContext().getRequestDispatcher("/other").forward(request, response);
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
        doGet(request, response);
    }

}
  1. 创建一个名为otherservlet的servlet,获取请求转发后的域对象值,在获取form提交的账号和密码,将填写的内容输出
public class otherservlet extends HttpServlet {

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
	   //设置字符集
		response.setContentType("text/html;charset=utf-8");
        //获取
	  	String username=request.getParameter("username");
	  	String password=request.getParameter("password");
	  	String sex=request.getAttribute("sex").toString();
	  	String language=request.getAttribute("language").toString();
	  	PrintWriter writer = response.getWriter();
	  	//账号
	  	writer.println("账号: "+username+"<br>");
	  //账号
	  	writer.println("密码: "+password+"<br>");
	  //账号
	  	writer.println("性别: "+sex+"<br>");
	  //账号
	  	writer.println("兴趣: "+language+"<br>");

	}
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		doGet(request, response);
	}

}
  1. 创建前端html页面,也就是前端像后端提交的form表单
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
    <form action="/demo01/servlet1" method="GET">
        <table border="1" width="50%">
            <tr>
                <td colspan="2" align="center">编程帮wwww.biancheng.net</td>
            </tr>
            <tr>
                <td>输入账号</td>
                <td><input type="text" name="username" /></td>
            </tr>
            <tr>
                <td>输入密码</td>
                <td><input type="password" name="password" /></td>
            </tr>
            <tr>
                <td>选择性别</td>
                <td><input type="radio" name="sex" value="" /><input
                    type="radio" name="sex" value="" /></td>
            </tr>
            <tr>
                <td>请选择你的爱好</td>
                <td><input type="checkbox" name="language" value="游泳" />游泳
                    <input type="checkbox" name="language" value="游戏" />游戏
                    <input type="checkbox" name="language" value="学习" />学习 
                    <input type="checkbox" name="language" value="跑步" />跑步</td>
            </tr>
            <tr>
                <td colspan="2" align="center"><input type="submit" value="提交" />
                </td>
            </tr>
        </table>
    </form>
</body>
</html>

6.HttpServletResponse接口详解

​ Servlet 容器会针对每次请求创建一个 response 对象,并把它作为参数传递给 Servlet 的 service 方法。Servlet 处理请求后,会将响应信息封装到 response 对象中,并由容器解析后返回给客户端。

​ 由于 HTTP 响应消息由响应行、响应头、消息体三部分组成,所以 HttpServletResponse 接口中定义了向客户端发送响应状态码、响应头、响应体的方法,下面我们将针对这些方法进行介绍。

1.响应行相关方法

​ 当 Servlet 返回响应消息时,需要在响应消息中设置状态码。因此,HttpServletResponse 接口定义了发送状态码的方法,如下表。

返回值类型方法说明
voidsetStatus(int status)用于设置 HTTP 响应消息的状态码,并生成响应状态行。
voidsendError(int sc)用于发送表示错误信息的状态码。
  1. setStatus设置后,直接爆出相应状态码的错误页面,不能单独设自己的页面。
  2. sendError设置后,用户可以指定状态行的页面。

例如用户创建了一个错误页面,该页面指定状态行所对对应网页

  1. 在web.xml中设置对应状态行的页面
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">

   <!--
      指定对应状态行的页面
     -->
  <error-page> 
      <error-code>404</error-code>
      <location>/erro.html</location>
  </error-page>
</web-app>
  1. 创建servlet,设置响应行所返回的页面
public class someservlet extends HttpServlet {

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
	    response.setContentType("text/html;charset=utf-8");
        //设置响应的状态码
	    response.sendError(404);
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		doGet(request, response);
	}
}

2.响应头相关方法

​ HttpServletResponse 接口中定义了一系列设置 HTTP 响应头字段的方法,如下表所示。

返回值类型方法说明
voidaddHeader(String name,String value)用于增加响应头字段,其中,参数 name 用于指定响应头字段的名称,参数 value 用于指定响应头字段的值。
voidsetHeader (String name,String value)用于设置响应头字段,其中,参数 name 用于指定响应头字段的名称,参数 value 用于指定响应头字段的值。
voidaddIntHeader(String name,int value)用于增加值为 int 类型的响应头字段,其中,参数 name 用于指定响应头字段的名称,参数 value 用于指定响应头字段的值,类型为 int。
voidsetIntHeader(String name, int value)用于设置值为 int 类型的响应头字段,其中,参数 name 用于指定响应头字段的名称,参数 value 用于指定响应头字段的值,类型为 int。
voidsetContentType(String type)用于设置 Servlet 输出内容的 MIME 类型以及编码格式。
voidsetCharacterEncoding(String charset)用于设置输出内容使用的字符编码。

3.响应体相关方法

​ 由于在 HTTP 响应消息中,大量的数据都是通过响应消息体传递的。因此 ServletResponse 遵循以 I/O 流传递大量数据的设计理念,在发送响应消息体时,定义了两个与输出流相关的方法。

返回值类型方法说明
ServletOutputStreamgetOutputStream()用于获取字节输出流对象。
PrintWritergetWriter()用于获取字符输出流对象。

注意:getOutputStream() 和 getWriter() 方法互相排斥,不可同时使用,否则会发生 IllegalStateException 异常。

4.response解决中文乱码

​ response 对象向页面输出时有两种方式:字节流、字符流,这两种方式输出中文时都有可能出现乱码。下面我们针对这两种方式出现乱码的原因以及解决方案进行介绍。

1.使用字节流输出中文

ServletOutputStream outputStream = response.getOutputStream();
String name="我的世界";
outputStream.write(name.getBytes());
outputStream.close();

乱码原因

​ 字节流输出中文是否出现乱码,取决于中文转成字节数组时与浏览器打开时采用的字符集是否一致。若两者保持一致,则不会出现乱码问题,若不一致就会出现乱码问题。

解决方案

​ 将中文转成字节数组时和浏览器默认采用的字符集保持一致即可,代码如下。

response.setHeader("Content-Type","text/html;charset=utf-8");
ServletOutputStream outputStream = response.getOutputStream();
String name="我的世界";
outputStream.write(name.getBytes("utf-8"));
outputStream.close();

2.使用字符流输出中文

​ 使用字符流输出中文是一定会出现乱码的

乱码原因

​ 通过字符流输出的内容是存放在 response 缓冲区的,response 缓冲区的默认字符集是 ISO-8859-1,该字符集不支持中文。

解决方案

​ 将 response 缓冲区和浏览器采用的字符集保持一致即可,有如下 2 种的方式。

  1. response.setHeader("Content-Type","text/html;charset=utf-8");  //设置浏览器打开文件所采用的编码
    response.setCharacterEncoding("utf-8");   //设置response缓冲区的字符编码
    response.getWriter().println("我的世界");
    
  2. response.setContentType("text/html;charset=utf-8");
    response.getWriter().println("我的世界");
    

7.Servlet重定向

​ 重定向属于客户端行为。服务器在收到客户端请求后,会通知客户端浏览器重新向另外一个 URL 发送请求,这称为请求重定向。它本质上是两次 HTTP 请求,对应两个 request 对象和两个 response 对象。

1.重定向的工作流程

  1. 用户在浏览器中输入 URL,请求访问服务器端的 Web 资源。
  2. 服务器端的 Web 资源返回一个状态码为 302 的响应信息,该响应的含义为:通知浏览器再次发送请求,访问另一个 Web 资源(在响应信息中提供了另一个资源的 URL)。
  3. 当浏览器接收到响应后,立即自动访问另一个指定的 Web 资源。
  4. 另一 Web 资源将请求处理完成后,由容器把响应信息返回给浏览器进行展示。

示例

​ 实现两个项目之间的数据通信,这时候就不能使用请求转发和ServletContext接口进行数据之间的交换,在不同项目之间必须借助于重定向带上请求参数进行访问。

  1. 首先创建一个demo01项目,内部实现一个登录页面和一个servlet,这个servlet获取这个页面的用户名,密码,年龄,再加上一个性别同时重定向到另一个项目中的servlet。

  2. 创建一个demo02项目,内部实现一个servlet,获取重定向后来的值,并且输出到页面上。

demo01项目中创建llogin.jsp页面实现登录界面:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>Insert title here</title>
    </head>
    <body>
        <form action="/demo01/servlet1" method="post">
            用户名:<input  type="text" name="uname"><br>
            密码 :<input  type="password" name="upassword"> <br>
            年龄:<input  type="text" name="uage"><br>
            <input type="submit" value="login">
        </form>
    </body>
</html>

demo01项目中创建servlet实现获取登录页面数据并且重定向带参数。

public class servlet1 extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //设置request缓冲区的编码格式
    	 request.setCharacterEncoding("utf-8");
        //获取用户名
         String  uname = request.getParameter("uname");
        //获取密码
         String  upassword=request.getParameter("upassword");
        //获取年龄
         String  uage=request.getParameter("uage");
        //设置性别
         String  usex="男";
  response.sendRedirect("http://localhost:8080/demo02/otherservlet?uname="+URLEncoder.encode(uname,"utf-8")+"&upassword="+upassword+"&uage="+uage+"&usex="+URLEncoder.encode(usex,"utf-8"));
  //URLEncoder.encode将中文按utf-8编码
    }
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
}

demo02项目中创建servlet接受来自重定向的数据,并且将接受到的数据显示在web网页上

public class otherservlet extends HttpServlet {

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		//设置reques缓冲区的字符集
		request.setCharacterEncoding("utf-8");
        //设置response缓冲区的页面格式和字符编码
		response.setContentType("text/html;charset=utf-8");
        //获取用户名
		String uname = request.getParameter("uname");
        //获取密码
		String upassword=request.getParameter("upassword");
        //获取年龄
		String uage=request.getParameter("uage");
        //获取性别
		String usex=request.getParameter("usex");
		
        //创建字符流
		PrintWriter writer = response.getWriter();
		writer.println("用户名   "+uname+"<br>");
		writer.println("密码     "+upassword+"<br>");
		writer.println("年龄   "+uage+"<br>");
		writer.println("性别  "+usex+"<br>");
		writer.close();
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		doGet(request, response);
	}

}

8.cookie使用

​ 当浏览器发送 HTTP 请求到服务器时,服务器会响应客户端的请求,但当同一个浏览器再次发送请求到该服务器时,服务器并不知道它就是刚才那个浏览器,即 HTTP 协议的请求无法保存用户状态。

​ 通常情况下,用户通过浏览器访问 Web 应用时,服务器都需要保存和跟踪用户的状态。例如,用户在某购物网站结算商品时,Web 服务器必须根据请求用户的身份,找到该用户所购买的商品。由于 HTTP 协议是无协议的,无法保存和跟踪用户状态,所以需要其他的方案来解决问此题,它就是会话技术。

1.会话技术

​ 从打开浏览器访问某个网站,到关闭浏览器的过程,称为一次会话。会话技术是指在会话中,帮助服务器记录用户状态和数据的技术。

常见的会话技术有两种:

  1. cookie:客户端会话技术
  2. session:服务端会话技术

2.Cookie

​ Cookie 属于客户端会话技术,它是服务器发送给浏览器的小段文本信息,存储在客户端浏览器的内存中或硬盘上。当浏览器保存了 Cookie 后,每次访问服务器,都会在 HTTP 请求头中将这个 Cookie 回传给服务器。

Cookie分为两种:

  1. 会话级别 Cookie(默认):Cookie 保存到浏览器的内存中,浏览器关闭则 Cookie 失效。
  2. 持久的 Cookie:Cookie 以文本文件的形式保存到硬盘上。

Cookie的工作流程

  1. 客户端浏览器访问服务器时,服务器通过在 HTTP 响应中增加 Set-Cookie 字段,将数据信息发送给浏览器。
  2. 浏览器将 Cookie 保存在内存中或硬盘上。
  3. 再次请求该服务器时,浏览器通过在 HTTP 请求消息中增加 Cookie 请求头字段,将 Cookie 回传给 Web 服务器。服务器根据 Cookie 信息跟踪客户端的状态。

3.CookieAPI

javax.servlet.http 包中定义了一个 Cookie 类,利用它的带参构造方法,可以创建 Cookie 对象。例如:

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

其中参数 name 为 Cookie 的名称,参数 value 为 Cookie 的值,name 与 value 的取值不能包含 [ ] ( ) = , " / ? @ : ;等字符

  1. request接口用于获取客户端的Cookie,返回的是一个Cookie数组。Cookie[] getCookies()
  2. response接口用于在响应头中增加一个相应的 Set-Cookie 头字段。void addCookie(Cookie cookie)

javax.servlet.http.Cookie 类中提供了一系列获取或者设置 Cookie 的方法,如下表。

返回值类型方法说明
intgetMaxAge()用于获取指定 Cookie 的最大有效时间,以秒为单位。 默认情况下取值为 -1,表示该 Cookie 保留到浏览器关闭为止。
StringgetName()用于获取Cookie的名称
StringgetPath()用于获取Cookie的有效路径
booleangetSecure()如果浏览器只通过安全协议发送 Cookie,则返回 true;如果浏览器可以使用任何协议发送 Cookie,则返回 false。
StringgetValue()获取Cookie的值
intgetVersion()获取coookie遵守的协议版本
voidsetMaxAge(int expiry)用于设置 Cookie 的最大有效时间,以秒为单位。
取值为正值时,表示 Cookie 在经过指定时间后过期。取值为负值时,表示 Cookie 不会被持久存储,在 Web 浏览器退出时删除。取值为 0 时,表示删除该 Cookie。
voidsetPath(String uri)指定cookie的有效路径
voidsetSecure(boolean flag)用于设置浏览器是否只能使用安全协议(如 HTTPS 或 SSL)发送 Cookie。
voidsetValue(String newValue)设置cookie的值

cookie的使用细节

使用 Cookie 开发时需要注意以下细节:

  • 一个 Cookie 只能标识一种信息,它至少包含一个名称(NAME)和一个值(VALUE)。
  • 如果创建了一个 Cookie,并发送到浏览器,默认情况下它是一个会话级别的 Cookie。用户退出浏览器就被删除。如果希望将 Cookie 存到磁盘上,则需要调用 setMaxAge(int maxAge) 方法设置最大有效时间,以秒为单位。
  • 使用 setMaxAge(0) 手动删除 Cookie时,需要使用 setPath 方法指定 Cookie 的路径,且该路径必须与创建 Cookie 时的路径保持一致。

cookie的缺点

Cookie 虽然可以解决服务器跟踪用户状态的问题,但是它具有以下缺点:

  • 在 HTTP 请求中,Cookie 是明文传递的,容易泄露用户信息,安全性不高。
  • 浏览器可以禁用 Cookie,一旦被禁用,Cookie 将无法正常工作。
  • Cookie 对象中只能设置文本(字符串)信息。
  • 客户端浏览器保存 Cookie 的数量和长度是有限制的。

示例

我们制作一个简易的登录系统,主要运用到Cookie,重定向,请求转发技术。

  1. 创建登录页面 login.jsp,在没登陆或者访问主页没有对应的cookie就重定向到此网页
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>登录界面</title>
    </head>
    <body>
        <form action="/demo01/login" method="post">
            用户名:<input  type="text" name="uname"><br>
            密码    <input  type="password" name="upassword"> <br>
            <input type="submit" value="login">
        </form>
    </body>
</html>
  1. 创建成功页面 show.jsp,这个页面是请求转发得到的也就是/main的servlet
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
     <h1>登录成功</h1>
</body>
</html>
  1. 创建/login的servlet,如果登陆成功重定向到/main的servlet,否则重定向到此页面
public class servlet1 extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 设置request的请求编码
    	 request.setCharacterEncoding("utf-8");
        //设置页面的格式和字符编码
         response.setContentType("text/html;charset=utf-8");
        //获取用户名
         String name = request.getParameter("uname");
        //获取密码
         String password = request.getParameter("upassword");
        //判断密码和用户名
         if(name.equals("admin")&&password.equals("123456")) {
             //创建cookie,将值设置为密码
        	 Cookie cookie = new Cookie("uname",password);
             //设置cookie的有效时间,-1代表关闭浏览器自动删除
        	 cookie.setMaxAge(-1);
             //设置cookie的有效路径
        	 cookie.setPath("/demo01");
             //将cookie放在响应头中输出
        	 response.addCookie(cookie);
             //全部设置成功后跳转到/main的servlet
        	 response.sendRedirect("/demo01/main");
         }else {
             //判断密码和用户名失败后
        	 response.sendRedirect("/demo01/login.jsp");
         }
    }
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
}
  1. 创建/main的servlet,此servlet也要在判断一次cookie的判断,在判断成功后请求转发到show.jsp
public class otherservlet extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
      //设置request的缓冲区字符集
   	 request.setCharacterEncoding("utf-8");
      //设置到页面上的文本格式和字符编码
     response.setContentType("text/html;charset=utf-8");
      //获取Cookie返回一个cookie数组
     Cookie[] cookies = request.getCookies();
     //创建一个整形变量作为证明
     int a=0;
     if(cookies!=null) {
    	 for(Cookie cookie:cookies) {
    		 if(cookie.getName().equals("uname")&&cookie.getValue().equals("123456")) {
                 //在所有成功为true时,改变a的值为1
    		    a=1;
    		 }
    	 }
     }
      //a=1,也就是登录成功
     if(login==1) {
    	 request.getRequestDispatcher("/show.jsp").forward(request, response);
     }else {
    	 response.sendRedirect("/demo01/login.jsp");
     }
    }
    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }
}

9.Session的使用

​ Session 是服务器端会话技术。当浏览器访问 Web 服务器的资源时,服务器可以为每个用户浏览器创建一个 Session 对象,每个浏览器独占一个 Session 对象。

​ 由于每个浏览器独占一个 Session,所以用户在访问服务器的资源时,可以把数据保存在各自的 Session 中。当用户再次访问该服务器中的其它资源时,其它资源可以从 Session 中取出数据,为用户服务。

1.Session的工作原理

Session 虽然属于服务端会话技术,但是它的实现离不开客户端浏览器和 Cookie 的支持,其工作原理如下。

  1. 当客户端第一次请求会话对象时,服务器会创建一个 Session 对象,并为该 Session 对象分配一个唯一的 SessionID(用来标识这个 Session 对象);
  2. 服务器将 SessionID 以 Cookie(Cookie 名称为:“JSESSIONID”,值为 SessionID 的值)的形式发送给客户端浏览器;
  3. 客户端浏览器再次发送 HTTP 请求时,会将携带 SessionID 的 Cookie 随请求一起发送给服务器;
  4. 服务器从请求中读取 SessionID,然后根据 SessionID 找到对应的 Session 对象。

注意

  • 流程中的 Cookie 是容器自动生成的,它的 maxAge 属性取值为 -1,表示仅当前浏览器有效。
  • 浏览器关闭时,对应的 Session 并没有失效,但此时与此 Session 对应的 Cookie 已失效,导致浏览器无法再通过 Cookie 获取服务器端的 Session 对象。
  • 同一浏览器的不同窗口共享同一 Session 对象,但不同浏览器窗口之间不能共享 Session 对象。

2.Session与Cookie的对比

Session 和 Cookie 都属于会话技术,都能帮助服务器保存和跟踪用户状态,但两者也存在差异,如下表。

不同点CookieSession
存储位置不同Cookie 将数据存放在客户端浏览器内存中或硬盘上。Session 将数据存储在服务器端。
大小和数量限制不同浏览器对Cookie的大小和数量有限制Session的大小和数量不受限制
存放数据类型不同Cookie存放的是字符串Session存放的是对象
安全性不同Cookie 明文传递,安全性低,他人可以分析存放在本地的 Cookie 并进行 Cookie 欺骗。Session 存在服务器端,安全性较高。
对服务器造成的压力不同Cookie存放在客户端,不占用服务器资源Session 保存在服务端,每一个用户独占一个 Session。若并发访问的用户十分多,就会占用大量服务端资源。
跨域支持上不同Cookie支持跨域名访问Session不支持跨域名访问

3.SessionAPI

Session 对象由服务器创建,通过 HttpServletRequest.getSession() 方法可以获得 HttpSession 对象,例如:

 HttpSession session = request.getSession();  //获取Session对象

HttpSession 接口定义了一系列对 Session 对象操作的方法,如下表。

返回值类型方法说明
longgetCreationTime()返回创建 Session 的时间。
Stringgetid()返回Session的唯一id
longgetLastAccessedTime()返回客户端上一次发送与此 Session 关联的请求的时间。
intgetMaxInactiveInterval()返回在无任何操作的情况下,Session 失效的时间,以秒为单位。
ServletContextgetServletContext()返回 Session 所属的 ServletContext 对象。
voidinvalidate()使Session失效
voidsetMaxInactiveInterval(int interval)指定在无任何操作的情况下,Session 失效的时间,以秒为单位。负数表示 Session 永远不会失效。

4.设置Session的过期时间

Session 对象在服务器中驻留一段时间后没有被使用,就会被销毁,这个时间就是 Session 的过期时间。

Session 的默认过期时间为 30 分钟,我们可以通过如下两种方式设置过期时间。

1.在web.xml中设置session-config标签

在 web.xml 中,使用 及其子元素 可以配置 Session 的默认过期时间,代码如下。

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">

  <!--设置session的过期时间,以秒为的那位,如果为零或负数则表示session永远不会过期-->  
  <session-config>
      <session-timeout>15</session-timeout>
  </session-config>
</web-app>

2.调用 setMaxInactiveInterval() 方法

通过调用 session.setMaxInactiveInterval(int interval) 设置过期时间,以秒为单位,零和负数表示会话永远不会过期,代码如下。

HttpSession session = request.getSession();
//设置session 5秒后过期
session.setMaxInactiveInterval(5);

5.Session的生命周期

session对象创建

session由 request.getSession()方法创建。

session对象销毁

Session 对象在如下 3 种情况下会被销毁:

  • Session 过期;
  • 调用 session.invalidate() 方法,手动销毁 Session;
  • 服务器关闭或者应用被卸载。

6.Session域对象

Session 对象也是一种域对象,它可以对属性进行操作,进而实现会话中请求之间的数据通讯和数据共享。

返回值类型方法说明
voidsetAttribute(String name, Object o)把一个 Java 对象与一个属性名绑定,并将它作为一个属性存放到 Session 对象中。参数 name 为属性名,参数 object 为属性值。
ObjectgetAttribute(String name)根据指定的属性名 name,返回 Session 对象中对应的属性值。
voidremoveAttribute(String name)从 Session 对象中移除属性名为 name 的属性。
EnumerationgetAttributeNames()用于返回 Session 对象中的所有属性名的枚举集合。

Session 、request 以及 ServletContext 合称为 Servlet 的三大域对象,它们都能保存和传递数据,但是三者也存在许多差异,如下表。

不同requestSessionServletContext
类型javax.servlet.http.HttpServletRequestjavax.servlet.http.HttpSessionjavax.servlet.ServletContext
创建客户端向容器发送请求时创建。容器第一次调用 getSession() 方法时创建。Servlet 容器启动时创建。
销毁容器对这次请求做出响应后销毁。Session 销毁的时机: 关闭服务器或应用被卸载。Session 过期,默认为 30 分钟。手动调用 session.invalidate() 方法进行销毁。容器关闭或者 Web 应用被移除时销毁。
有效范围只对当前请求涉及的 Servlet 有效。Session 对本次会话期间的所有 Servlet 都有效。对整个 Web 应用内的所有 Servlet 有效。
数量Web 应用中的所有 Servlet 实例都可以有多个 request 对象。Web 应用中可以有多个 Session,多个 Servet 实例可以共享同一 Session 对象。在整个 Web 应用中只有一个 Context 对象。
数据共享每一次请求都是一个新的 request 对象。通过和请求转发的配合使用可以实现一次请求中 Web 组件之间共享的数据。每一次会话都是一个新的 Session 对象。通过 Session 域对象可以实现一次会话中的多个请求之间共享数据。在一个应用中有且只有一个 Context 对象,作用于整个 Web 应用,可以实现多次会话之间的数据共享。

示例

使用Session实现用户的登录,在访问主页时,如果没有进行登录,则跳转到登录页面登陆后在进入主页,还可以注销登录用户。

  1. 创建登录页面 login.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>登录界面</title>
    </head>
    <body>
        <form action="/demo01/login" method="post">
            用户名:<input  name="uname" type="text"><br/>
            密      码:<input  name="upassword" type="text"><br/>
            <input  type="submit"  value="提交">
        </form>
    </body>
</html>
  1. 创建成功页面,show.jsp
<%@page import="com.yc.User"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>Insert title here</title>
    </head>
    <body>
        <%
        User user=(User)request.getAttribute("user");
        if(user!=null){
            out.append("<h3>登录成功</h3>");
        }
        %>
        <h3>你的用户名是<%=user.getUsername() %></h3>
        <h3>你的密码是<%=user.getPassword() %></h3>
        <a href="/demo01/some">注销用户</a>
    </body>
</html>
  1. 创建login.jsp提交的登录servlet
public class Login extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        request.setCharacterEncoding("utf-8");
        response.setContentType("text/html;charset=utf-8");
        String username=request.getParameter("uname");
        String password=request.getParameter("upassword");
        //创建session
        HttpSession session = request.getSession();
        if("admin".equals(username)&&"123".equals(password)) {
            User user = new User();
            user.setUsername(username);
            user.setPassword(password);
            //将user对象保存到session域对象中
            session.setAttribute("user",user);
            //设置session的过期时间 8秒
            session.setMaxInactiveInterval(8);
            //成功后重定向到首页的servlet  /main
            response.sendRedirect("/demo01/main");
        }else {
            //如果判断不成功跳转到登录页面
            response.sendRedirect("/demo01/login.jsp");
        }
    }
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
}
  1. 创建/main的servlet,判断是否可以进入到成功页面
public class Main extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");
        //创建Session对象保存用户信息
        HttpSession session = request.getSession();
        User user = (User) session.getAttribute("user");
        if(user!=null) {
            //请求转发带user对象一起
            request.setAttribute("user",user);
            request.getRequestDispatcher("/show.jsp").forward(request, response);
        }else {
            //如果user为空表示没登陆,重定向到login.jsp
            response.sendRedirect("/demo01/login.jsp");
        }

    }
    public void doPost(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
        doGet(request, response);
    }
}
  1. 创建注销的session的servlet
public class some extends HttpServlet {

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // TODO Auto-generated method stub
        //Session对象删除保存User对象,不要手动删除整个session,因为在登录验证只用到了user对象,如果
        //在session中还保存了其他的阈值,之后在不登陆的情况下可能还会用到。
        request.getSession().removeAttribute("user");
        response.sendRedirect("/demo01/main");
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // TODO Auto-generated method stub
        doGet(request, response);
    }

}
  1. 创建用户类
public class User {
    private String username;
    private String password;

    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
}

10.Filter(过滤器)

Servlet Filter 又称 Servlet 过滤器,它是在 Servlet 2.3 规范中定义的,能够对 Servlet 容器传给 Web 资源的 request 对象和 response 对象进行检查和修改。

Filter 不是 Servlet,不能直接访问,它本身也不能生成 request 对象和 response 对象,它只能为 Web 资源提供以下过滤功能:

  • 在 Web 资源被访问前,检查 request 对象,修改请求头和请求正文,或对请求进行预处理操作。
  • 将请求传递到下一个过滤器或目标资源。
  • 在 Web 资源被访问后,检查 response 对象,修改响应头和响应正文。

注意:过滤器并不是必须要将请求传递到下一个过滤器或目标资源,它可以自行对请求进行处理,并发送响应给客户端,也可以将请求转发或重定向到其他的 Web 资源。

​ Filter 是 Servlet 规范中最实用的技术,通过它可以对服务器管理的所有 Web 资源(例如 JSP、Servlet、静态 HTML 文件、静态图片等)进行拦截,从而实现一些特殊的功能,例如用户的权限控制、过滤敏感词、设置统一编码格式等。

1.Filter接口

​ 与开发 Servlet 需要实现 javax.servlet.Servlet 接口类似,开发过滤器要实现 javax.servlet.Filter 接口,并提供一个公开的不带参的构造方法。在 Filter 接口中,定义了 3 个方法,如下表所示。

返回值类型方法说明
voidinit (FilterConfig filterConfig)该方法用于初始化过滤器。
voiddoFilter(ServletRequest request,SeivletResponse response, FilterChain chain)该方法完成实际的过滤操作,当客户端请求的 URL 与过滤器映射的 URL 匹配时,容器会先调用该方法对请求进行拦截。
参数 request 和 response 表示请求和响应对象。
参数 chain 代表当前 Filter 链对象,在该方法内部,调用 chain.doFilter() 方法,才能把请求交付给 Filter 链中的下一个 Filter 或者 Web 资源。
voiddestroy()该方法在销毁 Filter 对象之前被调用,用于释放被 Filter 对象占用的资源。

2.Filter工作流程

  1. 客户端请求访问容器内的 Web 资源。
  2. Servlet 容器接收请求,并针对本次请求分别创建一个 request 对象和 response 对象。
  3. 请求到达 Web 资源之前,先调用 Filter 的 doFilter() 方法,检查 request 对象,修改请求头和请求正文,或对请求进行预处理操作。
  4. 在 Filter 的 doFilter() 方法内,调用 FilterChain.doFilter() 方法,将请求传递给下一个过滤器或目标资源。
  5. 目标资源生成响应信息返回客户端之前,处理控制权会再次回到 Filter 的 doFilter() 方法,执行 FilterChain.doFilter() 后的语句,检查 response 对象,修改响应头和响应正文。
  6. 响应信息返回客户端。

3.Filter的生命周期

1.初始化阶段

Servlet 容器负责加载和实例化 Filter。容器启动时,读取 web.xml 或 @WebFilter 的配置信息对所有的过滤器进行加载和实例化。

加载和实例化完成后,Servlet 容器调用 init() 方法初始化 Filter 实例。在 Filter 的生命周期内, init() 方法只执行一次。

2.拦截和过滤阶段

​ 该阶段是 Filter 生命周期中最重要的阶段。当客户端请求访问 Web 资源时,Servlet 容器会根据 web.xml 或 @WebFilter 的过滤规则进行检查。当客户端请求的 URL 与过滤器映射匹配时,容器将该请求的 request 对象、response 对象以及 FilterChain 对象以参数的形式传递给 Filter 的 doFilter() 方法,并调用该方法对请求/响应进行拦截和过滤。

3.过滤器销毁

Filter 对象创建后会驻留在内存中,直到容器关闭或应用被移除时销毁。销毁 Filter 对象之前,容器会先调用 destory() 方法,释放过滤器占用的资源。在 Filter 的生命周期内,destory() 只执行一次。

4.注册和映射Filter

1.web.xml配置

在 web.xml 中,通过 及其子元素注册 Filter,代码如下。

<filter>
    <filter-name>myFilter</filter-name>
    <filter-class>com.yc.www.MyFilter</filter-class>
    <init-param>
        <param-name>name</param-name>
        <param-value>URL</param-value>
    </init-param>
    <init-param>
        <param-name>URL</param-name>
        <param-value>www.yc.cmo</param-value>
    </init-param>
</filter>

以上元素说明如下:

  • 用于注册过滤器
  • 是 元素的子元素, 用于指定过滤器的注册名,该元素的内容不能为空。
  • 是 元素的子元素,用于指定过滤器的完整限定名(包名+类名)。
  • 是 元素的子元素,用于为过滤器指定初始化参数,它的子元素 指定参数的名称, 指定参数的值。

在 web.xml 中,通过使用 及其子元素映射 Filter,代码如下。

<filter-mapping>
    <filter-name>myFilter</filter-name>
    <url-pattern>/login</url-pattern>
    <dispatcher>REQUEST</dispatcher>
    <dispatcher>FORWARD</dispatcher>
</filter-mapping>

<filter-mapping>
    <filter-name>myFilter</filter-name>
    <servlet-name>ServletDemo</servlet-name>
</filter-mapping>

以上元素说明如下:

  • 元素用于设置 Filter 负责拦截的资源。
  • 是 元素的子元素,用于设置 Filter 的注册名,该值必须在 元素的子元素 中声明过。
  • 是 元素的子元素,用于设置 Filter 拦截的请求路径。
  • 是 元素的子元素,用于设置 Filter 拦截的 Servlet 名称。
  • 是 元素的子元素,用于指定 Filter 拦截的资源被 Servlet 容器调用的方式,可以是 REQUEST、INCLUDE、FORWARD 和 ERROR 之一,默认 REQUEST。用户可以设置多个 子元素指定 Filter 对资源的多种调用方式进行拦截。

元素的取值及其意义:

  • REQUEST:当用户直接访问页面时,容器将会调用过滤器。如果目标资源是通过 RequestDispatcher 的 include() 或 forward() 方法访问,则该过滤器就不会被调用。
  • INCLUDE:如果目标资源通过 RequestDispatcher 的 include() 方法访问,则该过滤器将被调用。除此之外,该过滤器不会被调用。
  • FORWARD:如果目标资源通过 RequestDispatcher 的 forward() 方法访问,则该过滤器将被调用,除此之外,该过滤器不会被调用。
  • ERROR:如果目标资源通过声明式异常处理机制访问,则该过滤器将被调用。除此之外,过滤器不会被调用。

5.Filter应用

​ filter可以帮助我们解决许多的重复性的拦截的供能,例如判断是否登录或者设置返回的字符编码。以后就不需要在每个servlet中还进行重复性处理

示例

​ 我们实现一个小小的案例,也就是设置request的请求编码和response的页面格式和字符编码,设置字符集就用FilterConfig的初始化值。

  1. 创建过滤器,MyFilter.java
public class MyFilter implements Filter {
    //私有化FilterConfig
    private FilterConfig fconfig;
    public MyFilter() {
        // TODO Auto-generated constructor stub
    }

    public void destroy() {
        // TODO Auto-generated method stub
    }


    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        //获取filter的默认值
        String string = fconfig.getInitParameter("encoding").toString();
        String string2 = fconfig.getInitParameter("content-type").toString();
        request.setCharacterEncoding(string);
        response.setContentType(string2);
        //过滤器通过
        chain.doFilter(request, response);
    }
    public void init(FilterConfig fConfig) throws ServletException {
        //初始化时将设置fconfig
        this.fconfig=fConfig;
    }
}
  1. web.xml设置filter的参数
<!-- 过滤器申明 -->
<filter>
    <filter-name>myfilter</filter-name>
    <filter-class>com.yc.MyFilter</filter-class>

    <init-param>
        <param-name>encoding</param-name>
        <param-value>utf-8</param-value>
    </init-param>
    <init-param>
        <param-name>content-type</param-name>
        <param-value>text/html;charset=utf-8</param-value>
    </init-param>
</filter>

<filter-mapping>
    <filter-name>myfilter</filter-name>
    <!--拦截登录模块的servlet-->
    <url-pattern>/login</url-pattern>
</filter-mapping>
  1. 创建login的servlet
public class servlet1 extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        PrintWriter writer = response.getWriter();
        writer.append("过滤器已经过滤了字符编码<br>");
        writer.close();
    }
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
}

二.jsp

1.jsp脚本

在 JSP 中,可以使用 JSP 脚本写入 Java 代码。

JSP 脚本可以包含任意数量的 Java 语句,变量、方法和表达式。JSP 脚本会把包含的内容插入到 Servlet 的 service() 方法中。

jsp脚本语法如下:

<% Java语句 %>

任何文本、HTML 标签和 JSP 元素(声明,表达式等)都必须在脚本程序之外。

示例

下面创建 login.html 和 welcome.jsp 两个文件,login.html 提供表单供用户输入用户名,welcome.jsp 获取用户输入用户名并显示欢迎消息。

  1. login.html
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
    </head>
    <body>
        <form action="welcome.jsp">
            <input type="text" name="name">
            <input type="text" name="url">
            <input type="submit" value="go">
        </form>
    </body>
</html>
  1. welcome.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
    </head>
    <body>
        <%
        String   name=request.getParameter("name");
        String  url=request.getParameter("url");
        out.println("欢迎"+name+"  您输入的网址是<br>"+url);
        %>
    </body>
</html>

2.jsp声明语句

JSP 声明语句用于声明一个或多个变量、方法,以供后面的 Java 代码使用。您必须先对变量和方法进行声明,才能使用它们。

jsp声明语句语法如下:

<%! 声明语句 %>

jsp脚本与jsp声明的区别

  1. JSP 脚本只能声明变量,不能声明方法。JSP 声明语句可以声明变量和方法。
  2. JSP 脚本会把包含的内容转译插入到 Servlet 的 service() 方法中,也就是 <% %> 中定义的变量是局部变量。这也是 JSP 脚本不能声明方法的原因,因为 Java 不允许方法中嵌套方法。
  3. JSP 声明会把包含的内容添加到 Servlet 类中(在任何方法之外),也就是 <%! %> 中定义的变量是成员变量,方法是成员方法。

3.jsp表达式

JSP 表达式可以把变量或者表达式输出到网页上,不需要 out.print() 就能输出数据。通常用于打印变量和方法的值。

jsp表达式语法:

<%= 表达式 %>

**注意:**可以将 <%=表达式 %> 理解为 <% out.println(表达式) %> 的简写方式。这里需要注意,JSP 表达式不能以分号结尾。

4.jsp注释(4种)

说到注释,相信大家肯定都不陌生,它是对程序代码的解释和说明。注释可以提高代码的可读性,让他人能够更加轻松地了解代码,从而提高团队合作开发的效率。

在 JSP 中可以使用以下 4 种注释:

  • HTML 注释
  • 带有 JSP 表达式的注释
  • 隐藏注释
  • 脚本程序(Scriptlet)中的注释

HTML注释

<!-- 注释内容 -->

HTML 注释中的内容是不会在客户端浏览器中显示的,但可以通过 HTML 源代码看到这些注释内容。

带有jsp表达式的注释

<!--HTML 注释内容<%=JSP 表达式%>-->

包含该注释语句的 JSP 页面被请求后,服务器能够自动识别并执行注释中的 JSP 表达式,对于注释中的其他内容则不做任何操作。

当服务器将执行结果返回给客户端浏览器后,注释的内容也不会在浏览器中显示。

当我们查看 HTML 源代码时,只能查看到 JSP 表达式执行后的结果,并不能看到原来的 JSP 表达式。

隐藏注释

无论是普通的 HTML 注释还是带有 JSP 表达式的注释,虽然都不能在客户端浏览器中显示,但是它们却都存在于 HTML 源代码中,客户端可以通过 HTML 源代码看到被注释的内容,所以严格来说,这两种注释其实并不安全。下面我们介绍的隐藏注释,就可以解决这个问题,

隐藏注释的内容,不会显示在客户端的任何位置(包括 HTML 源代码),安全性较高,其注释格式如下:

<%--注释内容--%>

脚本程序(Scriptlet)中的注释

脚本程序中包含的是一段 Java 代码,所以在脚本程序中的注释与在 Java 中的注释是相同的。

脚本程序中包括下面 3 种注释方法。

  • 单行注释
  • 多行注释
  • 文档注释

5.jsp指令

JSP 指令(directive)用来告诉 Web 服务器如何处理 JSP 页面的请求和响应。

服务器会根据 JSP 指令来编译 JSP,生成 Java 文件。JSP 指令不产生任何可见输出,在生成的 Java 文件中,不存在 JSP 指令。

JSP 指令以<%@开始,以%>结束,语法如下:

<%@ directive attribute = "value" [attribute2 = "value2" ...]%>

注意:[ ]中的内容是可选的,@ 符号和指令名称之间,以及最后一个属性和结束%>之间的空格是可选的。

1.jsp page指令

JSP page 指令用来定义当前页面的相关属性。page 指令可以在 JSP 页面的任意位置编写,通常放在 JSP 页面的顶部。

page指令语法如下

<%@ page attribute = "value" %>

下表是与 page 指令相关的属性。

2.jsp include指令

include 指令用于在 JSP 页面引入其它内容,可以是 JSP 文件、html 文件和文本文件等,相当于把文件的内容复制到 JSP 页面。引入的文件和 JSP 页面同时编译运行。

使用 include 指令有以下优点:

  • 增加代码的可重用性
  • 使 JSP 页面的代码结构清晰易懂
  • 维护简单

include指令语法如下

<%@ include file="URL" %>  

其中,file 指定需要引入文件的相对路径。可以在页面的任何位置编写 include 指令。

示例

在index.jsp页面使用include指令引入show.jsp,show.jsp如下

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>Insert title here</title>
    </head>
    <body>
        <h1>Hello World</h1>
    </body>
</html>

index.jsp如下

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>登录界面</title>
    </head>
    <body>
        <h1>引入的show.jsp页面</h1>
        <%@ include file="show.jsp" %>
    </body>
</html>

此示例中,head.jsp 和 index.jsp 在同一目录中,如果不在同一目录,引入 head.jsp 文件时需要指定完整路径。

3.jsp taglib指令

在 JSP 中,我们可以使用 taglib 指令声明并引入标签库。Java API 允许在页面中自定义标签,标签库就是自定义标签的集合。

taglib 指令的语法如下

<%@ taglib uri="tagliburl" prefix="tagPre" %>

其中,uri 指定自定义标签库的存放位置;prefix 指定标签库的前缀。为了区分不同的标签库,在页面中使用标签库以对应的 prefix 开头。

6.jsp动作

JSP 动作使用 XML 语法格式的标签来控制服务器的行为。利用 JSP 动作可以动态地插入文件、重用 JavaBean 组件、把用户重定向到另一个页面、为 Java 插件生成 HTML 代码等。

JSP 动作与 JSP 指令的不同之处如下:

  • JSP 指令在翻译阶段执行,从而设置整个 JSP 页面的属性。JSP 页面被执行时首先进入翻译阶段,程序会先查找页面中的 JSP 指令,并将它们转换成 Servlet。所以,JSP 指令是在页面转换时期被编译执行的,且编译一次。
  • JSP 动作在请求处理阶段执行,它们只有执行时才实现自己的功能。通常用户每请求一次,动作标识就会执行一次。

jsp动作语法格式如下

<jsp:action_name attribute = "value" />

或者

<jsp:action_name attribute="value"></jsp:action_name>

action_name 表示 JSP 动作名称,attribute 表示相应 JSP 动作的属性名称。常用的 JSP 动作如下表所示:

1.jsp include动作

<jsp:include> 动作用来在页面中引入文件,文件可以是 HTML、JSP 页面和文本文件等。通过 include 动作,我们可以多次使用同一个页面,增加了代码可重用性。例如,在页面中使用 include 动作引入头部和底部页面。

<jsp:include> 的语法如下:

<jsp:include page="relativeURL | <%=expression%>" flush="true" />  

page 指定引入页面的路径,flush 表示在引入文件前是否刷新缓冲区,默认为 false。

注意:“jsp”和“:”以及“include”三者之间没有空格。

示例

show.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>Insert title here</title>
    </head>
    <body>
        <h1>Hello World</h1>
    </body>
</html>

index.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>登录界面</title>
    </head>
    <body>
        <h2>引入的show.jsp页面</h2>
        <%
        String name="show.jsp";
        %>
        <jsp:include page="<%=name %>" flush="true"></jsp:include>
    </body>
</html>

2.jsp forward动作

<jsp:forward> 动作用来将请求转发到另一个页面中,请求的参数数据会被一起转发到目标页面。

<jsp:forward> 的语法如下:

<jsp:forward page="url"/>

其中,page 指定需要转发文件的相对路径,且指定的文件只能是该 Web 应用中的文件。

示例

login.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>登录界面</title>
    </head>
    <body>
        <h2>引入的show.jsp页面</h2>
        <%
        String name="show.jsp";
        %>
        <jsp:forward page="<%=name %>"></jsp:forward>
    </body>
</html>

show.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>Insert title here</title>
    </head>
    <body>
        <h1>Hello World</h1>
    </body>
</html>

访问login.jsp,显示的是show.jsp

3.jsp param动作

<jsp:param> 动作用来传递参数信息,经常和其它动作一起使用,例如 <jsp:include> 和 <jsp:forward>。

<jsp:param> 的语法如下:

<jsp: param name="param_name" value="param_value" />

name 指定参数名称,value 指定参数值。

示例

在 login.jsp 中传递参数,并转发到 show.jsp 页面。login.jsp 代码如下:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>登录界面</title>
    </head>
    <body>
        <h2>login.jsp页面</h2>
        <%
        request.setCharacterEncoding("utf-8");
        %>
        <jsp:forward page="show.jsp">
            <jsp:param value="用户名" name="name"/>
            <jsp:param value="密码" name="password"/>
        </jsp:forward>
    </body>
</html>

show.jsp

<%@page import="java.util.Date"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>Insert title here</title>
    </head>
    <body>
        <h3>这是show.jsp</h3>
        <p>这是 <%=request.getParameter("name") %></p>
        <p>这是 <%=request.getParameter("password") %></p>
    </body>
</html>

<jsp:param> 在 <jsp:forward> 和 <jsp:include> 中的用法基本一样,用户可以将上述示例中的 <jsp:forward> 替换成 <jsp:include> 动作,运行结果相同。

4.useBean动作

<jsp:useBean> 用于获取 Bean 对象。<jsp:useBean> 首先会在指定范围内查找 Bean 对象,如果 Bean 对象不存在,则创建 Bean 对象。

<jsp:useBean> 的语法如下:

<jsp:useBean id = "name" class = "package.class" scope= "page | request | session | application" />
1)id

表示 Bean 实例化对象的变量名,可以在指定范围内使用该变量名。

2)class

表示需要实例化 Bean 的类路径,指定的类必须包含 public 且无参数的构造方法。

3)scope

指定 Bean 的作用域,取值为:

  • page:只能在当前页面使用该 Bean 对象;
  • request:只能在一次请求范围内使用该 Bean 对象;
  • session:只能在一次会话范围内使用该 Bean 对象;
  • application:可以在整个应用范围内使用该 Bean 对象。

示例

创建一个Test类,在这个类中实现方法

Test.java

public class Test {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}

创建index.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>登录界面</title>
    </head>
    <body>
        <jsp:useBean id="test" class="com.yc.Test"></jsp:useBean>
        <%
        String name="Hello world";
        test.setName(name);
        %>
        <div>最终的值是<%=test.getName() %></div>
    </body>
</html>

7.EL表达式

之前的 JSP 页面中,我们经常使用 JSP 表达式来输出变量或者页面之间传递的参数,大大降低了页面的可读性。

为了简化 JSP 页面,JSP 2.0 新增了 EL(Expression Language)表达式语言。EL 提供了更为简洁、方便的形式来访问变量和参数,不仅可以简化 JSP 页面代码,还会使开发者的逻辑变得更加清晰 。

EL 表达式语法如下:

${EL表达式}

EL 表达式语法以${开头,以}结束,中间为合法的表达式。

${param.name} 表示获取参数 name 的值,它等同于 <%=request.getParameter('name') %>。从形式和语法上可以看出,EL 表达式简化了 JSP 原有的表达式。在实际开发中,EL 表达式也是经常使用的方式。

EL 表达式定义了许多运算符,如算术运算符、比较运算符、逻辑运算符等,使用这些运算符,可以使 JSP 页面更加简洁。

EL内置对象

示例

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
    <head>
        <title>编程帮(www.biancheng.net)</title>
    </head>
    <body>
        <%
        request.setCharacterEncoding("UTF-8");
        %>
        <form action="${pageContext.request.contextPath}/index.jsp" method="post">
            网站名称: <input type="text" name="name" value="编程帮" /> <br>
            <input type="text" name="name" value="C语言中文网" /> <br>
            <input type="text" name="name" value="百度" /> <br>

            网址: <input type="text" name="url" value="www.biancheng.net" /> <br>
            <input type="submit" value="提交" />
        </form>
    </body>
</html>

表单的 action 属性也是一个 EL 表达式。${pageContext.request.contextPath} 等价于 <%=request.getContextPath()%>,意思就是取出部署的应用程序名,或者是当前的项目名称。本例中项目名称是 jspDemo,因此 p a g e C o n t e x t . r e q u e s t . c o n t e x t P a t h 或 < {pageContext.request.contextPath} 或 <%=request.getContextPath()%> 就等于 `/jspDemo`。实际项目中应该这样写: pageContext.request.contextPath<{pageContext.request.contextPath}/login.jsp。

三.JSTL标签库

JSTL(JSP Standard Tag Library,核心标签库)是 JSP 标签的集合,它封装了 JSP 应用的通用核心功能。

JSP 标签是一组与 HTML 标签相似,但又比 HTML 标签强大的功能标签。JSTL 用来简化 JSP 开发,可以使我们不用嵌入 Java 代码就能够开发出复杂的 JSP 页面。

JSTL 包含 5 类标签库:core 标签库、fmt 标签库、fn 标签库、XML 标签库和 SQL 标签库。这 5 类标签库基本覆盖了 Web 开发中的所涉及的技术展示。

使用 JSTL 需要引入 JSTL 的 JAR 包和标签库描述符文件(扩展名为 .tld),标签库描述符文件内包含标签库中所有标签的定义、标签名、功能类及各种属性。

注意:本教程使用的 JSTL 1.2,如果你使用的是 JSTL 1.1 或之前的版本,需要导入 jstl.jar 和 standard.jar 两个包。

JSTL 核心(core)标签是最常用的 JSTL 标签,导入核心标签库的语法如下:

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

1.c:out

JSTL <c:out> 标签与 JSP 表达式<%= %> 作用相似,用于把表达式的结果输出到页面中。它们的区别就是 <c:out> 标签可以直接通过.操作符来访问属性。比如访问 user.address.city,只需要这样写:<c:out value="user.address.city">

语法如下:

<c:out value="表达式" [default="表达式"] [escapeXml="<true|false>"] />

其中:

  • value:指定要输出的内容
  • default:可选项,指定输出的默认值
  • escapeXml:可选项,默认为 true。表示是否转换特殊字符,如将<转换为<>转换为>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>登录界面</title>
    </head>
    <body>
        <%
        String name="hello world &lt &gt";
        %>
        <c:out value="<%=name%>" escapeXml="true" default="默认值"></c:out><br>
        <c:out value="<%=name%>" escapeXml="false" default="默认值"></c:out><br>
        <c:out value="${null}" escapeXml="true" default="默认值"></c:out><br>
    </body>
</html>

2.c:if

JSTL <c:if> 标签类似于 Java if 语句,用于条件判断。判断条件为 true 时执行其代码块。

语法:

<c:if test="判断条件" [var="varname"] [scope="request|page|session|application"] >
   代码块
</c:if>

其中:

  • test:指定判断条件,返回值为 boolean
  • var:可选项,判断条件的执行结果
  • scope:可选项,执行结果的作用域
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>登录界面</title>
    </head>
    <body>
        <c:set var="age" value="20"></c:set>
        <c:if test="${age>10}" var="res">
            <c:out value="age大于10"></c:out>
        </c:if>
        <c:if test="${age<10}" var="res">
            <c:out value="age小于10"></c:out>
        </c:if>
        <c:out value="${res}"></c:out>
    </body>
</html>

3.c:forEach

JSTL <c:forEach> 标签类似于 Java 中的 for 循环语句,用来迭代一个集合中的对象。

语法:

<c:forEach [var="varname"] [varStatus="varstatusName"] [begin="开始"] [end="结束"] [step="step"]>
    Java程序或HTML代码
</c:forEach>

<c:forEach items="collection" [var="varname" [varStatus="varstatusName"] [begin="开始"] [end="结束"] [step="step"]]>
    Java程序或HTML代码
</c:forEach>

其中:

  • items:要被循环的信息,可以是数组、Java 集合等;
  • var:可选项,指定迭代之的别名;
  • varStatus:可选项,当前迭代的状态信息;
  • begin:可选项,迭代开始的元素,起始下标为 0;
  • end:可选项,迭代结束的元素;
  • step:可选项,迭代的步长;
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>登录界面</title>
    </head>
    <body> 
        <%
        String[] a={"a","b","c","d","e","f"};
        %>
        <c:forEach items="<%=a%>"  var="m"> 
            <c:out value="${m }"></c:out>
        </c:forEach>
    </body>
</html>

四.文件上传和下载

1.文件上传的必要条件

  1. 提供form表单,method值必须是post
  2. form表单的enctype必须是multipart/form-data(以字节流形式上传),所以request.getParameter(name)将会失效
  3. 提供type=file上传

2.借助第三方包实现上传文件

  1. commons-fileupload-1.4.jar 核心包
  2. commons-io-2.11.0.jar 依赖包

FileUpload是Apache组织(www.apache.org)提供的免费的上传组件,但是FileUpload组件本身还依赖于commons组件,所以从Apache下载此组件的时候还需要连同commons组件的IO包一起下载。

commons-fileUpload上传组件对中文进行了良好的处理,对上传文件不会出现中文乱码问题,是目前最广泛的组件。

他们的主要作用就是帮我们解析 request.getInputStream();

1.fileupload核心有:

  1. 创建磁盘工厂:DiskFileItemFactory factory = new DiskFileItemFactory();
  2. 创建处理工具:ServletFileUpload upload = new ServletFileUpload(factory);
  3. 设置上传文件大小:upload.setFileSizeMax(3145728);
  4. 接收全部内容:List<FileItem> items = upload.parseRequest(request);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值