JavaWeb之Servlet《笔记》

JavaWeb之《Servlet》

在集成开发环境中开发Servlet程序

  • 集成开发工具很多,其中目前使用较多为IDEA
  • 实现javax.servlet接口中的五个方法

Servlet生命周期

  • Servlet对象的创建,对象上方法的调用,对象最终的销毁,javaweb程序员无权干涉。

  • 是由Tomcat服务器(WEB Server)全权负责的。

  • Tomcat服务器通常我们又称为:WEB容器

  • WEB容器来管理Servlet对象的死活

  • 我们自己new的Servlet对象是不受WEB容器管理的

  • 研究:服务器在启动的Servlet对象有没有被创建出来?

    • 在Servlet中提供一个无参数的构造方法,启动服务器的时候看看构造方法是否执行
    • 经过测试得出结论:默认情况下,服务器在启动的时候Servlet并不会被初始化
    • 用户没有发送请求之前,如果提前创建出来所有的Servlet对象,必然是耗费内存的。
  • 怎么在服务器启动的时候创建Servlet对象

    • 在servlet配置标签中用子标签

    • <load-on-startup>0<load-on-startup>
      
    • 数字越小,启动越靠前,最小值0,用户的体验更好

  • 用户在发送第一次请求的时候Servlet对象被实例化

  • Servlet对象被创建出来之后,Tomcat服务器马上调用了Servlet对象的init方法。(init方法在执行的时候,Servlet对象已经存在了,已经被创建出来了)

  • 用户发送第一次请求的时候,init方法执行之后,Tomcat服务器马上调用Servlet对象的service方法

  • 用户发送第二次请求的时候,控制台只执行了service方法,说明:

    • Servlet对象是单例的(单实例的。但是要注意:Servlet对象是单实例的,但是Servlet类并不符合单例模式。我们称之为假单例)
    • 无参数构造方法,init方法只在第一次用户发送请求的时候执行。也就是说无参数构造方法只执行一次。init方法也只被Tomcat服务器调用一次。
    • 只要用户发送一次请求:service方法必然会被Tomcat服务器调用一次。发送100次请求,service方法会被调用100次。
  • Servlet的destroy方法只会被Tomcat服务器调用一次

    • destroy方法是在什么时候被调用的?
      • 在服务器关闭的时候
      • 因为服务器关闭的时候要销毁Servlet对象的内存
      • 服务器在销毁Servlet对象内存之前,Tomcat服务器会自动调用Servlet对象的destroy方法
    • destroy方法调用的时候,对象销毁了还是没有销毁?
      • destroy方法执行的时候Servlet对象还在,没有被销毁
  • 关于Servlet类中方法的调用次数?

    • 构造方法只执行一次
    • init方法只执行一次
    • service方法,发送N次请求则执行N次
    • destroy方法,只执行一次
  • 当我们Servlet类中编写一个有参数的构造方法,如果没有手动编写无参数构造方法会出现什么问题?

    • 报错:500
    • 一般情况下是因为服务器端的Java程序出现了异常
    • 所以在Servlet开发中,不建议程序员定义构造方法
  • 思考:Servlet的无参构造方法是在对象第一次创建的时候执行,并且只执行一次。init方法也是在对象第一次创建的时候执行,并且只执行一次,那么这个无参构造方法可以代替init方法吗?

    • 不能
    • Servlet规范中有要求,不建议手动编写构造方法,因为编写构造方法,很容易让无参构造方法消失,这个操作可能会导致Servlet对象无法实例化。所以init方法是有存在的必要的。
  • init和destroy一般用不上

适配器模式改造Servlet

  • 我们编写一个Servlet类直接实现Servlet接口有什么缺点?

    • 我们只需要service方法,其他方法大部分情况下是不需要使用的。代码很丑陋
  • 适配器设计模式Adapter

    • 编写一个标准通用的Servlet,起名:GenericServlet
    • 以后所有的Servlet类都不要直接实现Servlet接口了
    • 以后所有的Servlet类都要继承GenericServlet类
    • GenericServlet就是一个适配器
    • 把GenericServlet类设置为抽象类
    • 把其中常用的方法设置为抽象方法,其他方法都固定写好
    • 然后子类重写GenericServlet中的抽象方法
  • 思考:GenericServlet类是否需要改造一下?怎么改造?更利于子类程序的编写?

    • 思考第一个问题:我提供了一个GenericServlet之后,init方法还会执行吗?

      • 还会执行,会执行GenericServlet类中的init方法
    • 思考第二个问题:init方法是谁调用的?

      • Tomcat服务器调用的
    • 思考第三个问题:init方法中的ServletConfig对象是谁创建的?是谁传过来的?

      • 都是Tomcat干的
      • Tomcat服务器创建了ServletConfig对象,然后调用init方法,将ServletConfig对象传给了init方法
    • 思考一下Tomcat服务器伪代码:

      public class Tomcat{
          public static void main(String[] args){
              //...
              //Tomcat服务器伪代码
              //创建LoginServlet对象(通过反射机制,调用无参构造方法来实例化LoginServlet对象)
              Class clazz = Class.forName("xxxxxxxxxxx");
              Object o = clazz.newInstance();
              
              //向下转型,Object类是Servlet类的父类
              Servlet servlet = (Servlet)o;
              
              //创建ServletConfig对象
              //Tomcat服务器负责将ServletConfig对象实例化出来
              
              //调用init方法
              
              //调用service方法
          }
      }
      
    • 有没有一种可能,需要我在LoginServlet类中重写init方法

      • 可以把Servlet类中的init方法前设为final,创建一个无参的init方法,然后在init方法中调用该类中的另一个init方法,在子类中可以重写无参的init方法

ServletConfig

  • 是Servlet规范中的一员

  • ServletConfig是一个接口

  • 谁实现了ServletConfig这个接口?

    • Tomcat(服务器)
  • 一个Servlet对象中有一个ServletConfig对象,1对1。

  • ServletConfig对象是什么时候创建的?

    • 在创建Servlet对象的时候,同时创建ServletConfig对象
  • ServletConfig接口到底是干什么的?有什么用?

    • Configuration
    • ServletConfig被翻译为:Servlet对象的配置信息对象
  • ServletConfig对象中到底包装了什么信息?

    • <servlet>
              <servlet-name>UserServlet</servlet-name>
              <servlet-class>servlets.UserServlet</servlet-class>
      </servlet>
      
    • ServletConfig对象中包装的信息是:

      • web.xml文件中标签的配置信息
  • ServletConfig接口中有哪些方法,可以获取到web.xml文件中的初始化参数配置信息

    • <!--在<servlet></servlet>中配置-->
      <init-param>
                  <param-name>key</param-name>
                  <param-value>value</param-value>
       </init-param>
      
    • getInitParameterNames()    //获取所有的初始化参数的name,是一个集合
      getInitParameter(java.lang.String name)      //获取指定名字的value
      
    • 可以不用获得config对象,直接用this调用上面两个方法也可以

ServletContext

  • ServletContext是什么?

    • 是Servlet规范中的一员
  • 是谁实现的?

    • Tomcat
  • 是谁创建的?在什么时候创建的?

    • 对于一个webapp来说,ServletContext对象只有一个

    • ServletContext对象在服务器关闭的时候销毁

  • 怎么理解?

    • Servlet对象的环境对象。(Servlet对象的上下文对象)
    • ServletContext对象其实对应的就是整个web.xml文件
    • 放在ServletContext对象当中的数据,所有Servlet共享的。
  • 常用方法?

    • public String getInitParameter (String name); //通过初始化参数的name获取value
      public Enumeration<String> getInitParameterNames(); //获取所有的初始化参数的name
      
  • ServletContext的public Enumeration getInitParameterNames(),获得的是以下代码的name

     <context-param>
            <param-name>pageSize</param-name>
            <param-value>10</param-value>
     </context-param>
    
    <!--注意:以上的配置信息属于应用级的配置信息,一般一个项目中共享的配置信息会放到以上的标签中。如果配置信息只是想给某一个serevlet作为参考,那么配置到servlet标签中即可,使用ServletConfig来获取-->
    
  • //获取应用的根路径(非常重要),因为在java源代码当中有一些地方可能会需要应用根路径,这个方法可以动态获取应用的根路径
    //在java源码当中,不要将应用的根路径写死,因为永远也不知道这个应用在最终部署的时候,起一个什么名字
    
    public String getContextPath();
    String contextPath = application.getContextPath();
    
  • //获取文件的绝对路径
    public String getRealPath(String path);
    String realPath = application.getRealPath("/index.html");
    "/"代表的是web的根
    
  • //ServletContext对象还有另一个名字:应用域(后面还有其他域,例如:请求域、会话域)
    //所有的用户共享一份数据,并且这个数据很少的被修改,并且这个数据量很少。放到应用域中,会大大提升效率,因为应用域相当于一个缓存,放到缓存中的数据,下次再使用的时候,不需要从数据库中再次获取,大大提升执行效率
    
    //存(怎么向ServletContext应用域中存数据)
    public void setAttribute(String name,Object value);
    //取(怎么从ServletContext应用域中取数据)
    public Object getAttribute(String name);
    //删(怎么删除ServletContext应用域中的数据)
    public void removeAttribute(String name);
    

HTTP协议

  • 什么是协议?

    • 协议其实是某些人,或者某些组织提前制定好的一套规范
  • 什么是HTTP协议

    • 是W3C指定的一种超文本协议
    • 这种协议游走在B和S之间,B向S发数据要遵循HTTP协议。S向B发数据同样需要遵循HTTP协议,这样B和S才能解耦合
    • 什么是解耦合?
      • B和S互相都不依赖
  • 什么是超文本?

    • 不是普通文本,比如流媒体:声音,视频。。。
  • B/S表示:B/S结构的系统(浏览器访问WEB服务器的系统)

  • 浏览器向WEB服务器发送数据,叫做:请求(request)

  • WEB服务器向浏览器发送数据,叫做:响应(response)

  • HTTP协议包括:

    • 请求协议(B->S)

      • 请求行
      • 请求头
      • 空白行:用来分隔头和体
      • 请求体
    • 响应协议 (S->B)

      • 状态行
      • 响应头
      • 空白行
      • 响应体
  • 405表示前端发送的请求方式与后端请求的处理方式不一致时发生:

    • 比如:前端是POST请求,后端的处理方式按照get方式进行处理时,发生405
    • 比如:前端是GET请求,后端的处理方式按照post方式进行处理时,发生405
  • 404是前端错误

  • 500表示服务器端的程序出现了异常,一般认为是服务器端的错误导致的

  • 以4开始的一般是浏览器端的错误导致的

  • 以5开始的一般是服务器端的错误导致的

  • 请求行包括三部分

    • 请求方式
      • get(常用)
      • post(常用)
      • delete
      • put
      • head
      • options
      • trace
    • URI
      • 什么是URI?统一资源标识符。代表网络种某个资源的名字。但是通过URI是无法定位资源的。
      • 什么是URL?统一资源定位符。代表网络中某个资源,同时,通过URL是可以定位到该资源的。
      • URI和URL是什么关系,有什么区别?
        • URL包括URI
        • http://localhost:8080/servlet/index.html 这是URL
        • /servlet/index.html 这是URI
    • HTTP协议版本号
  • 怎么向服务器发送GET请求,怎么向服务器发送POST请求?

    • 到目前为止,只有一种情况可以发送POST请求:使用form表单,并且form标签中的method属性值为:method =“post”。
    • 其他情况一律都是get请求
      • 在浏览器地址栏上直接输入URL,敲回车,属于get请求
      • 在浏览器上直接点击超链接,属于get请求
  • GET请求和POST请求有什么区别?

    • GET请求发送数据的时候,数据会挂在URI的后面,并且在URI后面添加一个”?“,"?"后面是数据,这样会导致发送的数据回显在浏览器的地址栏上。(get请求在请求行上发送数据)
    • GET请求只能发送普通的字符串。并且发送的字符串长度有限制,不同的浏览器限制不同,这个没有明确的规定
    • GET请求无法发送大数据量,是绝对安全的
    • post请求发送数据的时候,在请求体当中发送。不会回显到浏览器的地址栏上,也就是说post发送的数据,在浏览器地址栏上看不到。(post在”请求体“当中发送数据)
    • POST请求可以发送任何类型的数据,包括普通字符串,流媒体等信息
    • POST请求可以发送大数据量,是危险的,POST是为了提交数据,所以一般情况下拦截请求的时候,大部分会拦截POST请求
    • GET请求比较适合从服务器端获取数据
    • POST请求比较适合向服务器端发送数据
    • GET请求支持缓存
    • POST请求不支持缓存
    • 任何一个get请求最终的”响应结果“都会被浏览器缓存起来。在浏览器缓存当中:一个get请求的路径对应一个资源
    • 大部分form表单提交,都是post方式,因为form表单中要填写大量的数据,这些数据是收集用户的信息,一般是需要传给服务器,服务器将这些数据保存/修改等。
  • GET请求和POST请求如何选择?

    • 不管你是get请求还是post请求,发送的请求数据格式是完全相同的,只不过位置不同,格式都是统一的;
    • name=value&name=value&name=value&name=value
    • name是什么?
      • 以form表单为例:form表单中input标签的name
    • value是什么?
      • 以form表单为例:form表单中input标签的value

模板方法设计模式

  • 什么是模板方法设计模式?

  • //模板方法
       //添加final之后,这个方法无法被覆盖,这样核心的算法也可以得到保护。
       //模板方法定义核心的算法骨架,具体的实现步骤可以延迟到子类当中去实现。
       //核心算法一方面是得到了保护,不能被改变。另外一方面就是算法得到了重复使用。
       //另外代码也得到了复用,因为算法中某些步骤的代码是固定的,这一部分代码可以写到模板类当中
       public final void day(){
           qichaung();
           xishu();
           chizaocan();
           dosome();
           shuijiao();
     
       }
     
       public void qichaung(){
           System.out.println("起床");
       }
       public void xishu(){
           System.out.println("洗漱");
       }
       public void chizaocan(){
           System.out.println("吃早餐");
       }
       public abstract void dosome();
       public void shuijiao(){
           System.out.println("睡觉");
       }
    

HttpServlet源码分析

  • HttpServlet在哪个包下?

    • javax.servlet.http.HttpServlet
  • http包下都有哪些类和接口?

    • javax.servlet.http.HttpServlet(抽象类)
    • javax.servlet.http.HttpServletRequest(Http请求对象)
    • javax.servlet.http.HttpServletResponse(Http响应对象)
  • HttpServletRequest对象中封装了什么信息?

    • HttpServletRequest中封装了请求协议的全部内容
    • Tomcat服务器(WEB服务器)将”请求协议“中的数据全部解析处理,然后将这些数据全部封装到request对象当中了。
    • 也就是说,我们只要面向HttpServletRequest,就可以获取请求协议中的数据。
  • HttpServletResponse是专门响应HTTP协议到浏览器的。

  • 源码分析:

    public class HelloServlet extends HttpServlet {
    
        //通过无参构造方法创建对象
    
        //调用GenericSerevlet中的init方法,先调用有SerevletConfig参数的init方法,再调用无参的init方法
    
        //再调用Service方法
        
        //当前端发送的请求是get请求的时候,我这里重写doGet方法
        
        //当前端发送的请求是post请求的时候,我这里重写doPost方法
    }
    
    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 var6) {
                throw new ServletException(lStrings.getString("http.non_http"));
            }
    		//调用重载的service方法
            this.service(request, response);
        }
    
    
        protected void service(HttpServletRequest req, HttpServletResponse resp) 
            throws ServletException, IOException {
            //获取请求方式
            //这个请求方式可能是""
            // req.getMethod()方法获取的是请求方式,可能是七种之一
            String method = req.getMethod();
            long lastModified;
            
            if (method.equals("GET")) {
                lastModified = this.getLastModified(req);
                if (lastModified == -1L) {
                    //如果是get,则会执行doget方法
                    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")) {
                //如果是post,则会执行dopost方法
                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);
            }
    
        }
    
    //假设前端发送的是get请求,后端程序员重写的是post方法,会发生什么?
    //报405错误,发送的请求方式不对,和服务器不一致,不是服务器需要的请求方式
    //前端发什么请求,是后端说的算
    
  • 有些人,你会看到为了避免405错误,就会重写doget和dopost,没有必要,因为该报错的时候就应该让他报错,如果都重写了,还不如重写serevice方法

最终的Servlet开发

  • 编写一个Servlet类,直接继承HttpServlet
  • 重写doGet方法还是doPost方法,到底重写谁,javaweb程序员说的算
  • 将Servlet类配置到web.xml文件当中
  • 准备前端的页面(form表单),form表单中指定请求路径,请求方法为你重写的那个方法即可

注意

  • 以后我们编写Servlet类的时候,实际上是不会去直接继承GenericServlet类的,因为我们是B/S结构的系统,这种系统是基于HTTP超文本传输协议的,在Servlet规范中,提供了一个类HttpServlet,它是专门为HTTP协议准备的一个Servlet类,我们编写的Servlet类要继承HttpServlet。(HttpServlet是HTTP协议专用的)

补充

  • 向上转型和向下转型
    • 父类/超类在上层
    • 子类/继承类/派生类在下层
    • 所以向上转型就是子类转父类
    • 向下转型就是父类转子类
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值