动态资源与静态资源:
静态资源 :
- 无需在程序运行时通过代码运行生成的资源,在程序运行之前就写好的资源.
- 例如:html css js img ,音频文件和视频文件
动态资源
- 需要在程序运行时通过代码运行生成的资源,在程序运行之前无法确定的数据,
- 运行时动态生成,例如Servlet,Thymeleaf … …
- 动态资源指的不是视图上的动画效果或者是简单的人机交互效果
举例
- 前端页面 直接验证 用户名密码格式是否准确 : 静态资源
- 需要验证用户名是否已经存在 : 动态资源
Servlet
(server applet) 是运行在服务端(tomcat)的Java小程序,
是sun公司提供一套定义动态资源规范;
从代码层面上来讲Servlet就是一个接口
用来接收、处理客户端请求、响应给浏览器的动态资源。
Servlet
是能处理客户端请求并做出响应的一套技术标准。
Servlet
是运行在服务端的,所以 Servlet 必须在WEB
项目中开发且在Tomcat
这样的服务容器中运行
Servlet 流程:
校验注册时,用户名是否被占用:
通过客户端向一个Servlet
发送请求,携带username,如果用户名是’dougwake’,则向客户端响应 NO,如果是其他,响应YES
<body>
<h1>hello </h1>
<!-- get or post -->
<form method="get" action="userServlet">
用户名: <input type="text" name="username"/>
<input type="submit" value="校验"/>
</form>
</body>
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 告诉客户端响应的数据类型是什么,就用什么类型解析。
<!--
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>
用了注解 就不用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容器创建的,生命周期方法都是由容器(目前我们使用的是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中,必须要对处理请求的方法进行重写
获取 xml 相应的配置文件信息
Servlet
提供初始配置参数的一种对象,每个Servlet都有自己独立唯一的ServletConfig对象Servlet
实例化一个ServletConfig对象
,并通过Servlet生命周期的init
方法传入给Servlet作为属性方法名 | 作用 |
---|---|
getServletName() | 获取<servlet-name>HelloServlet</servlet-name>定义的Servlet名称 |
getServletContext() | 获取ServletContext对象 |
getInitParameter() | 获取配置Servlet时设置的『初始化参数』,根据名字获取值 |
getInitParameterNames() | 获取所有初始化参数名组成的Enumeration对象 |
上下文对象
,或者叫应用域对象(后面统一讲解域对象)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>
获取资源的真实路径 :
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);
一些用于存储数据和传递数据的对象,传递数据不同的范围,我们称之为不同的域
ServletContext
代表应用,所以ServletContext域也叫作应用域,
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);
service
方法时传入常见API :
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 | 功能解释 |
---|---|
String getServletPath(); | 获取请求的Servlet的映射路径 |
ServletContext getServletContext(); | 获取ServletContext对象 |
Cookie[] getCookies(); | 获取请求中的所有cookie |
HttpSession getSession(); | 获取Session对象 |
void setCharacterEncoding(String encoding) ; | 设置请求体字符集 |
常见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 | 功能解释 |
---|---|
void sendError(int code, String message) throws IOException; | 向客户端响应错误信息的方法,需要指定响应码和响应信息 |
void addCookie(Cookie cookie); | 向响应体中增加cookie |
void setCharacterEncoding(String encoding); | 设置响应体字符集 |
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 |
HttpServletRequest
实现HttpServletResponse
实现总结:
实现页面跳转的情况,优先使用响应重定向
举例来说:
servletA 接收 到从客户端发送来的请求 后将生成的请求、响应对象转发给servletB,由B来执行。
并且这个过程 客户端是不知情的,全由服务端处理,
也就是客户端只发送了一次请求,服务端也只产生一对请求响应对象。
客户端的地址栏也不会发生变化。
请求转发运行逻辑图 :
特点:
HttpServletRequest
对象实现(getRequestDispatcher
方法)req
和resp
对象servlet
动态资源,也可以是HTML
静态资源代码举例:
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.csdn.net/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);
}
}
地址栏不会变
参数也传递了
客户端发送请求 给 servletA ,A接收后发出响应 302 以及目标资源地址,
客户端接收响应,再次发送请求给 servletB, B 接收响应
过程中 地址栏发生变化,即参数和域中值是无法传递的
并且因为是客户端行为,无法重定向WEB-INF下的资源,但可以重定向外部链接。
特点:
HttpServletResponse
对象实现(sendRedirect
方法)request对象
,此时的请求参数就不能自动传递代码实现:
@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.csdn.net/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