6.1、Servlet简介
-
servlet是sun公司开发动态web的一门技术
-
sum在这些API中提供了一个接口叫做:Servlet.开发的两个步骤:
- 编写一个类,实现Servlet接口
- 把开发好的Java类部署到web服务器中
把实现了Servlet接口的Java程序叫做Servlet
6.2、Hello Servlet
-
构建普通的Maven工程,删除src目录
-
建立子工程
-
这个子工程选取Maven模板中的webapp
-
父工程:
<modules> <module>demo-01</module> </modules>
-
子工程:
<parent> <artifactId>JavaWebLearning</artifactId> <groupId>org.example</groupId> <version>1.0-SNAPSHOT</version> </parent>
-
父工程与子工程间依赖的关系类似于java的继承关系
-
-
Maven环境优化:
-
修改web.xml
注:web.xml中的内容可以参考对应的Tomcat中的样例并与之相匹配
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" > <web-app> <display-name>Archetype Created Web Application</display-name> </web-app>
-
将maven的结构搭建完整:添加java和resources目录(注意Mark As)
-
-
编写一个Servlet程序
-
编写一个普通类并继承Http
package com.guan.servlet; import javax.servlet.http.HttpServlet; public class TestServlet extends HttpServlet { }
注:这里遇到找不到HttpServlet的问题需要导入相关的依赖,可以
Alt+Enter
,选择Add Maven dependency
,将javax.servlet.http依赖导入
(前提本地的Maven仓库里有,否则需要到Maven的中央仓库下载.补充一下查找应该下载什么的思路:我们已经安装了Tomcat,而Tomcat是java开发的,既然javaweb能在里面跑肯定是由对应的jar,因此我们直接点开Tomcat的文件夹看看即可) -
实现Servlet接口
ctrl+o
选择override两个方法:doGet
+doPost
注:通常两个方法的基本逻辑是一样,所以我们可以选择直接在doPost方法中调用doGet方法
public class TestServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //响应的类型:html resp.setContentType("text/html"); //获取响应的输出流 PrintWriter writer = resp.getWriter(); writer.println("<html>"); writer.println("<head>"); writer.println("<title>Hello World!</title>"); writer.println("</head>"); writer.println("<body>"); writer.println("<h1>12哈哈</h1>"); writer.println("</body>"); writer.println("</html>"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doPost(req, resp); } }
-
-
在web.xml中注册servlet的实现类,编写Servlet的映射
为什么要用映射:我们写的Java程序要通过浏览器访问,而浏览器要通过web服务器访问,所以我们要在web服务器中注册我们写的Servlet,还需要给它一个浏览器能访问的路径
<!-- 注册--> <servlet> <servlet-name>hello</servlet-name> <servlet-class>com.guan.servlet.TestServlet</servlet-class> </servlet> <!-- 映射--> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping>
-
此时你会发现程序还无法run(相关的标记是灰色的),配置Tomcat:,直接在模板中选择,有的话直接可以选择,没有的话创建一个,详见之前的博文,注意两点:
- 点击
+
才是往里面添加东西 - 需要fix一下deployment
- 点击
-
测试
在浏览器中查看相应的链接,发现
/hello
下的内容有乱码无法正常显示:12??
,这是因为Java中默认的字符集是ISO8859-1
,我们需要将其改为utf-8
public class TestServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //响应的类型:html resp.setContentType("text/html"); //将输出的编码格式改为utf-8 resp.setCharacterEncoding("utf-8"); //获取响应的输出流 PrintWriter writer = resp.getWriter(); writer.println("<html>"); writer.println("<head>"); writer.println("<title>Hello World!</title>"); writer.println("</head>"); writer.println("<body>"); writer.println("<h1>12哈哈</h1>"); writer.println("</body>"); writer.println("</html>"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doPost(req, resp); } }
6.3、分析servlet源码:主要查看post/get方法的运作
-
结构:
-
点开HttpServlet:
public abstract class HttpServlet extends GenericServlet
我们可以看到它继承了GenericServlet这个类
-
继续点开GenericServlet:
public abstract class GenericServlet implements Servlet, ServletConfig, Serializable
我们可以看到这是一个抽象类,继承了三个接口,其中servletConfig显然是用于配置的,Seralizable是用于实体序列化的接口,所以我们重点看Servlet这个接口
-
继续点开Servlet
public interface Servlet { void init(ServletConfig var1) throws ServletException; ServletConfig getServletConfig(); void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException; String getServletInfo(); void destroy(); }
好家伙,终于到底了!分析一下几个方法:
- init:显然是用于初始化的
- ServletConfig:显然是用于配置的
- servic:没错和post/get应该就是这个,我们后面回溯主要就看这个方法
- getServletInfo:显然是用于获得Servlet信息的
- destory:显然是用于销毁的
-
-
回溯:
-
查看GenericServlet抽象类:
public abstract void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
service方法没有被实现
-
查看HttpServlet类:
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; try { ifModifiedSince = req.getDateHeader("If-Modified-Since"); } catch (IllegalArgumentException var9) { ifModifiedSince = -1L; } if (ifModifiedSince < lastModified / 1000L * 1000L) { 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); } }
可以看到正是这个方法完成了Post/Get等方法的实际处理
-
-
总结:
-
HttpServlet继承了GenericServlet抽象类,而GenericServlet又是继承了Servlet接口
-
HttpServlet中的Post/Get方法实际是对Servlet接口的Service方法的扩充
-
6.3 Servlet原理
Servlet是由服务器调用,web服务器在收到浏览器请求之后,会:
- 浏览器直接访问的对象是web容器(Tomcat),浏览器发起请求,web容器响应
- 相关的Servlet的class文件只会生成一次,后面会直接调用(所以初始化较慢
- web容器会产生两个对象:请求(Request)和响应(Response)
- 这两个对象会调用2中生成的Servlet对象的doPost/doGet方法,当然本质上是service方法
- Request会从service(请求)拿到请求并且把请求之后的响应交给Response
- 我们重写了doGet/doPost方法,web容器通过调用service方法间接地调用doGet/doPost方法
6.4、Mapping
-
一个Servlet可以指定一个映射路径
<servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping>
-
一个Servlet可以指定多个映射路径
<!-- 映射--> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello1</url-pattern> </servlet-mapping>
-
一个Servlet可以指定通用映射路径
<!-- 映射--> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello/*</url-pattern> </servlet-mapping>
-
默认请求路径
<!-- 映射--> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping>
-
指定一些后缀
注:此时*前面不能加映射的路径
<!-- 无法使用--> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/*.hello</url-pattern> </servlet-mapping> <!-- 无法使用--> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello/*.hello</url-pattern> </servlet-mapping> <!-- 可以使用--> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>*.hello</url-pattern> </servlet-mapping>
6.5、拦截器
优先级的区别:一对一的最高,通配的其次
-
写好相应的Error类
public class Test extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setCharacterEncoding("utf-8"); resp.setContentType("text/html"); PrintWriter writer = resp.getWriter(); writer.write("<h1>404</h1>"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } }
-
在web.xml中注册
<servlet> <servlet-name>error</servlet-name> <servlet-class>com.guan.servlet.Test</servlet-class> </servlet>
-
映射
<servlet-mapping> <servlet-name>error</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping>
6.6、ServletContext
架构:
获得:this.getServletContext():获得Servlet上下文
web容器在启动的时候,它会为每个web程序都创建一个对应的ServletContext对象,它代表了当前的web应用:
-
数据共享
注:这里我们最好用一个全新的module进行演示.但是deployment有需要注意设置的地方:每一个module的deployment应该是独立的,否则如果一个deployment中有多个module,很可能会造成打包的时候把其它module也打包了.显然项目一多,打包就会显得很慢
我们在这个Servlet中保存的数据,可以在另一个Servlet中拿到
public class setContext extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setCharacterEncoding("utf-8"); resp.setContentType("text/html"); ServletContext context = req.getServletContext(); String username = (String) context.getAttribute("username"); System.out.println(username); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } }
public class getContext extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //响应的类型:html resp.setContentType("text/html"); //将输出的编码格式改为utf-8 resp.setCharacterEncoding("utf-8"); //获得上下文 ServletContext context = req.getServletContext(); context.setAttribute("username","guan"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doPost(req, resp); } }
<servlet> <servlet-name>set</servlet-name> <servlet-class>com.guan.servlet.getContext</servlet-class> </servlet> <servlet> <servlet-name>get</servlet-name> <servlet-class>com.guan.servlet.setContext</servlet-class> </servlet> <!-- 映射--> <servlet-mapping> <servlet-name>set</servlet-name> <url-pattern>/set</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>get</servlet-name> <url-pattern>/get</url-pattern> </servlet-mapping>