简介
Servlet (Server Applet) 用 Java编写的服务器端程序,可以独立于平台和协议,用于交互式浏览和生成数据,生成动态 Web 内容。
Servlet 指 服务器端,和客户端产生交互需要接口,实现这些接口的类叫 Servlet
开发一个简单的servlet
- 导入依赖的 jar 包,安装路径:Library/Tomcat/lib/servlet-api.jar
- 创建一个类实现 javax.servlet.Servlet 接口,实现5个方法
- 方法中有 arg0,arg1,解决方法,导入源码,tomcat,下载 Source Code,并且导入即可。
- 在 service 方法中打印一句话,
- 通知 Tomcat 服务器来管理咱们自定义的类,web 的配置文件 web.xml
- 服务器需要的配置文件为 server.xml
将配置文件 server.xml
中加入标签<Context docBase="绝对路径" path="day3"/>
作用:path 为 Context容器 的路径,Tomcat 会从conf/server.xml文件,获取所有的Context 容器,判断哪个的 path 属性值为 day3
,接着找到当前 Context容器 的docBase属性值,该属性值就是当前访问项目的根的路径。
web.xml:配置文件
1 |
|
小 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 | Tomcat tomcat = getTomcatInstance(); |
清单1 的代码是创建一个 Tomcat 实例并且新增一个 Web 应用,启动 Tomcat 并调用其中一个 HelloworldExample Servlet,看是否正确返回预期数据。
Tomcat 的 addWebapp 方法如下:
清单 3 .Tomcat.addWebapp
1 | /** |
接着,会调用 Tomcat 的 start 方法启动 Tomcat ,Tomcat 的启动是基于观察者模式设计的,所有的容器都会集成一个 Lifecycle 接口,其管理着容器的整个生命中期,所有容器的修改和状态的改变都会通知已经注册的观察者(Listener),
应用对应 Context容器 的启动过程
ContextConfig 集成了 LifecycleListener 接口,成为了 Context容器 的监听者,ContextConfig 是在调用清单3 时被加入到 StandardContext 容器中的,负责整个 Web 应用的配置文件的解析工作。
ContextConfig 的 init 方法将会主要完成以下工作:
- 创建用于解析 xml 配置文件的 ContextDigester 对象
- 读取 context.xml 文件、Host 配置文件,Context 自身的配置文件,设置 Context 的DocBase
在 init 方法完成后, Context 容器会执行 startInternal 方法,该方法主要有以下功能:
- 创建读取源文件的对象,创建 ClassLoader 对象
- 设置应用的工作目录
- 启动相关的辅助类:logger、realm、resource
- 修改启动状态,通知观察者(Web 应用的配置)
- 子容器的初始化,获取 ServletContext 并设置必要的参数
- 初始化
load on startup
的 Servlet(即初始化 Web 应用)
Web 应用的初始化工作
Web 应用的初始化工作是在 ContextConfig 的 configureStart 方法中实现的,应用的初始化主要是要解析 web.xml 文件,这个文件描述了一个 Web 应用的关键信息,也是一个 Web 应用的入口。
Tomcat 首先会找 globalWebXml ,搜索路径是 engine 个工作目录下寻找以下两个文件中的任一个,org/apache/catalin/startup/NO_DEFAULT_XML
或 conf/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 | for (ServletDef servlet : servlets.values()) { |
这段代码清楚的将 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.DefaultServlet
和 org.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
初始化 Servlet
在 StandardWrpper
的 initServlet
方法中,就是调用了 Servlet
的 init
方法,同时把 StandardWrapper
的 StandardWrapperFacade
作为 ServletConfig
传给 Servlet
。
Tomcat 容器为什么要把 StandardWrapperFacade 给 Servlet 对象呢?
如果 Servlet 关联的是 jsp文件,那么前面初始化的就是 JspServlet ,接下来会模拟一次简单的请求,请求调用这个 jsp 文件,以编译这个 jsp 文件为 class,并且初始化这个 class。
这样,Servlet 对象就初始化完成了,实际情况是 Servlet 从被 web.xml 中解析到完成初始化,这个过程非常复杂,中间有很多过程,包括各种容器状态的转化引起的监听事件的触发、各种访问权限的控制和一些不可预料的错误发生的判断行为等等。
下图是完整的时序图,忽略了一些细节。
Servlet 体系结构
Java Web 应用基于 Servlet 规范运转,Servlet 本身又如何运转,为何要设计这样的体系结构?
顶层关联图
与 Servlet 主动关联的是3个类,ServletConfig
、ServletRequest
、ServletResponse
,这3个类都是通过容器传递给 Servlet 的
其中 ServletConfig
是在 Servlet 初始化的时候就传给 Servlet(StandardWrapperFacade) 了,
后面两个是在请求达到时调用 Servlet 传递过来的,我们都知道是什么意思。 ServletConfig 接口中声明的方法都是为了获取这个 Servlet 的一些配置属性,这些属性可能在 Servlet 运行时被用到。
Servlet 的运行模式是 握手型的交互式 模式,两个模块为了交换数据,通常都会准备一个连接场景,这个场景一直跟随这这个连接过程直到连接完成。连接场景的初始化时根据连接对象指定的参数定制的,参数通常是一个配置类,所以连接场景就由 ServletContext 来描述,ServletContext 的集合就由 ServletConfig 描述,而 ServletRequest 和 ServletResponse 就是需要交互的对象,它们作为运输工具传递交互结果。
Servlet 的继承体系
Command + t
可以看到一个类的继承体系
1 | public abstract class MyHttpServlet extends GenericServlet { |
实际上,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.Request
和 org.apache.coyote.Response
,这两个类是 Tomcat 内部使用的描述一次请求和响应的信息类,它们是轻量级类,作用是在服务器接受到请求后,经过简单的解析将请求快速分配给后续线程处理。这两个对象很小,很容易被 GC 回收。
交给用户线程去处理请求时,又会创建 org.apache.catalina.connector.Request
和 org.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 | // MapperListener.init |
上述代码做的事情就是将 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 | <servlet> |
1 |
|
HttpServletRequest 接口
ServletRequest 的子接口,封装了请求的数据以及操作符合 http 协议相关的方法
常用的方法:
- getMethod 返回请求方式
- getRequestURI 返回请求行中的资源名字部分 /test/index.html
- getRequestURL 返回浏览器地址栏中的所有信息
- 返回客户端请求的完整 URL(包括协议、服务器名、端口号、资源路径信息),不含查询参数
- getContextPath 获取当前项目上下文路径
标签的 path 属性 - getRemoteAddr 返回发出请求的客户机的 IP 地址
- getHeader 返回指定名称的头字段的值。
- setCharacterEncoding(String env) 设置字符集编码
1 | protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { |
获取表单请求参数:
- `String getParameter(String name) 返回指定名字参数的值
String[] getParameterValues(String name)
返回指定名字参数的多个参数值- `Enumeration
getParameterNames() 返回所有参数名的 Enumeration 对象 - `Map<String, String[]> getParameterMap() 返回所有的参数和值所组成的 Map 对象
开发中,表单一般使用 POST 请求
请求编码问题
乱码的原因:Tomcat 服务器在处理 get 和 post 请求时,使用的是 ISO-8859-1 编码
1 | /** |
如何处理 GET 请求乱码问题
在 server.xml 中 70 行,<connector>
标签下,Tomcat8之前默认是有一条属性,URIEncoding=”ISO-8859-1” 改为 “utf-8” 即可。
HttpServletResponse 常用方法
ServletResponse 接口 表示响应对象,做响应处理,封装了响应的一些 API
HttpServletResponse 接口 ServletResponse 的子接口,封装了 符合 http协议相关的响应方法
常用方法:
OutputStream getOutStream()
获取字节输出流对象,文件下载PrintWriter getWriter()
获取字符输出流对象,上述方法无法共存,否则报错
1 | response.setContentType("text/html"); // 设置输出的 MIME 类型 |
必须先设置 MIME 类型和编码,在获取输出流,否则没有效果
web.xml 配置细节
Servet-name 不能使用
default
这个值- 原因:在 Tomcat/conf/web.xml 中配置了一个 servlet-name 为
default
的DefaultServlet,这个 Servlet 是用来处理静态资源的访问,如果我们配置了就覆盖了 DefaultServlet 的功能
- 原因:在 Tomcat/conf/web.xml 中配置了一个 servlet-name 为
映射路径
<url-pattern>
- 可以配置多个
<url-pattern>
注意:资源路径需要唯一,需要使用/开头 - 配置多个
<servlet-mapping>
- 可以配置多个
通配符:在
中使用通配符 - *表示任意字符
- /*:任意的资源路径都可以访问的 servlet
- /ooxx/*:任意以/ooxx/开头的资源路径都可以访问到 servlet
- *.ooxx:任意以.ooxx结尾的资源路径都可以访问到 servlet,不能加/
配置欢迎页面
- 默认是 Tomcat/conf/web.xml
1
2
3
4<!-- 配置欢迎页面,这个标签会覆盖 tomcat 的 web.xml 配置 -->
<welcome-file-list>
<welcome-file>/welcome.html</welcome-file>
</welcome-file-list>服务器启动初始化 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 | <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" |
JDK 1.6+
1 | <web-app xmlns="http://java.sun.com/xml/ns/javaee" |
JDK 1.5+
1 | <web-app xmlns="http://java.sun.com/xml/ns/javaee" |
1 | <web-app xmlns="http://java.sun.com/xml/ns/j2ee" |
Servlet注解(web.xml配置)
在 Servlet 类上贴一个标签,@WebServlet(path)
,path 就相当 <url-pattern>
中的内容
web.xml中metadata-complete="true"
表示是否忽略扫描注解
- metadata-complete=”false” 扫描注解(默认)
- metadata-complete=”true” 不扫描注解
1 | "/register", initParams = { (name = "encoding", value = "utf-8")}) (value = |
1 |
|
注解存在硬编码,需要根据需求来选用。一般通用的配置用注解,个性化的配置用 web.xml。防止硬编码的存在
JSP
目的:前后端分离,不在 Java 中书写 html
使用:
- 创建一个 jsp 文件
- 修改了 jsp 的默认编码
- 在 jsp 页面中可以直接书写引用 java 代码
<%=new Date() %>
- web.xml 配置
JSP 原理:
- 所有的.jsp 结尾的操作交给 JspServlet 处理
- jsp文件转为 java 文件
- java 编译为 class 文件
- 执行 class 文件
/Library/Tomcat/work/Catalina
路径下存放着你的jsp运行时会产生的 .java 和 .class 文件
JSP 语法:
JSP 的注释:
<%--这是注释,不会翻译到 Servlet 中--%>
JSP 中的 Java 脚本片段:
- 实际开发中,应做到 JSP 中不能出现一行 Java 脚本片段
- 语法:
1
2
3
4<%
语句1;
语句2;
%>- 原理:其中的语句会原封不动的被服务器翻译到对应的 Servlet 的 _jspService 方法中
JSP 的 Java 脚本表达式:
<%=表达式%>
利用输出流打印到页面上,out.print(表达式);
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 | <%@ page language="java" contentType="text/html; charset=UTF-8" |
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 | <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> |
用法: ${status.index}
创建JavaWeb项目结构
java web 严格来说分为两类工程结构:一个是工程编译目录结构,一个是工程发布目录结构。工程发布目录结构结构,为servlet容器加载web应用的统一标准目录,而工程编译目录结构是为了方便工程编写、修改的临时结构,需要通过工程构建工具(ant,maven,gradle等)编译生成最终的运行时发布结构。因此,工程编译目录结构并不统一,与所使用的编译工具有关(也和配置有关)。
1 | ├── pom.xml // maven的配置文件 |
创建 folder 时,可以用/
来一次性创建多级文件夹结构
使用 [Maven]管理框架
跳转和信息共享
在一个 servlet 中完成任务会去执行另一个 servlet
Web组件之间的跳转方式
- 请求转发(forward)
- URL重定向(redirect)
- 请求包含(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 | "/forward/a") ( |
1 | "/forward/b") ( |
打印结果如下:
1 | B username:默认的 |
页面 -> 最后停留在 a.end,
请求转发的特点:
- 浏览器地址栏不发生改变,依然是 /forward/s1,不是目标地址(/forward/s2).
- 请求转发只发送一个请求.
- 共享同一个请求中的数据.
- 最终响应给浏览器的由Servlet2来决定.
- 请求转发不能跨域访问,只能跳转到当前应用中的资源.
- 请求转发可以访问 WEB-INF 目录中的资源.
URL 重定向
语法:
- `response对象.sendRedirect(String path);`
- path指的是这个资源路径(资源名称)
特点:
- 浏览器地址发生改变,从 /redirect/a,跳转到(/redirect/b)
- 发送两个请求(相当于把资源拷贝到浏览器上,在浏览器再起去发送请求)
- 没有共享请求数据(因为是两个请求,所以不能共享数据)
- 最终响应给浏览器的由 BServlet 决定
- URL 重定向支持跨域访问,可以跳转到其他网站
1 | "/redirect/b") ( |
运行结果:
1 | B username:默认的 |
有无/的区别
同域资源访问:
- 带了
/
表示从当前上下文路径下去查找资源 - 没有带
/
表示从当前资源所在目录下去查找资源
跨域访问 redirect
- 带
/
直接在外部地址上找/+资源名称
- 不带
/
直接将资源地址放入地址栏执行
建议:同域建议直接加
/
直接在 webapp 下去查找,比较清晰,跨域直接写地址
Servlet 的三大作用域对象
作用:用来共享数据
作用域 | 类型 | 描述 |
---|---|---|
request | HttpServletRequest | 作用:在一个请求中的多个跳转间共享数据 |
session | HttpSession | 会话,打开浏览器->访问网站(会话开始),一次与服务器的连接 |
application | ServletContext | 应用对象,一个应用只有一个ServletContext对象,作用:多个会话中可以共享一个 application,只要服务器不关闭,Web 应用不 reload ,就不会被销毁 |
如何取值:
- request: 直接在 service 方法中使用
- session: 使用请求对象去获取 request.getSession();
- application/servletContext:
- 通过父类获取
- super.getServletContext() / super.getServletConfig().getServletContext()
- 通过请求对象去获取
- request.getServletContext()
- 通过 session 对象去获取
- request.getSession().getServletContext();
- 通过父类获取
数据如何共享:
- 如何在作用域对象中去设置/存入要共享的数据
- 作用域对象.setAttribute(String name, Object value)
- 参数:name:要共享的数据起一个唯一的标志。 value:要共享的数据
- 如何作用域对象中去获取共享的数据
- Object value = 作用域对象.getAttribute(String name);
- 参数:name:要获取共享数据的唯一标志
- 如何删除作用域对象中的数据
- 作用域对象.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 | ServletContext.getRealPath("/") |
结论就是:
在使用ServletContext.getRealPath() 时,传入的参数是从 当前servlet 部署在tomcat中的文件夹算起的相对路径,要以”/“ 开头,否则会找不到路径,导致NullPointerException
getContextpath()
获取上下文路径
全局的初始化参数配置:
1 |
|
1 | ServletContext context = super.getServletContext(); |
分页实现和设计
过滤器
作用:
- 以常规方式调用资源(Servlet/jsp)
- 利用修改过的请求信息调用资源
- 调用资源之后,在响应到客户端之前,对响应做出修改
- 组织当前资源调用,代之转到其他资源
过滤在开发中的应用:
- 可以做请求字符的编码处理
- 可以做登录验证过滤器
- 做敏感字(非法字符)过滤
- 做 MVC 框架的前端控制器(处理请求,进行分发)
做自己擅长的事情,不要复制粘贴
开发流程
同 Servlet
- 创建一个类去实现 javax.servlet.Filter 接口
- 实现接口中的3个方法
destroy()
doFilter()
init(FilterConfig filterConfig)
- 通知服务器管理我们自定义的 Filter 类(web.xml @WebFilter())
请求编码过滤
web.xml
1 |
|
CharacterEncodingFilter
1 |
|
CheckLoginFilte
1 |
|
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有四种可能的属性:
- REQUEST
- 只要发起的操作是一次HTTP请求,比如请求某个URL、发起了一个GET请求、表单提交方式为POST的POST请求、表单提交方式为GET的GET请求。一次重定向则前后相当于发起了两次请求,这些情况下有几次请求就会走几次指定过滤器。
- FOWARD
- 只有当当前页面是通过请求转发转发过来的情形时,才会走指定的过滤器
- INCLUDE
- 只要是通过<jsp:include page=”xxx.jsp” />,嵌入进来的页面,每嵌入的一个页面,都会走一次指定的过滤器。
- ERROR
- 假如web.xml里面配置了
:
1 | <error-page> |