一、ServletConfig
1. 基本介绍
ServletConfig类是 Servlet程序的配置信息的类
Servlet程序和ServletConfig对象都是由Tomcat负责创建
Servlet程序默认是第1次访问的时候创建,在Servlet程序创建时,就创建一个对应的ServletConfig对象
2. 能干什么
获取Servlet程序的servlet-name的值
获取初始化参数init-param
获取ServletContext对象
3. ServletConfig应用实例
完成以下功能:
编写DBServlet.java,在 web.xml配置连接mysql的用户名和密码,在DBServlet执行doGet()/doPost()时,可以获取到web.xml配置的用户名和密码
思路分析:
可以这样配置:
@WebServlet(urlPatterns = {"/db"}, initParams = {@WebInitParam(name="username",value = "root"),@WebInitParam(name="password", value="1234")})
public class DbServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ServletConfig servletConfig = getServletConfig();
String username = servletConfig.getInitParameter("username");
String pwd = servletConfig.getInitParameter("password");
System.out.println("username = " + username + "; password = " + pwd);
}
}
ServletConfig如果注掉super.init(config),则会报错
也就是说当DbServlet对象初始化时,会同时创建一个ServletConfig对象,如果DbServlet的init方法调用了super.init(config) ,也就是调用了父类GenericServlet没有初始化ServletConfig对象,则该对象没办法传给DbServlet,所以是空指针异常
public class DbServlet extends HttpServlet {
@Override
public void init(ServletConfig config) throws ServletException {
System.out.println("config1 = " + config);
super.init(config);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ServletConfig servletConfig = getServletConfig();
System.out.println("config2 = " + servletConfig);
String username = servletConfig.getInitParameter("username");
String pwd = servletConfig.getInitParameter("password");
System.out.println("username = " + username + "; password = " + pwd);
}
}
如果真的要重写init方法,则必须初始化ServletConfig对象,也就是调用super.init(config)
二、ServletContext
1. 为什么需要ServletContext
需求:如果我们希望统计某个web应用的所有Servlet被访问的次数,怎么办?
方案1:
方案2:
2. ServletContext基本介绍
ServletContext是一个接口,它表示Servlet上下文对象
一个web工程,只有一个ServletContext对象实例
ServletContext对象是在web工程启动的时候创建,在web工程停止的时销毁
ServletContext对象可以通过ServletConfig.getServletContext方法获得对ServletContext对象的引用,也可以通过this.getServletContext()来获得其对象的引用
由于一个web应用中的所有Servlet共享同一个ServletContext对象,因此Servlet对象之间可以通过ServletContext对象来实现多个Servlet间通讯。ServletContext对象通常也被称之为域对象
3. ServletContext可以做什么
- 获取web.xml中配置的上下文参数context-param(信息和整个 web 应用相关,而不是属于某个 Servlet)
- 获取当前的工程路径,格式:/工程路径 -> 比如 /servlet
- 获取工程部署后在服务器硬盘上的绝对路径
- 像Map一样存取数据,多个Servlet共享数据
代码实现:
web.xml配置:
<servlet>
<servlet-name>ServletContext_</servlet-name>
<servlet-class>com.olivier.servlet.ServletContext_</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ServletContext_</servlet-name>
<url-pattern>/servletContext_</url-pattern>
</servlet-mapping>
<!--配置整个网站的信息-->
<context-param>
<param-name>website</param-name>
<param-value>http://www.olivier.net</param-value>
</context-param>
<context-param>
<param-name>company</param-name>
<param-value>橄榄油</param-value>
</context-param>
通过如下代码可以获取参数:
public class ServletContext_ extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request,response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取web.xml的context-parameter
//1. 获取ServletContext对象
ServletContext servletContext = getServletContext();
//2. 获取Website
String website = servletContext.getInitParameter("website");
System.out.println("website = " + website);
String company = servletContext.getInitParameter("company");
System.out.println("company = " + company);
//3. 获取工程路径
String contextPath = servletContext.getContextPath();
System.out.println("contextPath = " + contextPath);
//4. 获取真实路径,这个"/"就表示发布后的根路径
//D:\Knowledge Files\code in here\oliver_servlet\out\artifacts\oliver_servlet_war_exploded
String realPath = servletContext.getRealPath("/");
System.out.println("realPath = " + realPath);
}
}
结果如下:
真实路径就是项目发布后的绝对路径,如下图所示:
4. 实际案例
需求:完成一个简单的网站访问次数计数器,
使用Chrome访问Servlet01, 每访问一次,就增加1访问次数,在后台输出,并将结果返回给浏览器显示
使用火狐访问Servlet02,每访问一次,就增加1访问次数,在后台输出,并将结果返回给浏览器显示
如下图:
写两个servlet由于code都一样,就写一个:
public class Servlet01 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取到ServletContext对象
ServletContext servletContext = getServletContext();
//获取visit_count属性
Object visit_count = servletContext.getAttribute("visit_count");
//判断visit_count是否为null
if (visit_count == null) {
servletContext.setAttribute("visit_count", 1);
visit_count = 1;
} else {
visit_count = Integer.parseInt(visit_count + "") + 1;
servletContext.setAttribute("visit_count", visit_count);
}
//输出显示
response.setContentType("text/html;charset=utf-8");
PrintWriter writer = response.getWriter();
writer.write("<h1>网站被访问了" + visit_count + "次</h1>");
writer.flush();
writer.close();
}
}
这样确实实现了记录网站被访问次数的功能
三、HttpServletRequest
1. HttpServletRequest介绍
HttpServletRequest对象代表客户端的请求
当客户端/浏览器通过HTTP协议访问服务器时,HTTP请求头中的所有信息都封装在这个对象中
通过这个对象的方法,可以获得客户端这些信息
2. HttpServletRequest类图
继承关系
ServletRequest
HttpServletRequest
3. HttpServletRequest常用方法
- getRequestURI():获取请求的资源路径:http://localhost:8080/servlet/loginServlet
- getRequestURL():获取请求的统一资源定位符(绝对路径)http://localhost:8080/servlet/loginServlet
- getRemoteHost():获取客户端的主机,getRemoteAddr()
- getHeader():获取请求头
- getParameter():获取请求的参数
- getParameterValues():获取请求的参数(多个值的时候使用),比如checkbox,返回的数组
- getMethod():获取请求的方式GET或POST
- setAttribute(key,value):设置域数据
- getAttribute(key):获取域数据
- getRequestDispatcher():获取请求转发对象,请求转发的核心对象
实例:
@WebServlet(name = "httpServletRequestMethods", urlPatterns = "/httpReq")
public class HttpServletRequestMethods extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//使用request获取表单提交的各种数据
System.out.println("请求的资源路径URI: " + request.getRequestURI());
System.out.println("请求的统一资源定位符(绝对路径) URL=\n: " + request.getRequestURL());
System.out.println("请求的客户端ip地址:" + request.getRemoteAddr());
System.out.println("http请求头HOST:" + request.getHeader("Host"));
System.out.println("http请求头HOST:" + request.getHeader("Host"));
System.out.println("该请求的发起地址是= " + request.getHeader("Referer"));
System.out.println("User-Agent= " + request.getHeader("User-Agent"));
//思考题:如发现某个ip在10s中,访问的次数超过100次,就封ip
//实现思路:1. 用一个集合concurrentHashmap[ip:访问次数],线程/定时扫描,做成处理
System.out.println("http 请求头 HOST= " + request.getHeader("Host"));
//说明,如果我们希望得到请求的头的相关信息,可以使用
request.getHeader("请求头字段");
System.out.println("该请求的发起地址是= " + request.getHeader("Referer"));
//请获取访问网站的浏览器是什么?
String userAgent = request.getHeader("User-Agent");
System.out.println("User-Agent= " + userAgent);
//取出fireFox, 也就是取出最后一个参数
String[] s = userAgent.split(" ");
System.out.println("浏览器=" + s[s.length - 1].split("\\/")[0]);
//课堂练习: 要求同学们取出Windows NT10.0和Win64
System.out.println("http 请求方式~= " + request.getMethod());
//获取和请求参数相关信息, 注意要求在返回数据前,获取参数
//1. 获取表单的数据[单个数据]
//username=tom&pwd=&hobby=hsp&hobby=spls
String username = request.getParameter("username");
String pwd = request.getParameter("pwd");
//2. 获取表单一组数据
String[] hobbies = request.getParameterValues("hobby");
System.out.println("username= " + username);
System.out.println("pwd= " + pwd);
//增强for循环的快捷键iter->回车即可 , 能使用快捷键,就使用快捷键
for (String hobby : hobbies) {
System.out.println("hobby=" + hobby);
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
4. HttpServletRequest的细节
- 获取doPost参数中文乱码解决方案,注 意setCharacterEncoding("utf-8")要写在request.getParameter()前
- 注意:如果通过PrintWriter writer,有返回数据给浏览器,建议将获取参数代码写在writer.print()之前,否则可能获取不到参数值(doPost)
- 处理http响应数据中文乱码问题
- 再次理解Http协议响应Content-Type含义,比如text/plain application/x-tar
5. 请求转发
为什么需要请求转发?目前我们学习的都是一次请求,对应一个Servlet
但是在实际开发中,往往业务比较复杂,需要在一次请求中,使用到多个Servlet完成
一个任务(Servlet链,流水作业)如图:
请求转发说明:
- 实现请求转发:请求转发指一个Web资源收到客户端请求后,通知服务器去调用另外一个Web资源进行处理
- HttpServletRequest对象(也叫Request对象)提供了一个getRequestDispatcher方法,该方法返回一个RequestDispatcher对象,调用这个对象的forward方法可以实现请求转发
- request对象同时也是一个域对象,开发人员通过request对象在实现转发时,把数据通过request对象带给其它Web资源处理
- setAttribute方法
- getAttribute方法
- removeAttribute方法
- getAttributeNames方法
请求转发原理示意图
四、HttpServletResponse
1. HttpServletResponse介绍
每次HTTP请求,Tomcat会创建一个HttpServletResponse对象传递给Servlet程序去使用
HttpServletRequest表示请求过来的信息,HttpServletResponse表示所有响应的信息,如果需要设置返回给客户端的信息,通过HttpServletResponse对象来进行设置即可
几个方法:
- setStatus
- setHeader
- getWriter:常用于回传字符串
- getOutputStream:常用于下载,处理二进制数据
向客户端返回数据一般使用getWriter和getOutputStream,两个流同时只能使用一个。 使用了字节流,就不能再使用字符流,反之亦然,否则就会报错
2. HttpServletResponse类图
3. HttpServletRequest的使用
案例:浏览器请求 , 返回 hello world httpServletRequest
public class HttpServletResponse_ extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html; charset=utf-8");
PrintWriter writer = response.getWriter();
writer.write("<h1>hello world httpServletResponse</h1>");
writer.write("<h1>你好世界</h1>");
writer.flush();
writer.close();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
注意:
-
setContentType会设置服务器和客户端都用utf-8字符集,还设置了响应头
-
setContentType要在获取流对象(getWriter)之前调用才有效
-
处理浏览器中文乱码问题:
- 方法1:
//设置服务器字符集为UTF-8 resp.setCharacterEncoding("UTF-8"); //通过响应头,设置浏览器也使用UTF-8字符集 resp.setHeader("Content-Type", "text/html; charset=UTF-8");
- 方法2:
//setContentType会设置服务器和客户端都使用utf-8字符集,还设置了响应头 resp.setContentType("text/html;charset=utf-8"); PrintWriter writer = resp.getWriter();
4. 请求重定向
请求重定:一个web资源收到客户端请求后,通知客户端去访问另外一个web资源,这称之为请求重定向
案例:演示请求重定向的使用当访问DownServlet下载文件,重定向到DownServletNew下载文件
DownlodServlet代码:
public class DownloadServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String contextPath = request.getContextPath();
System.out.println("contextPath = " + contextPath);
response.setContentType("text/html; charset=utf-8");
response.setStatus(302);
response.setHeader("Location",contextPath + "/downloadNew");
PrintWriter writer = response.getWriter();
writer.write("请求的资源要重定向到downloadNew");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
DownlodServletNew代码:
public class DownloadServletNew extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("DownServletNew 被调用...");
response.setContentType("application/x-tar;charset=utf-8");
PrintWriter writer = response.getWriter();
writer.print("下载天龙八部..");
writer.flush();
writer.close();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
5. 请求重定向的细节
最佳应用场景:网站迁移,比如原域名是www.olivier.com 迁移到 www.oliviernew.co ,但是百度抓取的还是原来网址
浏览器地址会发生变化,本质是两次HTTP请求
不能共享Request域中的数据,本质是两次HTTP请求,会生成两个HttpServletRequest对象
不能重定向到/WEB-INF下的资源
可以重定向到Web工程以外的资源,比如到www.baidu.com
重定向有两种方式,推荐使用第1种:
- 第一种:
- 第二种: