简述

服务器的资源可以分为静态资源和动态资源。静态资源是指访问同一个页面时所展现的内容没有发生变化,动态资源就是指同一页面展现的内容发生了变化。

Servlet的几种实现方式

手写一个Servlet应用

用IDEA方式实现一个Servlet应用

方式一,继承GenericServlet,并在web.xml里配置

  1. //IDEA方式一,继承GenericServlet,并在web.xml里配置
  2. public class SecondServlet extends GenericServlet {
  3. @Override
  4. public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
  5. System.out.println("hello");
  6. servletResponse.getWriter().println("second");
  7. }
  8. }
  1. <servlet>
  2. <servlet-name>servlet1</servlet-name>
  3. <servlet-class>com.simon.SecondServlet</servlet-class>
  4. </servlet>
  5. <servlet-mapping>
  6. <servlet-name>servlet1</servlet-name>
  7. <url-pattern>/second</url-pattern>
  8. </servlet-mapping>

image.png

方式二,HttpServlet,并在web.xml里配置

  1. //IDEA方式二,HttpServlet,并在web.xml里配置
  2. public class ThirdServlet extends HttpServlet {
  3. @Override
  4. public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
  5. System.out.println("hello,http");
  6. servletResponse.getWriter().println("third...");
  7. }
  8. }
  1. <servlet>
  2. <servlet-name>servlet2</servlet-name>
  3. <servlet-class>com.simon.ThirdServlet</servlet-class>
  4. </servlet>
  5. <servlet-mapping>
  6. <servlet-name>servlet2</servlet-name>
  7. <url-pattern>/third</url-pattern>
  8. </servlet-mapping>

image.png

方式三,继承HttpServlet,并在@WebServlet里配置

  1. //IDEA方式三,继承HttpServlet,并在@WebServlet里配置
  2. @WebServlet(name = "servlet4",urlPatterns = "/fourth")
  3. public class FourthServlet extends HttpServlet {
  4. @Override
  5. public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
  6. System.out.println("hello,http");
  7. servletResponse.getWriter().println("fourth");
  8. }
  9. }

image.png

方式三进化版,继承HttpServlet,并在@WebServlet里配置,注解里参数可简写

  1. //IDEA方式三,继承HttpServlet,并在@WebServlet里配置,注解里参数可简写
  2. @WebServlet("/fifth")
  3. public class FifthServlet extends HttpServlet {
  4. @Override
  5. public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
  6. System.out.println("hello,http");
  7. servletResponse.getWriter().println("fifth");
  8. }
  9. }

image.png

Tomcat与IDEA的关联方式

当我们在IDEA中,将一个写好的应用放置于Tomcat中后,IDEA与Tomcat是这样关联的:
首先,在IDEA的Tomcat Catalina Log里找到CATALINA_BASE,得到的这个目录,显示如下:
image.png
image.png
这个与Tomcat的解压缩目录高度雷同,其实就是因为IDEA复制了Tomcat的配置文件,据此自己生成了一个有类似功能的Tomcat。打开conf\Catalina\localhost\app.xml,其实可以看到如下内容:
image.png
而上图的docBase,在文件中的情况如下:
image.png
我们自己的开发环境的目录如下:
image.png

其实可以发现,IDEA在整个过程中是这样的情况:
IDEA从Tomcat那里复制一个IDEA版本的Tomcat来运行我们的应用,conf中app.xml的docBase指向的目录其实就是部署根目录,即部署根目录里WEB-INF和静态资源文件都是部署在这个目录中的。
image.png
这是IDEA版的Tomcat部署根目录,其组成其实是许多不同的资源或应用。
web根目录里包括WEB-INF、1.html和index.jsp,其中WEB-INF和index.jsp是从创建应用就有的,而1.html是自己创建的,创建之后临时放在web目录里,但最终作为静态资源会被放到部署根目录里;而开发环境的众多src源代码经过编译之后,在编译路径里有一份,可以在project compiler output路径或者Moduls的paths的Compiler output里找到,与此同时还有一份.class文件是要放到部署根目录的classes里的。
image.png
image.png
部署描述符是IDEA帮我们创建的web.xml文件,不需要我们手动创建。
Web Resource Directories是将我们开发环境下的web目录的所有文件在编译之后放到部署根目录中。

image.png
项目在编译完之后会打包成一个war格式的文件,部署到根目录之后,Tomcat会将这些war包展开,是为war exploded,形成解压形式的文件。这里是将war exploded作一个映射,servlet:war exploded就是整个输出路径(其就是部署根目录)的变量名。Output Layout就是输出的排布:classes里面的‘servlet’ compile output 其实就是Moduls的编译路径,即modules的.class文件复制到了部署根目录的classes目录下;‘servlet’modules:‘Web’ facet resources是指开发环境的web里所有资源会被同步到部署根目录的响应资源文件的位置上,比如web.xml和1.html、index.jsp等。
到此为止,部署根目录的所有文件或者应用的来源都搞清楚了,即:

  • WEB-INF的classes来源于Modules的编译输出的.class文件;
  • WEB-INF的web.xml来源于Facets的新创建的Web的部署描述符对应的路径,IDEA一开始就创建好的;
  • 1.html和index.jsp是静态资源文件,来源于要么我们在web的自建的一个1.html,要么创建web应用时就包含的index.jsp。

将一个SE项目改造成EE项目

初始状态

image.png
image.png

改造过程

image.png
image.png

展示效果

image.png

生命周期

介绍

servlet从出生到死亡就三个时期,servlet实例化、业务处理和销毁方法,其中实例化和销毁在一个servlet的生命周期只能执行一次,而service方法可以执行多次。

  1. package com.simon;
  2. import javax.servlet.ServletConfig;
  3. import javax.servlet.ServletException;
  4. import javax.servlet.annotation.WebServlet;
  5. import javax.servlet.http.HttpServlet;
  6. import javax.servlet.http.HttpServletRequest;
  7. import javax.servlet.http.HttpServletResponse;
  8. import java.io.IOException;
  9. @WebServlet("/life")
  10. public class LifeServlet extends HttpServlet {
  11. int count = 0;
  12. @Override
  13. public void init(ServletConfig config) throws ServletException {
  14. System.out.println("servlet init...");
  15. }
  16. protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  17. count++;
  18. System.out.println("servlet post..");
  19. }
  20. protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  21. count++;
  22. System.out.println("servlet get..");
  23. }
  24. @Override
  25. public void destroy() {
  26. System.out.println("访问次数:"+count);
  27. System.out.println("servlet destory...");
  28. }
  29. }

运行效果如下:
image.png

提前加载的问题

有时候希望初始化能在请求来到之前,这样使得servlet的容错性更强,即更能及早地发现问题。参数是load-on-startup这里有2种方式,一种是配置文件,一种是注解形式。

配置文件

  1. <servlet>
  2. <servlet-name>Life</servlet-name>
  3. <servlet-class>com.simon.LifeServlet</servlet-class>
  4. <load-on-startup>1</load-on-startup>
  5. </servlet>
  6. <servlet-mapping>
  7. <servlet-name>Life</servlet-name>
  8. <url-pattern>/life</url-pattern>
  9. </servlet-mapping>

注解

image.png

效果显示

image.png

关于url-partten

简述

url-partten是指浏览器过来的请求所要访问的实际应用名的映射,需要知道的是,一个servlet可以对应多个url-partten,但是不同servlet不能对应任何一个相同的url-partten。url-partten的写法有2种,一种是/开头,一种是*.开头,当一个请求附带url-partten来的时候,总是优先级最高的servlet去接收它,而这个优先级,以下面这个例子说明:

普通url-partten

假设有4个servlet和4个请求,每一个请求来都会有一个servlet来接收它:

  • Servlet1 映射到 /abc/*
  • Servlet2 映射到 /*
  • Servlet3 映射到 /abc
  • Servlet4 映射到 *.do

    1. public class UrlPartten1 extends HttpServlet {
    2. @Override
    3. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    4. resp.getWriter().println("/abc/*");
    5. }
    6. }
    1. public class UrlPartten2 extends HttpServlet {
    2. @Override
    3. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    4. resp.getWriter().println("/*");
    5. }
    6. }
    1. public class UrlPartten3 extends HttpServlet {
    2. @Override
    3. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    4. resp.getWriter().println("/abc");
    5. }
    6. }
    1. public class UrlPartten4 extends HttpServlet {
    2. @Override
    3. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    4. resp.getWriter().println("*.do");
    5. }
    6. }
    1. <servlet>
    2. <servlet-name>urlpartten1</servlet-name>
    3. <servlet-class>com.simon.urlpartten.UrlPartten1</servlet-class>
    4. </servlet>
    5. <servlet-mapping>
    6. <servlet-name>urlpartten1</servlet-name>
    7. <url-pattern>/abc/*</url-pattern>
    8. </servlet-mapping>
    9. <servlet>
    10. <servlet-name>urlpartten2</servlet-name>
    11. <servlet-class>com.simon.urlpartten.UrlPartten2</servlet-class>
    12. </servlet>
    13. <servlet-mapping>
    14. <servlet-name>urlpartten2</servlet-name>
    15. <url-pattern>/*</url-pattern>
    16. </servlet-mapping>
    17. <servlet>
    18. <servlet-name>urlpartten3</servlet-name>
    19. <servlet-class>com.simon.urlpartten.UrlPartten3</servlet-class>
    20. </servlet>
    21. <servlet-mapping>
    22. <servlet-name>urlpartten3</servlet-name>
    23. <url-pattern>/abc</url-pattern>
    24. </servlet-mapping>
    25. <servlet>
    26. <servlet-name>urlpartten4</servlet-name>
    27. <servlet-class>com.simon.urlpartten.UrlPartten4</servlet-class>
    28. </servlet>
    29. <servlet-mapping>
    30. <servlet-name>urlpartten4</servlet-name>
    31. <url-pattern>*.do</url-pattern>
    32. </servlet-mapping>

    image.png
    通过试验可以证明:①/开头的url-partten优先级高于开头的,即开头的请求总是会被/*对应的servlet接收到。 ②统一是/开头的url-partten,匹配程度越高,优先级越高。

特殊url-partten

  1. public class SpecialUrpartten1 extends HttpServlet {
  2. @Override
  3. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  4. resp.getWriter().println("/*");
  5. }
  6. }
  1. <servlet>
  2. <servlet-name>special1</servlet-name>
  3. <servlet-class>com.simon.specialUrlpartten.SpecialUrpartten1</servlet-class>
  4. </servlet>
  5. <servlet-mapping>
  6. <servlet-name>special1</servlet-name>
  7. <url-pattern>/*</url-pattern>
  8. </servlet-mapping>
  1. public class SpecialUrpartten2 extends HttpServlet {
  2. @Override
  3. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  4. resp.getWriter().println("/");
  5. }
  6. }
  1. <servlet>
  2. <servlet-name>special2</servlet-name>
  3. <servlet-class>com.simon.specialUrlpartten.SpecialUrpartten2</servlet-class>
  4. </servlet>
  5. <servlet-mapping>
  6. <servlet-name>special2</servlet-name>
  7. <url-pattern>/</url-pattern>
  8. </servlet-mapping>

image.png
当一开始发起1.html请求和index.jsp请求的时候,都是/*对应的servlet在接收,如下图:
image.png

将/注释之后,相同条件下,idnex.jsp却不会被/接收,而是输出自己的内容。如下图:
image.png
这是因为没有servlet可以处理/1.html请求,这个时候正常应该交给缺省servlet,但是你的应用中又一个 /,表示你重新实现了一个新的缺省servlet,系统默认提供的就不会再给你使用了,所以最终显示出来 /
缺省Servlet的本质:*不管你去请求动态资源还是去请求静态资源html、jpg、png等,最终都会有一个servlet来处理你的请求

请求的执行流程 HTTP

以访问http://localhost/app/servlet1 http://localhost/app/form.html

1.浏览器生成一个HTTP请求报文,传到目的机器之后

2.被监听着80端口号的Connector接收到,将请求报文解析成为Request对象,同时还提供一个response对象

3.这两个对象被传给engine,engine挑选合适的host将对象进行进一步传递

4.host去挑选一个叫做app的 Context,将这两个对象传给Context

5.请求是/servlet1或者/form.html,请求到来首先看有没有servlet可以处理该请求,如果有的话,那么交给优先级最高的那个servlet,(如果是第一次访问,通过反射实例化一个对象出来,执行init方法),直接执行该servlet的service方法,将这两个对象作为参数传递进去;如果没有servlet可以处理该请求,还是会交给缺省servlet来处理,大体的逻辑就是找到该文件,然后做出对应的响应,如果你系统中重新实现了一个新的缺省servlet,那么最终会调用你自己实现的这个。

6.service方法在执行的过程中,我们传入了response对象,往response里面写入数据,Connector读取response里面的内容,然后生成响应报文

7.响应报文再传回给客户端,客户端进行解析,渲染

ServletConfig

  1. package com.simon.config.servletConfig;
  2. import javax.servlet.*;
  3. import java.io.IOException;
  4. public class ServletConfigDemo1 extends GenericServlet {
  5. @Override
  6. public void service(ServletRequest servletRequest, ServletResponse servletResponse)
  7. throws ServletException, IOException {
  8. //tomcat在启动的时候,会去解析init-param标签,然后将这个键值对封装到ServletConfig对象中
  9. //ServletConfig里面存了一个map,解析init-param里面的数据,然后放入config对象中
  10. //你需要去做的,只是拿到ServletConfig对象,然后利用它提供的一个方法,来获取数据即可
  11. ServletConfig servletConfig = getServletConfig();
  12. //就是对map的一层封装
  13. String username = servletConfig.getInitParameter("username");
  14. String password = servletConfig.getInitParameter("password");
  15. System.out.println("username:" + username + ",password:" + password);
  16. }
  17. }
  1. <servlet>
  2. <servlet-name>servletconfig</servlet-name>
  3. <servlet-class>com.simon.config.servletConfig.ServletConfigDemo1</servlet-class>
  4. <init-param>
  5. <param-name>username</param-name>
  6. <param-value>zs</param-value>
  7. </init-param>
  8. <init-param>
  9. <param-name>password</param-name>
  10. <param-value>iamzs</param-value>
  11. </init-param>
  12. </servlet>
  13. <servlet-mapping>
  14. <servlet-name>servletconfig</servlet-name>
  15. <url-pattern>/servletconfig</url-pattern>
  16. </servlet-mapping>

image.png

ServletContext

上下文对象。该对象会随着应用的启动而创建,只有当应用被卸载,该对象才会消失。它的生命周期基本和应用的生命周期是一模一样的。

domain。共享空间。如果两个servlet在运行的时候,希望能够共享数据?
servlet1运行时产生了部分数据,接下来其他servlet也需要用到,如果将数据在多个servlet之间进行共享呢?
image-20210409112316716.png

  1. package com.simon.servletContext;
  2. import javax.servlet.*;
  3. import javax.servlet.annotation.WebServlet;
  4. import java.io.IOException;
  5. @WebServlet("/domain1")
  6. public class ServletContextDemo1 extends GenericServlet {
  7. @Override
  8. public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
  9. ServletContext servletContext = getServletContext();
  10. servletContext.setAttribute("username","what is love");
  11. }
  12. }
  1. package com.simon.servletContext;
  2. import javax.servlet.*;
  3. import javax.servlet.annotation.WebServlet;
  4. import java.io.IOException;
  5. @WebServlet("/domain2")
  6. public class ServletContextDemo2 extends GenericServlet {
  7. @Override
  8. public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
  9. ServletContext servletContext = getServletContext();
  10. Object username = servletContext.getAttribute("username");
  11. System.out.println(username);
  12. }
  13. }

image.png

Demo:实现网站的历史访问人数

  1. package com.simon.historyCount;
  2. import javax.servlet.*;
  3. import javax.servlet.annotation.WebServlet;
  4. import java.io.IOException;
  5. @WebServlet("/v1")
  6. public class ViewPage1 extends GenericServlet {
  7. @Override
  8. public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
  9. ServletContext servletContext = getServletContext();
  10. Integer count = (Integer) servletContext.getAttribute("count");
  11. if (count == null){
  12. count = 1;
  13. }else {
  14. count++;
  15. }
  16. servletContext.setAttribute("count",count);
  17. System.out.println("访问次数:" + count);
  18. }
  19. }
  1. package com.simon.historyCount;
  2. import javax.servlet.*;
  3. import javax.servlet.annotation.WebServlet;
  4. import java.io.IOException;
  5. @WebServlet("/v2")
  6. public class ViewPage2 extends GenericServlet {
  7. @Override
  8. public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
  9. ServletContext servletContext = getServletContext();
  10. Integer count = (Integer) servletContext.getAttribute("count");
  11. if (count == null){
  12. count = 1;
  13. }else {
  14. count++;
  15. }
  16. servletContext.setAttribute("count",count);
  17. System.out.println("访问次数:" + count);
  18. }
  19. }

image.png

获取绝对路径

image.pngimage.png

  1. package com.simon.path;
  2. import javax.servlet.*;
  3. import javax.servlet.annotation.WebServlet;
  4. import java.io.File;
  5. import java.io.IOException;
  6. @WebServlet("/path")
  7. public class AbsolutePath extends GenericServlet {
  8. @Override
  9. public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
  10. //获取form.html绝对路径
  11. //1.为什么会到tomcat/bin目录下?
  12. //new File里面输入的是一个相对路径,相对的是哪个路径,user.dir jvm
  13. //ee项目没有一个main方法,程序是tomcat运行之后调用我们的代码 tomcat开启jvm
  14. //2.怎么才能获取到部署根目录下的一个文件的绝对路径
  15. // 想一个概念 应用 Context path docBase 应用的根目录
  16. //docBase的地址 tomcat绝对知道 它给我们提供了一个入口
  17. File file = new File("form.html");
  18. //输出——> D:\Software\Tomcat\apache-tomcat-8.5.73\bin\form.html
  19. System.out.println(file.getAbsolutePath());
  20. //
  21. ServletContext servletContext = getServletContext();
  22. //这个方法其实就是对于docBase的一个封装
  23. //如果你输入的是一个空字符串,那么它返回的就是docBase的地址
  24. //接下来,如果你希望获取部署根目录下面的任意一个文件的绝对路径
  25. //其实只需要docBase + 相对部署根目录的相对路径 === 绝对路径
  26. String realPath = servletContext.getRealPath("");
  27. File f = new File(realPath + "/" + "form.html");
  28. System.out.println(f.exists()); //true
  29. //能不能获取到WEB-INF目录下面一个文件的绝对路径
  30. //WEB-INF只是用来拦截客户端的直接访问 屏蔽客户端的访问
  31. //坚固的堡垒总是从内部突破
  32. File f2 = new File(realPath + "/WEB-INF/1.txt");
  33. System.out.println(f2.exists()); //true
  34. }
  35. }