文章目录
动态资源与静态资源:
静态资源 :
- 无需在程序运行时通过代码运行生成的资源,在程序运行之前就写好的资源.
- 例如:html css js img ,音频文件和视频文件
动态资源
- 需要在程序运行时通过代码运行生成的资源,在程序运行之前无法确定的数据,
- 运行时动态生成,例如Servlet,Thymeleaf … …
- 动态资源指的不是视图上的动画效果或者是简单的人机交互效果
举例
- 前端页面 直接验证 用户名密码格式是否准确 : 静态资源
- 需要验证用户名是否已经存在 : 动态资源
Servlet是什么?
Servlet(server applet) 是运行在服务端(tomcat)的Java小程序,
是sun公司提供一套定义动态资源规范;
从代码层面上来讲Servlet就是一个接口
-
用来接收、处理客户端请求、响应给浏览器的动态资源。
- 在整个Web应用中,Servlet主要负责接收处理请求、协同调度功能以及响应数据。
- 我们可以把Servlet称为Web应用中的控制器
-
Servlet是能处理客户端请求并做出响应的一套技术标准。- 不是所有的JAVA类都能用于处理客户端请求
-
Servlet是运行在服务端的,所以 Servlet 必须在WEB项目中开发且在Tomcat这样的服务容器中运行
Servlet 流程:

一、Servlet开发流程
- web项目,配置tomcat,添加为项目依赖 (tomcat依赖包括servlet的jar包)
- 前端 创建 HTML 要提交的表单 信息 method/action
- 后端 创建类 继承 HttpServlet
- 类重写servers方法 ,实现业务处理代码
- 路径匹配 :在web.xml中,配置servlet对应的请求映射路径
简单需求与实现
校验注册时,用户名是否被占用:
通过客户端向一个Servlet发送请求,携带username,如果用户名是’dougwake’,则向客户端响应 NO,如果是其他,响应YES
- HTML 表单
<body>
<h1>hello </h1>
<!-- get or post -->
<form method="get" action="userServlet">
用户名: <input type="text" name="username"/>
<input type="submit" value="校验"/>
</form>
</body>
- servlet类 继承 HttpServlet 重写方法service 处理业务逻辑
public class UserServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.从request对象中获取请求中的任何信息(username参数)
String username = req.getParameter("username");
//2.处理业务逻辑
String info = "Yes";
if ("dougwake".equals(username)) {
info = "NO";
}
//3.将要响应的数据放入response
// 手动设置响应头
//resp.setHeader("Content-Type","text/css");
resp.setContentType("text/html");
PrintWriter writer = resp.getWriter();
writer.write(info);
}
}
Content-Type 告诉客户端响应的数据类型是什么,就用什么类型解析。

- web.xml配置servlet关联路径
<!--
1. 配置servlet类,并且起一个别名
2. servlet-class 对应要实例化的servlet类 (反射方式)
3. servlet-name 用于关联请求的映射路径
url-pattern 与前端关联的action (举例: 就是/aaa )
-->
<servlet>
<servlet-name>userServlet</servlet-name>
<servlet-class>com.wake.servlet.UserServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>userServlet</servlet-name>
<url-pattern>/userServlet</url-pattern>
</servlet-mapping>
url-pattern匹配的特殊写法(基本是使用精确匹配) :
<servlet>
<servlet-name>servletTest1</servlet-name>
<servlet-class>com.wake.servlet.ServletTest1</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>servletTest1</servlet-name>
<!-- 可以为一个Servlet匹配多个不同的映射路径,但是不同的Servlet不能使用相同的url-pattern-->
<url-pattern>/s1</url-pattern>
<!-- <url-pattern>/userServlet2</url-pattern>-->
<!--
/ 表示通配所有资源,不包括jsp文件
/* 表示通配所有资源,包括jsp文件
/a/* 匹配所有以a前缀的映射路径
*.action 匹配所有以action为后缀的映射路径
-->
<!-- <url-pattern>/*</url-pattern>-->
</servlet-mapping>
二、Servlet注解方式配置
用了注解 就不用xml配置
@WebServlet("/s6")
public class ServletTest1 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("servletTest执行!");
}
}
@WebServlet(“/s6”) 不写/ 会报错
详细写法:
@WebServlet(
name = "userServlet",
//value = "/user",
urlPatterns = {"/userServlet1","/userServlet2","/userServlet"},
initParams = {@WebInitParam(name = "encoding",value = "UTF-8")},
loadOnStartup = 6
)
public class UserServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String encoding = getServletConfig().getInitParameter("encoding");
System.out.println(encoding);
// 获取请求中的参数
String username = req.getParameter("username");
if("atguigu".equals(username)){
//通过响应对象响应信息
resp.getWriter().write("NO");
}else{
resp.getWriter().write("YES");
}
}
}

三、Servlet生命周期
Servlet对象是Servlet容器创建的,生命周期方法都是由容器(目前我们使用的是Tomcat)调用的。
越来越多的对象交给容器或框架来创建,越来越多的方法由容器或框架来调用,开发人员要尽可能多的将精力放在业务逻辑的实现上。
Servlet主要的生命周期执行特点:
| 生命周期 | 对应方法 | 执行时机 | 执行次数 |
|---|---|---|---|
| 构造对象 | 构造器 | 第一次请求或者容器启动 | 1 |
| 初始化 | init() | 构造完毕后 | 1 |
| 处理服务 | service(HttpServletRequest req,HttpServletResponse resp) | 每次请求 | 多次 |
| 销毁 | destory() | 容器关闭 | 1 |
public class ServletLifeCycle extends HttpServlet {
public ServletLifeCycle(){
System.out.println("构造器");
}
@Override
public void init() throws ServletException {
System.out.println("初始化方法");
}
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("service方法");
}
@Override
public void destroy() {
System.out.println("销毁方法");
}
}
<servlet>
<servlet-name>servletLifeCycle</servlet-name>
<servlet-class>com.atguigu.servlet.ServletLifeCycle</servlet-class>
<!--load-on-startup
如果配置的是正整数则表示容器在启动时就要实例化Servlet,
数字表示的是实例化的顺序
-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>servletLifeCycle</servlet-name>
<url-pattern>/servletLiftCycle</url-pattern>
</servlet-mapping>
生命周期总结:
1.Servlet对象在容器Tomcat中是单例的
2. Servlet的成员变量在多个线程之中的共享的
3. 所以不建议在service中修改成员变量 , 在并发请求时,会引发线程安全问题!
4.default-servlet用于加载静态资源的servlet , 默认随服务启动,默认启动序号为1
5. Tomcat容器中,已经定义了一些随系统启动实例化的servlet,自定义的servlet的load-on-startup尽量不要占用数字1-5
6.load-on-startup中定义的正整数表示实例化顺序,如果数字重复了,容器会自行解决实例化顺序问题,但是应该避免重复
7. 容器是可以处理并发的用户请求的,每个请求在容器中都会开启一个线程
四、Servlet继承结构
图解:

自定义Servlet中,必须要对处理请求的方法进行重写
- 要么重写service方法
- 要么重写doGet/doPost方法
五、ServletConfig和ServletContext
获取 xml 相应的配置文件信息
5.1 ServletConfig
- 为
Servlet提供初始配置参数的一种对象,每个Servlet都有自己独立唯一的ServletConfig对象 - 容器会为每个
Servlet实例化一个ServletConfig对象,并通过Servlet生命周期的init方法传入给Servlet作为属性

ServletConfig是一个接口
| 方法名 | 作用 |
|---|---|
| getServletName() | 获取<servlet-name>HelloServlet</servlet-name>定义的Servlet名称 |
| getServletContext() | 获取ServletContext对象 |
| getInitParameter() | 获取配置Servlet时设置的『初始化参数』,根据名字获取值 |
| getInitParameterNames() | 获取所有初始化参数名组成的Enumeration对象 |
5.2 ServletContext
- ServletContext对象有称呼为
上下文对象,或者叫应用域对象(后面统一讲解域对象) - 容器会为每个app创建一个独立的唯一的ServletContext对象(单例)
- ServletContext对象为所有的Servlet所共享
- ServletContext可以为所有的Servlet提供初始配置参数

ServletContext的使用:
public class ServletA extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 从ServletContext中获取为所有的Servlet准备的参数
ServletContext servletContext = this.getServletContext();
String valueA = servletContext.getInitParameter("paramA");
System.out.println("paramA:"+valueA);
// 获取所有参数名
Enumeration<String> initParameterNames = servletContext.getInitParameterNames();
// 迭代并获取参数名
while (initParameterNames.hasMoreElements()) {
String paramaterName = initParameterNames.nextElement();
System.out.println(paramaterName+":"+servletContext.getInitParameter(paramaterName));
}
}
}
配置文件:
<context-param>
<param-name>paramA</param-name>
<param-value>valueA</param-value>
</context-param>
<context-param>
<param-name>paramB</param-name>
<param-value>valueB</param-value>
</context-param>

5.2.1 servletContext 获取文件路径和上下文
获取资源的真实路径 :
String realPath = servletContext.getRealPath("资源在web目录中的路径");

ServletContext application = this.getServletContext();
String path = application.getRealPath("upload");
System.out.println(path);

获取项目的上下文路径 :
String contextPath = servletContext.getContextPath();
System.out.println(contextPath);


5.2.2 servletContext 域对象相关API
-
一些用于存储数据和传递数据的对象,传递数据不同的范围,我们称之为不同的域
- 不同的域对象代表不同的域,共享数据的范围也不同
-
ServletContext代表应用,所以ServletContext域也叫作应用域,- 是webapp中最大的域,可以在本应用内实现数据的共享和传递
-
webapp中的三大域对象,分别是应用域,会话域,请求域
三大域对象都具有的API如下:
| API | 功能解释 |
|---|---|
| void setAttribute(String key,Object value); | 向域中存储/修改数据 |
| Object getAttribute(String key); | 获得域中的数据 |
| void removeAttribute(String key); | 移除域中的数据 |
举例:
可以将servlet1 的数据 存入 域中
使用servlet2 or servlet3 … 在域中拿数据
// servlet1
ServletContext application = this.getServletContext();
application.setAttribute("ak","value");
// servlet2
ServletContext application = this.getServletContext();
String ak = (String) application.getAttribute("ak");
System.out.println(ak);

六、HttpServletRequest
- HttpServletRequest是一个接口,其父接口是ServletRequest
- HttpServletRequest是Tomcat将请求报文转换封装而来的对象,在Tomcat调用
service方法时传入 - HttpServletRequest代表客户端发来的请求,所有请求中的信息都可以通过该对象获得
常见API :
- 获取请求行信息相关(方式,请求的url,协议及版本)
| API | 功能解释 |
|---|---|
| StringBuffer getRequestURL(); | 获取客户端请求的url |
| String getRequestURI(); | 获取客户端请求项目中的具体资源 |
| int getServerPort(); | 获取客户端发送请求时的端口 |
| int getLocalPort(); | 获取本应用在所在容器的端口 |
| int getRemotePort(); | 获取客户端程序的端口 |
| String getScheme(); | 获取请求协议 |
| String getProtocol(); | 获取请求协议及版本号 |
| String getMethod(); | 获取请求方式 |
- 获得请求头信息相关
| API | 功能解释 |
|---|---|
| String getHeader(String headerName); | 根据头名称获取请求头 |
| Enumeration getHeaderNames(); | 获取所有的请求头名字 |
| String getContentType(); | 获取content-type请求头 |
- 获得请求参数相关
| API | 功能解释 |
|---|---|
| String getParameter(String parameterName); | 根据请求参数名获取请求单个参数值 |
| String[] getParameterValues(String parameterName); | 根据请求参数名获取请求多个参数值数组 |
| Enumeration getParameterNames(); | 获取所有请求参数名 |
| Map<String, String[]> getParameterMap(); | 获取所有请求参数的键值对集合 |
| BufferedReader getReader() throws IOException; | 获取读取请求体的字符输入流 |
| ServletInputStream getInputStream() throws IOException; | 获取读取请求体的字节输入流 |
| int getContentLength(); | 获得请求体长度的字节数 |



- 其他API
| API | 功能解释 |
|---|---|
| String getServletPath(); | 获取请求的Servlet的映射路径 |
| ServletContext getServletContext(); | 获取ServletContext对象 |
| Cookie[] getCookies(); | 获取请求中的所有cookie |
| HttpSession getSession(); | 获取Session对象 |
| void setCharacterEncoding(String encoding) ; | 设置请求体字符集 |
七、HttpServletResponse
- HttpServletResponse是一个接口,其父接口是ServletResponse
- HttpServletResponse是Tomcat预先创建的,在Tomcat调用service方法时传入
- HttpServletResponse代表对客户端的响应,该对象会被转换成响应的报文发送给客户端,通过该对象我们可以设置响应信息
常见API:
- 设置响应行相关
| API | 功能解释 |
|---|---|
| void setStatus(int code); | 设置响应状态码 |
- 设置响应头相关
| API | 功能解释 |
|---|---|
| void setHeader(String headerName, String headerValue); | 设置/修改响应头键值对 |
| void setContentType(String contentType); | 设置content-type响应头及响应字符集(设置MIME类型) |
- 设置响应体相关
| API | 功能解释 |
|---|---|
| PrintWriter getWriter() throws IOException; | 获得向响应体放入信息的字符输出流 |
| ServletOutputStream getOutputStream() throws IOException; | 获得向响应体放入信息的字节输出流 |
| void setContentLength(int length); | 设置响应体的字节长度,其实就是在设置content-length响应头 |
- 其他API
| API | 功能解释 |
|---|---|
| void sendError(int code, String message) throws IOException; | 向客户端响应错误信息的方法,需要指定响应码和响应信息 |
| void addCookie(Cookie cookie); | 向响应体中增加cookie |
| void setCharacterEncoding(String encoding); | 设置响应体字符集 |
MIME类型:
- MIME类型,可以理解为文档类型,用户表示传递的数据是属于什么类型的文档
- 浏览器可以根据MIME类型决定该用什么样的方式解析接收到的响应体数据
- 可以这样理解: 前后端交互数据时,告诉对方发给对方的是 html/css/js/图片/声音/视频/… …
- tomcat/conf/web.xml中配置了常见文件的拓展名和MIMIE类型的对应关系
- 常见的MIME类型举例如下:
| 文件拓展名 | MIME类型 |
|---|---|
| .html | text/html |
| .css | text/css |
| .js | application/javascript |
| .png /.jpeg/.jpg/… … | image/jpeg |
| .mp3/.mpe/.mpeg/ … … | audio/mpeg |
| .mp4 | video/mp4 |
| .m1v/.m1v/.m2v/.mpe/… … | video/mpeg |
八、请求转发和响应重定向
- 请求转发和响应重定向是web应用中间接访问项目资源的两种手段,也是Servlet控制页面跳转的两种手段
- 请求转发通过
HttpServletRequest实现 - 响应重定向通过
HttpServletResponse实现
总结:
实现页面跳转的情况,优先使用响应重定向
8.1 请求转发
举例来说:
servletA 接收 到从客户端发送来的请求 后将生成的请求、响应对象转发给servletB,由B来执行。
并且这个过程 客户端是不知情的,全由服务端处理,
也就是客户端只发送了一次请求,服务端也只产生一对请求响应对象。
客户端的地址栏也不会发生变化。
请求转发运行逻辑图 :

特点:
- 请求转发通过
HttpServletRequest对象实现(getRequestDispatcher方法) - 请求转发为服务器内部行为,客户端是不知道的。
- 客户端只发送一次请求,服务端也只产生一对
req和resp对象 - 客户端地址栏不会变化
- 请求转发时,请求、响应对象传递给下一个servlet,请求中的参数也继续向下传递。
- 目标资源可以是
servlet动态资源,也可以是HTML静态资源 - 还可以说WEB-INF下受保护的资源,但不可以是外部资源
代码举例:

ServletA 转发 给 ServletB :
以及 转发 给 HTML
@WebServlet("/sa")
public class ServletA extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("我是A");
// 获取请求参数
String num = req.getParameter("num");
System.out.println("num:" + num);
// 域中值:
req.setAttribute("keyA","A requestMsg");
//获取请求转发器
// 转发给servlet
//RequestDispatcher dispatcher = req.getRequestDispatcher("sb");
//转发给静态视图资源HTML
//RequestDispatcher dispatcher = req.getRequestDispatcher("bbb.html");
// 转发给WEB-INF下的资源 可以通过服务端访问 成功
RequestDispatcher dispatcher = req.getRequestDispatcher("WEB-INF/aaa.html");
//转发给外部资源 失败 不可以哦
//RequestDispatcher dispatcher = req.getRequestDispatcher("https://blog.youkuaiyun.com/GavinGroves");
// 做出转发动作
dispatcher.forward(req,resp);
}
}
sb接收转发 来的报文对象:
@WebServlet("/sb")
public class ServletB extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("我是B");
//域值
String keyA = (String)req.getAttribute("keyA");
System.out.println("域中:"+keyA);
//参数
String num = req.getParameter("num");
System.out.println(num);
}
}
地址栏不会变

参数也传递了

8.2 响应重定向
客户端发送请求 给 servletA ,A接收后发出响应 302 以及目标资源地址,
客户端接收响应,再次发送请求给 servletB, B 接收响应
过程中 地址栏发生变化,即参数和域中值是无法传递的
并且因为是客户端行为,无法重定向WEB-INF下的资源,但可以重定向外部链接。
特点:
- 重定向是由
HttpServletResponse对象实现(sendRedirect方法) - 响应重定向是在服务端的指示下 的客户端行为
- 客户端的地址栏会发生变化,客户端至少发送两次请求,即产生多次请求。
- 服务端因为客户端的多次请求,就会有多个
request对象,此时的请求参数就不能自动传递 - 目标资源可以是视图静态资源
- 但不能是WEB-INF下的资源
- 可以转发到外部资源
代码实现:
@WebServlet("/sc1")
public class ServletC extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("sc1");
//参数获取
String count = req.getParameter("count");
System.out.println("count:"+count);
//域中存入数据
req.setAttribute("keyC","requestMsg");
//响应重定向
resp.sendRedirect("sd2");
//resp.sendRedirect("bbb.html");
//WEB-INF的资源是不能通过浏览器直接访问的 失败
//resp.sendRedirect("WEB-INF/aaa.html");
// 重定向到外部资源 是可以的
//resp.sendRedirect("https://blog.youkuaiyun.com/GavinGroves");
}
}
@WebServlet("/sd2")
public class ServletD extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("sd2");
//获取请求参数
String count = req.getParameter("count");
System.out.println("sd2count : " +count);
//获取请求域 中 数据 为 null
String keyC =(String) req.getAttribute("keyC");
System.out.println(keyC);
//做出响应
resp.getWriter().write("sd2 response");
}
}

sc1 响应码302 重定向 给了sd2

地址变化


总结
客户端 向 服务端 -> 请求 响应 静态数据和动态数据 区别:

地址 对应 关系 图:

映射关系图:

乱码设置:
-Dfile.encoding=UTF-8


1386

被折叠的 条评论
为什么被折叠?



