java EE web三大核心之-Servlet全面解析

发布于:2022-12-22 ⋅ 阅读:(381) ⋅ 点赞:(0)

一、什么是 Servlet

servlet(Server Applet) 是javaEE Web的三大核心组件之一。狭义上说Servlet是java语言实现的一个接口,广义上来说是指任何实现了这个接口的类,人们通常将Servlet理解为后者。需要注意的是,servlet接口来自于web服务容器,比如tomcat,所以,servlet的运行依赖于容器的调用,自身没有main函数。

servlet的运行,需要部署在web服务容器中,有访问请求时,容器将请求分派给相应的servlet实现类进行处理。

二、Servlet 生命周期及注解

注解类,3.0 版本后可以使用注解替换web.xml文件。

public @interface WebServlet {
    String name() default ""; //标识servlet的名称

    String[] value() default {}; //全名匹配的访问路径,支持多个,必须"/"开头且与urlPatterns二选一,同时存在时将会报错无法启动

    String[] urlPatterns() default {};//模式匹配的访问路径,支持多个,必须"/"开头且与value二选一,同时存在时将会报错无法启动

    int loadOnStartup() default -1; //定义初始化时机与顺序,不配置或设置值小于0时,将在第一次请求到访时执行初始化;设置值大于0时,将伴随着容器的启动而初始化数值越小初始化越早。

    WebInitParam[] initParams() default {}; //初始化参数

    boolean asyncSupported() default false; //支持异步

    String smallIcon() default "";//小图标

    String largeIcon() default "";//大图标

    String description() default "";//介绍

    String displayName() default "";//展示名称
}

serlvet 接口的定义:

public interface Servlet {
    //1、初始化servlet,容器启动或第一个请求进入时调用,在容器的一个生命周期里仅执行一次,通过注解的loadOnStartup值决定。对于实现类对象的创建时机总与init调用时机一致,但是前者早于后者一步
    void init(ServletConfig var1) throws ServletException;

    //获取servlet的配置信息,如servlet的名称,初始化参数等
    ServletConfig getServletConfig();

    //2、处理web请求,每个请求调用一次
    void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;

    获取servlet的所在容器的上下文信息,即全局配置环境信息
    String getServletInfo();

    //3、销毁,容器关闭时有容器调用
    void destroy();
}

测试类:


@WebServlet(name="servletLife", displayName = "生命周期测试",loadOnStartup = -8,value = "/life")
public class ServletLife implements Servlet {

    public ServletLife(){
        System.out.println("完成对象的创建,但不是初始化");
    }

    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        System.out.println("完成初始化");
    }

    @Override
    public ServletConfig getServletConfig() {

        return null;
    }

    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("欢迎光临");
    }

    @Override
    public String getServletInfo() {
        return null;
    }

    @Override
    public void destroy() {
        System.out.println("被销毁了。。。");
    }
}

测试结果:

[2022-09-09 11:28:51,441] Artifact ServletDemo:war exploded: Artifact is deployed successfully
[2022-09-09 11:28:51,441] Artifact ServletDemo:war exploded: Deploy took 1,174 milliseconds
09-Sep-2022 23:28:59.926 信息 [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deploying web application directory [D:\myjava\tomcat8\webapps\manager]
09-Sep-2022 23:28:59.995 信息 [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory [D:\myjava\tomcat8\webapps\manager] has finished in [69] ms
完成对象的创建,但不是初始化
完成初始化
欢迎光临
欢迎光临
欢迎光临
欢迎光临
欢迎光临
欢迎光临
欢迎光临
欢迎光临
D:\myjava\tomcat8\bin\catalina.bat stop
Using CATALINA_BASE:   "C:\Users\tim\AppData\Local\JetBrains\IntelliJIdea2021.1\tomcat\97722028-ee40-4e44-8b69-a4b2464707b1"
Using CATALINA_HOME:   "D:\myjava\tomcat8"
Using CATALINA_TMPDIR: "D:\myjava\tomcat8\temp"
Using JRE_HOME:        "D:\myjava\jdk11"
Using CLASSPATH:       "D:\myjava\tomcat8\bin\bootstrap.jar;D:\myjava\tomcat8\bin\tomcat-juli.jar"
NOTE: Picked up JDK_JAVA_OPTIONS:  --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=java.rmi/sun.rmi.transport=ALL-UNNAMED
09-Sep-2022 23:32:23.878 信息 [main] org.apache.catalina.core.StandardServer.await A valid shutdown command was received via the shutdown port. Stopping the Server instance.
09-Sep-2022 23:32:23.878 信息 [main] org.apache.coyote.AbstractProtocol.pause Pausing ProtocolHandler ["http-nio-7070"]
09-Sep-2022 23:32:24.457 信息 [main] org.apache.coyote.AbstractProtocol.pause Pausing ProtocolHandler ["ajp-nio-8009"]
09-Sep-2022 23:32:25.004 信息 [main] org.apache.catalina.core.StandardService.stopInternal Stopping service [Catalina]
被销毁了。。。
09-Sep-2022 23:32:25.031 信息 [main] org.apache.coyote.AbstractProtocol.stop Stopping ProtocolHandler ["http-nio-7070"]
09-Sep-2022 23:32:25.033 信息 [main] org.apache.coyote.AbstractProtocol.stop Stopping ProtocolHandler ["ajp-nio-8009"]

三、Servlet 的继承体系

在这里插入图片描述

GenericServlet是一个与Http协议无关的抽象实现类,通过继承GenericServlet,只需要实现一个service方法。而HttpServlet是一个Http协议相关的实现,在GenericServlet的基础上,根据http请求的方法(get,post,put等)定义了不同的实现,通过查看源码可以发现,在HttpServlet中的service方法已不直接处理请求,而是根据请求的方法对请求做了分发,get请求调用了doGet方法处理,post请求调用了doPost方法处理,而这些方法的形参也从ServletRequest变为了HttpServletRequest,ServletResponse变为了HttpServletResponse。

自定义Servlet时,可以通过继承Servlet、GenericServlet、HttpServlet实现。直接继承Servlet时需要实现5个方法,所以,通常的Web开发中,都建议继承HttpServlet来定义。

需要注意的是,如果通过实现Servlet或GenericServlet自定义servlet,在方法中需要通过ServletRequst获取Http协议相关的内容时,需要先向下封装成HttpServletRequest,通过后者的实例去获取到Http相关的请求信息。

GenericServlet抽象类中service方法源码:
在这里插入图片描述

四、Servlet 中的三大作用域

总所周知,http协议本身是无状态的,但是,用户在持续的通信中,却需要保留相关的状态信息,以便于有效的通信。所有,servlet中定义了三大作用域,Request < Session < Application(ServletContext) 分别代表了请求级别、会话级别及应用程序级别,他们保存的自定义变量将在作用域范围内有效。

  • 请求作用域,Request级别。他的出现支持了一个请求在多个servlet中跳转,当然,这里的多个servlet必须处于同一个组件内。request.SetAttribute(key,value) 仅在这个请求作用域内有效,在request.getDispatch(“/a”).forward(req,res),跳转的过程中有效,可以用来在不同的servlet中传值。request.getAttribute(key)获取值。

     // /a请求中设置变量值,跳转到另一个servlet中,同一个请求!
     req.setAttribute("from", "china");
     req.getRequestDispatcher("/allscope").forward(req, resp);
    
    // /allscope请求
    Object from = req.getAttribute("from");
    System.out.println("得到一个请求作用域的变量 from=" + from);
    
  • seession,回话结束后失效,浏览关闭时回话结束。

    HttpSession session = req.getSession();
    //设置key-value
    session.setAttribute("name","tim");
    
    //根据key 获取value
    session.getAttribute("name");
    
  • ServletContext 容器的一个生命周期内有效,ServletContext 实例可以通过多种方式获得。

    	 ServletContext servletContext = req.getServletContext();
            Object coutObj = servletContext.getAttribute("count");
            if (coutObj != null) {
                int count = (int) coutObj;
                servletContext.setAttribute("count", ++count);
                System.out.println("得到一个全局作用域的变量 count=" + count);
            } else {
                servletContext.setAttribute("count", 1);
            }
    

    总体来说,三大作用域都实现了不同级别的状态保存。而且,都是通过各自实例的 setAttribute getAttribute 来设置、读取值。值类型为object。

五、突破 Servlet 三大作用域

那就是将状态信息保存在cookie中,在服务端创建cookie并设置key-value,随着response返回到客户端并最终保存在客户端,客户端再次发送请求时,浏览器自动携带cookie供服务端读取。当然,cookie支持设置有效期及路径一定程度上保证其中键值对信息的安全。
功夫再高,也怕菜刀,如果用户设置浏览器禁止cookie,那就GG了。

@WebServlet("/sc")
public class SetCookie extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)  {

        Cookie cookie=new Cookie("name","tim");
        cookie.setMaxAge(24*60*60);
        //仅 /ServletDemo_war_exploded/sc  路径下的请求可以见本cookie
        //修改cookie时,以cookie名称和路径比对,存在两者相同的,则修改,否则将新建
        cookie.setPath("/ServletDemo_war_exploded/sc");

        Cookie cookie2=new Cookie("age","100");
        cookie2.setMaxAge(24*60*60);

        resp.addCookie(cookie);
        resp.addCookie(cookie2);
    }
}
@WebServlet("/gc")
public class GetCookie extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)  {
        Cookie[] cookies=req.getCookies();
        if(cookies!=null){
            for(Cookie cookie:cookies){
                System.out.println(cookie.getName()+":"+cookie.getValue());
            }
        }
    }
}

网站公告

今日签到

点亮在社区的每一天
去签到