1、Servlet简介
Servlet是Sun公司开发动态Web的一门技术。Sun公司在这些API中提供了一个Servlet接口,如果你想开发一个Servlet程序只需要完成如下两个步骤:
- 编写一个Java类实现Servlet接口。
- 把开发好的Java类部署到Web服务器中。
我们把实现了Servlet接口的Java程序叫做Servlet。
注:
- 一个Servlet就是一个实现了Servlet接口的Java类。
- 一个Servlet对应一个URL。
2、父项目与子项目
关于父子项目的理解:
(1)父项目的pom.xml
配置文件中存在<modules>……</modules>
标签,记录了子项目的信息:
1 2 3
| <modules> <module>servlet-01</module> </modules>
|
(2)相对应的,子项目(即:Module
模块)中存在<parent>……</parent>
标签,记录了父项目的信息:
1 2 3 4 5
| <parent> <artifactId>javaWeb-Servlet</artifactId> <groupId>com.atangbiji</groupId> <version>1.0-SNAPSHOT</version> </parent>
|
(3)父项目中的依赖包,子项目可以直接使用。(类似于Java中的继承)
这样我们把所有项目的依赖都添加到父项目的pom.xml
配置文件中后,那么所有的子项目就不用每次再重复导入依赖包了。
(4)Servlet接口在子项目中实现。
3、Hello Servlet
3.1、创建父项目
(1)创建一个普通的Maven项目(具体步骤参考《Maven详解》),项目名称为javaWeb-Servlet
。
(2)删除项目中的src
目录,所得到的空项目就是我们的Maven父项目,我们可以在其中创建多个子项目(即:Module
)。
(3)在父项目的pom.xml
文件中导入父/子工程需要的依赖,根据需求查询有哪些依赖需要导入。
以Servlet
项目为例,需要在父项目的pom.xml
文件中导入如下依赖:
1 2 3 4 5 6 7
| <dependencies> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> </dependency> </dependencies>
|
注:若在父项目的pom.xml
文件中已经添加了某个依赖后,则子项目的pom.xml
文件中不要再重复添加该依赖了,否则Maven项目编译会出错。
至此,父项目创建完毕。
3.2、创建子项目
(1)在父项目中新建一个模块(子项目)。
(2)使用Maven模板创建一个MavenWeb子项目。
(3)填写子项目名称和Maven项目GAV。
(4)配置子项目Maven。点击Finish
按钮完成子项目创建,等待Maven依赖包导入完毕。
(5)由于使用Maven模板创建的MavenWeb项目的版本较低(“web-app_2_3”
)。为了避免以后出现兼容性问题,建议我们先将该子项目中的web.xml
文件替换成“web-app_4_0”
版本。具体可以参考Tomcat
安装目录下的(D:\Environment\apache-tomcat-9.0.65\webapps\examples\WEB-INF
)web.xml
文件。
web.xml
文件:
1 2 3 4 5 6 7 8 9 10
| <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0" metadata-complete="true">
</web-app>
|
注:Web.xml
是Java Web
项目的一个核心配置文件,主要用于配置首页、Servlet
、Filter
、Listener
等。
(6)在子项目的main
文件夹下新建一个java
文件夹和一个resource
文件夹,并将它们分别标记为“源码根目录”和“资源根目录”。(具体步骤参考《Maven详解》)
(7)至此,子项目创建完毕。编译父项目或子项目,此时父项目与子项目的目录结构如下图所示。
3.3、编写一个Java类实现Servlet接口(重点)
(1)在子项目的java目录下新建一个com.atangbiji.servlet
包,并在该包下新建一个HelloServlet
类,用于实现Servlet
接口。
(2)让HelloServlet
类继承(extends)HttpServlet
类。(此时,Maven会通过父项目中的依赖自动导入依赖包。)
HelloServlet.java
文件:
1 2 3
| public class HelloServlet extends HttpServlet { }
|
注:
- Servlet是一个接口程序,也就是一个java程序,其存放于main-java文件夹中。
- 由于sun公司为我们提供了两个默认的
Servlet
接口实现类:①HttpServlet
,②GenericServlet
。因此,我们只需继承HttpServlet
类便可实现Servlet
接口。
附:Servlet接口、GenericServlet类和HttpServlet类详解
a、Servlet接口源码
1 2 3 4 5 6 7
| public interface Servlet { void init(ServletConfig var1); ServletConfig getServletConfig(); void service(ServletRequest var1, ServletResponse var2); String getServletInfo(); void destroy(); }
|
b、GenericServlet类源码(部分)
1 2 3 4 5
| public abstract class GenericServlet implements Servlet, ServletConfig, Serializable { …… public abstract void service(ServletRequest var1, ServletResponse var2); …… }
|
c、HttpServlet类源码(部分)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
| public abstract class HttpServlet extends GenericServlet { …… public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { if (req instanceof HttpServletRequest && res instanceof HttpServletResponse) { HttpServletRequest request = (HttpServletRequest)req; HttpServletResponse response = (HttpServletResponse)res; this.service(request, response); } else { throw new ServletException("non-HTTP request or response"); } } protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String method = req.getMethod(); long lastModified; if (method.equals("GET")) { lastModified = this.getLastModified(req); if (lastModified == -1L) { this.doGet(req, resp); } else { long ifModifiedSince = req.getDateHeader("If-Modified-Since"); if (ifModifiedSince < lastModified) { this.maybeSetLastModified(resp, lastModified); this.doGet(req, resp); } else { resp.setStatus(304); } } } else if (method.equals("HEAD")) { lastModified = this.getLastModified(req); this.maybeSetLastModified(resp, lastModified); this.doHead(req, resp); } else if (method.equals("POST")) { this.doPost(req, resp); } else if (method.equals("PUT")) { this.doPut(req, resp); } else if (method.equals("DELETE")) { this.doDelete(req, resp); } else if (method.equals("OPTIONS")) { this.doOptions(req, resp); } else if (method.equals("TRACE")) { this.doTrace(req, resp); } else { String errMsg = lStrings.getString("http.method_not_implemented"); Object[] errArgs = new Object[]{method}; errMsg = MessageFormat.format(errMsg, errArgs); resp.sendError(501, errMsg); }
} protected void doGet(HttpServletRequest req, HttpServletResponse resp) { …… }
protected void doPost(HttpServletRequest req, HttpServletResponse resp){ …… } …… }
|
分析源码,我们可以发现:
- 我们自定义的
Servlet
类与Servlet
接口、GenericServlet
类和HttpServlet
类的关系如下图所示。
HttpServlet
类重写的service(ServletRequest,ServletResponse)
方法中会
把ServletRequest
和ServletResponse
强转成HttpServletRequest
和HttpServletResponse
,
然后再调用service(HttpServletRequest,HttpServletResponse)
方法。
HttpServlet
类重载了service(HttpServletRequest,HttpServletResponse)
方法,该方法是HttpServlet
自己重载的方法,而不是从Servlet
继承来的。
结论:HttpServlet
类在重载的service(HttpServletRequest,HttpServletResponse)
方法中,会根据不同的Http
请求方式,调用不同的处理方法。因此,我们需要在我们自己编写的Servlet类中重写相应的Http
请求处理方法(重点掌握doGet和doPost方法)。
**注:**我们可以通过IDEA为我们提供的Structure
窗口来查看HttpServlet类的成员变量和成员函数。
(3)在HelloServlet
类中重写doGet
和doPost
方法。
HelloServlet.java
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class HelloServlet extends HttpServlet {
@Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("进入了doGet方法"); PrintWriter writer = resp.getWriter(); writer.print("Hello Servlet!"); }
@Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req,resp); } }
|
**注:**因为get
和post
只是不同的Http请求方式,所以它们之间可以相互调用。
3.4、编写Servlet的映射(重点)
**注:**因为我们写的是一个Java
程序,但是如果我们要通过浏览器去访问它,就需要连接web
服务器。因此,我们需要在Web
配置文件(web.xml)中注册(映射)我们自己编写的Servlet
,并为其提供一个浏览器可以访问的路径。
web.xml
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| <?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0" metadata-complete="true">
<servlet> <servlet-name>servlet1</servlet-name> <servlet-class>com.atangbiji.servlet.HelloServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>servlet1</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping>
</web-app>
|
3.5、配置Tomcat服务器
(1)配置Tomcat服务器。(具体步骤参考《Tomcat详解》)
(2)deploy(发布)servlet-01:war
包,并为该子项目添加虚拟路径映射:/s1
。
3.6、启动测试
在IDEA中启动Tomcat。
(1)在浏览器中输入http://localhost:8080/s1/
,访问结果如下:
**注:**此时访问的是该Web项目的index.jsp
文件。
index.jsp
文件:
1 2 3 4 5
| <html> <body> <h2>Hello World!</h2> </body> </html>
|
(2)在浏览器中输入http://localhost:8080/s1/hello
,访问结果如下:
**注:**此时访问的是我们自己编写的Servlet
类。
4、Servlet与Servlet容器
Servlet
是平台独立的Java
类。编写一个Servlet
,实际上就是按照Servlet
规范编写一个Java
类。
Servlet
容器是Web服务器或应用程序服务器的一部分,用于在发送的请求和响应之上提供网络服务。
注:
Servlet
不能独立运行,必须部署在Servlet
容器中,由容器来实例化和调用Servlet
的方法。
- 在JSP技术推出后,管理和运行
Servlet/JSP
的容器也称“Web
容器”。
Servlet
容器、JSP容器以及Web容器基本上是同义的。
5、Servlet的运行原理
Servlet程序是由Web服务器调用的,Servlet程序的执行过程如下图所示。
(1)用户在浏览器中输入URL
,向Web服务器(Tomcat
)发送Http
请求。
(2)Web服务器接收到请求后,先将请求交给Servlet
容器。
(3)Servlet
容器判断是否需要创建Servlet
实例。
注:
- 当一个
Servlet
在第一次接收请求时,Servlet
容器才会创建该Servlet
的实例。然后调用其init
方法进行初始化,接着通过service
方法调用相应处理方法(如:doGet
、doPost
等),进而产生一个响应。
- 当一个
Servlet
在第二次及以后接收请求时,Servlet
容器会直接通过**service
**方法调用相应处理方法(如:doGet
、doPost
等),进而产生一个响应。
(4)Servlet
实例使用请求对象(HttpServletRequest
)得到浏览器的请求信息,然后进行相应的处理。
(5)Servlet
实例将处理的结果通过响应对象(HttpServletResponse
)返回给Web服务器。
(6)Web
服务器将响应包装后,以Http
响应的形式返回给浏览器。
(7)在Servlet
容器正常关闭或在Servlet
实例超时时,Servlet
容器则调用Servlet
实例的destroy
方法销毁Servlet
。
6、Servlet的映射(访问)路径
Servlet
的映射(访问)路径是在Web
配置文件(web.xml
)中编写的。如:
1 2 3 4 5
| <servlet-mapping> <servlet-name>servlet1</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping>
|
6.1、servlet映射(访问)路径的方式
映射路径方式 |
url-pattern示例 |
浏览器访问路径 |
1、精确匹配 |
/first |
http://localhost:8080/项目路径/first |
|
/test/demo1 |
http://localhost:8080/项目路径/test/demo1 |
2、模糊匹配 |
/* |
http://localhost:8080/项目路径/任意字符或字符串 (含/ 等) |
|
/test/* |
http://localhost:8080/项目路径/test/任意字符或字符串 (含/ 等) |
|
*.后缀名 |
http://localhost:8080/项目路径/任意字符或字符串.后缀名 (含/ 等) |
|
*.do |
http://localhost:8080/项目路径/任意字符或字符串.do (含/ 等) |
|
*.action |
http://localhost:8080/项目路径/任意字符或字符串.action (含/ 等) |
|
*.html (伪静态) |
http://localhost:8080/项目路径/任意字符或字符串.html (含/ 等) |
3、默认路径 |
/ |
http://localhost:8080/项目路径/ |
注:
- 一个
Servlet
程序可以指定一个或者多个映射路径。
url-pattern
要么以/
开头,要么以*
开头。( 例如, test
为非法路径。)
- 不能同时使用两种模糊匹配。(例如 :
/*.do
和/test/*.do
均为非法路径。)
6.2、优先级
- servlet映射(访问)路径方式之间的优先级为:精确匹配 > 模糊匹配。
7、Servlet上下文
7.1、ServletContext类详解
Web容器在启动时,会为每一个Web
项目创建一个对应的ServletContext
对象。它是当前项目中所有Servlet
实例之间信息交互的“中间商”。(参见“Servlet的运行原理”)
注:一个Web项目对应一个ServletContext
。(即:同一个Web项目中的所有servlet
实例共用同一个ServletContext
对象。)
7.2、常见应用
ServletContext
的常见应用场景主要包括:
- **数据共享:**在一个
Servlet
中保存的数据,可以在另一个Servlet
中使用。
- 获取初始化参数
- 请求转发
- 读取资源文件
7.3、使用ServletContext实现:数据共享
(1)在上述父项目下再使用Maven模板新建一个MavenWeb子项目:servlet-02
。
(2)同样地,为了避免以后出现兼容性问题,建议我们先将该子项目中的web.xml
文件替换成“web-app_4_0”
版本。
(3)同样地,在该子项目的main
文件夹下新建一个java
文件夹和一个resource
文件夹,并将它们分别标记为“源码根目录”和“资源根目录”。
(4)编译父项目或子项目,此时父项目与子项目的目录结构如下图所示。
(5)配置Tomcat服务器,deploy(发布)servlet-02:war
包,并为该子项目添加虚拟路径映射:/s2
。
**注:**如果只发布某一子项目,那么其它子项目的war
包和虚拟路径映射可以删除。若不删除,则一起发布。
(6)在该子项目的java目录下新建一个com.atangbiji.servlet
包,并在该包下新建一个Servlet1
类,用于将数据以“键-值“对的形式保存在ServlContex对象中。同样地,让Servlet1
类继承(extends)HttpServlet
类,以实现Servlet
接口。然后,在Servlet1
类中重写doGet
和doPost
方法。
Servlet1.java
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class Servlet1 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ServletContext servletContext = this.getServletContext(); String usernameVal = "阿汤笔迹"; servletContext.setAttribute("username",usernameVal); }
@Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req,resp); } }
|
(7)同样地,com.atangbiji.servlet
包下再新建一个Servlet2
类,用于获取ServlContex对象中保存的“键-值“对。
Servlet2.java
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class Servlet2 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ServletContext servletContext = this.getServletContext(); String usernameVal = (String)servletContext.getAttribute("username");
resp.setHeader("Content-Type", "text/html; charset=utf-8"); resp.getWriter().print("Servlet2从ServletContext中获取到username的值为:" + usernameVal); }
@Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req,resp); } }
|
(8)同样地,在该子项目的Web
配置文件(web.xml)中注册(映射)我们自己编写的两个Servlet
,并为它们分别提供一个浏览器可以访问的路径。
web.xml
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| <?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0" metadata-complete="true"> <servlet> <servlet-name>SetServletContext</servlet-name> <servlet-class>com.atangbiji.servlet.Servlet1</servlet-class> </servlet> <servlet-mapping> <servlet-name>SetServletContext</servlet-name> <url-pattern>/setVal</url-pattern> </servlet-mapping>
<servlet> <servlet-name>GetServletContext</servlet-name> <servlet-class>com.atangbiji.servlet.Servlet2</servlet-class> </servlet> <servlet-mapping> <servlet-name>GetServletContext</servlet-name> <url-pattern>/getVal</url-pattern> </servlet-mapping>
</web-app>
|
(9)在IDEA中启动Tomcat。在浏览器中先输入http://localhost:8080/s2/setVal
加载Servlet1
,然后再输入http://localhost:8080/s2/getVal
,访问结果如下:
**注:**我们可以通过IDEA为我们提供的Web
窗口来查看Web项目的结构。
7.4、使用ServletContext实现:获取初始化参数
(1)在Web.xml
文件中配置Web
应用的初始化参数(如:连接mybatis
的url
)。
Web.xml
文件:
1 2 3 4 5
| <context-param> <param-name>url</param-name> <param-value>jdbc:mysql://localhost:3306/mybatis</param-value> </context-param>
|
(2)在该子项目com.atangbiji.servlet
包下新建一个Servlet3
类,用于获取Web
应用的初始化参数。同样地,让Servlet3
类继承HttpServlet
类,以实现Servlet
接口。然后,在Servlet3
类中重写doGet
和doPost
方法。
Servlet3.java
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class Servlet3 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ServletContext servletContext = this.getServletContext();
String url = servletContext.getInitParameter("url"); resp.getWriter().print(url); }
@Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req,resp); } }
|
(3)同样地,在该子项目的Web
配置文件(web.xml)中注册(映射)我们自己编写的Servlet
,并为它提供一个浏览器可以访问的路径。
web.xml
文件:
1 2 3 4 5 6 7 8
| <servlet> <servlet-name>GetInitParameter</servlet-name> <servlet-class>com.atangbiji.servlet.Servlet3</servlet-class> </servlet> <servlet-mapping> <servlet-name>GetInitParameter</servlet-name> <url-pattern>/getInitParameter</url-pattern> </servlet-mapping>
|
(4)在IDEA中启动Tomcat。在浏览器中输入http://localhost:8080/s2/getInitParameter
,访问结果如下:
7.5、使用ServletContext实现:请求转发
7.5.1、什么是请求转发
请求转发(RequestDispatcher)指的是:一个web资源(Servlet1
)接受到客户端的请求后,通知服务器去调用另外一个web资源(Servlet2
)进行处理。
7.5.2、请求转发的工作原理
在 Servlet
中,通常使用 forward()
方法将当前请求转发给其他的 Web
资源进行处理。请求转发的工作原理如下图所示:
7.5.3、请求转发的特点
- 请求转发是服务器端的行为,浏览器不知道在服务器内部发生了转发行为,更无法得知转发的次数。
- 请求转发之后,浏览器地址栏中的 URL 不会发生变化。
- 请求转发不支持跨域访问,只能跳转到当前应用中的资源。
- 参与请求转发的
Web
资源之间共享同一 request
对象和 response
对象。
- 只有转发到最后一个
Web
资源时,生成的响应才会被发送到客户端。
7.5.4、请求转发的实现
(1)在该子项目com.atangbiji.servlet
包下新建一个Servlet4
类,用于将访问Servlet4的请求转发到Servlet3的请求路径上来。同样地,让Servlet4
类继承HttpServlet
类,以实现Servlet
接口。然后,在Servlet4
类中重写doGet
和doPost
方法。
Servlet4.java
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class Servlet4 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ServletContext servletContext = this.getServletContext(); RequestDispatcher requestDispatcher = servletContext.getRequestDispatcher("/getInitParameter"); requestDispatcher.forward(req,resp); }
@Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req,resp); } }
|
(2)同样地,在该子项目的Web
配置文件(web.xml)中注册(映射)我们自己编写的Servlet
,并为它提供一个浏览器可以访问的路径。
web.xml
文件:
1 2 3 4 5 6 7 8
| <servlet> <servlet-name>RequestDispatcher</servlet-name> <servlet-class>com.atangbiji.servlet.Servlet4</servlet-class> </servlet> <servlet-mapping> <servlet-name>RequestDispatcher</servlet-name> <url-pattern>/requestDispatcher</url-pattern> </servlet-mapping>
|
(3)在IDEA中启动Tomcat。在浏览器中输入http://localhost:8080/s2/requestDispatcher
,此时服务器会将我们的请求转发到/getInitParameter
路径上来。访问结果如下:
注:请注意请求转发与请求重定向的区别,我们将在介绍重定向是对比说明。
7.6、ServletContext应用示例:读取资源文件
(1)在该子项目的resources
文件夹下新建一个db.properties
文件。
db.properties
文件:
1 2
| username = root password = 123456789
|
(2)在该子项目com.atangbiji.servlet
包下新建一个propertiesServlet
类,用于读取db.properties
资源文件。同样地,让propertiesServlet
类继承HttpServlet
类,以实现Servlet
接口。然后,在propertiesServlet
类中重写doGet
和doPost
方法。
propertiesServlet.java
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public class propertiesServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ServletContext servletContext = this.getServletContext(); InputStream stream = servletContext.getResourceAsStream("/WEB-INF/classes/db.properties");
Properties prop = new Properties(); prop.load(stream); String username = prop.getProperty("username"); String password = prop.getProperty("password"); resp.getWriter().print(username + ":" + password); }
@Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req,resp); } }
|
(3)同样地,在该子项目的Web
配置文件(web.xml)中注册(映射)我们自己编写的Servlet
,并为它提供一个浏览器可以访问的路径。
web.xml
文件:
1 2 3 4 5 6 7 8
| <servlet> <servlet-name>PropertiesServlet</servlet-name> <servlet-class>com.atangbiji.servlet.propertiesServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>PropertiesServlet</servlet-name> <url-pattern>/propertiesServlet</url-pattern> </servlet-mapping>
|
(4)在IDEA中启动Tomcat。在浏览器中输入http://localhost:8080/s2/propertiesServlet
,访问结果如下:
**注:**由于Maven的“约定大于配置”思想,我们以后可能会遇到我们自己写的配置文件(如:我们配置文件*.xml
写在了项目中的Java
目录下)无法被导出或者无法生效的问题。该问题的解决方法是:在pom.xml
的<build>……</build>
中配置resources
,修改Maven约定的过滤条件,来防止我们的资源导出失败。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| <build> <resources> <resource> <directory>src/main/resources</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>false</filtering> </resource> <resource> <directory>src/main/java</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>false</filtering> </resource> </resources> </build>
|
8、HttpServletResponse响应
Web
服务器接收到客户端的http
请求后,会针对这个请求分别创建一个代表请求的HttpServletRequest
对象和一个代表响应的HttpServletResponse
对象。
- 若我们需要获取客户端发送的请求参数,则使用
HttpServletRequest
对象。
- 若我们需要给客户端响应一些信息,则使用
HttpServletResponse
对象。
8.1、响应方法简单分类
查看源码,可以将HttpServletResponse
类及其父类(ServletResponse
类)中常用的响应方法简单分为如下几类:
(1)负责向浏览器发送数据的方法
1 2 3
| ServletOutputStream getOutputStream() throws IOException;
PrintWriter getWriter() throws IOException;
|
(2)负责设置响应头的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| void setCharacterEncoding(String var1);
void setContentLength(int var1);
void setContentLengthLong(long var1);
void setContentType(String var1);
void setBufferSize(int var1);
void setDateHeader(String var1, long var2);
void addDateHeader(String var1, long var2);
void setHeader(String var1, String var2);
void addHeader(String var1, String var2);
void setIntHeader(String var1, int var2);
void addIntHeader(String var1, int var2);
|
此外,我们还可以在HttpServletResponse
类的源码中查看JavaWeb
基于HTTP
协议定义的响应状态码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| int SC_CONTINUE = 100; int SC_SWITCHING_PROTOCOLS = 101; int SC_OK = 200; int SC_CREATED = 201; int SC_ACCEPTED = 202; int SC_NON_AUTHORITATIVE_INFORMATION = 203; int SC_NO_CONTENT = 204; int SC_RESET_CONTENT = 205; int SC_PARTIAL_CONTENT = 206; int SC_MULTIPLE_CHOICES = 300; int SC_MOVED_PERMANENTLY = 301; int SC_MOVED_TEMPORARILY = 302; int SC_FOUND = 302; int SC_SEE_OTHER = 303; int SC_NOT_MODIFIED = 304; int SC_USE_PROXY = 305; int SC_TEMPORARY_REDIRECT = 307; int SC_BAD_REQUEST = 400; int SC_UNAUTHORIZED = 401; int SC_PAYMENT_REQUIRED = 402; int SC_FORBIDDEN = 403; int SC_NOT_FOUND = 404; int SC_METHOD_NOT_ALLOWED = 405; int SC_NOT_ACCEPTABLE = 406; int SC_PROXY_AUTHENTICATION_REQUIRED = 407; int SC_REQUEST_TIMEOUT = 408; int SC_CONFLICT = 409; int SC_GONE = 410; int SC_LENGTH_REQUIRED = 411; int SC_PRECONDITION_FAILED = 412; int SC_REQUEST_ENTITY_TOO_LARGE = 413; int SC_REQUEST_URI_TOO_LONG = 414; int SC_UNSUPPORTED_MEDIA_TYPE = 415; int SC_REQUESTED_RANGE_NOT_SATISFIABLE = 416; int SC_EXPECTATION_FAILED = 417; int SC_INTERNAL_SERVER_ERROR = 500; int SC_NOT_IMPLEMENTED = 501; int SC_BAD_GATEWAY = 502; int SC_SERVICE_UNAVAILABLE = 503; int SC_GATEWAY_TIMEOUT = 504; int SC_HTTP_VERSION_NOT_SUPPORTED = 505;
|
8.2、HttpServletResponse的常见应用
HttpServletResponse
的常见应用场景主要包括:
- 向浏览器输出信息(不再赘述)
- 文件下载
- 生成网页验证码图片
- 网页重定向
8.3、使用HttpServletResponse实现:文件下载
(1)在上述父项目下再使用Maven模板新建一个MavenWeb子项目:response
。
(2)同样地,为了避免以后出现兼容性问题,建议我们先将该子项目中的web.xml
文件替换成“web-app_4_0”
版本。
(3)同样地,在该子项目的main
文件夹下新建一个java
文件夹和一个resource
文件夹,并将它们分别标记为“源码根目录”和“资源根目录”。
(4)在resource
目录下放置一个供下载的图片文件测试图片.png
。
(5)同样地,配置Tomcat服务器,deploy(发布)response:war
包,并为该子项目添加虚拟路径映射:/resp
。
(6)同样地,在该子项目的java目录下新建一个com.atangbiji.servlet
包,并在该包下新建一个FileServlet
类,用于实现文件下载。同样地,让FileServlet
类继承(extends)HttpServlet
类,以实现Servlet
接口。然后,在FileServlet
类中重写doGet
和doPost
方法。
FileServlet.java
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| public class FileServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String realPath = this.getServletContext().getRealPath("/WEB-INF/classes/测试图片.png"); System.out.println(realPath); String fileName = realPath.substring(realPath.lastIndexOf("\\") + 1); resp.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(fileName,"UTF-8")); FileInputStream fileInputStream = new FileInputStream(realPath); int length = 0; byte[] buffer = new byte[1024]; ServletOutputStream outputStream = resp.getOutputStream(); while ((length = fileInputStream.read(buffer)) > 0){ outputStream.write(buffer,0,length); } outputStream.close(); fileInputStream.close(); }
@Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req,resp); } }
|
(7)同样地,在该子项目的Web
配置文件(web.xml)中注册(映射)我们自己编写的Servlet
,并为它提供一个浏览器可以访问的路径。
web.xml
文件:
1 2 3 4 5 6 7 8
| <servlet> <servlet-name>downFileServlet</servlet-name> <servlet-class>com.atangbiji.servlet.FileServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>downFileServlet</servlet-name> <url-pattern>/download</url-pattern> </servlet-mapping>
|
(8)在IDEA中启动Tomcat。在浏览器中输入http://localhost:8080/resp/download
,便可实现测试图片.png
文件的下载。
8.4、使用HttpServletResponse实现:生成验证码图片
网页验证码既可以在前端实现,也可以在后端实现。我们这里介绍如何使用java的图片类在后端生成验证码。
(1)同样地,在com.atangbiji.servlet
包下新建一个ImageServlet
类,用于生成验证码图片。同样地,让ImageServlet
类继承(extends)HttpServlet
类,以实现Servlet
接口。然后,在ImageServlet
类中重写doGet
和doPost
方法。
ImageServlet.java
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| public class ImageServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setHeader("refresh","3"); BufferedImage bufferedImage = new BufferedImage(100,20,BufferedImage.TYPE_INT_RGB); Graphics2D graphics = (Graphics2D)bufferedImage.getGraphics(); graphics.setColor(Color.white); graphics.fillRect(0,0,100,40); graphics.setColor(Color.red); graphics.setFont(new Font(null,Font.BOLD,20)); graphics.drawString(SetNum(),20,20); resp.setContentType("image/png"); resp.setDateHeader("expires",-1); resp.setHeader("Cache-Control", "no-cache"); resp.setHeader("program", "no-cache"); ImageIO.write(bufferedImage,"png",resp.getOutputStream()); }
private String SetNum(){ Random random = new Random(); String strNum = random.nextInt(99999) + ""; StringBuffer stringBuffer = new StringBuffer(); for (int i = 0; i < 5 - strNum.length(); i++) { stringBuffer.append("0"); } strNum = stringBuffer.toString() + strNum; return strNum; }
@Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req,resp); } }
|
(2)同样地,在该子项目的Web
配置文件(web.xml)中注册(映射)我们自己编写的Servlet
,并为它提供一个浏览器可以访问的路径。
web.xml
文件:
1 2 3 4 5 6 7 8
| <servlet> <servlet-name>ImageServlet</servlet-name> <servlet-class>com.atangbiji.servlet.ImageServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>ImageServlet</servlet-name> <url-pattern>/img</url-pattern> </servlet-mapping>
|
(3)在IDEA中启动Tomcat。在浏览器中输入http://localhost:8080/resp/img
,访问结果如下:
8.5、使用HttpServletResponse实现:网页重定向(重点)
8.5.1、什么是请求重定向
重定向(Redirect)指的是:一个web资源(Servlet1
)接受到客户端的请求后,通知客户端去调用另外一个web资源(Servlet2
)进行处理。
当浏览器向Tomcat
服务器发送请求时,由于一个Servlet1
类无法完成所有响应工作,此时Servlet1
类会通知浏览器重新定向到另一个Servlet2
类。浏览器则再向Servlet2
发送一个请求,来获取Servlet2
的响应。
8.5.2、重定向的工作原理
重定向的工作原理如下图所示:
8.5.3、重定向的特点
- 请求重定向发是客户端的行为。
- 请求重定向之后,浏览器地址栏中的 URL 会发生变化。
- 请求重定向支持跨域访问。
- 参与请求重定向的
Web
资源使用的是不同的 request
对象和 response
对象。
8.5.4、结论:“请求转发” VS ”请求重定向“(重点)
区别 |
请求转发 |
请求重定向 |
浏览器地址栏中的 URL 是否发生变化 |
否 |
是 |
是否支持跨域跳转 |
否 |
是 |
请求和响应的次数 |
1次请求和1次响应 |
2次请求和2次响应 |
是否共享 request 对象和 response 对象 |
是 |
否 |
是否通过request 域对象传递数据 |
是 |
否 |
速度 |
相对较快 |
相对较慢 |
行为类型 |
服务端行为 |
客户端行为 |
HTTP响应状态码 |
302 |
307 |
默认映射地址 |
localhost:8080/当前项目路径 |
localhost:8080 |
8.5.5、重定向的实现
(1)同样地,在com.atangbiji.servlet
包下新建一个RedirectServlet
类,用于实现重定向。同样地,让RedirectServlet
类继承(extends)HttpServlet
类,以实现Servlet
接口。然后,在RedirectServlet
类中重写doGet
和doPost
方法。
RedirectServlet.java
文件:
1 2 3 4 5 6 7 8 9 10 11
| public class RedirectServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.sendRedirect("/resp/img"); }
@Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req,resp); } }
|
(2)同样地,在该子项目的Web
配置文件(web.xml)中注册(映射)我们自己编写的Servlet
,并为它提供一个浏览器可以访问的路径。
web.xml
文件:
1 2 3 4 5 6 7 8
| <servlet> <servlet-name>RedirectServlet</servlet-name> <servlet-class>com.atangbiji.servlet.RedirectServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>RedirectServlet</servlet-name> <url-pattern>/redirect</url-pattern> </servlet-mapping>
|
(3)在IDEA中启动Tomcat。在浏览器中输入http://localhost:8080/resp/redirect
,此时浏览器地址栏中的 URL 会重定向为:http://localhost:8080/resp/img
。访问结果如下:
9、HttpServletRequest请求
HttpServletRequest
代表客户端的请求,通过HttpServletRequest
类及其父类(ServletRequest
类)的方法,可以获得客户端的所有信息。
9.1、获取前端传递的参数
HttpServletRequest
常用于获取用户的输入,即拿到前端传递的参数。常用成员函数主要包括:
1 2 3
| String getParameter(String var1);
String[] getParameterValues(String var1);
|
- 接下来,我们将通过实现登录功能,使用
HttpServletRequest
获取前端传递的参数。具体实现步骤如下:
(1)在上述父项目下再使用Maven模板新建一个MavenWeb子项目:request
。
(2)同样地,为了避免以后出现兼容性问题,建议我们先将该子项目中的web.xml
文件替换成“web-app_4_0”
版本。
(3)同样地,在该子项目的main
文件夹下新建一个java
文件夹和一个resource
文件夹,并将它们分别标记为“源码根目录”和“资源根目录”。
(4)同样地,配置Tomcat服务器,deploy(发布)request:war
包,并为该子项目添加虚拟路径映射:/req
。
(5)在父项目的pom.xml
文件中导入jsp
需要的依赖,那么所有的子项目就可以重复使用该依赖包了。
父项目的pom.xml
文件:
1 2 3 4 5 6 7 8 9 10
| <dependencies> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>javax.servlet.jsp-api</artifactId> <version>2.3.3</version> <scope>provided</scope> </dependency> </dependencies>
|
(6)修改该子项目下的index.jsp
文件,在其中编写登录页面。
index.jsp
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>登录页面</title> </head> <body> <h1>登录页面</h1> <div style="text-align: center"> <%-- (1)${pageContext.request.contextPath}表示当前项目路径。 (2)请求方式:post。 --%> <form action="${pageContext.request.contextPath}/login" method="post"> 用户名:<input type="text" name="username"><br> 密码:<input type="password" name="password"><br> 爱好: <input type="checkbox" name="hobbies" value="唱歌">唱歌 <input type="checkbox" name="hobbies" value="跳舞">跳舞 <input type="checkbox" name="hobbies" value="乒乓球">乒乓球 <input type="checkbox" name="hobbies" value="羽毛球">羽毛球
<br> <input type="submit" value="登录"> </form> </div> </body> </html>
|
注:
${pageContext.request.contextPath}
表示当前项目地址。
- 使用
post
请求方式不会在浏览器的URL地址栏显示数据内容;安全,但效率相对较低。
(7)同样地,在该子项目的java目录下新建一个com.atangbiji.servlet
包,并在该包下新建一个LoginServlet
类,用于在服务端获取登录信息。同样地,让LoginServlet
类继承(extends)HttpServlet
类,以实现Servlet
接口。然后,在LoginServlet
类中重写doGet
和doPost
方法。
LoginServlet.java
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| public class LoginServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setCharacterEncoding("utf-8"); req.setCharacterEncoding("utf-8"); String username = req.getParameter("username"); String password = req.getParameter("password"); String[] hobbies = req.getParameterValues("hobbies"); System.out.println("======================================"); System.out.println(username); System.out.println(password); System.out.println(Arrays.toString(hobbies)); System.out.println("======================================"); req.getRequestDispatcher("/success.jsp").forward(req,resp); }
@Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req,resp); } }
|
(8)同样地,在该子项目的Web
配置文件(web.xml)中注册(映射)我们自己编写的Servlet
,并为它提供一个浏览器可以访问的路径。
web.xml
文件:
1 2 3 4 5 6 7 8
| <servlet> <servlet-name>LoginServlet</servlet-name> <servlet-class>com.atangbiji.servlet.LoginServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>LoginServlet</servlet-name> <url-pattern>/login</url-pattern> </servlet-mapping>
|
(9)在该子项目的webapp
目录下新建一个success.jsp
文件,作为登录成功界面。在登录成功后,服务端通过请求转发的方式将页面跳转到该页面上来。
success.jsp
文件:
1 2 3 4 5 6 7 8 9
| <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>登录成功页面</title> </head> <body> <h1>恭喜你,登录成功!</h1> </body> </html>
|
(10)在IDEA中启动Tomcat。在浏览器中输入http://localhost:8080/req
进入登录页面,并在页面中输入用户名、密码和爱好。
(11)点击“登录”按钮,此时服务端通过请求转发的方式将页面跳转到登录成功的页面上来。
与此同时,服务端输出从前端获取到的信息:
9.2、请求转发(RequestDispatcher)
在Servlet
上下文中,我们已经介绍了如何使用ServletContext
实现请求转发。此外,在HttpServletRequest
获取前端传递参数的示例中,我们已经介绍了如何使用HttpServletRequest
实现请求转发。
具体实现代码如下:
1
| req.getRequestDispatcher("/success.jsp").forward(req,resp);
|
**注:**请求转发的默认映射地址为: localhost:8080/当前项目路径
;重定向的默认映射地址为:localhost:8080
。
(本讲完,系列博文持续更新中…… )
关注**“阿汤笔迹”** 微信公众号,获取更多学习笔记。
原文地址:http://www.atangbiji.com/2022/10/04/ServletInDetail
博主最新文章在个人博客 http://www.atangbiji.com/ 发布。