• Servlet基础使用


    程序员学习一门新的技术,永远绕不开一个永恒的话题——Hello World!,在我们学习servlet的时候也首先来实现一个Hello World!入门程序!

    Hello World!入门程序

    首先编写一个Servlet类,在其中返回hello world!

    public class IndexServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            resp.setCharacterEncoding("utf-8");
            PrintWriter out = resp.getWriter();
            out.write("hello world!");
            out.flush();
            out.close();
        }
        @Override
        protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            this.doGet(req, resp);
        }
    }
    

    然后需要配置web.xml让容器识别到。

    <?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">
        <welcome-file-list>
            <welcome-file>index.jsp</welcome-file>
        </welcome-file-list>
        <servlet>
            <!-- servlet名字 -->
            <servlet-name>helloworldServlet</servlet-name>
            <!-- servlet的类路径,会将这个类命名为上面配置的名字 -->
            <servlet-class>cn.fzkj.servlet.IndexServlet</servlet-class>
        </servlet>
        <servlet-mapping>
            <servlet-name>helloworldServlet</servlet-name>
            <!-- 拦截路径,当访问hello的时候就会交给这个servlet处理 -->
            <url-pattern>/hello</url-pattern>
        </servlet-mapping>
    </web-app>
    

    这样配置了之后一个简单的hello world入门程序就完成了。

    servlet3.0版本中,将xml配置进一步简化了,可以使用注解来完成。上面的servlet代码就可以修改为:

    // 标注这是一个servlet,名字叫helloworldServlet,拦截的路径是/hello,这个注解的效果和之前的xml配置一样。
    @WebServlet(name = "helloworldServlet", urlPatterns = "/hello")
    public class IndexServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            resp.setCharacterEncoding("utf-8");
            PrintWriter out = resp.getWriter();
            out.write("hello world!");
            out.flush();
            out.close();
        }
        @Override
        protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            this.doGet(req, resp);
        }
    }
    

    servlet

    Java Servlet 是运行在 Web 服务器或应用服务器上的程序,它是作为来自 Web 浏览器或其他 HTTP 客户端的请求和 HTTP 服务器上的数据库或应用程序之间的中间层。 ——菜鸟教程

    http请求通过servlet来和程序进行交互。

    servlet生命周期

    servlet生命周期指的是servlet从创建到销毁的整个过程。它遵循以下的几个过程:

    • 初始化后会调用init()方法
    • 调用service()方法来处理客户端请求
    • 销毁前调用destroy()方法
    • 最后由垃圾回收器回收

    img

    创建servlet的方式

    创建servlet有三种方式,这里介绍其中两种比较常用的方式。

    第一种:实现Servlet接口

    public class ServletDemo1 implements Servlet {
    
        @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("=== 销毁执行 ===");
        }
    }
    

    *第二种:继承HttpServlet

    其实通过查看源代码可以发现,HttpServlet类最终也是实现了Servlet接口,只是对Servlet中一些方法做了实现。

    public abstract class HttpServlet extends GenericServlet {}
    public abstract class GenericServlet implements Servlet, ServletConfig, Serializable {}
    

    使用HttpServlet类创建

    @WebServlet(name = "httpServletDemo", urlPatterns = "/hello")
    public class ServletHttpDemo extends HttpServlet {
    
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            System.out.println("hello world");
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            super.doPost(req, resp);
        }
    }
    

    HttpServletServlet接口做了实现,同时提供了一些用于处理http请求的方法,在继承了HttpServlet类之后就可以非常方便的处理用户请求,而不用在去关心其他。

    手写一个HttpServlet

    根据上面的发现,我们自己也可以来对Servlet接口进行一个实现,来达到和HttpServlet类相似的功能。说干就干。

    由于Servlet中实际会调用Service方法进行业务处理,那就需要将该方法进行重写,让它支持http请求。

    /**
     * @DESCRIPTION: 继承了servlet接口,实现类似于HttpServlet相似的功能
     * @AUTHOR: AnotherOne
     * @DATE: 2021/7/14 16:23
     */
    public class BaseServlet implements Servlet {
    
        public BaseServlet() {
            // do nothing
        }
    
        @Override
        public void init(ServletConfig servletConfig) throws ServletException {
            System.out.println("=== BaseServlet ===");
        }
    
        @Override
        public ServletConfig getServletConfig() {
            return null;
        }
    
        // 重写该方法
        @Override
        public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
            HttpServletRequest request;
            HttpServletResponse response;
            try {
                request = (HttpServletRequest) servletRequest;
                response = (HttpServletResponse) servletResponse;
                this.service(request, response);
            }catch (Exception e){
                System.out.println("无效的请求");
            }
        }
    
        // 实现不同请求方式的判断与业务的转发
        protected void service(HttpServletRequest res, HttpServletResponse rep) throws ServletException, IOException {
            String method = res.getMethod();
            if (HttpType.GET.name().equalsIgnoreCase(method)){
                this.doGet(res, rep);
            } else if (HttpType.POST.name().equalsIgnoreCase(method)){
                this.doPost(res, rep);
            } else {
                System.out.println("暂不支持的请求类型");
            }
        }
    
        @Override
        public String getServletInfo() {
            return null;
        }
    
        @Override
        public void destroy() {
            System.out.println("=== BaseServlet ===");
        }
    
        protected void doGet(ServletRequest req, ServletResponse res){
            System.out.println("BaseServlet get 默认实现");
        }
    
        protected void doPost(ServletRequest req, ServletResponse res){
            System.out.println("BaseServlet post 默认实现");
        }
    
        /**
         * 定义http请求枚举类
         */
         enum HttpType {
            GET,
            POST,
            PUT,
            DELETE
        }
    }
    

    接下来来写个业务类验证一下。

    @WebServlet(name = "servletDemo2", urlPatterns = "/demo2")
    public class ServletHttpDemo2 extends BaseServlet {
    
        @Override
        protected void doGet(ServletRequest req, ServletResponse res) {
    //        super.doGet(req, res);
            System.out.println("子类业务处理");
        }
    }
    

    当访问/demo2时控制台可以打印出子类业务处理,说明效果不错。

    ServletConfig对象

    在容器创建Servlet实例对象的时候,会将一些初始化参数封装到ServletConfig对象中,并且在调用对象的init(ServletConfig config)方法时传递给该实例。所以可以通过该对象得到一些初始化参数信息。

    @WebServlet(name = "httpServletDemo", urlPatterns = "/demo", initParams = {
            @WebInitParam(name = "name", value = "fzkj"),
            @WebInitParam(name = "age", value = "23")
    })
    public class ServletHttpDemo extends HttpServlet {
    
        private ServletConfig config;
    
        @Override
        public void init(ServletConfig config) throws ServletException {
            this.config = config;
        }
    
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            // 获取配置的name和age属性值
            System.out.println(config.getInitParameter("name"));
            System.out.println(config.getInitParameter("age"));
            // 获取当前servlet名称
            System.out.println(config.getServletName());
            // 获取当前参数属性名称的枚举
            Enumeration<String> names = config.getInitParameterNames();
            while(names.hasMoreElements()){
                System.out.println(names.nextElement());
            }
            // 获取servletcontext对象
            ServletContext servletContext = config.getServletContext();
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            super.doPost(req, resp);
        }
    }
    

    上面代码是使用注解的方式定义初始化参数。也可以使用xml配置的方式进行。

    <servlet>
        <servlet-name>indeServlet</servlet-name>
        <servlet-class>cn.fzkj.servlet.IndexServlet</servlet-class>
        <init-param>
            <param-name>namespace</param-name>
            <param-value>fzkj</param-value>
        </init-param>
        <init-param>
            <param-name>age</param-name>
            <param-value>23</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>indeServlet</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
    

    这两者的效果并没有什么不同。

    上述代码运行结果:

    fzkj
    23
    httpServletDemo
    name
    age
    
    ServletContext对象

    web容器在启动的时候,会为每个web应用程序创建一个ServletContext对象,它代表的就是当前web应用。

    所以在同一个web应用中,所有的servlet共享同一个ServletContext对象,因此可以通过这个对象来实现不同Servlet之间的通信。

    servletContext实现多个servlet之间数据共享

    /**
     * @DESCRIPTION: 多个servlet数据共享 和 demo2
     * @AUTHOR: AnotherOne
     * @DATE: 2021/7/15 11:15
     */
    @WebServlet(name = "demo1", urlPatterns = "/demo1")
    public class Demo1 extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            resp.setCharacterEncoding("utf-8");
            ServletContext context = this.getServletContext();
            context.setAttribute("name", "fzkj");
            PrintWriter out = resp.getWriter();
            out.write("数据共享成功");
            out.flush();
            out.close();
        }
    }
    
    /**
     * @DESCRIPTION: TODO 多个servlet数据共享 和 demo1
     * @AUTHOR: AnotherOne
     * @DATE: 2021/7/15 11:15
     */
    @WebServlet(name = "demo2", urlPatterns = "/demo2")
    public class Demo2 extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            resp.setCharacterEncoding("utf-8");
            ServletContext context = this.getServletContext();
            String name = (String)context.getAttribute("name");
            PrintWriter out = resp.getWriter();
            out.write("数据读取成功" + name);
            out.flush();
            out.close();
        }
    }
    

    读取web应用初始化参数

    /**
     * @DESCRIPTION: TODO 读取web应用初始化参数 -> 在xml中配置<context-param></context-param>
     * @AUTHOR: AnotherOne
     * @DATE: 2021/7/15 11:28
     */
    @WebServlet(name = "demo3", urlPatterns = "/demo3")
    public class Demo3 extends HttpServlet {
    
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            ServletContext context = this.getServletContext();
            System.out.println(context.getInitParameter("names"));
        }
    }
    
        <context-param>
            <param-name>names</param-name>
            <param-value>fzkj123</param-value>
        </context-param>
    

    是不是有一种可以通过注解的方式创建web初始化参数?后面再来研究

    ServletContext对象实现请求转发

    /**
     * @DESCRIPTION: TODO 实现请求转发,转发给Demo5
     * @AUTHOR: AnotherOne
     * @DATE: 2021/7/15 11:39
     */
    @WebServlet(name = "dmeo4", urlPatterns = "/demo4")
    public class Demo4 extends HttpServlet {
    
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            ServletContext context = this.getServletContext();
            resp.setCharacterEncoding("utf-8");
            resp.getOutputStream().write("这是再demo4中".getBytes());
            // 转发给demo5
            RequestDispatcher rd = context.getRequestDispatcher("/demo5");
            rd.forward(req, resp);
        }
    }
    
    /**
     * @DESCRIPTION: TODO 接收demo4的转发请求并处理
     * @AUTHOR: AnotherOne
     * @DATE: 2021/7/15 11:43
     */
    @WebServlet(name = "dmeo5", urlPatterns = "/demo5")
    public class Demo5 extends HttpServlet {
    
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            resp.setCharacterEncoding("utf-8");
            resp.getOutputStream().write("这是再demo5中".getBytes());
        }
    }
    

    由于再demo4的处理逻辑中将请求转发给了demo5,所以实际上页面会打印这是再demo5中

    Filter

    Servlet 过滤器可以动态地拦截请求和响应,以变换或使用包含在请求或响应中的信息。

    可以将一个或多个 Servlet 过滤器附加到一个 Servlet 或一组 Servlet。Servlet 过滤器也可以附加到 JavaServer Pages (JSP) 文件和 HTML 页面。调用 Servlet 前调用所有附加的 Servlet 过滤器。

    Servlet 过滤器是可用于 Servlet 编程的 Java 类,可以实现以下目的:

    • 在客户端的请求访问后端资源之前,拦截这些请求。
    • 在服务器的响应发送回客户端之前,处理这些响应。

    根据规范建议的各种类型的过滤器:

    • 身份验证过滤器(Authentication Filters)。
    • 数据压缩过滤器(Data compression Filters)。
    • 加密过滤器(Encryption Filters)。
    • 触发资源访问事件过滤器。
    • 图像转换过滤器(Image Conversion Filters)。
    • 日志记录和审核过滤器(Logging and Auditing Filters)。
    • MIME-TYPE 链过滤器(MIME-TYPE Chain Filters)。
    • 标记化过滤器(Tokenizing Filters)。
    • XSL/T 过滤器(XSL/T Filters),转换 XML 内容。

    过滤器通过 Web 部署描述符(web.xml)中的 XML 标签来声明,然后映射到您的应用程序的部署描述符中的 Servlet 名称或 URL 模式。

    当 Web 容器启动 Web 应用程序时,它会为您在部署描述符中声明的每一个过滤器创建一个实例。

    Filter的执行顺序与在web.xml配置文件中的配置顺序一致,一般把Filter配置在所有的Servlet之前。

    ​ ——菜鸟教程

    Filter也称之为过滤器,它是Servlet技术中最实用的技术,Web开发人员通过Filter技术,对web服务器管理的所有web资源:例如Jsp, Servlet, 静态图片文件或静态 html 文件等进行拦截,从而实现一些特殊的功能。例如实现URL级别的权限访问控制、过滤敏感词汇、压缩响应信息等一些高级功能。

    它主要用于对用户请求进行预处理,也可以对HttpServletResponse进行后处理。使用Filter的完整流程:Filter对用户请求进行预处理,接着将请求交给Servlet进行处理并生成响应,最后Filter再对服务器响应进行后处理。

    FilterChain:在一个web应用中,可以开发编写多个Filter,这些Filter组合起来称之为一个Filter链。

    web服务器根据Filter在web.xml文件中的注册顺序,决定先调用哪个Filter,当第一个Filter的doFilter方法被调用时,web服务器会创建一个代表Filter链的FilterChain对象传递给该方法。在doFilter方法中,开发人员如果调用了FilterChain对象的doFilter方法,则web服务器会检查FilterChain对象中是否还有filter,如果有,则调用第2个filter,如果没有,则调用目标资源。

    创建一个简单的过滤器

    /**
     * @DESCRIPTION: TODO
     * @AUTHOR: AnotherOne
     * @DATE: 2021/7/15 12:47
     */
    // 标注这是一个过滤器,urlPatterns用于配置拦截的url规则,符合规则的url才会被拦截
    @WebFilter(filterName = "fzkj_filter_demo1", urlPatterns = "/*", initParams = {
            @WebInitParam(name = "name", value = "fzkj_filter_demo1"),
            @WebInitParam(name = "age", value = "23")
    })
    public class Demo1 implements Filter {
    
        private FilterConfig config;
    
        @Override
        public void init(FilterConfig config) throws ServletException {
            this.config = config;
            System.out.println("=== demo1 过滤器初始化 ===");
            // 获取当前过滤器名称
            System.out.println(config.getFilterName());
            System.out.println("=========================");
            // 获取初始化参数‘name’的值
            System.out.println(config.getInitParameter("name"));
            System.out.println("=========================");
            // 获取所有初始化参数的名称
            Enumeration<String> names = config.getInitParameterNames();
            while(names.hasMoreElements()){
                System.out.println(names.nextElement());
            }
            System.out.println("=========================");
        }
    
        @Override
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
            System.out.println("经过 " + this.config.getFilterName() + " filter");
            // 交给下一过滤器
            chain.doFilter(servletRequest, servletResponse);
        }
    
        @Override
        public void destroy() {
            System.out.println("=== demo1 过滤器销毁 ===");
        }
    }
    

    Filter的中的方法与用法和Servlet非常相似,不在过多赘述


    持续更新~~

  • 相关阅读:
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
  • 原文地址:https://www.cnblogs.com/mrjkl/p/15015360.html
Copyright © 2020-2023  润新知