Servlet详解

目录

零、码仙励志

一、Servlet概述

1. 什么是Servlet

2. 实现Servlet的方式

3. 创建HelloServlet应用

二、Servlet接口

1.Servlet的生命周期

1.Servlet的出生 

 2.Servlet服务

3.Servlet的离去

4.测试生命周期方法代码(查看Servlet接口中的方法)

2.Servlet接口相关类型

1.ServletRequest和ServletResponse(简单说明)

 2.ServletConfig

三、GenericServlet

1.GenericServlet概述

2.GenericServlet的init()方法

3.实现了ServletConfig接口

四、HttpServlet

1.HttpServlet概述

2.HttpServlet覆盖了service()方法 

3.doGet()和doPost()

五、Servlet细节

1.Servlet与线程安全

2.让服务器在启动时就创建Servlet 

3.url-pattern(Servlet的访问路径)

4.web.xml文件的继承(了解)

六、ServletContext(重要)

1.ServletContext概述

2.获取ServletContext

3.域对象的功能

4.获取应用初始化参数

5.获取资源相关方法

1.获取真实路径 (重要)

2 获取资源流 

3.获取指定目录下所有资源路径

6.练习:访问量统计

七、获取类路径下资源

八、response

1.response简介

2.发送状态码

3.响应头的相关方法

4.重定向

5.定时刷新

6.禁用浏览器缓存

7.响应体

九、request

1.request概述

2.request域方法

3.request获取请求头

4.获取请求URL

5.request请求数据相关其他方法

1.重点

2.非重点

6.获取请求参数

7.请求转发和请求包含

1.请求转发

2.请求包含

3.请求转发与请求包含比较

4.请求转发与重定向比较

十、编码

1.请求编码

1.直接在地址栏中给出中文

2.在页面中发出请求

3.GET请求解读编码

4.POST请求解读编码

2.响应编码

3.URL编码

十一、路径

1.web.xml中路径

2.转发和包含路径

3.重定向路径

4.页面中超链接和表单路径

5.ServletContext获取资源路径 

6.ClassLoader获取资源路径

7.Class获取资源路径


零、码仙励志

你迷茫的原因往往只有一个,那就是在本该拼命去努力的年纪,想得太多,做得太少

一、Servlet概述

1. 什么是Servlet

Servlet是JavaWeb的三大组件之一,它属于动态资源。Servlet的作用是处理请求,服务器会把接收到的请求交给Servlet来处理,在Servlet中通常需要:

  1. 接收请求数据
  2. 处理请求
  3. 完成响应

例如客户端发出登录请求,或者输出注册请求,这些请求都应该由Servlet来完成处理!Servlet需要我们自己来编写 

2. 实现Servlet的方式

实现Servlet有三种方式:

  1. 实现javax.servlet.Servlet接口;
  2. 继承javax.servlet.GenericServlet类;
  3. 继承javax.servlet.http.HttpServlet类;

 通常我们会去继承HttpServlet类来完成我们的Servlet,但学习Servlet还要从javax.servlet.Servlet接口开始学习。

3. 创建HelloServlet应用

1.首先创建一个叫Test01_1的Web Project的工程
2.在工程中创建一个叫com.maxian.web.servlet的包名
3.在包中新建一个叫HelloServlet的类并且让它实现Servlet接口

接下来我们开始准备完成HelloServlet,完成HelloServlet需要分为两步:
1.编写HelloServlet类;
2.在web.xml文件中配置HelloServlet;

HelloServlet.java

package com.maxian.web.servlet;

import java.io.IOException;

import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class HelloServlet implements Servlet {
	public void init(ServletConfig config) throws ServletException {}
	public ServletConfig getServletConfig() {return null;}
	public void destroy() {}
	public String getServletInfo() {return null;}

	public void service(ServletRequest req, ServletResponse res)
			throws ServletException, IOException {
		System.out.println("Hello Servlet");
	}
}

web.xml

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

	<servlet>
		<servlet-name>xxx</servlet-name>
		<servlet-class>com.maxian.web.servlet.HelloServlet</servlet-class>
	</servlet>
	<servlet-mapping>
		<servlet-name>xxx</servlet-name>
		<url-pattern>/Hello</url-pattern>
	</servlet-mapping>

	<display-name></display-name>
	<welcome-file-list>
		<welcome-file>index.jsp</welcome-file>
	</welcome-file-list>
</web-app>

在web.xml中配置Servlet的目的其实只有一个,就是把访问路径与一个Servlet绑定到一起,上面配置是把访问路径:“/Hello”与“com.maxian.web.servlet.HelloServlet”绑定到一起。

1.<servlet>和<servlet-mapping>通过<servlet-name>这个元素关联在一起了!
2.<servlet-name>:指定HelloServlet这个Servlet的名称为xxx;(名字可以任意起)
3.<servlet-class>:Servlet的路径。包名和类名加在一起
4.<url-pattern>:指定Servlet的浏览器访问路径

在浏览器中访问:http://localhost:8080/Test01_1/Hello即可在控制台上看到输出!

二、Servlet接口

1.Servlet的生命周期

所谓xxx的生命周期,就是说xxx的出生、服务,以及死亡。Servlet生命周期也是如此!与Servlet的生命周期相关的方法有:

1.void init(ServletConfig);
2.void service(ServletRequest,ServletResponse);
3.void destroy();

1.Servlet的出生 

  1. 服务器会在Servlet第一次被访问时创建Servlet,或者是在服务器启动时创建Servlet。如果服务器启动时就创建Servlet,那么还需要在web.xml文件中配置。也就是说默认情况下,Servlet是在第一次被访问时由服务器创建的。
  2. 而且一个Servlet类型,服务器只创建一个实例对象,例如在我们首次访问http://localhost:8080/day01_1/Hello时,服务器通过“/Hello”找到了绑定的Servlet名称为com.maxian.web.servlet.HelloServlet.,然后服务器查看这个类型的Servlet是否已经创建过,如果没有创建过,那么服务器才会通过反射来创建HelloServlet的实例。当我们再次访http://localhost:8080/day01_1/Hello时,服务器就不会再次创建HelloServlet实例了,而是直接使用上次创建的实例。
  3. 在Servlet被创建后,服务器会马上调用Servlet的void init(ServletConfig)方法。请记住, Servlet出生后马上就会调用init()方法,而且一个Servlet的一生。这个方法只会被调用一次。这好比小孩子出生后马上就要去剪脐带一样,而且剪脐带一生只有一次。
  4. 我们可以把一些对Servlet的初始化工作放到init方法中!

 2.Servlet服务

当服务器每次接收到请求时,都会去调用Servlet的service()方法来处理请求。服务器接收到一次请求,就会调用service() 方法一次,所以service()方法是会被调用多次的。正因为如此,所以我们才需要把处理请求的代码写到service()方法中!

3.Servlet的离去

Servlet是不会轻易离去的,通常都是在服务器关闭时Servlet才会离去!在服务器被关闭时,服务器会去销毁Servlet,在销毁Servlet之前服务器会先去调用Servlet的destroy()方法,我们可以把Servlet的临终遗言放到destroy()方法中,例如对某些资源的释放等代码放到destroy()方法中。

4.测试生命周期方法代码(查看Servlet接口中的方法)

/*
 * 查看Servlet接口中的方法
 */
public class HelloServlet implements Servlet {
	/*
	 * 它也是生命周期方法
	 * 它会在Servlet被销毁之前调用,并且它只会被调用一次!
	 */
	@Override
	public void destroy() {
		System.out.println("destory()...");
	}
	/*
	 * 可以用来获取Servlet的配置信息
	 */
	@Override
	public ServletConfig getServletConfig() {
		System.out.println("getServletConfig()...");
		return null;
	}
	/*
	 * 获取Servlet的信息
	 */
	@Override
	public String getServletInfo() {
		System.out.println("getServletInfo()...");
		return "我是一个快乐的Servlet";
	}
	/*
	 * 它是生命周期方法
	 * 它会在Servlet对象创建之后马上执行,并只执行一次!(出生之后)
	 * 
	 */
	@Override
	public void init(ServletConfig servletConfig) throws ServletException {
		System.out.println("init()...");
	}
	/*
	 * 它是生命周期方法
	 * 它会被调用多次!!!
	 * 每次处理请求都是在调用这个方法!
	 */
	@Override
	public void service(ServletRequest request, ServletResponse response)
			throws ServletException, IOException {
		System.out.println("service()...");
	}
}

2.Servlet接口相关类型

在Servlet接口中还存在三个我们不熟悉的类型:

  1. ServletRequest:service() 方法的参数,它表示请求对象,它封装了所有与请求相关的数据,它是由服务器创建的;
  2. ServletResponse:service()方法的参数,它表示响应对象,在service()方法中完成对客户端的响应需要使用这个对象;
  3. ServletConfig:init()方法的参数,它表示Servlet配置对象,它对应Servlet的配置信息,那对应web.xml文件中的<servlet>元素。

1.ServletRequest和ServletResponse(简单说明)

  1. ServletRequest和ServletResponse是Servlet#service() 方法的两个参数,一个是请求对象,一个是响应对象,可以从ServletRequest对象中获取请求数据,可以使用ServletResponse对象完成响应。你以后会发现,这两个对象就像是一对恩爱的夫妻,永远不分离,总是成对出现。
  2. ServletRequest和ServletResponse的实例由服务器创建,然后传递给service()方法。如果在service() 方法中希望使用HTTP相关的功能,那么可以把ServletRequest和ServletResponse强转成HttpServletRequest和HttpServletResponse。这也说明我们经常需要在service()方法中对ServletRequest和ServletResponse进行强转,这是很心烦的事情。不过后面会有一个类来帮我们解决这一问题的

 2.ServletConfig

ServletConfig对象对应web.xml文件中的<servlet>元素。例如你想获取当前Servlet在web.xml文件中的配置名,那么可以使用servletConfig.getServletName()方法获取!

ServletConfig对象是由服务器创建的,然后传递给Servlet的init()方法,你可以在init()方法中使用它! 

  1. String getServletName():获取Servlet在web.xml文件中的配置名称,即<servlet-name>指定的名称;
  2. ServletContext getServletContext():用来获取ServletContext对象,ServletContext会在后面讲解;
  3. String getInitParameter(String name):用来获取在web.xml中配置的初始化参数,通过参数名来获取参数值;
  4. Enumeration getInitParameterNames():用来获取在web.xml中配置的所有初始化参数名称; 

web.xml文件中<servlet>元素中还可以配置初始化参数:

	<servlet>
		<servlet-name>xxx</servlet-name>
		<servlet-class>com.maxian.web.servlet.HelloServlet</servlet-class>
		<init-param>
			<param-name>paramName1</param-name>
			<param-value>paramValue1</param-value>
		</init-param>
		<init-param>
			<param-name>paramName2</param-name>
			<param-value>paramValue2</param-value>
		</init-param>
	</servlet>
	
	<servlet-mapping>
		<servlet-name>xxx</servlet-name>
		<url-pattern>/Hello</url-pattern>
	</servlet-mapping>

 HelloServlet.java

	@Override
	public void init(ServletConfig servletConfig) throws ServletException {
		//获取当前Servlet在web.xml文件中的配置名
		System.out.println(servletConfig.getServletName());
		//获取初始化参数
		System.out.println(servletConfig.getInitParameter("paramName1"));
		System.out.println(servletConfig.getInitParameter("paramName2"));
		//获取所有初始化参数的名称
		Enumeration e = servletConfig.getInitParameterNames();
		while (e.hasMoreElements()) {
			System.out.println(e.nextElement());
		}
	}

运行结果:

xxx
paramValue1
paramValue2
paramName2
paramName1

三、GenericServlet

1.GenericServlet概述

GenericServlet是Servlet接口的实现类,我们可以通过继承GenericServlet来编写自己的Servlet。下面是GenericServlet类的源代码:

GenericServlet.java

public abstract class GenericServlet implements Servlet, ServletConfig,
        java.io.Serializable {
    private static final long serialVersionUID = 1L;
    private transient ServletConfig config;
    public GenericServlet() {}
    @Override
    public void destroy() {}
    @Override
    public String getInitParameter(String name) {
        return getServletConfig().getInitParameter(name);
    }
    @Override
    public Enumeration<String> getInitParameterNames() {
        return getServletConfig().getInitParameterNames();
    }
    @Override
    public ServletConfig getServletConfig() {
        return config;
    }
    @Override
    public ServletContext getServletContext() {
        return getServletConfig().getServletContext();
    }
    @Override
    public String getServletInfo() {
        return "";
    }
    @Override
    public void init(ServletConfig config) throws ServletException {
        this.config = config;
        this.init();
    }
    public void init() throws ServletException {}
    public void log(String msg) {
        getServletContext().log(getServletName() + ": " + msg);
    }
    public void log(String message, Throwable t) {
        getServletContext().log(getServletName() + ": " + message, t);
    }
    @Override
    public abstract void service(ServletRequest req, ServletResponse res)
            throws ServletException, IOException;
    @Override
    public String getServletName() {
        return config.getServletName();
    }
}

2.GenericServlet的init()方法

  1. 在GenericServlet中,定义了一个ServletConfig config实例变量,并在init(ServletConfig)方法中把参数ServletConfig赋给了实例变量。然后在该类的很多方法中使用了实例变量config。
  2. 如果子类覆盖了GenericServlet的init(StringConfig)方法,那么this.config=config这一条语句就会被覆盖了,也就是说GenericServlet的实例变量config的值为null,那么所有依赖config的方法都不能使用了。如果真的希望完成一些初始化操作,那么去覆盖GenericServlet提供的init()方法,它是没有参数的init()方法,它会在init(ServletConfig)方法中被调用。

3.实现了ServletConfig接口

GenericServlet还实现了ServletConfig接口,所以可以直接调用getInitParameter()、getServletContext()等ServletConfig的方法。

四、HttpServlet

1.HttpServlet概述

HttpServlet类是GenericServlet的子类,它提供了对HTTP请求的特殊支持,所以通常我们都会通过继承HttpServlet来完成自定义的Servlet。

2.HttpServlet覆盖了service()方法 

  1. HttpServlet类中提供了service(HttpServletRequest,HttpServletResponse)方法,这个方法是HttpServlet自己的方法,不是从Servlet继承来的。在HttpServlet的service(ServletRequest,ServletResponse)方法中会把ServletRequest和ServletResponse强转成HttpServletRequest和HttpServletResponse,然后调用service(HttpServletRequest,HttpServletResponse)方法,这说明子类可以去覆盖service(HttpServletRequest,HttpServletResponse)方法即可,这就不用自己去强转请求和响应对象了。
  2. 其实子类也不用去覆盖service(HttpServletRequest,HttpServletResponse)方法,因为HttpServlet还要做另一步简化操作,下面会介绍。

HttpServlet.java 

public abstract class HttpServlet extends GenericServlet {
    protected void service(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {
        ……
}
    @Override
    public void service(ServletRequest req, ServletResponse res)
        throws ServletException, IOException {

        HttpServletRequest  request;
        HttpServletResponse response;

        try {
            request = (HttpServletRequest) req;
            response = (HttpServletResponse) res;
        } catch (ClassCastException e) {
            throw new ServletException("non-HTTP request or response");
        }
        service(request, response);
}
……
}

3.doGet()和doPost()

在HttpServlet的service(HttpServletRequest,HttpServletResponse)方法会去判断当前请求是GET还是POST,如果是GET请求,那么会去调用本类的doGet()方法,如果是POST请求会去调用doPost()方法,这说明我们在子类中去覆盖doGet()或doPost()方法即可。

public class AServlet extends HttpServlet {
	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		System.out.println("hello doGet()...");
	}
}
public class BServlet extends HttpServlet {
	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		System.out.println("hello doPost()...");
	}
}

五、Servlet细节

1.Servlet与线程安全

  1. 因为一个类型的Servlet只有一个实例对象,那么就有可能会现时出一个Servlet同时处理多个请求,那么Servlet是否为线程安全的呢?答案是:“不是线程安全的”。这说明Servlet的工作效率很高,但也存在线程安全问题!
  2. 所以我们不应该在Servlet中便宜创建成员变量,因为可能会存在一个线程对这个成员变量进行写操作,另一个线程对这个成员变量进行读操作
  1. 不要在Servlet中创建成员!创建局部变量即可!
  2. 可以创建无状态成员!
  3. 可以创建有状态的成员,但状态必须为只读的! 

2.让服务器在启动时就创建Servlet 

  1. 默认情况下,服务器会在某个Servlet第一次收到请求时创建它。也可以在web.xml中对Servlet进行配置,使服务器启动时就创建Servlet。
  2. 在<servlet>元素中配置<load-on-startup>元素可以让服务器在启动时就创建该Servlet,其中<load-on-startup>元素的值必须是大于等于0的整数,它的使用是服务器启动时创建Servlet的顺序,数字越小优先级越高。下例中,根据<load-on-startup>的值可以得知服务器创建Servlet的顺序为Hello1Servlet、Hello2Servlet、Hello3Servlet。
	<servlet>
		<servlet-name>hello1</servlet-name>
		<servlet-class>com.maxian.web.servlet.Hello1Servlet</servlet-class>
		<load-on-startup>0</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>hello1</servlet-name>
		<url-pattern>/hello1</url-pattern>
	</servlet-mapping>
	<servlet>
		<servlet-name>hello2</servlet-name>
		<servlet-class>com.maxian.web.servlet.Hello2Servlet</servlet-class>
		<load-on-startup>1</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>hello2</servlet-name>
		<url-pattern>/hello2</url-pattern>
	</servlet-mapping>
	<servlet>
		<servlet-name>hello3</servlet-name>
		<servlet-class>com.maxian.web.servlet.Hello3Servlet</servlet-class>
		<load-on-startup>2</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>hello3</servlet-name>
		<url-pattern>/hello3</url-pattern>
	</servlet-mapping>

3.url-pattern(Servlet的访问路径)

<url-pattern>是<servlet-mapping>的子元素,用来指定Servlet的访问路径,即URL。它必须是以“/”开头

1.可以在<servlet-mapping>中给出多个<url-pattern>,例如:

  <servlet-mapping>
    <servlet-name>AServlet</servlet-name>
    <url-pattern>/AServlet</url-pattern>
    <url-pattern>/BServlet</url-pattern>
  </servlet-mapping>  

 那么这说明一个Servlet绑定了两个URL,无论访问/AServlet还是/BServlet,访问的都是AServlet。

 2.还可以在<url-pattern>中使用通配符,所谓通配符就是星号“*”,星号可以匹配任何URL前缀或后缀,使用通配符可以命名一个Servlet绑定一组URL,例如:

  1. <url-pattern>/servlet/*<url-patter>:/servlet/a、/servlet/b,都匹配/servlet/*;(路径匹配)
  2. <url-pattern>*.do</url-pattern>:/abc/def/ghi.do、/a.do,都匹配*.do;(扩展名匹配)
  3. <url-pattern>/*<url-pattern>:匹配所有URL;(啥都匹配)

请注意,通配符要么为前缀,要么为后缀,不能出现在URL中间位置,也不能只有通配符。例如:/*.do就是错误的,因为星号出现在URL的中间位置上了。*.*也是不对的,因为一个URL中最多只能出现一个通配符。 

注意,通配符是一种模糊匹配URL的方式,如果存在更具体的<url-pattern>,那么访问路径会去匹配具体的<url-pattern>。例如:

	<servlet>
		<servlet-name>hello1</servlet-name>
		<servlet-class>com.maxian.web.servlet.Hello1Servlet</servlet-class>
	</servlet>
	<servlet-mapping>
		<servlet-name>hello1</servlet-name>
		<url-pattern>/servlet/hello1</url-pattern>
	</servlet-mapping>
	<servlet>
		<servlet-name>hello2</servlet-name>
		<servlet-class>com.maxian.web.servlet.Hello2Servlet</servlet-class>
	</servlet>
	<servlet-mapping>
		<servlet-name>hello2</servlet-name>
		<url-pattern>/servlet/*</url-pattern>
	</servlet-mapping>

 当访问路径为http://localhost:8080/hello/servlet/hello1时,因为访问路径即匹配hello1的<url-pattern>,又匹配hello2的<url-pattern>,但因为hello1的<url-pattern>中没有通配符,所以优先匹配,即设置hello1。

4.web.xml文件的继承(了解)

在${CATALINA_HOME}\conf\web.xml中的内容,相当于写到了每个项目的web.xml中,它是所有web.xml的父文件。

每个完整的JavaWeb应用中都需要有web.xml,但我们不知道所有的web.xml文件都有一个共同的父文件,它在Tomcat的conf/web.xml路径。

<?xml version="1.0" encoding="ISO-8859-1"?>

<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
                      http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
	version="3.0">
	<servlet>
		<!-- 它的优先级最低,如果一个请求没有人处理,那么它来处理!它显示404。 -->
		<servlet-name>default</servlet-name>
		<!-- 当访问路径不存在时,会执行该Servlet!其实我们在访问index.html时也是在执行这个Servlet。 -->
		<servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
		<init-param>
			<param-name>debug</param-name>
			<param-value>0</param-value>
		</init-param>
		<init-param>
			<param-name>listings</param-name>
			<param-value>false</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>

	<servlet>
		<servlet-name>jsp</servlet-name>
		<servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
		<init-param>
			<param-name>fork</param-name>
			<param-value>false</param-value>
		</init-param>
		<init-param>
			<param-name>xpoweredBy</param-name>
			<param-value>false</param-value>
		</init-param>
		<load-on-startup>3</load-on-startup>
	</servlet>

	<servlet-mapping>
		<servlet-name>default</servlet-name>
		<!-- 匹配所有URL,也就是说用户访问的URL路径没有匹配的页面时 -->
		<!-- 那么执行的就是名为default的Servlet -->
		<!-- 即org.apache.catalina.servlets.DefaultServlet -->
		<url-pattern>/</url-pattern>
	</servlet-mapping>

	<servlet-mapping>
		<servlet-name>jsp</servlet-name>
		<!-- 任何URL后缀为jsp的访问,都会执行名为jsp的Servlet -->
		<!-- 即org.apache.jasper.servlet.JspServlet -->
		<url-pattern>*.jsp</url-pattern>
		<url-pattern>*.jspx</url-pattern>
	</servlet-mapping>

	<session-config>
		<!-- session的默认超时时间为30分钟,后面讲session时再深入。 -->
		<session-timeout>30</session-timeout>
	</session-config>

	<!-- 这里省略了大概4000多行的MIME类型的定义,这里只给出两种MIME类型的定义 -->
	<!-- MIME类型用来标识网络上资源的媒体类型,这里举例为bmp和html两种MIME类型。 -->
	<mime-mapping>
		<extension>bmp</extension>
		<mime-type>image/bmp</mime-type>
	</mime-mapping>
	<mime-mapping>
		<extension>htm</extension>
		<mime-type>text/html</mime-type>
	</mime-mapping>

	<!-- 在应用的web.xml中如果没有对<welcome-file-list>进行覆盖 -->
	<!-- 那么默认主页为index.html、index.htm、index.jsp -->
	<welcome-file-list>
		<welcome-file>index.html</welcome-file>
		<welcome-file>index.htm</welcome-file>
		<welcome-file>index.jsp</welcome-file>
	</welcome-file-list>
</web-app>

六、ServletContext(重要)

  1. 一个项目只有一个ServletContext对象!
  2. 我们可以在N多个Servlet中来获取这个唯一的对象,使用它可以给多个Servlet传递数据!
  3. 与天地同寿!!!这个对象在Tomcat启动时就创建,在Tomcat关闭时才会死去!

1.ServletContext概述

  1. 服务器会为每个应用创建一个ServletContext对象
  2. ServletContext对象的创建是在服务器启动时完成的;
  3. ServletContext对象的销毁是在服务器关闭时完成的。
  4. ServletContext对象的作用是在整个Web应用的动态资源之间共享数据!例如在AServlet中向ServletContext对象中保存一个值,然后在BServlet中就可以获取这个值,这就是共享数据了

2.获取ServletContext

在Servlet中获取ServletContext对象:

  1. 在void init(ServletConfig config)中:ServletContext context = config.getServletContext();,ServletConfig类的getServletContext()方法可以用来获取ServletContext对象;
  2. 在GenericeServlet或HttpServlet中获取ServletContext对象:GenericServlet类有getServletContext()方法,所以可以直接使用this.getServletContext()来获取;
public class MyServlet implements Servlet {
    public void init(ServletConfig config) {
        ServletContext context = config.getServletContext();
    }
    …
}
public class MyServlet extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response) {
        ServletContext context = this.getServletContext();
    }
}

3.域对象的功能

ServletContext是JavaWeb四大域对象之一:

  1. PageContext;
  2. ServletRequest;
  3. HttpSession;
  4. ServletContext;

所有域对象都有存取数据的功能,因为域对象内部有一个Map,用来存储数据,下面是ServletContext对象用来操作数据的方法: 

  1. void setAttribute(String name, Object value):用来存储一个对象,也可以称之为存储一个域属性,例如:servletContext.setAttribute(“xxx”, “XXX”),在ServletContext中保存了一个域属性,域属性名称为xxx,域属性的值为XXX。请注意,如果多次调用该方法,并且使用相同的name,那么会覆盖上一次的值,这一特性与Map相同;
  2. Object getAttribute(String name):用来获取ServletContext中的数据,当前在获取之前需要先去存储才行,例如:String value = (String)servletContext.getAttribute(“xxx”);,获取名为xxx的域属性;
  3. void removeAttribute(String name):用来移除ServletContext中的域属性,如果参数name指定的域属性不存在,那么本方法什么都不做;
  4. Enumeration getAttributeNames():获取所有域属性的名称;

4.获取应用初始化参数

  1. Servlet也可以获取初始化参数,但它是局部的参数;也就是说,一个Servlet只能获取自己的初始化参数,不能获取别人的,即初始化参数只为一个Servlet准备!
  2. 可以配置公共的初始化参数,为所有Servlet而用!这需要使用ServletContext才能使用!

使用ServletContext来获取在web.xml文件中配置的应用初始化参数!注意,应用初始化参数与Servlet初始化参数不同:

web.xml 

<web-app ...>
  ...
  <context-param>
	<param-name>paramName1</param-name>
	<param-value>paramValue1</param-value>  	
  </context-param>
  <context-param>
	<param-name>paramName2</param-name>
	<param-value>paramValue2</param-value>  	
  </context-param>
</web-app>
ServletContext context = this.getServletContext();
String value1 = context.getInitParameter("paramName1");
String value2 = context.getInitParameter("paramName2");
System.out.println(value1 + ", " + value2);
		
Enumeration names = context.getInitParameterNames();
while(names.hasMoreElements()) {
	System.out.println(names.nextElement());
}

5.获取资源相关方法

1.获取真实路径 (重要)

使用ServletContext对象来获取Web应用下的资源,例如在hello应用的根目录下创建a.txt文件,现在想在Servlet中获取这个资源,就可以使用ServletContext来获取。

  1. 获取a.txt的真实路径:String realPath = servletContext.getRealPath(“/a.txt”),realPath的值为a.txt文件的绝对路径:F:\tomcat6\webapps\hello\a.txt;
  2. 获取b.txt的真实路径:String realPath = servletContext.getRealPath(“/WEB-INF/b.txt”);

2 获取资源流 

不只可以获取资源的路径,还可以通过ServletContext获取资源流,即把资源以输入流的方式获取:

  1. 获取a.txt资源流:InputStream in = servletContext.getResourceAsStream(“/a.txt”);
  2. 获取b.txt资源流:InputStream in = servletContext.getResourceAsStream(“/WEB-INF/b.txt”);

3.获取指定目录下所有资源路径

还可以使用ServletContext获取指定目录下所有资源路径,例如获取/WEB-INF下所有资源的路径: 

Set set = context.getResourcePaths("/WEB-INF");
System.out.println(set);
//输出结果:
//[/WEB-INF/lib/, /WEB-INF/classes/, /WEB-INF/b.txt, /WEB-INF/web.xml]

注意,本方法必须以“/”开头!!!

6.练习:访问量统计

一个项目中所有的资源被访问都要对访问量进行累加!
创建一个int类型的变量,用来保存访问量,然后把它保存到ServletContext的域中,这样可以保存所有的Servlet都可以访问到!

  1. 最初时,ServletContext中没有保存访问量相关的属性;
  2. 当本站第一次被访问时,创建一个变量,设置其值为1;保存到ServletContext中;
  3. 当以后的访问时,就可以从ServletContext中获取这个变量,然后在其基础之上加1。 

获取ServletContext对象,查看是否存在名为count的属性,如果存在,说明不是第一次访问,如果不存在,说明是第一次访问;

  1. 第一次访问:调用Servletcontext的setAttribute()传递一个属性,名为count,值为1;
  2. 第2~N次访问:调用ServletContext的getAttribute()方法获取原来的访问量,给访问量加1,再调用Servletcontext的setAttribute()方法完成设置。 

相信各位一定见过很多访问量统计的网站,即“本页面被访问过XXX次”。因为无论是哪个用户访问指定页面,都会累计访问量,所以这个访问量统计应该是整个项目共享的!很明显,这需要使用ServletContext来保存访问量。

ServletContext application  = this.getServletContext(); 
Integer count = (Integer)application.getAttribute("count") ;
if(count == null) {
	count = 1; 
} else {
	count++ ;
}
response.setContentType("text/html;charset=utf-8");
response.getWriter().print("<h1>本页面一共被访问" + count + "次!</h1>") ;
application.setAttribute("count", count); 

七、获取类路径下资源

获取类路径资源,类路径对一个JavaWeb项目而言,就是/WEB-INF/classes和/WEB-INF/lib/每个jar包!

  1. Class
  2. ClassLoader

 

//一、Class类的getResourceAsStream(String path):
	//1.路径以“/”开头,相对classes路径;
	InputStream a1 = this.getClass().getResourceAsStream("/../../a.txt");
	InputStream b1 = this.getClass().getResourceAsStream("/../b.txt");
	InputStream c1= this.getClass().getResourceAsStream("/com/maxian/servlet/c.txt");
	InputStream d1 = this.getClass().getResourceAsStream("/d.txt");		
	//2.路径不以“/”开头,相对当前class文件所有路径,
	//例如在com.maxian.servlet.AServlet中执行,
	//那么相对/classes/com/maxian/servlet/路径;
	InputStream a2 = this.getClass().getResourceAsStream("../../../../../a.txt");
	InputStream b2 = this.getClass().getResourceAsStream("../../../../b.txt");
	InputStream c2= this.getClass().getResourceAsStream("../../../com/maxian/servlet/c.txt");
	InputStream d2 = this.getClass().getResourceAsStream("../../../d.txt");	
//二、ClassLoader类的getResourceAsStream(String path):
	//相对classes路径;
	InputStream a3 = this.getClass().getClassLoader().getResourceAsStream("../../a.txt");
	InputStream b3 = this.getClass().getClassLoader().getResourceAsStream("../b.txt");
	InputStream c3 = this.getClass().getClassLoader().getResourceAsStream("com/maxian/servlet/c.txt");
	InputStream d3 = this.getClass().getClassLoader().getResourceAsStream("d.txt");
	System.out.println(IOUtils.toString(d3));

八、response

1.response简介

  1.  response是Servlet.service方法的一个参数,类型为javax.servlet.http.HttpServletResponse。在客户端发出每个请求时,服务器都会创建一个response对象,并传入给Servlet.service()方法。response对象是用来对客户端进行响应的,这说明在service()方法中使用response对象可以完成对客户端的响应工作。
  2.   response的功能如下:

            * 设置响应头

             * 发送状态码

             * 设置响应正文

             * 重定向

2.发送状态码

状态码:200表示成功、302表示重定向、404表示客户端错(访问的资源不存在)、500表示服务器端错

sendError(int sc) --> 发送错误状态码,例如404、500
sendError(int sc, String msg) --> 也是发送错误状态码,还可以带一个错误信息!
setStatus(int sc) --> 发送成功的状态码,可以用来发送200、302

public class AServlet extends HttpServlet {
	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		response.sendError(404, "您访问的资源存在,就不给你看!");
	}
}

3.响应头的相关方法

响应头:Content-Type、Refresh、Location等等
头就是一个键值对!可能会存在一个头(一个名称,一个值),也可能会存在一个头(一个名称,多个值!)

setHeader(String name, String value):适用于单值的响应头(重要)
    response.setHeader("aaa", "AAA");
addHeader(String name, String value):适用于多值的响应头
    response.addHeader("aaa", "A");
    response.addHeader("aaa", "AA");
    response.addHeader("aaa", "AAA");
setIntHeader(String name, int value):适用于单值的int类型的响应头
    response.setIntHeader("Content-Length", 888);
addIntHeader(String name, int value):适用于多值的int类型的响应头
setDateHeader(String name, long value):适用于单值的毫秒类型的响应头
    response.setDateHeader("expires", 1000 * 60 * 60 * 24);
addDateHeader(String name, long value):适用于多值的毫秒类型的响应头

public void doGet(HttpServletRequest request, HttpServletResponse response)
		throws ServletException, IOException {
	response.setHeader("aaa", "AAA");
}

4.重定向

当浏览器请求BServlet的时候,会自动跳转到CServlet

BServlet.java

public class BServlet extends HttpServlet {
	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		System.out.println("BServlet");
		//重定向: 1. 设置Location 2. 发送302状态码
		//response.setHeader("Location", "/Test02_1/CServlet");
		//response.setStatus(302);
		// 快捷的重定向方法
		response.sendRedirect("/Test02_1/CServlet");
	}
}

CServlet.java

public class CServlet extends HttpServlet {
	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		System.out.println("CServlet");
	}
}

运行结果:
BServlet
CServlet

5.定时刷新

定时刷新:设置Refresh头,你可以把它理解成,定时重定向!

DServlet.java

public class DServlet extends HttpServlet {
	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		//下面是用来发送响应体!
		PrintWriter writer = response.getWriter();
		writer.print("DServlet");
		//设置名为Refresh的响应头
		//5秒后跳转到EServlet
		response.setHeader("Refresh", "5;URL=/Test02_1/EServlet");
	}
}

EServlet.java

public class EServlet extends HttpServlet {
	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		PrintWriter writer = response.getWriter();
		writer.print("EServlet");
	}
}

6.禁用浏览器缓存

public class FServlet extends HttpServlet {
	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		//Cache-Control、pragma、expires
		response.setHeader("Cache-Control", "no-cache");
		response.setHeader("pragma", "no-cache");
		response.setDateHeader("expires", -1);
		response.getWriter().print("hello hello!");
	}
}

7.响应体

响应体:通常是html、也可以是图片!

response的两个流:
1.ServletOutputStream,用来向客户端发送字节数据。ServletOutputStream out = resopnse.getOutputStream();
2.PrintWriter,用来向客户端发送字符数据!需要设置编码。PrintWriter writer = response.getWriter();
注意:两个流不能同时使用!

public class GServlet extends HttpServlet {
	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
/*
		String s = "Hello outputStream";
		byte[] bytes = s.getBytes();
		response.getOutputStream().write(bytes);
*/
		/**
		 * 演示响应字节数据
		 */
		// 把一张图片读取到字节数组中
		String path = "F:/F/灰太狼.jpg";
		FileInputStream in = new FileInputStream(path);
//		byte[] bytes = IOUtils.toByteArray(in);//读取输入流内容的字节到字节数组中。
//		response.getOutputStream().write(bytes);
		IOUtils.copy(in, response.getOutputStream());
	}
}

九、request

1.request概述

request是Servlet.service()方法的一个参数,类型为javax.servlet.http.HttpServletRequest。在客户端发出每个请求时,服务器都会创建一个request对象,并把请求数据封装到request中,然后在调用Servlet.service()方法时传递给service()方法,这说明在service()方法中可以通过request对象来获取请求数据。

request功能介绍:
 * 获取请求头
 * 获取请求参数
 * Servlet三大域对象之一
 * 请求包含和请求转发

2.request域方法

 * void setAttribute(String name, Object value):添加或替换request域属性
 * Object getAttribute(String name):获取request域指定名称的域属性
 * void removeAttribute(String name):移除request域指定名称的域属性
 * Enumeration getAttributeNames():获取所有request域的属性名称

3.request获取请求头

> *****String getHeader(String name),适用于单值头
> int getIntHeader(String name),适用于单值int类型的请求头
> long getDateHeader(String name),适用于单值毫秒类型的请求头
> Enumeration<String> getHeaders(String name),适用于多值请求头

案例:如果直接访问BServlet,会跳转到百度,访问a.html,点击超链接跳转,会访问到BServlet

a.html

<body>
    <a href="/day10_2/BServlet">xxx</a>
</body>

BServlet.java

public void doGet(HttpServletRequest request, HttpServletResponse response)
		throws ServletException, IOException {
	//使用Referer请求头,来防盗链
	String referer = request.getHeader("Referer");
	System.out.println(referer);
	if(referer == null || !referer.contains("localhost")) {
		response.sendRedirect("http://www.baidu.com");
	} else {
		System.out.println("hello!");
	}
}

4.获取请求URL

http://localhost:8080/Test02_2/AServlet?username=xxx&password=yyy
> String getScheme():获取协议,http
> String getServerName():获取服务器名,localhost
> String getServerPort():获取服务器端口,8080
> *****String getContextPath():获取项目名,/Test02_2
> String getServletPath():获取Servlet路径,/AServlet
> String getQueryString():获取参数部分,即问号后面的部分。username=xxx&password=yyy
> String getRequestURI():获取请求URI,等于项目名+Servlet路径。/Test02_2/AServlet
> String getRequestURL():获取请求URL,等于不包含参数的整个请求路径。http://localhost:8080/Test02_2/AServlet

5.request请求数据相关其他方法

1.重点

 * String getMethod():获取请求方式
 * void setCharacterEncoding(String):设置请求体的编码
 * String getRemoteAddr():获取客户端IP地址

public class AServlet extends HttpServlet {
	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		// 获取客户端的IP地址
		String addr = request.getRemoteAddr();
		// 获取请求方式
		String method = request.getMethod();
		// 获取名为User-Agent的请求头!
		String userAgent = request.getHeader("User-Agent");
		System.out.println(addr);
		System.out.println(method);
		System.out.println(userAgent);
	}
}
/*
运行结果:
127.0.0.1
GET
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36
*/

2.非重点

 * int getContentLength():获取请求体字节数
 * Locale getLocale():获取请求Locale,例如zh_CN表示中文,中国
 * String getCharacterEncoding():获取请求体编码,在没有调用setCharacterEncoding()之前该方法返回null

6.获取请求参数

请求参数是由客户端发送给服务器的!有可能是在请求体中(POST),也可能是在URL之后(GET)
请求参数:有一个参数一个值的,还有一个参数多个值!

> *****String getParameter(String name):获取指定名称的请求参数值,适用于单值请求参数
> String[] getParameterValues(String name):获取指定名称的请求参数值,适用于多值请求参数
> Enumeration<String> getParameterNames():获取所有请求参数名称
> *****Map<String,String[]> getParameterMap():获取所有请求参数,其中key为参数名,value为参数值。

a.html

<body>
	<h1>测试请求参数</h1>
	<a href="/Test02_2/AServlet?xxx=XXX&yyy=YYY">点击这里</a>
	<hr/>
	<form action="/Test02_2/AServlet" method="post">
	 	 用户名:<input type="text" name="username"/><br/>
	  	 密 码:<input type="password" name="password"/><br/>
	  	 爱 好:<input type="checkbox" name="hobby" value="cf"/>吃饭
	  	 <input type="checkbox" name="hobby" value="sj"/>睡觉
	  	 <input type="checkbox" name="hobby" value="ddm"/>打代码
	  	 <br/>
	  	 <input type="submit" value="提交"/>
	</form>
</body>

AServler.java 

public class AServlet extends HttpServlet {
	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		System.out.println("GET: " + request.getParameter("xxx"));//GET: XXX
		System.out.println("GET: " + request.getParameter("yyy"));//GET: YYY
	}

	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		String username = request.getParameter("username");
		String password = request.getParameter("password");
		String[] hobby = request.getParameterValues("hobby");
		//123, 456, [cf, sj, ddm]
		System.out.println(username + ", " + password + ", " + Arrays.toString(hobby));
		
		//测试获取所有请求参数的名称
		//password  username  hobby
		Enumeration names = request.getParameterNames();
		while(names.hasMoreElements()) {
			System.out.println(names.nextElement());
		}
		
		//获取所有请求参数,封装到Map中
		//password=[456]  username=[123]  hobby=[cf, sj, ddm]
		Map<String,String[]> map = request.getParameterMap();
		for(String name : map.keySet()) {
			String[] values = map.get(name);
			System.out.println(name + "=" + Arrays.toString(values));
		}
	}
}

7.请求转发和请求包含

无论是请求转发还是请求包含,都表示由多个Servlet共同来处理一个请求。例如Servlet1来处理请求,然后Servlet1又转发给Servlet2来继续处理这个请求。

* 请求包含和请求转发都是在一个请求中,访问两个Servlet。
 * 请求包含和请求转发都是有一个Servlet去调用执行另一个Servlet
 * 请求包含和请求转发都可以共享request中的数据,因为都是一个请求。

RequestDispatcher rd = request.getRequestDispatcher("/MyServlet");
使用request获取RequestDispatcher对象,方法的参数是被转发或包含的Servlet的Servlet路径
请求转发:rd.forward(request,response);
请求包含:rd.include(request,response);

1.请求转发

在AServlet中,把请求转发到BServlet:

public class AServlet extends HttpServlet {
	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		System.out.println("AServlet");
		RequestDispatcher rd = request.getRequestDispatcher("/BServlet");
		rd.forward(request, response);
	}
}
public class BServlet extends HttpServlet {
	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		System.out.println("BServlet");
	}
}

2.请求包含

在AServlet中,把请求包含到BServlet:

public class AServlet extends HttpServlet {
	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		System.out.println("AServlet");
		RequestDispatcher rd = request.getRequestDispatcher("/BServlet");
		rd.include (request, response);
	}
}
public class BServlet extends HttpServlet {
	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		System.out.println("BServlet");
	}
}

3.请求转发与请求包含比较

  1. 如果在AServlet中请求转发到BServlet,那么在AServlet中就不允许再输出响应体,即不能再使用response.getWriter()和response.getOutputStream()向客户端输出,这一工作应该由BServlet来完成;如果是使用请求包含,那么没有这个限制;
  2. 请求转发虽然不能输出响应体,但还是可以设置响应头的,例如:response.setContentType(”text/html;charset=utf-8”);
  3. 请求包含大多是应用在JSP页面中,完成多页面的合并;
  4. 请求请求大多是应用在Servlet中,转发目标大多是JSP页面;

4.请求转发与重定向比较

  1. 请求转发是一个请求,而重定向是两个请求;
  2. 请求转发后浏览器地址栏不会有变化,而重定向会有变化,因为重定向是两个请求;
  3. 请求转发的目标只能是本应用中的资源,重定向的目标可以是其他应用;
  4. 请求转发对AServlet和BServlet的请求方法是相同的,即要么都是GET,要么都是POST,因为请求转发是一个请求;
  5. 重定向的第二个请求一定是GET;

十、编码

1.请求编码

1.直接在地址栏中给出中文

请求数据是由客户端浏览器发送服务器的,请求数据的编码是由浏览器决定的。例如在浏览器地址栏中给出:http://localhost:8080/hello/AServlet?name=码仙,那么其中“码仙”是什么编码的呢?不同浏览器使用不同的编码,所以这是不确定的!

IE:使用GB2312;
FireFox:使用GB2312;
Chrome:使用UTF-8;
通常没有哪个应用要求用户在浏览器地址栏中输入请求数据的,所以大家只需了解一下即可。

2.在页面中发出请求

  1. 通常向服务器发送请求数据都需要先请求一个页面,然后用户在页面中输入数据。页面中有超链接和表单,通过超链接和表单就可以向服务器发送数据了。
  2. 因为页面是服务器发送到客户端浏览器的,所以这个页面本身的编码由服务器决定。而用户在页面中输入的数据也是由页面本身的编码决定的。

index.html

<!DOCTYPE html>
<html>
  <head>
    <title>index.html</title>
    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
  </head>
  
  <body>
    <form action="/hello/servlet/AServlet">
      名称:<input type="text" name="name"/>
      <input type="submit" value="提交"/>
    </form>
    <a href="/hello/servlet/AServlet?name=码仙">链接</a>
  </body>
</html>

 当用户在index.html页面中输入数据时,都是UTF-8列表的。因为这个页面本身就是UTF-8编码的!

页面的编译就是页面中输入数据的编码。

3.GET请求解读编码

当客户端通过GET请求发送数据给服务器时,使用request.getParameter()获取的数据是被服务器误认为ISO-8859-1编码的,也就是说客户端发送过来的数据无论是UTF-8还是GBK,服务器都认为是ISO-8859-1,这就说明我们需要在使用request.getParameter()获取数据后,再转发成正确的编码。

例如客户端以UTF-8发送的数据,使用如下转码方式:
String name = request.getParameter(“name”);
name = new String(name.getBytes(“iso-8859-1”), “utf-8”);

4.POST请求解读编码

当客户端通过POST请求发送数据给服务器时,可以在使用request.getParameter()获取请求参数之前先通过request.setCharacterEncoding()来指定编码,然后再使用reuqest.getParameter()方法来获取请求参数,那么就是用指定的编码来读取了。
也就是说,如果是POST请求,服务器可以指定编码!但如果没有指定编码,那么默认还是使用ISO-8859-1来解读。

request.setCharacterEncoding(“utf-8”);
String name = request.getParameter(“name”);

2.响应编码

响应:服务器发送给客户端数据!响应是由response对象来完成,如果响应的数据不是字符数据,那么就无需去考虑编码问题。当然,如果响应的数据是字符数据,那么就一定要考虑编码的问题了。

response.getWriter().print(“码仙”);

上面代码因为没有设置repsonse.getWriter()字符流的编码,所以服务器使用默认的编码(ISO-8859-1)来处理,因为ISO-8859-1不支持中文,所以一定会出现编码的。

所以在使用response.getWriter()发送数据之前,一定要设置response.getWriter()的编码,这需要使用response.setCharacterEncoding()方法:

response.setCharacterEncoding(“utf-8”);
response.getWriter().print(“码仙”);

上面代码因为在使用response.getWriter()输出之前已经设置了编码,所以输出的数据为utf-8编码。但是,因为没有告诉浏览器使用什么编码来读取响应数据,所以很可能浏览器会出现错误的解读,那么还是会出现乱码的。当然,通常浏览器都支持来设置当前页面的编码,如果用户在看到编码时,去设置浏览器的编码,如果设置的正确那么乱码就会消失。但是我们不能让用户总去自己设置编码,而且应该直接通知浏览器,服务器发送过来的数据是什么编码,这样浏览器就直接使用服务器告诉他的编码来解读!这需要使用content-type响应头。

response.setContentType(“text/html;charset=utf-8”);
response.getWriter().print(“码仙”);

上面代码使用setContentType()方法设置了响应头content-type编码为utf-8,这不只是在响应中添加了响应头,还等于调用了一次response.setCharacterEncoding(“utf-8”),也就是说,通过我们只需要调用一次response.setContentType(“text/html;charset=utf-8”)即可,而无需再去调用response.setCharacterEncoding(“utf-8”)了。

在静态页面中,使用<meta>来设置content-type响应头,例如:
<meta http-equiv="content-type" content="text/html; charset=UTF-8">

3.URL编码

通过页面传输数据给服务器时,如果包含了一些特殊字符是无法发送的。这时就需要先把要发送的数据转换成URL编码格式,再发送给服务器。

其实需要我们自己动手给数据转换成URL编码的只有GET超链接,因为表单发送数据会默认使用URL编码,也就是说,不用我们自己来编码。

表单的类型:Content-Type: application/x-www-form-urlencoded,就是把中文转换成%后面跟随两位的16进制。
为什么要用它:在客户端和服务器之间传递中文时需要把它转换成网络适合的方式。

* 它不是字符编码!
* 它是用来在客户端与服务器之间传递参数用的一种方式!
* URL编码需要先指定一种字符编码,把字符串解码后,得到byte[],然后把小于0的字节+256,再转换成16进制。前面再添加一个%。
* POST请求默认就使用URL编码!tomcat会自动使用URL解码!
* URL编码:String username = URLEncoder.encode(username, "utf-8");
* URL解码:String username = URLDecoder.decode(username, "utf-8");

十一、路径

1.web.xml中<url-pattern>路径

  1. 叫它Servlet路径!
  2. 要么以“*”开关,要么为“/”开头

2.转发和包含路径

  1. 以“/”开头:相对当前项目路径,例如:http://localhost:8080/项目名/ request.getRequestdispacher("/BServlet").for...();(重要)
  2. 不以“/”开头:相对当前Servlet路径。 request.getRequestdispacher("/BServlet").for...();,假如当前Servlet是:http://localhost:8080/项目名/servlet/AServlet, 就是http://localhost:8080/项目名/servlet/BServlet

3.重定向路径

  1. 客户端路径
  2. 以“/”开头:相对当前主机,例如:http://localhost:8080/, 所以需要自己手动添加项目名,例如;response.sendRedirect("/day10_1/Bservlet");

4.页面中超链接和表单路径

  1. 与重定向相同,都是客户端路径!需要添加项目名
  2. <form action="/day10_1/AServlet">
  3. <a href="/day10_/AServlet">
  4. <a href="AServlet">,如果不以“/”开头,那么相对当前页面所在路径。如果是http://localhost:8080/day10_1/html/form.html。 即:http://localhost:8080/day10_1/html/ASevlet
  5. 建议使用以“/”开头的路径,即绝对路径!

5.ServletContext获取资源路径 

相对当前项目目录,即当然index.jsp所在目录

6.ClassLoader获取资源路径

相对classes目录

7.Class获取资源路径

  1. 以“/”开头相对classes目录
  2. 不以“/”开头相对当前.class文件所在目录。

本篇博客来自于传智播客视频教程的总结以及笔记的整理,仅供学习交流,切勿用于商业用途,如有侵权,请联系博主删除,博主QQ:194760901 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值