Servlet

简介


Servlet (Server Applet) 用 Java编写的服务器端程序,可以独立于平台和协议,用于交互式浏览和生成数据,生成动态 Web 内容。

Servlet 指 服务器端,和客户端产生交互需要接口,实现这些接口的类叫 Servlet

开发一个简单的servlet


  1. 导入依赖的 jar 包,安装路径:Library/Tomcat/lib/servlet-api.jar
  2. 创建一个类实现 javax.servlet.Servlet 接口,实现5个方法
  3. 方法中有 arg0,arg1,解决方法,导入源码,tomcat,下载 Source Code,并且导入即可。
  4. 在 service 方法中打印一句话,
  5. 通知 Tomcat 服务器来管理咱们自定义的类,web 的配置文件 web.xml
  6. 服务器需要的配置文件为 server.xml

将配置文件 server.xml 中加入标签
<Context docBase="绝对路径" path="day3"/>

作用:path 为 Context容器 的路径,Tomcat 会从conf/server.xml文件,获取所有的Context 容器,判断哪个的 path 属性值为 day3,接着找到当前 Context容器 的docBase属性值,该属性值就是当前访问项目的根的路径。

web.xml:配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
version="2.4">

<!-- 这个xml 是配置 tomcat 服务器的,让服务器来将请求数据传递给 servlet 实现类 -->
<servlet>
<!-- 通过类名,找到类的全限定名,tomcat 就可以调用 Servlet 实现类和客户端通信 -->
<servlet-name></servlet-name>
<!-- 告诉服务器,HelloServlet 类会实现 Servlet 接口去做事情
有了全限定名,服务器内部可以通过反射创建该类的对象,将 ServletConfi 传递给我们 -->
<servlet-class>servlet.HelloServlet</servlet-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
<init-param>
<param-name>username</param-name>
<param-value>xiaoming</param-value>
</init-param>
</servlet>

<!-- 映射,将url-pattern 和管理该地址的类做映射 -->
<servlet-mapping>
<!-- servlet-mapping 映射,作用是客户端通过path找到服务器域名
后的地址,将路径和 servlet 类名关联,在通过映射找到 Servlet 实现类 -->
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
</web-app>

小 tips: 复制 .java 文件的全限定名是路径,复制 .class 文件的全限定名是 根路径Classpath开始 包名.class文件

如何安装 Tomcat 移步这里

Servlet 原理


Tomcat 启动工作


Tomcat 的容器等级中, Context 容器直接管理 Servlet 在容器中的包装类 Wrapper,所以Context 容器如何运行会直接影响 Servlet 的工作方式

Tomcat 的容器分为四个等级,真正管理 Servlet 容器的是 Context 容器,一个 Context 对应一个 Web 工程。

清单1. Context 配置参数:

1
<Context path="/projectOne" docBase="D:/projects/projectOne" reloadable="true" />

下面介绍 Tomcat 解析 Context 容器的过程。

Servlet 容器的启动过程


清单 2 . 给 Tomcat 增加一个 Web 工程

1
2
3
4
5
6
7
8
Tomcat tomcat = getTomcatInstance(); 
File appDir = new File(getBuildDirectory(), "webapps/examples");
tomcat.addWebapp(null, "/examples", appDir.getAbsolutePath());
// 添加了 Context 容器后,启动 tomcat,启动
tomcat.start();
ByteChunk res = getUrl("http://localhost:" + getPort() +
"/examples/servlets/servlet/HelloWorldExample");
assertTrue(res.toString().indexOf("<h1>Hello World!</h1>") > 0);

清单1 的代码是创建一个 Tomcat 实例并且新增一个 Web 应用,启动 Tomcat 并调用其中一个 HelloworldExample Servlet,看是否正确返回预期数据。

Tomcat 的 addWebapp 方法如下:

清单 3 .Tomcat.addWebapp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/** 
* url 和 path 是这个应用在 Tomcat 的访问路径和这个应用的实际物理路径
* 这两个参数与清单1中的两个参数是一致的。
*/
public Context addWebapp(Host host, String url, String path) {
silence(url);
// 创建一个 servlet 容器
Context ctx = new StandardContext();
ctx.setPath( url );
ctx.setDocBase(path);
if (defaultRealm == null) {
initSimpleAuth();
}
ctx.setRealm(defaultRealm);
ctx.addLifecycleListener(new DefaultWebXmlListener());
/**
* 重要的配置对象,这个类将会负责整个 Web 应用配置的解析工作。
*
*/
ContextConfig ctxCfg = new ContextConfig();
ctx.addLifecycleListener(ctxCfg);
ctxCfg.setDefaultWebXml("org/apache/catalin/startup/NO_DEFAULT_XML");
/**
* 将这个 Context 容器加到父容器的 Host 中
*
*/
if (host == null) {
getHost().addChild(ctx);
} else {
host.addChild(ctx);
}
return ctx;
}

接着,会调用 Tomcat 的 start 方法启动 Tomcat ,Tomcat 的启动是基于观察者模式设计的,所有的容器都会集成一个 Lifecycle 接口,其管理着容器的整个生命中期,所有容器的修改和状态的改变都会通知已经注册的观察者(Listener),

应用对应 Context容器 的启动过程


ContextConfig 集成了 LifecycleListener 接口,成为了 Context容器 的监听者,ContextConfig 是在调用清单3 时被加入到 StandardContext 容器中的,负责整个 Web 应用的配置文件的解析工作。

ContextConfig 的 init 方法将会主要完成以下工作:

  1. 创建用于解析 xml 配置文件的 ContextDigester 对象
  2. 读取 context.xml 文件、Host 配置文件,Context 自身的配置文件,设置 Context 的DocBase

在 init 方法完成后, Context 容器会执行 startInternal 方法,该方法主要有以下功能:

  1. 创建读取源文件的对象,创建 ClassLoader 对象
  2. 设置应用的工作目录
  3. 启动相关的辅助类:logger、realm、resource
  4. 修改启动状态,通知观察者(Web 应用的配置)
  5. 子容器的初始化,获取 ServletContext 并设置必要的参数
  6. 初始化 load on startup 的 Servlet(即初始化 Web 应用)

Web 应用的初始化工作


Web 应用的初始化工作是在 ContextConfig 的 configureStart 方法中实现的,应用的初始化主要是要解析 web.xml 文件,这个文件描述了一个 Web 应用的关键信息,也是一个 Web 应用的入口。

Tomcat 首先会找 globalWebXml ,搜索路径是 engine 个工作目录下寻找以下两个文件中的任一个,org/apache/catalin/startup/NO_DEFAULT_XMLconf/web.xml

接着会找 hostWebXml 这个文件可能会在 System.getProperty("catalina.base")/conf/${EngineName}/${HostName}/web.xml.default

接着寻找应用的配置文件 examples/WEB-INF/web.xml

web.xml 文件中的各个配置项将会被解析成响应的属性保存在 WebXml 对象中。

如果当前应用支持 Servlet3.0,解析还将完成额外 9 项工作,这个额外的 9 项工作主要是为 Servlet3.0 新增的特性,包括 jar 包中的 META-INF/web-fragment.xml 的解析以及对 annotations 的支持。

接下来,会将 WebXml 对象的属性设置到 Context 容器中,这里包括创建 Servlet 对象、filter、listener 等等,这段代码再 WebXml 的 configureContext 方法中。

下面是解析 Servlet 的代码片段:

清单4.创建 Wrapper 实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
for (ServletDef servlet : servlets.values()) { 
// 创建包装对象
Wrapper wrapper = context.createWrapper();

String jspFile = servlet.getJspFile();
if (jspFile != null) {
wrapper.setJspFile(jspFile);
}
if (servlet.getLoadOnStartup() != null) {
wrapper.setLoadOnStartup(servlet.getLoadOnStartup().intValue());
}
if (servlet.getEnabled() != null) {
wrapper.setEnabled(servlet.getEnabled().booleanValue());
}
wrapper.setName(servlet.getServletName());
Map<String,String> params = servlet.getParameterMap();
for (Entry<String, String> entry : params.entrySet()) {
wrapper.addInitParameter(entry.getKey(), entry.getValue());
}
wrapper.setRunAs(servlet.getRunAs());
Set<SecurityRoleRef> roleRefs = servlet.getSecurityRoleRefs();
for (SecurityRoleRef roleRef : roleRefs) {
wrapper.addSecurityReference(
roleRef.getName(), roleRef.getLink());
}
wrapper.setServletClass(servlet.getServletClass());
MultipartDef multipartdef = servlet.getMultipartDef();
if (multipartdef != null) {
if (multipartdef.getMaxFileSize() != null &&
multipartdef.getMaxRequestSize()!= null &&
multipartdef.getFileSizeThreshold() != null) {
wrapper.setMultipartConfigElement(new
MultipartConfigElement(
multipartdef.getLocation(),
Long.parseLong(multipartdef.getMaxFileSize()),
Long.parseLong(multipartdef.getMaxRequestSize()),
Integer.parseInt(
multipartdef.getFileSizeThreshold())));
} else {
wrapper.setMultipartConfigElement(new
MultipartConfigElement(
multipartdef.getLocation()));
}
}
if (servlet.getAsyncSupported() != null) {
wrapper.setAsyncSupported(
servlet.getAsyncSupported().booleanValue());
}
context.addChild(wrapper);
}

这段代码清楚的将 Servlet 包装成 Context 容器中的 StandardWrapper,

为什么要把 Servlet 包装成 Context 容器中的 StandardWrapper 对象

StandardWrapper 是 Tomcat 容器中的一部分,具备容器的特征,Servlet 是一个独立的 web 开发标准,不应该强耦合在 Tomcat 中。

Context 不仅包含了 standardWrpper ,还有 web.xml 的所有属性。所以 Context 容器才是真正运行 Servlet 的 Servlet 容器,一个 Web 应用对应一个 Context 容器。

创建 Servlet 实例


前面已经完成了 Servlet 的解析工作,Servlet 的类也被包装成 StandardWrapper 添加在 Context 容器中了,但是它还没有被实例化。

在解析配置文件时会读取默认的 globalWebXml,在 conf 下的 web.xml 文件中定义了两个 Servlet 分别是 org.apache.catalina.servlets.DefaultServletorg.apache.jasper.servlet.JspServlet 他们的 load-on-startup 属性分别是 1 和 3。 如果 Servlet 的 load-on-startup 配置项大于0,在 Context 容器启动的时候就会被实例化,所以当 Tomcat 启动这两个 Servlet 就会启动。

创建 Servlet 实例的方法是从 Wapper.loadServlet 开始,loadServlet 方法要做的事情是获取 servletClass 然后把它交给 InstanceManager 创建一个基于 servletClass.class 的对象(这里其实就是通过反射创建对象),如果 Servlet 配置了 jsp-file,那么这个 servletClass 就是conf/web.xml 中定义的 org.apache.jasper.servlet.JspServlet 了。

上图可关联UML类图连线示意一起食用

初始化 Servlet


初始化 ServletStandardWrpperinitServlet 方法中,就是调用了 Servletinit 方法,同时把 StandardWrapperStandardWrapperFacade 作为 ServletConfig 传给 Servlet

Tomcat 容器为什么要把 StandardWrapperFacade 给 Servlet 对象呢?

如果 Servlet 关联的是 jsp文件,那么前面初始化的就是 JspServlet ,接下来会模拟一次简单的请求,请求调用这个 jsp 文件,以编译这个 jsp 文件为 class,并且初始化这个 class。

这样,Servlet 对象就初始化完成了,实际情况是 Servlet 从被 web.xml 中解析到完成初始化,这个过程非常复杂,中间有很多过程,包括各种容器状态的转化引起的监听事件的触发、各种访问权限的控制和一些不可预料的错误发生的判断行为等等。

下图是完整的时序图,忽略了一些细节。

Servlet 体系结构


Java Web 应用基于 Servlet 规范运转,Servlet 本身又如何运转,为何要设计这样的体系结构?

顶层关联图

与 Servlet 主动关联的是3个类,ServletConfigServletRequestServletResponse,这3个类都是通过容器传递给 Servlet 的

其中 ServletConfig 是在 Servlet 初始化的时候就传给 Servlet(StandardWrapperFacade) 了,

后面两个是在请求达到时调用 Servlet 传递过来的,我们都知道是什么意思。 ServletConfig 接口中声明的方法都是为了获取这个 Servlet 的一些配置属性,这些属性可能在 Servlet 运行时被用到。

Servlet 的运行模式是 握手型的交互式 模式,两个模块为了交换数据,通常都会准备一个连接场景,这个场景一直跟随这这个连接过程直到连接完成。连接场景的初始化时根据连接对象指定的参数定制的,参数通常是一个配置类,所以连接场景就由 ServletContext 来描述,ServletContext 的集合就由 ServletConfig 描述,而 ServletRequest 和 ServletResponse 就是需要交互的对象,它们作为运输工具传递交互结果。

Servlet 的继承体系


Command + t 可以看到一个类的继承体系

1
2
3
4
5
6
7
8
9
10
public abstract class MyHttpServlet extends GenericServlet {
@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
service(request, response);
}

public abstract void service(HttpServletRequest req, HttpServletRequest res) throws ServletException, IOException;
}

实际上,httpServlet 内部就是这么做的。所以我们只需要继承 httpServlet 就可以对 http 请求进行操作。

ServletConfig


ServletConfig 在容器中的类关联图

Tomcat 的 context 容器中,StrandardWrapper 和 StandardWrapperFacade 都实现了 ServletConfig 接口,保证传给 Servlet 的 StandardWrapper 对象可以拿到 ServletConfig 规定的数据,而不会把 ServletConfig 不关心的数据暴露给 Servlet

ServletContext 也和 ServletConfig 有类似的结构,Servlet 中拿到的 ServletContext 的实际对象是 ApplicationContextFacade 对象,同样保证了 ServletContext 只能从其中拿到它该拿的数据,它们使用的都是门面设计模式

通过 ServletContext 可以拿到 Context 容器中一些必要的信息,比如应用的工作路径,容器支持的 Servlet 最小版本等等。

ServletRequest 和 ServletResponse


我们创建 request 和 response 对象时候,通常都是使用 HttpServletrequest 和 HttpServletResponse,它们继承了 ServletRequest 和 ServletResponse。下图为Request 相关类结构图

Tomcat 接受到请求,首先会创建 org.apache.coyote.Requestorg.apache.coyote.Response,这两个类是 Tomcat 内部使用的描述一次请求和响应的信息类,它们是轻量级类,作用是在服务器接受到请求后,经过简单的解析将请求快速分配给后续线程处理。这两个对象很小,很容易被 GC 回收。

交给用户线程去处理请求时,又会创建 org.apache.catalina.connector.Requestorg.apache.catalina.connector.Response 对象,这两个对象会穿越整个 Servlet 容器直到要传给 Servlet。

和上面的 ServletConfig 和 ServletContext 一样, Request 和 Response 传递给 Servlet 接口的也是包装过的门面类 RequestFacade 和 RequestFacade,封装容器中的数据,屏蔽掉不需要的信息。

Servlet 工作方式


用户发起请求,会包含如下信息:http://hostname:port/contextpath/servletpath, hostname 和 port 是用来与服务器建立 TCP 连接,后面的 URL 才是选择服务器中的哪个子容器服务用户的请求,服务器是如何通过这个 URL 来达到正确的 Servlet 容器中?

这种映射工作专门由一个类完成 org.apache.tomcat.util.http.mapper,这个类保存了 Tomcat 的 Container 容器的所有子容器的信息,当 Request 类进入 Container 容器之前,mapper 会根据这次请求的 hostname 和 contextpath 将 host 和 context 容器设置到 Request 的 mappingData 属性中,所以当 Request 进入 Container 之前,它要访问哪个子容器就已经确定了。

下图为 Request 和 Mapper 的关系图

MapperListener 类的初始化过程(为什么 mapper 会有容器的所有子容器)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// MapperListener.init
public void init() {
// 找到 host
findDefaultHost();
// 获取 容器
Engine engine = (Engine) connector.getService().getContainer();
// 添加 MapperListener 到容器中
engine.addContainerListener(this);
// 获取子容器
Container[] conHosts = engine.findChildren();
for (Container conHost : conHosts) {
Host host = (Host) conHost;
if (!LifecycleState.NEW.equals(host.getState())) {
// 将 mapperListener 到所有的子容器中
host.addLifecycleListener(this);
registerHost(host);
}
}
}

上述代码做的事情就是将 MapperListener 作为监听者添加到整个 Container 容器中的每个子容器中。

上图描述了一次 Request 请求是如何达到最终的 Wrapper 容器的,但是在达到最终的 Servlet 前,还要完成一些步骤,必须要执行 Filter 链,以及要通知你在 web.xml 中定义的 listener。

接下来就要执行 Servlet 的 service 方法了,通常,我们自己定义的 servlet 并不直接实现 javax.servlet.Servlet,类,而是去继承更简单的 HttpServlet 类或者 GenericServlet 类,有选择的覆盖相应的方法去实现我们要完成的工作。

Servlet 的确已经帮我们完成了所有的工作,但是现在 web 应用很少有直接将交互全部页面都用 servlet 来实现, 而是采用更加高效的 MVC 框架来实现。这些 MVC 框架基基本的原理都是将所有的请求都映射到一个 Servlet,然后去实现 service 方法,这个方法也是 MVC 框架的入口。

当 Servlet 从 Servlet 容器中移除时,就表明该 Servlet 的生命周期结束了,这时 Servlet 的 destroy 方法会被调用。

Servlet 可以提供给我们的信息


Servlet 能够给我们提供两部分数据,一个是 Servlet 初始化时设置的 ServletConfig,这个类基本涵盖了 Servlet 本身 和 Servlet 运行的容器的基本信息,ServletConfig 的实际对象是 StandardWrapperFacade,看看接口就可以知道有哪些信息。

还有一部分数据是 ServletRequest 类提供,实际对象是 RequestFacade,主要描述请求的 HTTP 协议信息,所以要掌握 Servlet 的工作方式,必须要清楚 HTTP 协议。关于这一块,还有 Session 与 Cookie。

Servlet 的使用


ServletConfig 接口的使用


方法:

  • String getServletName() 获取 Servlet 的 name 信息,web.xml中当前 <servlet> 中的文本。
  • ServletContext getServletContext() 获取上下文对象
  • String getInitParameter(String name 根据参数名称来获取初始化参数的值
  • Enumeration<String> getInitParameterNames() 获取所有初始化参数的名称

需求:在 service 方法中做编码的判断处理,如果是 utf-8 则打印中文,如果不是打印英文

xml 中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<servlet>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
<init-param>
<param-name>username</param-name>
<param-value>xiaoming</param-value>
</init-param>
<!-- 告诉服务器,HelloServlet 类会实现 Servlet 接口去做事情
有了全限定名,服务器内部可以通过反射去创建该类的对象,调用方法,
实际上就是 url 中的 baidu.com/(类型调用的方法的返回值) -->
<servlet-class>servlet.HelloServlet</servlet-class>
<servlet-name>HelloServlet</servlet-name> <!-- 类名 -->
</servlet>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
// 获取 web.xml 中当前 Servlet 配置的初始化参数值
String encoding = config.getInitParameter("encoding");
String username = config.getInitParameter("username");

System.out.println(username);
System.out.println(encoding);

if ("utf-8".equals(encoding)) {
System.out.println("你好");
} else {
System.out.println("hello");
}

Enumeration<String> names = config.getInitParameterNames();
while (names.hasMoreElements()) {
String name = names.nextElement();
String value = config.getInitParameter(name);
System.out.println(name + ":" + value);
}
}

HttpServletRequest 接口


ServletRequest 的子接口,封装了请求的数据以及操作符合 http 协议相关的方法

常用的方法:

  1. getMethod 返回请求方式
  2. getRequestURI 返回请求行中的资源名字部分 /test/index.html
  3. getRequestURL 返回浏览器地址栏中的所有信息
    • 返回客户端请求的完整 URL(包括协议、服务器名、端口号、资源路径信息),不含查询参数
  4. getContextPath 获取当前项目上下文路径标签的 path 属性
  5. getRemoteAddr 返回发出请求的客户机的 IP 地址
  6. getHeader 返回指定名称的头字段的值。
  7. setCharacterEncoding(String env) 设置字符集编码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println(req.getMethod()); // GET
System.out.println(req.getRequestURI()); // /config
System.out.println(req.getRequestURL()); // http://localhost/config
System.out.println(req.getContextPath()); // 获取 <Context /> 的 path 属性
System.out.println(req.getRemoteAddr()); // 0:0:0:0:0:0:0:1 客户机的 IP 地址
System.out.println(req.getHeader("User-Agent"));

/**
* username :xiaoming
* [java, c]
*/
System.out.println("username :" + req.getParameter("username"));
String[] values = req.getParameterValues("hobby");
System.out.println(Arrays.toString(values));

Enumeration<String> parameterNames = req.getParameterNames();
while (parameterNames.hasMoreElements()) {
String string = (String) parameterNames.nextElement();
System.out.println(string);
// username hobby
}
/**
* key:username,vlaue:[Ljava.lang.String;@76c783cb
* key:hobby,vlaue:[Ljava.lang.String;@78006363
*/
Map<String, String[]> map = req.getParameterMap();
Set<String> set = map.keySet();
for (String key : set) {
System.out.println("key:" + key + ",vlaue:" + map.get(key));
}
}

获取表单请求参数:

  1. `String getParameter(String name) 返回指定名字参数的值
  2. String[] getParameterValues(String name) 返回指定名字参数的多个参数值
  3. `Enumeration getParameterNames() 返回所有参数名的 Enumeration 对象
  4. `Map<String, String[]> getParameterMap() 返回所有的参数和值所组成的 Map 对象

开发中,表单一般使用 POST 请求

请求编码问题


乱码的原因:Tomcat 服务器在处理 get 和 post 请求时,使用的是 ISO-8859-1 编码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/**
* 序列化版本 id
*/
private static final long serialVersionUID = 1L;
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 设置字符集编码,此方法只支持 post 请求,在 Tomcat 8 之前不支持 get 请求
req.setCharacterEncoding("UTF-8");

System.out.println("id=" + req.getParameter("id"));
System.out.println("username=" + req.getParameter("username"));
System.out.println("password=" + req.getParameter("password"));
System.out.println("sex=" + req.getParameterValues("sex"));
System.out.println("hobby=" + req.getParameterValues("hobby"));
System.out.println("address=" + req.getParameter("address"));
System.out.println("intro=" + req.getParameter("intro"));
/**
* 写中文变成乱码
* 乱码的原因:Tomcat 服务器在处理 get 和 post 请求时,使用的是 ISO-8859-1 编码
* 此编码格式不支持中文
*/
}
public static String decodeISO(String isoCode) {
String utf8 = null;
byte[] buff = null;
try {
buff = isoCode.getBytes("ISO-8859-1");
utf8 = new String(buff);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return utf8;
}

如何处理 GET 请求乱码问题
在 server.xml 中 70 行,<connector>标签下,Tomcat8之前默认是有一条属性,URIEncoding=”ISO-8859-1” 改为 “utf-8” 即可。

HttpServletResponse 常用方法


ServletResponse 接口 表示响应对象,做响应处理,封装了响应的一些 API
HttpServletResponse 接口 ServletResponse 的子接口,封装了 符合 http协议相关的响应方法

常用方法:

  1. OutputStream getOutStream() 获取字节输出流对象,文件下载
  2. PrintWriter getWriter() 获取字符输出流对象,上述方法无法共存,否则报错
1
2
3
response.setContentType("text/html"); // 设置输出的 MIME 类型
response.setCaracterEncoding("UTF-8"); // 设置输出数据的编码方式
response.setContentType("test/html;charset=utf-8"); // 可以将上述两行代码合并成一行代码

必须先设置 MIME 类型和编码,在获取输出流,否则没有效果

web.xml 配置细节


  1. Servet-name 不能使用 default 这个值

    • 原因:在 Tomcat/conf/web.xml 中配置了一个 servlet-name 为 default 的DefaultServlet,这个 Servlet 是用来处理静态资源的访问,如果我们配置了就覆盖了 DefaultServlet 的功能
  2. 映射路径<url-pattern>

    • 可以配置多个 <url-pattern> 注意:资源路径需要唯一,需要使用/开头
    • 配置多个 <servlet-mapping>
  3. 通配符:在 中使用通配符

    • *表示任意字符
    • /*:任意的资源路径都可以访问的 servlet
    • /ooxx/*:任意以/ooxx/开头的资源路径都可以访问到 servlet
    • *.ooxx:任意以.ooxx结尾的资源路径都可以访问到 servlet,不能加/
  4. 配置欢迎页面

    • 默认是 Tomcat/conf/web.xml
    1
    2
    3
    4
     <!-- 配置欢迎页面,这个标签会覆盖 tomcat 的 web.xml 配置 -->
    <welcome-file-list>
    <welcome-file>/welcome.html</welcome-file>
    </welcome-file-list>
  5. 服务器启动初始化 servlet 配置

    • 设置 Servlet 的启动优先级就 OK
    • <load-on-startup> 中间的数字表示执行的优先级别,数值越小优先级越高

    <load-on-startup>参数的作用:如果 init 方法中的代码比较多,第一个用户就会等待很久,调整优先级会保证 Servlet 在服务器启动时就会去初始化。

    1
    2
    3
    <!-- 服务器启动就创建对象调用 init 方法,在<servlet>标签下 -->
    <load-on-startup>1</load-on-startup>
    </servlet>

web.xml 约束头


JDK 1.7+

1
2
3
4
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">

JDK 1.6+

1
2
3
4
<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">

JDK 1.5+

1
2
3
4
5
<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_2_5.xsd"
version="2.5">
version="3.1">
1
2
3
4
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"   
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
version="2.4">

Servlet注解(web.xml配置)

在 Servlet 类上贴一个标签,@WebServlet(path),path 就相当 <url-pattern>中的内容

web.xml中metadata-complete="true"表示是否忽略扫描注解

  • metadata-complete=”false” 扫描注解(默认)
  • metadata-complete=”true” 不扫描注解
1
2
3
4
5
6
7
8
9
10
@WebServlet(value = "/register", initParams = {@WebInitParam(name = "encoding", value = "utf-8")})
public class AnnotationServlet extends HttpServlet {

@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("进来了");
System.out.println(this.getServletConfig().getInitParameter("encoding"));
// utf-8
}
}
1
2
3
4
5
6
7
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1" metadata-complete="false"/>

<!-- metadata-complete 表示是否忽略扫描注解,true 是不扫描,false 是扫描 -->

注解存在硬编码,需要根据需求来选用。一般通用的配置用注解,个性化的配置用 web.xml。防止硬编码的存在

JSP


目的:前后端分离,不在 Java 中书写 html

使用:

  1. 创建一个 jsp 文件
  2. 修改了 jsp 的默认编码
  3. 在 jsp 页面中可以直接书写引用 java 代码 <%=new Date() %>
  4. web.xml 配置

JSP 原理:


  1. 所有的.jsp 结尾的操作交给 JspServlet 处理
  2. jsp文件转为 java 文件
  3. java 编译为 class 文件
  4. 执行 class 文件

/Library/Tomcat/work/Catalina 路径下存放着你的jsp运行时会产生的 .java 和 .class 文件

JSP 语法:


  1. JSP 的注释:

    • <%--这是注释,不会翻译到 Servlet 中--%>
  2. JSP 中的 Java 脚本片段:

    • 实际开发中,应做到 JSP 中不能出现一行 Java 脚本片段
    • 语法:
    1
    2
    3
    4
    <%
    语句1;
    语句2;
    %>

    • 原理:其中的语句会原封不动的被服务器翻译到对应的 Servlet 的 _jspService 方法中
  3. JSP 的 Java 脚本表达式:

    • <%=表达式%> 利用输出流打印到页面上,out.print(表达式);
  4. JSP 的声明:

    • 定义类的成员
    • 语法:
    1
    2
    3
    <%!
    Java 代码
    %>

JSP 的三大指令

设定 JSP 网页的整体配置信息

特点:不向客户端产生任何输出,指令在 JSP 整个文件内有效,为翻译阶段提供全局信息

语法:<%@ 指令 属性 %>

page

每个 jsp 页面都需要存在的一个指令,定义 JSP 页面的各种属性

  • language JSP 页面中使用脚本语言,默认值java,目前只支持 Java

  • extends 指示 JSP 对应的 Servlet 类的父类,不要修改

  • *import 导入 JSP 中的 Java 脚本使用到的类或包。(Java的 import语句)

  • JSP 引擎自动导入以下包:javax.servlet.*javax.servlet.http.*javax.servlet.jsp

  • *session 指示 JPS 页面是否创建 HttpSession 对象,默认值是 true

  • *buffer 指示 JSP 用的输出流的缓存大小,默认值是8kb

  • autoFlush 自动刷新输出流的缓存

  • isThreadSafe 指示页面是否是线程安全的,默认是 ture (已过时)

  • *errorPage 指示当前页面出错后转发的页面,目标页面如果以”/“(当前应用)就是绝对路径

    • 配置全局错误提示页面: web.xml
    1
    2
    <error-page>
    <exception-type>java.lang.Exception</exception-type> <location>/error.jsp</location> </error-page> <error-page> <error-code>404</error-code> <location>/404.jsp</location> </error-page>
  • *isErrorPage 指示当前页面是否产生 Exception 对象

  • *contextType 指定当前页面的 MIME 类型,(同 response.setContentType())

  • *pageEncoding 通知引擎读取 JSP 时采用的编码,还有contentType

  • *isELIgnored 是否忽略 EL 表达式 ${1+1},默认是 false

page 指令最简单的使用方式:

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

deferredSyntaxAllowedAsLiteral 参数
解决了不能使用 #{…} 取值的问题
该属性是在JSP 2.1规范中引入的,JSP 2.1规范对JSP 2.0和Java Server Faces 1.1中的表达式语言进行了统一。在JSP 2.1中,字符序列#{被保留给表达式语言使用,你不能在模板本中使用字符序列#{。如果JSP页面运行在JSP 2.1之前版本的容器中,则没有这个限制。对于JSP 2.1的容器,如果在模板文本中需要出现字符序列#{,那么可以将该属性设置为true。

include


(静态包含)

包含其他组件,语法:<%@include file=""%>,file 指定要包含的目标组件,路径如果以”/“(当前应用)就是绝对路径。

原理:把目标组件的内容加到源组件中,输出结果。

动态包含:<jsp:include page=””/> 路径如果以”/“(当前应用)就是绝对路径

taglib


导入jar包:taglibs-standard-impl-1.2.5.jar taglibs-standard-spec-1.2.5.jar

通过JSP的 taglib指令,引入标签库.

标签库 URI 前缀 使用方式
核心标签库 http://java.sun.com/jsp/jstl/core c <c:tagname…>
国际化标签(I18N) http://java.sun.com/jsp/jstl/fmt fmt <fmt:tagname…>
SQL标签库 http://java.sun.com/jsp/jstl/sql sql <sql:tagname…>
XML标签库 http://java.sun.com/jsp/jstl/xml x <x:tagname…>
1
2
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>

用法: ${status.index}

创建JavaWeb项目结构


java web 严格来说分为两类工程结构:一个是工程编译目录结构,一个是工程发布目录结构。工程发布目录结构结构,为servlet容器加载web应用的统一标准目录,而工程编译目录结构是为了方便工程编写、修改的临时结构,需要通过工程构建工具(ant,maven,gradle等)编译生成最终的运行时发布结构。因此,工程编译目录结构并不统一,与所使用的编译工具有关(也和配置有关)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
├── pom.xml // maven的配置文件
└── src
    ├── main
    │   ├── java // java代码的目录
    │   │   └── mygroup
    │   │       ├── controller
    │   │       │   ├── HomeController.java
    │   │       │   └── PersonController.java
    │   │       ├── dao
    │   │       │   └── PersonDao.java
    │   │       └── model
    │   │           └── Person.java
    │   ├── resources // 静态资源目录
    │   │   ├── db.properties
    │   │   ├── log4j.xml
    │   │   └── META-INF
    │   │       └── persistence.xml
    │   └── webapp // web应用部署根目录
    │       ├── index.html // 因为是静态html文件,不用放到WEB-INF目录下
    │       ├── META-INF
    │       │   ├── context.xml
    │       │   └── MANIFEST.MF
    │       ├── resources // css,js等静态资源是不能放到WEB-INF目
     // 录下的,因为WEB-INF下的资源,客户端无法直接访问
    │       │   └── css
    │       │       └── screen.css
    │       └── WEB-INF // jsp都会放到这里,以保证用户无法直接访问jsp,而是通过
     // controller。这个目录下的所有内容客户端都无法直接访问,
     // 所以不要放静态文件
    │           ├── spring
    │           │   ├── app
    │           │   │   ├── controllers.xml
    │           │   │   └── servlet-context.xml
    │           │   ├── db.xml
    │           │   └── root-context.xml
    │           ├── views
    │           │   ├── edit.jsp
    │           │   ├── home.jsp
    │           │   └── list.jsp
    │           └── web.xml
    └── test
        ├── java
        │   └── mygroup
        │       ├── controller
        │       │   ├── DataInitializer.java
        │       │   ├── HomeControllerTest.java
        │       │   └── PersonControllerTest.java
        │       └── dao
        │           └── PersonDaoTest.java
        └── resources
##             ├── db.properties
            ├── log4j.xml
            ├── test-context.xml
            └── test-db.xml

创建 folder 时,可以用/来一次性创建多级文件夹结构

使用 [Maven]管理框架

跳转和信息共享


在一个 servlet 中完成任务会去执行另一个 servlet

Web组件之间的跳转方式


  1. 请求转发(forward)
  2. URL重定向(redirect)
  3. 请求包含(include) 不用

请求转发 forward


请求转发:
从 Servlet1 , 请求转发到Servlet2.
Servlet1 完成一部分的功能,再跳转到 Servlet2 ,继续完成剩下的功能.

  • 语法:
    • request对象.getRequestDispatcher(String path).forward(request,response);
    • 参数:path,表示跳转的目标的路径(资源的名称).
    • 注意:别忘了forward.
    • 不支持跨域名访问,可以访问 WEB-INF 中的资源
      • req.getRequestDispatcher("/WEB-INF/hello.jsp").forward(req, resp)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@WebServlet("/forward/a")
public class AServlet extends HttpServlet {

@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html; charset=utf-=8");

PrintWriter out = resp.getWriter();

String username = req.getParameter("username");
System.out.println("A username:" + username);
System.out.println("A end");

out.print("a end...");
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@WebServlet("/forward/b")
public class BServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

req.setCharacterEncoding("utf-8");

PrintWriter out = resp.getWriter();
String username = req.getParameter("username");
System.out.println("B username:" + username);
out.print("B username:" + username);

req.getRequestDispatcher("/a").forward(req, resp);
/**
* 先运行 AServlet 然后再往下运行
*/

System.out.println("B end...");
out.print("B end...");
}
}

打印结果如下:

1
2
3
4
B username:默认的
A username:默认的
A end
B end...

页面 -> 最后停留在 a.end,

请求转发的特点:

  1. 浏览器地址栏不发生改变,依然是 /forward/s1,不是目标地址(/forward/s2).
  2. 请求转发只发送一个请求.
  3. 共享同一个请求中的数据.
  4. 最终响应给浏览器的由Servlet2来决定.
  5. 请求转发不能跨域访问,只能跳转到当前应用中的资源.
  6. 请求转发可以访问 WEB-INF 目录中的资源.

URL 重定向


语法:

- `response对象.sendRedirect(String path);`
- path指的是这个资源路径(资源名称)

特点:

  1. 浏览器地址发生改变,从 /redirect/a,跳转到(/redirect/b)
  2. 发送两个请求(相当于把资源拷贝到浏览器上,在浏览器再起去发送请求)
  3. 没有共享请求数据(因为是两个请求,所以不能共享数据)
  4. 最终响应给浏览器的由 BServlet 决定
  5. URL 重定向支持跨域访问,可以跳转到其他网站
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@WebServlet("/redirect/b")
public class BServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

req.setCharacterEncoding("utf-8");
resp.setContentType("text.html; charset=utf-8");

PrintWriter out = resp.getWriter();
String username = req.getParameter("username");
System.out.println("B username:" + username);
out.print("B username:" + username);
resp.sendRedirect("a");

System.out.println("B end...");
out.print("B end...");
}
}

运行结果:

1
2
3
4
B username:默认的
B end...
A username:null
A end

有无/的区别


同域资源访问:

  1. 带了/表示从当前上下文路径下去查找资源
  2. 没有带 / 表示从当前资源所在目录下去查找资源

跨域访问 redirect

  1. / 直接在外部地址上找 /+资源名称
  2. 不带 / 直接将资源地址放入地址栏执行

建议:同域建议直接加 / 直接在 webapp 下去查找,比较清晰,跨域直接写地址

Servlet 的三大作用域对象


作用:用来共享数据

作用域 类型 描述
request HttpServletRequest 作用:在一个请求中的多个跳转间共享数据
session HttpSession 会话,打开浏览器->访问网站(会话开始),一次与服务器的连接
application ServletContext 应用对象,一个应用只有一个ServletContext对象,作用:多个会话中可以共享一个 application,只要服务器不关闭,Web 应用不 reload ,就不会被销毁

如何取值:

  • request: 直接在 service 方法中使用
  • session: 使用请求对象去获取 request.getSession();
  • application/servletContext:
    1. 通过父类获取
      • super.getServletContext() / super.getServletConfig().getServletContext()
    2. 通过请求对象去获取
      • request.getServletContext()
    3. 通过 session 对象去获取
      • request.getSession().getServletContext();

数据如何共享:

  1. 如何在作用域对象中去设置/存入要共享的数据
    • 作用域对象.setAttribute(String name, Object value)
    • 参数:name:要共享的数据起一个唯一的标志。 value:要共享的数据
  2. 如何作用域对象中去获取共享的数据
    • Object value = 作用域对象.getAttribute(String name);
    • 参数:name:要获取共享数据的唯一标志
  3. 如何删除作用域对象中的数据
    • 作用域对象.removeAttribute(String name);

ServletContext

表示应用对象:一个应用只有一个 ServletContext 对象

常用方法:

  • getRealpath(String path) 获取相对 web 根路径的路径的绝对路径

  • getContextpath() 获取上下文路径

  • getRealpath(String path) 获取相对 web 根路径的路径的绝对路径

ServletContext.getRealPath() 是从当前servlet 在tomcat 中的存放文件夹开始计算起的

比如,有个servlet 叫 UploadServlet,它部署在tomcat 下面以后的绝对路径如下:

“C:\Program Files\apache-tomcat-8.0.3\webapps\UploadServlet”

1
2
3
4
5
6
7
8
ServletContext.getRealPath("/") 
// 返回 "C:\Program Files\apache-tomcat-8.0.3\webapps\UploadServlet"

ServletContext.getRealPath("/attachment")
// 返回 "C:\Program Files\apache-tomcat-8.0.3\webapps\UploadServlet\attachment"

ServletContext.getRealPath("attachment")
// 会导致NullPointerException

结论就是:

在使用ServletContext.getRealPath() 时,传入的参数是从 当前servlet 部署在tomcat中的文件夹算起的相对路径,要以”/“ 开头,否则会找不到路径,导致NullPointerException

  • getContextpath() 获取上下文路径

全局的初始化参数配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1" metadata-complete="false">
<!-- 配置全局的初始化参数 -->
<context-param>
<param-name>username</param-name>
<param-value>lizhao</param-value>
</context-param>
<context-param>
<param-name>password</param-name>
<param-value>123456</param-value>
</context-param>
</web-app>
1
2
3
4
5
ServletContext context = super.getServletContext();
System.out.println(context.getContextPath());
// 返回 service.xml 中 Context 标签的 path 属性
System.out.println(context.getInitParameter("username")); // 输出 lizhao
System.out.println(context.getInitParameter("password")); // 输出 123456

分页实现和设计


过滤器


作用:

  1. 以常规方式调用资源(Servlet/jsp)
  2. 利用修改过的请求信息调用资源
  3. 调用资源之后,在响应到客户端之前,对响应做出修改
  4. 组织当前资源调用,代之转到其他资源

过滤在开发中的应用:

  1. 可以做请求字符的编码处理
  2. 可以做登录验证过滤器
  3. 做敏感字(非法字符)过滤
  4. 做 MVC 框架的前端控制器(处理请求,进行分发)

做自己擅长的事情,不要复制粘贴

开发流程


同 Servlet

  1. 创建一个类去实现 javax.servlet.Filter 接口
  2. 实现接口中的3个方法
    • destroy()
    • doFilter()
    • init(FilterConfig filterConfig)
  3. 通知服务器管理我们自定义的 Filter 类(web.xml @WebFilter())

请求编码过滤


web.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1" metadata-complete="false">


<filter>
<filter-name>CharacterEncodingFilter.java</filter-name>
<filter-class>cn.lizhaoloveit.filter.CharacterEncodingFilter</filter-class>

<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>

<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

<filter>
<filter-name>CheckLoginFilter.java</filter-name>
<filter-class>cn.lizhaoloveit.filter.CheckLoginFilter</filter-class>
</filter>

<filter-mapping>
<filter-name>CheckLoginFilter.java</filter-name>
<url-pattern>/check/*</url-pattern>
</filter-mapping>

</web-app>

CharacterEncodingFilter

1
2
3
4
5
6
7
8
9
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// 在过滤器中写死,存在硬编码问题,使用web.xml 来配置初始化参数,在过滤器中获取初始化参数
chain.doFilter(request, response);
if (!"".equals(encoding)) {
request.setCharacterEncoding(encoding);
}
chain.doFilter(request, response);
}

CheckLoginFilte

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;

Object obj = req.getSession().getAttribute("USER_IN_SESSION");
if (obj == null) {
resp.sendRedirect("/login.jsp");
return;
}
chain.doFilter(request, response);

// 以上代码不会加以甄别,过滤所有请求,如果要区别哪些资源需要登录,哪些不需要

// 在 Servlet 的 url-pattren 加上 /check/ 就行了,有了 /check/* 去过滤,就不需要判断是否是 login 了
}

dispatcher元素

2.4版本的servlet规范在部属描述符中新增加了一个 <dispatcher> 元素,这个元素有四个可能的值:即REQUEST,FORWARD,INCLUDE和ERROR,可以在一个<filter-mapping>元素中加入任意数目的<dispatcher>,使得filter将会作用于直接从客户端过来的request(REQUEST),通过forward过来的request(FORWARD),通过include过来的request(INCLUDE)和通过<error-page>过来的request(ERROR)。如果没有指定任何< dispatcher >元素,默认值是REQUEST。
注意:<dispatcher></dispatcher>必须写在filter-mapping的最后。dispatcher的前提条件当然是要先满足url-pattern,然后dispatcher有四种可能的属性:

  1. REQUEST
  • 只要发起的操作是一次HTTP请求,比如请求某个URL、发起了一个GET请求、表单提交方式为POST的POST请求、表单提交方式为GET的GET请求。一次重定向则前后相当于发起了两次请求,这些情况下有几次请求就会走几次指定过滤器。
  1. FOWARD
  • 只有当当前页面是通过请求转发转发过来的情形时,才会走指定的过滤器
  1. INCLUDE
  • 只要是通过<jsp:include page=”xxx.jsp” />,嵌入进来的页面,每嵌入的一个页面,都会走一次指定的过滤器。
  1. ERROR
  • 假如web.xml里面配置了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<error-page>
<error-code>400</error-code>
<location>/filter/error.jsp</location>
</error-page>

<error-page>
<error-code>404</error-code>
<location>/filter/error.jsp</location>
</error-page>

<error-page>
<error-code>500</error-code>
<location>/filter/error.jsp</location>
</error-page>
文章作者: Ammar
文章链接: http://lizhaoloveit.cn/2019/07/07/Servlet/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Ammar's Blog
打赏
  • 微信
  • 支付宝

评论