🥽 模板方法设计模式
- 什么是设计模式?
- 某个问题的固定的解决方案。(可以被重复使用。)
- 你知道哪些设计模式?
- GoF设计模式:
- 通常我们所说的23种设计模式。(Gang of Four:4人组提出的设计模式)
- 单例模式
- 工厂模式
- 代理模式
- 门面模式
- 责任链设计模式
- 观察者模式
- 模板方法设计模式
- …
- JavaEE设计模式:
- DAO
- DTO
- VO
- PO
- pojo
- …
- …
- 什么是模板方法设计模式?
- 在模板类的模板方法当中定义核心算法骨架,具体的实现步骤可以延迟到子类当中完成。
- 模板类通常是一个抽象类,模板类当中的模板方法定义核心算法,这个方法通常是final的(但也可以不是final的)
- 模板类当中的抽象方法就是不确定实现的方法,这个不确定怎么实现的事儿交给子类去做。
package com.bjpowernode.template1; public class Student { /** * 这个方法描述学生的一天 */ public void day(){ // 和Teacher的算法相同。 qiChuang(); xiShu(); chiZaoCan(); doSome(); chiWanFan(); shuiJiao(); } public void qiChuang(){ System.out.println("起床"); } public void xiShu(){ System.out.println("洗漱"); } public void chiZaoCan(){ System.out.println("吃早餐"); } public void doSome(){ System.out.println("学生上学,学习"); } public void chiWanFan(){ System.out.println("吃晚饭"); } public void shuiJiao(){ System.out.println("睡觉"); } } package com.bjpowernode.template1; /** * 存在的问题: * 第一:算法没有得到重复的使用。 * 第二:代码没有得到复用。 */ public class Teacher { /** * 这个方法描述老师的一天 */ public void day(){ // 和Student的算法相同。 qiChuang(); xiShu(); chiZaoCan(); doSome(); chiWanFan(); shuiJiao(); } public void qiChuang(){ System.out.println("起床"); } public void xiShu(){ System.out.println("洗漱"); } public void chiZaoCan(){ System.out.println("吃早餐"); } public void doSome(){ System.out.println("老师正在课堂上授课,教授学生知识"); } public void chiWanFan(){ System.out.println("吃晚饭"); } public void shuiJiao(){ System.out.println("睡觉"); } }
package com.bjpowernode.template2; /** * Teacher和Student都是Person * 1. Person就是模板方法设计模式当中的模板类。 * 2. day()方法就是模板方法设计模式当中的模板方法。 */ public abstract class Person { // 模板类通常是抽象类。 // 模板方法 // 添加了final之后,这个方法无法被覆盖,这样核心的算法也可以得到保护。 // 模板方法定义核心的算法骨架,具体的实现步骤可以延迟到子类当中去实现。 // 核心算法一方面是得到了保护,不能被改变。另外一方面就是算法得到了重复使用。 // 另外代码也得到了服用,因为算法中某些步骤的代码是固定的。这些固定的代码不会随着子类的变化而变换,这一部分代码可以写到模板类当中。 public final void day(){ // 第一步 qiChuang(); // 第二步 xiShu(); // 第三步 chiZaoCan(); // 第四步 doSome(); // 第五步 chiWanFan(); // 第六步 shuiJiao(); } // 其中的某些步骤,不会随着子类的变化而变化,这些代码可以写到父类中,得到代码复用。 public void qiChuang(){ System.out.println("起床"); } public void xiShu(){ System.out.println("洗漱"); } public void chiZaoCan(){ System.out.println("吃早餐"); } // 这一步是要做,但是具体这一步怎么做,子类说了算。 public abstract void doSome(); public void chiWanFan(){ System.out.println("吃晚饭"); } public void shuiJiao(){ System.out.println("睡觉"); } } package com.bjpowernode.template2; public class Student extends Person { public void doSome(){ System.out.println("学生上学,学习"); } } package com.bjpowernode.template2; public class Teacher extends Person{ public void doSome(){ System.out.println("老师正在课堂上授课,教授学生知识"); } }
🥽 HttpServlet源码分析
- HttpServlet类是专门为HTTP协议准备的。比GenericServlet更加适合HTTP协议下的开发。
- HttpServlet在哪个包下?
- jakarta.servlet.http.HttpServlet
- 到目前为止我们接触了servlet规范中哪些接口?
- jakarta.servlet.Servlet 核心接口(接口)
- jakarta.servlet.ServletConfig Servlet配置信息接口(接口)
- jakarta.servlet.ServletContext Servlet上下文接口(接口)
- jakarta.servlet.ServletRequest Servlet请求接口(接口)
- jakarta.servlet.ServletResponse Servlet响应接口(接口)
- jakarta.servlet.ServletException Servlet异常(类)
- jakarta.servlet.GenericServlet 标准通用的Servlet类(抽象类)
- http包下都有哪些类和接口呢?jakarta.servlet.http.*;
- jakarta.servlet.http.HttpServlet (HTTP协议专用的Servlet类,抽象类)
- jakarta.servlet.http.HttpServletRequest (HTTP协议专用的请求对象)
- jakarta.servlet.http.HttpServletResponse (HTTP协议专用的响应对象)
- HttpServletRequest对象中封装了什么信息?
- HttpServletRequest,简称request对象。
- HttpServletRequest中封装了请求协议的全部内容。
- Tomcat服务器(WEB服务器)将“请求协议”中的数据全部解析出来,然后将这些数据全部封装到request对象当中了。
- 也就是说,我们只要面向HttpServletRequest,就可以获取请求协议中的数据。
- HttpServletResponse对象是专门用来响应HTTP协议到浏览器的。
- 回忆Servlet生命周期?
- 用户第一次请求
- Tomcat服务器通过反射机制,调用无参数构造方法。创建Servlet对象。(web.xml文件中配置的Servlet类对应的对象。)
- Tomcat服务器调用Servlet对象的init方法完成初始化。
- Tomcat服务器调用Servlet对象的service方法处理请求。
- 用户第二次请求
- Tomcat服务器调用Servlet对象的service方法处理请求。
- 用户第三次请求
- Tomcat服务器调用Servlet对象的service方法处理请求。
- …
- Tomcat服务器调用Servlet对象的service方法处理请求。
- 服务器关闭
- Tomcat服务器调用Servlet对象的destroy方法,做销毁之前的准备工作。
- Tomcat服务器销毁Servlet对象。
- HttpServlet源码分析:
public class HelloServlet extends HttpServlet { // 用户第一次请求,创建HelloServlet对象的时候,会执行这个无参数构造方法。 public HelloServlet() { } //override 重写 doGet方法 //override 重写 doPost方法 } public abstract class GenericServlet implements Servlet, ServletConfig, java.io.Serializable { // 用户第一次请求的时候,HelloServlet对象第一次被创建之后,这个init方法会执行。 public void init(ServletConfig config) throws ServletException { this.config = config; this.init(); } // 用户第一次请求的时候,带有参数的init(ServletConfig config)执行之后,会执行这个没有参数的init() public void init() throws ServletException { // NOOP by default } } // HttpServlet模板类。 public abstract class HttpServlet extends GenericServlet { // 用户发送第一次请求的时候这个service会执行 // 用户发送第N次请求的时候,这个service方法还是会执行。 // 用户只要发送一次请求,这个service方法就会执行一次。 @Override public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { HttpServletRequest request; HttpServletResponse response; try { // 将ServletRequest和ServletResponse向下转型为带有Http的HttpServletRequest和HttpServletResponse request = (HttpServletRequest) req; response = (HttpServletResponse) res; } catch (ClassCastException e) { throw new ServletException(lStrings.getString("http.non_http")); } // 调用重载的service方法。 service(request, response); } // 这个service方法的两个参数都是带有Http的。 // 这个service是一个模板方法。 // 在该方法中定义核心算法骨架,具体的实现步骤延迟到子类中去完成。 protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 获取请求方式 // 这个请求方式最终可能是:"" // 注意:request.getMethod()方法获取的是请求方式,可能是七种之一: // GET POST PUT DELETE HEAD OPTIONS TRACE String method = req.getMethod(); // 如果请求方式是GET请求,则执行doGet方法。 if (method.equals(METHOD_GET)) { long lastModified = getLastModified(req); if (lastModified == -1) { // servlet doesn't support if-modified-since, no reason // to go through further expensive logic doGet(req, resp); } else { long ifModifiedSince; try { ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE); } catch (IllegalArgumentException iae) { // Invalid date header - proceed as if none was set ifModifiedSince = -1; } if (ifModifiedSince < (lastModified / 1000 * 1000)) { // If the servlet mod time is later, call doGet() // Round down to the nearest second for a proper compare // A ifModifiedSince of -1 will always be less maybeSetLastModified(resp, lastModified); doGet(req, resp); } else { resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED); } } } else if (method.equals(METHOD_HEAD)) { long lastModified = getLastModified(req); maybeSetLastModified(resp, lastModified); doHead(req, resp); } else if (method.equals(METHOD_POST)) { // 如果请求方式是POST请求,则执行doPost方法。 doPost(req, resp); } else if (method.equals(METHOD_PUT)) { doPut(req, resp); } else if (method.equals(METHOD_DELETE)) { doDelete(req, resp); } else if (method.equals(METHOD_OPTIONS)) { doOptions(req,resp); } else if (method.equals(METHOD_TRACE)) { doTrace(req,resp); } else { // // Note that this means NO servlet supports whatever // method was requested, anywhere on this server. // String errMsg = lStrings.getString("http.method_not_implemented"); Object[] errArgs = new Object[1]; errArgs[0] = method; errMsg = MessageFormat.format(errMsg, errArgs); resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg); } } protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException{ // 报405错误 String msg = lStrings.getString("http.method_get_not_supported"); sendMethodNotAllowed(req, resp, msg); } protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 报405错误 String msg = lStrings.getString("http.method_post_not_supported"); sendMethodNotAllowed(req, resp, msg); } } /* 通过以上源代码分析: 假设前端发送的请求是get请求,后端程序员重写的方法是doPost,子类没有实现doGet方法,会执行父类的doGet方法,父类的doGet方法会报405错误 假设前端发送的请求是post请求,后端程序员重写的方法是doGet 会发生什么呢? 发生405这样的一个错误。 405表示前端的错误,发送的请求方式不对。和服务器不一致。不是服务器需要的请求方式。 通过以上源代码可以知道:只要HttpServlet类中的doGet方法或doPost方法执行了,必然405. 怎么避免405的错误呢? 后端重写了doGet方法,前端一定要发get请求。 后端重写了doPost方法,前端一定要发post请求。 这样可以避免405错误。 这种前端到底需要发什么样的请求,其实应该后端说了算。后端让发什么方式,前端就得发什么方式。 有的人,你会看到为了避免405错误,在Servlet类当中,将doGet和doPost方法都进行了重写。 这样,确实可以避免405的发生,但是不建议,405错误还是有用的。该报错的时候就应该让他报错。 如果你要是同时重写了doGet和doPost,那还不如你直接重写service方法好了。这样代码还能 少写一点。 */
- 我们编写的HelloServlet直接继承HttpServlet,直接重写HttpServlet类中的service()方法行吗?
- 可以,只不过你享受不到405错误。享受不到HTTP协议专属的东西。
- 到今天我们终于得到了最终的一个Servlet类的开发步骤:
- 第一步:编写一个Servlet类,直接继承HttpServlet
- 第二步:重写doGet方法或者重写doPost方法,到底重写谁,javaweb程序员说了算。
- 第三步:将Servlet类配置到web.xml文件当中。
- 第四步:准备前端的页面(form表单),form表单中指定请求路径即可。
🥽 关于一个web站点的欢迎页面
- 什么是一个web站点的欢迎页面?
- 对于一个webapp来说,我们是可以设置它的欢迎页面的。
- 设置了欢迎页面之后,当你访问这个webapp的时候,或者访问这个web站点的时候,没有指定任何“资源路径”,这个时候会默认访问你的欢迎页面。
- 我们一般的访问方式是:
- http://localhost:8080/servlet06/login.html 这种方式是指定了要访问的就是login.html资源。
- 如果我们访问的方式是:
- http://localhost:8080/servlet06 如果我们访问的就是这个站点,没有指定具体的资源路径。它默认会访问谁呢?
- 默认会访问你设置的欢迎页面。
- 怎么设置欢迎页面呢?
- 第一步:我在IDEA工具的web目录下新建了一个文件login.html
- 第二步:在web.xml文件中进行了以下的配置
<welcome-file-list> <welcome-file>login.html</welcome-file> </welcome-file-list>
- 注意:设置欢迎页面的时候,这个路径不需要以“/”开始。并且这个路径默认是从webapp的根下开始查找。
- 第三步:启动服务器,浏览器地址栏输入地址
- http://localhost:8080/servlet07
- 如果在webapp的根下新建一个目录,目录中再给一个文件,那么这个欢迎页该如何设置呢?
- 在webapp根下新建page1
- 在page1下新建page2目录
- 在page2目录下新建page.html页面
- 在web.xml文件中应该这样配置
<welcome-file-list> <welcome-file>page1/page2/page.html</welcome-file> </welcome-file-list>
- 注意:路径不需要以“/”开始,并且路径默认从webapp的根下开始找。
- 一个webapp是可以设置多个欢迎页面的
<welcome-file-list> <welcome-file>page1/page2/page.html</welcome-file> <welcome-file>login.html</welcome-file> </welcome-file-list>
- 注意:越靠上的优先级越高。找不到的继续向下找。
- 你有没有注意一件事:当我的文件名设置为index.html的时候,不需要在web.xml文件中进行配置欢迎页面。这是为什么?
- 这是因为小猫咪Tomcat服务器已经提前配置好了。
- 实际上配置欢迎页面有两个地方可以配置:
- 一个是在webapp内部的web.xml文件中。(在这个地方配置的属于局部配置)
- 一个是在CATALINA_HOME/conf/web.xml文件中进行配置。(在这个地方配置的属于全局配置)
<welcome-file-list> <welcome-file>index.html</welcome-file> <welcome-file>index.htm</welcome-file> <welcome-file>index.jsp</welcome-file> </welcome-file-list>
- Tomcat服务器的全局欢迎页面是:index.html index.htm index.jsp。如果你一个web站点没有设置局部的欢迎页面,Tomcat服务器就会以index.html index.htm index.jsp作为一个web站点的欢迎页面。
- 注意原则:局部优先原则。(就近原则)
- 欢迎页可以是一个Servlet吗?
- 当然可以。
- 你不要多想,欢迎页就是一个资源,既然是一个资源,那么可以是静态资源,也可以是动态资源。
- 静态资源:index.html welcome.html …
- 动态资源:Servlet类。
- 步骤:
- 第一步:写一个Servlet
public class WelcomeServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html"); PrintWriter out = response.getWriter(); out.print("<h1>welcome to bjpowernode!</h1>"); } }
- 第二步:在web.xml文件中配置servlet
<servlet> <servlet-name>welcomeServlet</servlet-name> <servlet-class>com.bjpowernode.javaweb.servlet.WelcomeServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>welcomeServlet</servlet-name> <url-pattern>/fdsa/fds/a/fds/af/ds/af/dsafdsafdsa</url-pattern> </servlet-mapping>
- 第三步:在web.xml文件中配置欢迎页
<welcome-file-list> <welcome-file>fdsa/fds/a/fds/af/ds/af/dsafdsafdsa</welcome-file> </welcome-file-list>
🥽 关于WEB-INF目录
- 在WEB-INF目录下新建了一个文件:welcome.html
- 打开浏览器访问:http://localhost:8080/servlet07/WEB-INF/welcome.html 出现了404错误。
- 注意:放在WEB-INF目录下的资源是受保护的。在浏览器上不能够通过路径直接访问。所以像HTML、CSS、JS、image等静态资源一定要放到WEB-INF目录之外。
🥽 HttpServletRequest接口详解
- HttpServletRequest是一个接口,全限定名称:jakarta.servlet.http.HttpServletRequest
- HttpServletRequest接口是Servlet规范中的一员。
- HttpServletRequest接口的父接口:ServletRequest
public interface HttpServletRequest extends ServletRequest {}
- HttpServletRequest接口的实现类谁写的? HttpServletRequest对象是谁给创建的?
- 通过测试:org.apache.catalina.connector.RequestFacade 实现了 HttpServletRequest接口
public class RequestFacade implements HttpServletRequest {}
- 测试结果说明:Tomcat服务器(WEB服务器、WEB容器)实现了HttpServletRequest接口,还是说明了Tomcat服务器实现了Servlet规范。而对于我们javaweb程序员来说,实际上不需要关心这个,我们只需要面向接口编程即可。我们关心的是HttpServletRequest接口中有哪些方法,这些方法可以完成什么功能!!!!
- HttpServletRequest对象中都有什么信息?都包装了什么信息?
- HttpServletRequest对象是Tomcat服务器负责创建的。这个对象中封装了什么信息?封装了HTTP的请求协议。
- 实际上是用户发送请求的时候,遵循了HTTP协议,发送的是HTTP的请求协议,Tomcat服务器将HTTP协议中的信息以及数据全部解析出来,然后Tomcat服务器把这些信息封装到HttpServletRequest对象当中,传给了我们javaweb程序员。
- javaweb程序员面向HttpServletRequest接口编程,调用方法就可以获取到请求的信息了。
- request和response对象的生命周期?
- request对象和response对象,一个是请求对象,一个是响应对象。这两个对象只在当前请求中有效。
- 一次请求对应一个request。
- 两次请求则对应两个request。
- …
- HttpServletRequest接口中有哪些常用的方法?
- 怎么获取前端浏览器用户提交的数据?
Map<String,String[]> getParameterMap() 这个是获取Map Enumeration<String> getParameterNames() 这个是获取Map集合中所有的key String[] getParameterValues(String name) 根据key获取Map集合的value String getParameter(String name) 根据key获取value这个一维数组当中的第一个元素。这个方法最常用。 // 以上的4个方法,和获取用户提交的数据有关系。
- 思考:如果是你,前端的form表单提交了数据之后,你准备怎么存储这些数据,你准备采用什么样的数据结构去存储这些数据呢?
- 前端提交的数据格式:username=abc&userpwd=111&aihao=s&aihao=d&aihao=tt
- 我会采用Map集合来存储:
Map<String,String> key存储String value存储String 这种想法对吗?不对。 如果采用以上的数据结构存储会发现key重复的时候value覆盖。 key value --------------------- username abc userpwd 111 aihao s aihao d aihao tt 这样是不行的,因为map的key不能重复。 Map<String, String[]> key存储String value存储String[] key value ------------------------------- username {"abc"} userpwd {"111"} aihao {"s","d","tt"}
- 注意:前端表单提交数据的时候,假设提交了120这样的“数字”,其实是以字符串"120"的方式提交的,所以服务器端获取到的一定是一个字符串的"120",而不是一个数字。(前端永远提交的是字符串,后端获取的也永远是字符串。)
- 手工开发一个webapp。测试HttpServletRequest接口中的相关方法。
- 先测试了4个常用的方法,获取请求参数的四个方法。
Map<String,String[]> parameterMap = request.getParameterMap(); Enumeration<String> names = request.getParameterNames(); String[] values = request.getParameterValues("name"); String value = request.getParameter("name");
package com.bjpowernode.javaweb.servlet; import jakarta.servlet.*; import jakarta.servlet.http.*; import java.io.*; import java.util.*; /* username=zhangsan&userpwd=123&interest=s&interest=d Map<String,String[]> key value --------------------------- "username" {"zhangsan"} "userpwd" {"123"} "interest" {"s", "d"} 总结一下:request接口中四个非常重要的方法。 Map<String,String[]> parameterMap = request.getParameterMap(); Enumeration<String> names = request.getParameterNames(); String[] values = request.getParameterValues("name"); String value = request.getParameter("name"); */ public class RequestTestServlet extends HttpServlet{ public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException,ServletException{ // 面向接口编程:HttpServletRequest接口。 // 获取前端提交的数据 // 前端会提交什么数据呢? // username=zhangsan&userpwd=123&interest=s&interest=d // 以上的数据会被小猫咪封装到request对象中。 // 获取参数Map集合 Map<String,String[]> parameterMap = request.getParameterMap(); // 遍历Map集合(获取Map集合中所有的key,遍历) Set<String> keys = parameterMap.keySet(); Iterator<String> it = keys.iterator(); while(it.hasNext()){ String key = it.next(); //System.out.println(key); // 通过key获取value String[] values = parameterMap.get(key); /* username=[Ljava.lang.String;@7cce40b4 userpwd=[Ljava.lang.String;@7453f0b9 interest=[Ljava.lang.String;@4063ebb5 */ //System.out.println(key + "=" + values); // 遍历一维数组 System.out.print(key + "="); for(String value : values){ System.out.print(value + ","); } // 换行 System.out.println(); } // 直接通过getParameterNames()这个方法,可以直接获取这个Map集合的所有key Enumeration<String> names = request.getParameterNames(); while(names.hasMoreElements()){ String name = names.nextElement(); System.out.println(name); } // 直接通过name获取value这个一维数组。 String[] usernames = request.getParameterValues("username"); String[] userpwds = request.getParameterValues("userpwd"); String[] interests = request.getParameterValues("interest"); // 遍历一维数组 for(String username : usernames){ System.out.println(username); } for(String userpwd : userpwds){ System.out.println(userpwd); } for(String interest : interests){ System.out.println(interest); } // 通过name获取value这个一维数组的第一个元素 // 这个方法使用最多,因为这个一维数组中一般只有一个元素。 String username = request.getParameter("username"); String userpwd = request.getParameter("userpwd"); //String interest = request.getParameter("interest"); // 既然是checkbox,调用方法:request.getParameterValues("interest") String[] interests2 = request.getParameterValues("interest"); // 获取的都是一维数组当中的第一个元素。 System.out.println(username); System.out.println(userpwd); //System.out.println(interest); for(String interest : interests2){ System.out.println(interest); } } }
- request对象实际上又称为“请求域”对象。
- 应用域对象是什么?
- ServletContext (Servlet上下文对象。)
- 什么情况下会考虑向ServletContext这个应用域当中绑定数据呢?
- 第一:所有用户共享的数据。
- 第二:这个共享的数据量很小。
- 第三:这个共享的数据很少的修改操作。
- 在以上三个条件都满足的情况下,使用这个应用域对象,可以大大提高我们程序执行效率。
- 实际上向应用域当中绑定数据,就相当于把数据放到了缓存(Cache)当中,然后用户访问的时候直接从缓存中取,减少IO的操作,大大提升系统的性能,所以缓存技术是提高系统性能的重要手段。
- 你见过哪些缓存技术呢?
- 字符串常量池
- 整数型常量池 [-128~127],但凡是在这个范围当中的Integer对象不再创建新对象,直接从这个整数型常量池中获取。大大提升系统性能。
- 数据库连接池(提前创建好N个连接对象,将连接对象放到集合当中,使用连接对象的时候,直接从缓存中拿。省去了连接对象的创建过程。效率提升。)
- 线程池(Tomcat服务器就是支持多线程的。所谓的线程池就是提前先创建好N个线程对象,将线程对象存储到集合中,然后用户请求过来之后,直接从线程池中获取线程对象,直接拿来用。提升系统性能)
- 后期你还会学习更多的缓存技术,例如:redis、mongoDB…
- ServletContext当中有三个操作域的方法:
void setAttribute(String name, Object obj); // 向域当中绑定数据。 Object getAttribute(String name); // 从域当中根据name获取数据。 void removeAttribute(String name); // 将域当中绑定的数据移除 // 以上的操作类似于Map集合的操作。 Map<String, Object> map; map.put("name", obj); // 向map集合中放key和value Object obj = map.get("name"); // 通过map集合的key获取value map.remove("name"); // 通过Map集合的key删除key和value这个键值对。
- “请求域”对象
- “请求域”对象要比“应用域”对象范围小很多。生命周期短很多。请求域只在一次请求内有效。
- 一个请求对象request对应一个请求域对象。一次请求结束之后,这个请求域就销毁了。
- 请求域对象也有这三个方法:
void setAttribute(String name, Object obj); // 向域当中绑定数据。 Object getAttribute(String name); // 从域当中根据name获取数据。 void removeAttribute(String name); // 将域当中绑定的数据移除
- 请求域和应用域的选用原则?
- 尽量使用小的域对象,因为小的域对象占用的资源较少。
- 跳转
- 转发(一次请求)
// 这样做可以吗? // 在AServlet当中new一个BServlet对象,然后调用BServlet对象的doGet方法,把request对象传过去。 // 这个代码虽然可以实现功能,但是Servlet对象不能自己由程序员来new。自己new的Servlet对象生命周期不受Tomcat服务器的管理。 /*BServlet bServlet = new BServlet(); bServlet.doGet(request, response);*/ // 使用Servlet当中的转发机制。 // 执行了AServlet之后,跳转到BServlet。(这个资源跳转可以使用转发机制来完成。) // 怎么转发?代码怎么写? // 第一步:获取请求转发器对象 // 相当于把"/b"这个路径包装到请求转发器当中,实际上是把下一个跳转的资源的路径告知给Tomcat服务器了。 RequestDispatcher dispatcher = request.getRequestDispatcher("/b"); // 第二步:调用请求转发器RequestDispatcher的forward方法。进行转发。 // 转发的时候:这两个参数很重要。request和response都是要传递给下一个资源的。 dispatcher.forward(request, response); // 一行代码搞定转发。 request.getRequestDispatcher("/b").forward(request, response);
- 两个Servlet怎么共享数据?
- 将数据放到ServletContext应用域当中,当然是可以的,但是应用域范围太大,占用资源太多。不建议使用。
- 可以将数据放到request域当中,然后AServlet转发到BServlet,保证AServlet和BServlet在同一次请求当中,这样就可以做到两个Servlet,或者多个Servlet共享同一份数据。
- 转发的下一个资源必须是一个Servlet吗?
- 不一定,只要是Tomcat服务器当中的合法资源,都是可以转发的。例如:html…
- 注意:转发的时候,路径的写法要注意,转发的路径以“/”开始,不加项目名。
// 转发到一个Servlet,也可以转发到一个HTML,只要是WEB容器当中的合法资源即可。 request.getRequestDispatcher("/test.html").forward(request, response);
- 关于request对象中两个非常容易混淆的方法:
// uri?username=zhangsan&userpwd=123&sex=1 String username = request.getParameter("username"); // 之前一定是执行过:request.setAttribute("name", new Object()) Object obj = request.getAttribute("name"); // 以上两个方法的区别是什么? // 第一个方法:获取的是用户在浏览器上提交的数据。 // 第二个方法:获取的是请求域当中绑定的数据。
- HttpServletRequest接口的其他常用方法:
// 获取客户端的IP地址 String remoteAddr = request.getRemoteAddr(); // request.getRemoteHost(); 获取远程主机地址 // request.getRemotePort(); 获取端口 // get请求在请求行上提交数据。 // post请求在请求体中提交数据。 // 设置请求体的字符集。(显然这个方法是处理POST请求的乱码问题。这种方式并不能解决get请求的乱码问题。) // Tomcat10之后,request请求体当中的字符集默认就是UTF-8,不需要设置字符集,不会出现乱码问题。 // Tomcat9前(包括9在内),如果前端请求体提交的是中文,后端获取之后出现乱码,怎么解决这个乱码?执行以下代码。 request.setCharacterEncoding("UTF-8"); // 在Tomcat9之前(包括9),响应中文也是有乱码的,怎么解决这个响应的乱码? response.setContentType("text/html;charset=UTF-8"); // 在Tomcat10之后,包括10在内,响应中文的时候就不在出现乱码问题了。以上代码就不需要设置UTF-8了。 // 注意一个细节 // 在Tomcat10包括10在内之后的版本,中文将不再出现乱码。(这也体现了中文地位的提升。) // get请求乱码问题怎么解决? // get请求发送的时候,数据是在请求行上提交的,不是在请求体当中提交的。 // get请求乱码怎么解决 // 方案:修改CATALINA_HOME/conf/server.xml配置文件 <Connector URIEncoding="UTF-8" /> // 注意:从Tomcat8之后,URIEncoding的默认值就是UTF-8,所以GET请求也没有乱码问题了。 // 获取应用的根路径 String contextPath = request.getContextPath(); // 获取请求方式 String method = request.getMethod(); // 获取请求的URI String uri = request.getRequestURI(); // /aaa/testRequest // 获取servlet path,不带项目名 String servletPath = request.getServletPath(); // /testRequest




