韩老师学生
- ServletConfig
- ServletContext
- 网站计数器
- HttpServletRequest
- 细节1
- 细节2
- 细节3
- Dispathcer
- 请求转发应用实例
- 请求转发细节和注意事项
- 习题
- HttpServletResponse
- 请求重定向
- 请求重定向注意事项
- 动态获取到application context
- 练习题
ServletConfig
●ServletConfig基本介绍
1.ServletConfig
类是为Servlet
程序配置信息的类
2.Servlet
程序和ServletConfig
对象都是由Tomcat
负责创建的
3.Servlet
程序默认是第1
次访问的时候创建.ServletConfig
在Servlet
程序创建时, 就创建一个对应的ServletConfig
对象
●ServletConfig类能干什么
1.获取Servlet
程序的servlet-name
的值
2.获取初始化参数init-param
3.获取ServletContext
对象
●ServletConfig应用实例
需求: 编写DBServlet.java
, 实现如下功能
1.在web.xml
中编写连接mysql
的用户名和密码
2.在DBServlet
执行doGet() / doPost()
方法时, 均可以获取到web.xml
配置的用户名和密码
3.示意图(思路分析)
1.web.xml
配置DBServlet
<servlet><servlet-name>DBServlet</servlet-name><servlet-class>com.zzw.servlet.DBServlet</servlet-class><!--配置信息, 而不是硬编码到程序--><init-param><param-name>username</param-name><param-value>zzw</param-value></init-param><init-param><param-name>pwd</param-name><param-value>123456</param-value></init-param>
</servlet>
<servlet-mapping><servlet-name>DBServlet</servlet-name><url-pattern>/dbServlet</url-pattern>
</servlet-mapping>
2.在com.zzw.servlet
下新建DBServlet
public class DBServlet extends HttpServlet {/*** 老师梳理ServletConfig config 使用流程* 1.当DBServlet对象初始化时, tomcat会同时创建一个 ServletConfig对象* 2.这时如果DBServlet init() 方法中你调用super.init(config)* 3.调用 父类 GenericServlet* public void init(ServletConfig config) throws ServletException {* this.config = config;* this.init();* }* 4.这时就会把 Tomcat创建的 ServletConfig对象赋给 GenericServlet的属性 config* 5.如果注销super.init(config), 那么在doGet,doPost和其它方法中将无法通过 getServletConfig() 方法获取ServletConfig* 6.因此如果你要重写init()方法, 还想在其它方法通过 getServletConfig() 方法获取ServletConfig* , 则一定要记住 在init()方法中 调用 super.init(config)* @param config* @throws ServletException*/@Overridepublic void init(ServletConfig config) throws ServletException {System.out.println("init() config=" + config);super.init(config);}@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {doPost(request, response);}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//在DBServlet执行doGet() / doPost()方法时, 均可以获取到web.xml配置的用户名和密码//OOP程序员->现有的方法或对象来实现//DBServlet的父类有GenericServlet有getServletConfig()/*** 解读* 1. getServletConfig() 方法是 GenericServlet* 2. 返回的 servletConfig对象是 GenericServlet private transient ServletConfig config;* 3. 当一个属性被transient修饰, 表示该属性不会被串行化(有些重要信息, 不希望保存到文件)*/ServletConfig servletConfig = this.getServletConfig();System.out.println("doPost() servletConfig=" + servletConfig);String username = servletConfig.getInitParameter("username");String pwd = servletConfig.getInitParameter("pwd");System.out.println("初始化参数username=" + username);System.out.println("初始化参数pwd=" + pwd);}
}
ServletContext
●为什么需要ServletContext
1.先看一个需求: 如果我们希望统计某个web
应用所有的Servlet
被访问的次数, 怎么办?
2.方案1-DB
2.方案2-ServletContext
●ServletContext基本介绍
ServletContext
是一个接口, 他表示Servlet
上下文对象- 一个
web
工程, 只有一个ServletContext
对象实例 ServletContext
对象在web
工程启动的时候创建, 在web
工程停止的时候销毁ServletContext
对象可以通过this.getServletConfig().getServletContext()
方法获得ServletContext
对象的引用, 也可以通过this.getServletContext()
来获得其对象的引用- 由于一个
WEB
应用中的所有Servlet
共享同一个ServletContext
对象, 因此Servlet
对象之间可以通过ServletContext
对象来实现通信.ServletContext
对象通常也称之为域对象.
●ServletContext可以做什么
1.获取web.xml
中配置的上下文参数context-param
[这个信息和整个web
应用相关, 而不是属于某个Servlet
]
2.获取当前的工程路径, 格式: /工程路径 ⇒ 比如 /servlet
3.获取工程部署后在服务器硬盘上的绝对路径 (比如: D:\idea_project\zzw_javaweb\servlet\out\artifacts\servlet_war_exploded
)
4.像Map
一样存取数据, 多个Servlet
共享数据
●应用实例1-获取工程相关信息
●需求如下
1.获取web.xml
中配置的上下文参数 context-param
2.获取当前的工程路径, 格式: /工程路径
3.获取工程部署后在服务器硬盘上的绝对路径
●代码实现
1.修改web.xml
, 增加相关配置
<!--配置整个网站的信息-->
<context-param><param-name>website</param-name><param-value>http://www.baidu.com</param-value>
</context-param>
<context-param><param-name>company</param-name><param-value>百度</param-value>
</context-param>
2.web.xml
配置ServletContext_
<!--配置ServletContext_-->
<servlet><servlet-name>ServletContext_</servlet-name><servlet-class>com.zzw.servlet.servletcontext.ServletContext_</servlet-class>
</servlet>
<servlet-mapping><servlet-name>ServletContext_</servlet-name><url-pattern>/servletContext_</url-pattern>
</servlet-mapping>
3.com.zzw.servlet.servletcontext
下新建ServletContext_
public class ServletContext_ extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {doPost(request, response);}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//获取web.xml的context-parameter//1.获取ServletContext对象ServletContext servletContext =this.getServletConfig().getServletContext();//2.获取websiteString website = servletContext.getInitParameter("website");String company = servletContext.getInitParameter("company");//3.获取项目的工程路径String contextPath = servletContext.getContextPath();//4.获取项目发布后, 真正的工作路径// / 表示我们的项目(发布后)的 根路径// D:\idea_project\zzw_javaweb\servlet3\out\artifacts\servlet3_war_explodedString realPath = servletContext.getRealPath("/");System.out.println("项目发布后的绝对路径 realPath=" + realPath);System.out.println("contextPath=" + contextPath);System.out.println("website=" + website);System.out.println("company=" + company);}
}
网站计数器
●需求分析/图解
1.需求: 完成一个简单的网站访问次数计数器
2.使用Chrome
访问Servlet01
, 每访问一次, 就增加1
访问次数, 在后台输出, 并将结果返回给浏览器显示.
3.使用火狐访问Servlet02
, 每访问一次, 就增加1
访问次数, 在后台输出, 并将结果返回给浏览器显示.
●代码实现
1.在web.xml
配置OrderServlet, PayServlet
<servlet><servlet-name>OrderServlet</servlet-name><servlet-class>com.zzw.servlet.servletcontext.OrderServlet</servlet-class>
</servlet>
<servlet-mapping><servlet-name>OrderServlet</servlet-name><url-pattern>/orderServlet</url-pattern>
</servlet-mapping>
<servlet><servlet-name>PayServlet</servlet-name><servlet-class>com.zzw.servlet.servletcontext.PayServlet</servlet-class>
</servlet>
<servlet-mapping><servlet-name>PayServlet</servlet-name><url-pattern>/payServlet</url-pattern>
</servlet-mapping>
2.com.zzw.servlet.servletcontext
创建OrderServlet
public class OrderServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {doPost(request, response);}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//获取到ServletContext对象ServletContext servletContext =this.getServletConfig().getServletContext();//System.out.println("OrderServlet servletContext=" + servletContext + " 运行类型=" + servletContext.getClass());从servletContext获取 visit_count 属性//Object visit_count = servletContext.getAttribute("visit_count");判断visit_count是否为null//if (visit_count == null) {//说明是第1次访问// servletContext.setAttribute("visit_count", 1);// visit_count = 1;//} else {//是第二次或以后// //取出visit_count属性的值+1// visit_count = Integer.parseInt(visit_count + "") + 1;// //放回到servletContext// servletContext.setAttribute("visit_count", visit_count);//}Integer visit_count = WebUtils.visitCount(servletContext);//输出显示response.setContentType("text/html;charset=utf-8");response.getWriter().write("<h1>新网站被访问的次数是</h1>" + visit_count + "次</h1>");}
}
3.com.zzw.servlet.servletcontext
创建PayServlet
public class PayServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {doPost(request, response);}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//获取到ServletContext对象ServletContext servletContext =this.getServletConfig().getServletContext();//System.out.println("PayServlet servletContext=" + servletContext + " 运行类型=" + servletContext.getClass());从servletContext获取 visit_count 属性//Object visit_count = servletContext.getAttribute("visit_count");判断visit_count是否为null//if (visit_count == null) {//说明是第1次访问// servletContext.setAttribute("visit_count", 1);// visit_count = 1;//} else {//是第二次或以后// //取出visit_count属性的值+1// visit_count = Integer.parseInt(visit_count + "") + 1;// //放回到servletContext// servletContext.setAttribute("visit_count", visit_count);//}Integer visit_count = WebUtils.visitCount(servletContext);//输出显示response.setContentType("text/html;charset=utf-8");response.getWriter().write("<h1>新网站被访问的次数是</h1>" + visit_count + "次</h1>");}
}
4.在com.zzw.servlet.servletcontext
新建WebUtils
public class WebUtils {//这个方法就是对访问的次数累积, 同时返回次数public static Integer visitCount(ServletContext servletContext) {//从servletContext获取 visit_count 属性Object visit_count = servletContext.getAttribute("visit_count");//判断visit_count是否为nullif (visit_count == null) {//说明是第1次访问servletContext.setAttribute("visit_count", 1);visit_count = 1;} else {//是第二次或以后//取出visit_count属性的值+1visit_count = Integer.parseInt(visit_count + "") + 1;//放回到servletContextservletContext.setAttribute("visit_count", visit_count);}return Integer.parseInt(visit_count + "");}
}
5.测试
HttpServletRequest
- HttpServletRequest对象代表客户端的请求
- 当客户端/浏览器通过HTTP协议访问服务器时, HTTP请求头中所有的信息都封装在这个对象中
- 通过这个对象的方法, 可以获取客户端信息
- 常用方法
- getRequestURI() 获取请求的资源路径 http://localhost:8080/servlet/login.html
- getRequestURL() 获取请求的统一资源定位符(绝对路径) http://localhost:8080/servlet/login.html
- getHeader() 获取请求头
- getParameter() 获取请求的参数
- getParameterValues() 获取请求的参数(多个值时使用) 比如checkbox, 或返回的数组
- getRemoteHost() 获取客户端的主机
- getRemoteAddr() 获取客户端的ip
- getMethod() 获取请求的方式(GET或POST)
- setAttribute(key, value) 设置域数据
- getRequestDispatcher() 获取请求转发对象(核心对象)
public class HttpServletRequestMethods extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {doPost(request, response);}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {/********************************* 获取http请求头相关信息 *********************************/System.out.println("请求头信息");System.out.println("资源路径URI(请求中的)=" + request.getRequestURI());System.out.println("统一资源定位符(绝对路径)URL=" + request.getRequestURL());System.out.println("客户端ip地址=" + request.getRemoteAddr());//获取http请求头的信息: 可以使用request.getHeader("请求头字段")// 可以指定 Accept, Accept-Encoding, Accept-Language, Connection, Cookie, Host, RefererString accept = request.getHeader("Accept");System.out.println("http请求头中的Accept信息=" + accept);/********************************* 获取表单提交的数据 *********************************///1.获取单个表单数据// 解决接收参数的中文乱码问题, 不能写在request.getParameter()后面request.setCharacterEncoding("utf-8");//浏览器按照urlEncode编码方式提交数据, 后端要用utf-8来接收String username = request.getParameter("username");String password = request.getParameter("password");System.out.println("username=" + username);System.out.println("password=" + password);//前端不填传给后端则是一个空字符串"", 不是null//2.获取一组表单数据 复选框, 下拉框String[] favorites = request.getParameterValues("favorite");if (favorites != null) {//复选框前端不选则后端返回一个nullfor (String favorite : favorites) {System.out.println(favorite);}}}
}
细节1
- text, date, password, email类型的输入框和下拉框如果不填值会将空字符串传给后端:username=&password=&date=, 后端用request.getParameter()接收这些值便是空字符串
- 单选框, 复选框如果不选(不包括下拉框), 则不向后台传值, 即username=&password=&date=这里根本就没有单选框和复选框的参数, 后台通过request.getParameter()或request.getParameterValues()获取实际上是获取了一个不存在的参数. 如果单选框, request.getParameter()返回一个字符串, 这个字符串为null, 如果是复选框, request.getParameterValues()返回字符串数组, 那么这个字符串数组为null, 如果对这个字符串数组遍历会报错, 因为null.length会报错
细节2
- 浏览器按照urlEncode编码方式提交数据, 后端要用utf-8来接收: request.setCharacterEncoding(“utf-8”); 且这句话要写在request.getParameter()前面
- 后台将接收的数据返回给页面时, 要设置响应头的Content-Type参数为text/html; charset=utf-8, 因为数据中包含中文, 并且是文本类型
//本质实在http响应头加上Content-Type: text/html;charset=utf-8response.setContentType("text/html; charset=utf-8");PrintWriter writer = response.getWriter();writer.print("<h1>提交的用户名=" + username + "</h1>");writer.flush();writer.close();
细节3
再次理解Http协议Content-Type的含义, text/html 表示返回的数据类型, 浏览器会根据这个类型来解析数据
//本质实在http响应头加上Content-Type: text/html;charset=utf-8// text/plain 表示返回的数据,请浏览器使用文本方式解析// application/x-tar 表示返回的是一个文件,浏览器就会以下载文件的方式处理response.setContentType("application/x-tar; charset=utf-8");PrintWriter writer = response.getWriter();writer.print("<h1>提交的用户名=" + username + "</h1>");writer.flush();writer.close();
Dispathcer
- 实现请求转发: 请求转发指一个web资源收到客户端请求后, 通知服务器(比如Tomcat)去调用另外一个web资源进行处理
- HttpServletRequest对象(也叫Request对象)提供了一个getRequestDispather方法, 该方法返回一个RequestDispacher对象, 调用这个对象的forward方法可以实现请求转发
- request对象同时也是一个域对象, 开发人员通过request对象在实现转发时, 把数据通过request对象带给其它web资源
setAttribute方法
getAttribute方法
removeAttribute方法
getAttributeNames方法
请求转发应用实例
前提: 新建两个Servlet, 并做好配置
public class CheckServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {doPost(request, response);}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {System.out.println("CheckServlet doPost 输出");//根据用户名来确定该用户是什么身份request.setCharacterEncoding("utf-8");String username = request.getParameter("username");//注意: 如果是同一个request对象(请求转发), 那么可以在不同的Servlet中使用getParameter取出同一个参数if ("猫".equals(username)) {//分配管理员权限request.setAttribute("role", "管理员");} else {request.setAttribute("role", "普通用户");}//获取分发器//解读一下:1./managerServlet写的是 要转发的servlet的url// 2./ 会被解析成 /servletRequestDispatcher requestDispatcher = request.getRequestDispatcher("/managerServlet");// 3.forward(request, response) 会把当前Servlet的request,response对象传给下一个Servlet使用requestDispatcher.forward(request, response);}
}
public class ManagerServlet extends HttpServlet {private int count;@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {doPost(request, response);}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {request.setCharacterEncoding("utf-8");String username = request.getParameter("username");String role = (String) request.getAttribute("role");//输出response.setContentType("text/html; charset=utf-8");PrintWriter writer = response.getWriter();writer.println("<h1>用户名 " + username + "</h1>");writer.print("<h1>角色=" + role + "</h1>");writer.flush();writer.close();}
}
请求转发细节和注意事项
- 浏览器地址栏不会发生变化(地址会停留在第1个servlet的url)
- 在同一次HTTP请求中, 进行多次转发, 仍然是一次HTTP请求
- 在同一次HTTP请求中, 进行多次转发, 多个Servlet可以共享request域/对象的数据(因为始终是同一个request对象)
- 可以转发到WEB-INF目录下
- 不能访问当前WEB工程外的资源
- 转发和重定向的后续代码会被执行, 根据情况可添加return 👉参考
习题
编写一个Servlet获取请求参数中的操作系统和位数
public class GetInfoServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {doPost(request, response);}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//谷歌浏览器:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36String userAgent = request.getHeader("User-Agent");String regStr = "\\(([\\w\\ \\.;]+)\\)";Pattern pattern = Pattern.compile(regStr);Matcher matcher = pattern.matcher(userAgent);matcher.find();//group(0) = (Windows NT 10.0; Win64; x64)//group(1) = Windows NT 10.0; Win64; x64String[] split = matcher.group(1).split(";");System.out.println("操作系统=" + split[0]);System.out.println("操作系统位数=" + split[1].trim());}
}
HttpServletResponse
- 基本介绍
- 每次HTTP请求, Tomcat会创建一个HttpServletResponse对象传递给Servlet使用
- 乱码问题
- 处理乱码问题方案1
- 处理乱码问题方案2
请求重定向
- 请求重定向是指: 一个web资源收到客户端请求后, 通知客户端去访问另外一个web资源, 这称之为重定向
- 请求重定向分析图
测试代码
public class DownServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {doPost(request, response);}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {System.out.println("DownServlet 自己的业务");//1.sendRedirect 本质会返回一个 302状态码 和 Location: /servlet/downServletNew//2.因此 302状态码 和 /servlet/downServletNew是浏览器解析的//3.浏览器会将 /servlet/downServletNew 解析成http://localhost:8080/servlet/downServletNewresponse.sendRedirect("/servlet/downServletNew");}
}
public class DownServletNew extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {doPost(request, response);}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {System.out.println("DownServletNew doPost");response.setContentType("application/x-tar; charset=utf-8");PrintWriter writer = response.getWriter();writer.println("<h1>下载完成</h1>");writer.flush();writer.close();}
}
<body>
<h2>下载文件</h2>
<a href="http://localhost:8888/servlet/downServlet">下载<<三体>>小说</a>
</body>
测试结果
请求重定向注意事项
- 最佳应用场景: 网站迁移, 比如原域名是 www.zzw.com 迁移到 www.world.cn, 但是百度抓取的还是原来的网址
- 浏览器地址会发生变化, 本质是两次Http请求
- 不能共享Request域中的数据, 本质是两次Http请求, 会生成两个HttpServletRequest对象
- 不能重定向到WEB-INF下的资源
- 可以重定向到Web工程以外的资源, 比如到http://www.baidu.com
- 重定向有两种方式, 推荐使用第1种
//第一种重定向使用
response.sendRedirect("/servlet/downServletNew");
//第二种重定向使用
response.setStatus(302);//设置http响应的状态码
//设置http响应的Location: /servlet/downServletNew
response.setHeader("Location", "/servlet/downServletNew");
动态获取到application context
//动态获取 application context
String contextPath = getServletContext().getContextPath();
System.out.println(contextPath);// /servlet
response.sendRedirect(contextPath + "/downServletNew");
练习题
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>支付页面</title>
</head>
<body>
<h1>支付页面</h1>
<!--这里action的第1个/ 会被浏览器解析成浏览器地址栏的主机名-->
<form action="/servlet/myPayServlet">用户编号:<input type="text" name="userId"/>支付金额:<input type="text" name="money"/><input type="submit" value="点击支付">
</form>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>支付成功</title>
</head>
<body>
<h1>恭喜你支付成功</h1>
</body>
</html>
public class MyPayServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {doPost(request, response);}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {request.setCharacterEncoding("utf-8");String userId = request.getParameter("userId");String money = request.getParameter("money");String contextPath = getServletContext().getContextPath();if (WebUtils.parseString(money) > 100) {response.sendRedirect(contextPath + "/pay_ok.html");} else {response.sendRedirect(contextPath + "/pay.html");}}
}
public class WebUtils {public static int parseString(String str) {int num = 0;try {//shortcuts: ctrl+alt+tnum = Integer.parseInt(str);} catch (NumberFormatException e) {System.out.println("输入的str格式不正确");}return num;}
}
<servlet><servlet-name>MyPayServlet</servlet-name><servlet-class>com.zzw.servlet.response.MyPayServlet</servlet-class></servlet><servlet-mapping><servlet-name>MyPayServlet</servlet-name><url-pattern>/myPayServlet</url-pattern></servlet-mapping>