本节目录
一个Servlet就是运行在web服务器中的Java小程序, 它接收并且响应来自客户端的请求, 通过http的形式.
作用:
- 接收请求
- 处理请求
- 完成响应
由Servlet容器(Tomcat)创建
HelloWorld!
- 创建自己的类实现servlet接口
- 在service方法中写代码
- 在web.xml里面配置
<!-- servlet标签配置servlet的类信息, 告诉服务器, 我有这个servlet -->
<servlet>
<servlet-name>MyFirstServlet</servlet-name>
<servlet-class>com.wang.servlet.MyFirstServlet</servlet-class>
</servlet>
<!-- 映射信息 -->
<servlet-mapping>
<!-- 刚才配置的servlet-name -->
<servlet-name>MyFirstServlet</servlet-name>
<!-- servlet用来处理哪个请求 -->
<url-pattern>/helloworld</url-pattern>
</servlet-mapping>
类信息:
-
servlet-class: 配置全类名, 服务器通过全类名寻找servlet
-
servlet-name: 给自己看的
映射信息:
- servlet用来处理哪一个信息
- url-pattern: 告诉服务器servlet用来处理哪个请求
没有/hello静态资源, 就扫描web.xml里面 发现url-pattern中有, 就是告诉服务器, 这个动态资源使用servlet-class中的类处理的, 注意看localhost:8080/08.servlet/helloworld, 后没有后缀
在地址栏输入(或跳转)http://localhost:8080/08.servlet/helloworld访问时:
localhost:8080
: tomcat服务器接收请求08.servlet/helloworld
是否有静态资源与之对应- 没有就去web.xml里面查找, 如果在url-pattern查找到了就是动态资源, 就会有上面的servalet-name, 再去上面的servlet中找全类名
- 找到了类, 就调用service方法
(实际上是先在web.xml中查找动态资源, 再去搜索静态页面, 上述流程是为了帮助理解)
Servlet的生命周期
生命周期: 出生到死亡
没有手动创建对象; 而是请求动态资源就hi启动这段servlet并调用其中的service方法, 但我们没有创建对象和调用方法
Servlet是跑在服务器上的, Tomcat服务器就是Servlet容器, 是容器(也就是Tomcat服务器)创建了对象调用了方法.
-
第一次请求相关url:
- 先调用构造器创建Servlet对象
- 初始化servlet方法
- 调用service方法
-
以后访问, 只调用service方法;
-
当项目从服务器上卸载时, 服务器会调用destroy方法(包括移除项目和停止服务器)
整个web运行期间, 只创建了一个servlet对象(单实例), 但是是多线程运行的; 一般不会再servlet中写共享变量, 可能会存下线程安全问题
从init到ServletConfig
餐馆, 一个Servlet就像一个服务员, 有一个自己唯一的config配置, 例如别名和一些属性; 但所有这个web项目下的Servlet服务员, 都有一个共同的代表了web项目信息(类似于餐厅老板)的ServletContext
- ServletConfig是什么?
是封装了当前servlet配置信息的对象, 一个ServletConfig只对应一个servlet, 即this
:ConfigServlet - 有什么功能?
- 获取servletname的别名
- 获取初始化参数
- 获取servletContext对象, 代表当前web项目信息, 一个web项目只有一个context
package com.wang.servlet;
public class ConfigServlet implements Servlet {
private ServletConfig config = null;
@Override
// init方法由服务器来调用, config参数由服务器传入
public void init(ServletConfig config) throws ServletException {
this.config = config;
String servletname = config.getServletName();
System.out.println(servletname);
String servletinitpara = config.getInitParameter("username");
System.out.println("初始化参数:"+ servletinitpara);
ServletContext context = config.getServletContext();
System.out.println(context);
}
//可以直接returnconfig属性
@Override
public ServletConfig getServletConfig() {
// TODO Auto-generated method stub
return this.config;
}
@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
// TODO Auto-generated method stub
}
@Override
public String getServletInfo() {
// TODO Auto-generated method stub
return "ConfigServlet";
}
//服务器停止和项目卸载时调用
@Override
public void destroy() {
// TODO Auto-generated method stub
}
}
ServletContext
获取到ServletContext:
-
首先需要获取ServletConfig, 初始化后不会为空
-
用ServletConfig获取ServletContext
ServletContext是什么?
一个web应用的对应一个ServletContext, 代表了整个web项目
ServletContext的功能是什么?
-
getInitParameter(param): 获取web项目的配置信息, 初始化参数(
<context-param>
) -
getContextPath():获取web项目路径
-
getRealPath(资源):获取资源的真实路径
-
虚拟路径: 网络访问使用(地址栏里的), 都对应了一个实际资源;
- 动态资源: 启动一段程序代码
- 静态资源: 打开一个文件
-
真实路径: 文件在磁盘中的存储路径
-
由虚拟路径找到真实路径的方式:
-
-
作为最大的域对象共享数据
package com.wang.servlet;
public class ContextServlet implements Servlet {
private ServletConfig config;
@Override
public void init(ServletConfig config) throws ServletException {
// TODO Auto-generated method stub
this.config = config;
}
@Override
public ServletConfig getServletConfig() {
// TODO Auto-generated method stub
return this.config;
}
@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
// TODO Auto-generated method stub
ServletConfig config = this.getServletConfig();
ServletContext context = config.getServletContext();
//1. context-param:获取配置信息
String name = context.getInitParameter("user");
System.out.println(name);//root
//2. 获取项目路径
String path = context.getContextPath();
System.out.println(path);///08.servlet
//3. 获取资源路径
String realPath = context.getRealPath("/index.html");
System.out.println(realPath); //D:\Java_learning\tomcatMirror\wtpwebapps\08.servlet\index.html
}
}
HttpServlet
为处理http请求定制的
- post请求: 表单提交指定method=“post”
- 回车/超链接/img src =“…”都是get请求
httpservletrequest: 收到的浏览器端的请求信息, 封装的浏览器请求信息对象
httpservletresponse: 发送给浏览器的响应对象, 封装我们的相应信息
HttpServletResponse
-
可以给浏览器响应字符串:
PrintWriter writer = response.getWriter(); writer.write("Hello, HttpServlet");
-
可以重定向到一个页面或其他资源
重定向: 服务器告诉浏览器去请求别的资源
response.sendRedirect("资源");
例如一个index页面:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Insert title here</title> </head> <body> <a href="http://localhost:8080/08.servlet/helloworld">链接</a> <br /> <form action="MyEclipseServlet" method="post"> 用户名: <input type="text"> <input type="submit" value="提交"> </form> </body> </html>
form表单提交地址为MyEclipseServlet, 方式为post, 写好响应的动态资源的代码, 并在xml中进行配置
package com.wang.servlet; /** * Servlet implementation class MyEclipseServlet */ public class MyEclipseServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.sendRedirect("success.html"); } } `` http://localhost:8080/08.servlet/MyEclipseServlet, http://localhost:8080/08.servlet/success.html 都在当前项目下, 和index.html都是同一目录下, 所以使用绝对路径, 即在 MyEclipseServlet和 success.html两个资源名称前都不加斜杠
HttpServletRequest
-
获取请求数据: get放在url后; post放在请求体中
getParameter(“标签的name属性”)
: 获取单个值getParameterValue(“标签的name属性”)
: 获取多个值例如复选框等
-
获取请求头
request.getHeader(“User-Agent”)
-
转发一个页面或资源, 例如上文中的index ➡ 登录成功页面, 可以使用:
- response重定向
- request转发
request转发步骤:
- 先获取请求转发器
request.getRequestDispatcher("资源地址")
- 将请求转发出去
dispatcher.forward(req resp)
注意: 转发和重定向, 都是对请求的响应, 因此只能在一次请求中只能使用两者中的一个. 否则就是对请求进行多次响应
- 作为域对象共享数据(ServletContext–> application, request)
转发与重定向
重定向: 告诉浏览器重新请求资源, 同样可以请求动态资源
转发: 服务器处理完成后转交到另一个资源
- 转发的资源是静态资源: 服务器会给浏览器返回这个资源
- 转发的资源是动态资源, 即转交给下一个servlet(可以形成处理链, 直到最后一个servlet)
两种跳转的区别:
请求转发 | 重定向 | |
---|---|---|
地址栏 | 动态资源地址 | 转发后的资源地址 |
请求 | 同一次请求 | 两次请求 |
API | Request | Response |
响应状态码 | 200 | 第一次为302, 第二次为200 |
位置 | 服务器内部完成 | 浏览器完成 |
WEB-INFO | 可以访问 | 不能访问 |
目标资源 | 必须是当前web应用中的资源 | 不局限于当前web应用 |
共享请求数据域 | 可以共享 | 不能 |
图解
请求转发:
重定向:
乱码问题–编码解码方式不统一
响应给浏览器的数据是乱码
常见问题: 在正常显示中文的html页面中写入中文乱码writer.write(“中文字符”), 显示的是????
需要在还没有产生流的时候(在第一次操作response之前)告诉浏览器编码格式以及传输数据的内容类型(MIME)
方法有如下三种:
-
reponse.setContentType("text/html"); reponse.setCharsetEncoding("utf-8");
-
reponse.addHeader("Content-Type","text/html/charset=utf-8")
-
reponse.setContentType("text/html/charset=utf-8")
响应给浏览器的数据是乱码, 原因是浏览器不知道数据的类型以及编码格式. 解决的方式是告诉浏览器了内容类型以及编码格式, 万变不离其宗, 需要设置的都是响应头中的ContentType: text/html/charset=utf-8
表单中的乱码
表单中输入中文, 提交过来是乱码
post乱码原因
浏览器将数据提交, 但服务器不知道浏览器的编码规则.
解决方法: 让服务器直到编码规则, 重新设置请求的编码格式, 同样需要在开始使用之前就设置, 这个对请求体有作用.
request.setCharacterEncoding(“utf-8”);
get乱码原因
数据传输流程: 浏览器将表单数据等请求编码好, 发过来给服务器, 服务器解码封装成request对象.
原因: get请求传来的数据在url中, 也被浏览器编码.过来的时候早已被服务器按照默认的解码方式解码然后封装为request对象.
解决办法: 需要在server.xml在8080端口那里直接设置URIEncoding="utf-8"
项目路径
绝对路径: 以/开始, 代表的是当前项目的根目录08.servlet
注意: 转发与重定向的方法里面对根目录的区别
- 转发使用绝对路径来写, 从当前项目的根目录08.servlet开始
http://localhost:8080/08.servlet
, 例如/index.html
- 重定向到绝对路径: 容器的根目录
http://localhost:8080
, 写绝对路径的时候需要带上项目名/08.servet/index.html
原因: 转发是服务器内部转发, 在项目内部, 所以根目录定位在项目上; 而重定向, 请求由浏览器来发, 所以根目录定位在容器的根目录上
web.xml中服务器解析的时候对应的是项目的根目录, 服务器解析哪个项目对应的就是项目的当前根
动态获取项目路径(项目根目录)两种方法:
ServletContext context = getServletContext();
String path = context.getContextPath();
//这样写是一样的
String path2 = request.getContextPath()
//绝对路径再由项目路径和资源路径拼串产生即可
path+"/pages/a.html"