Javaweb基础之Servlet详解

  • 2019-10-30
  • 37
  • 0

Servlet是什么

servlet接口定义的是一套处理网络请求的规范,所有实现servlet的类,都需要实现它那五个方法,其中最主要的是两个生命周期方法 init()和destroy(),还有一个处理请求的service(),也就是说,所有实现servlet接口的类,或者说,所有想要处理网络请求的类,都需要思考问题:初始化时要做什么,销毁时要做什么,接受到请求时要做什么。servlet不会直接和客户端打交道!那请求怎么来到servlet呢?答案是servlet容器,比如我们最常用的tomcat。tomcat才是与客户端直接打交道的家伙,他监听了端口,请求过来后,根据url等信息,确定要将请求交给哪个servlet去处理,然后调用那个servlet的service方法,service方法返回一个response对象,tomcat再把这个response返回给客户端。

IDEA配置开发环境

https://www.cnblogs.com/javabg/p/7976977.html

Servlet使用流程

  • 创建普通的Java类,继承HttpServlet
  •  重写service方法,在其中书写逻辑
  • web.xml文件中配置servlet

Servlet生命周期

  • 从第一次调用到服务器关闭
  • 如果servlet在web.xml文件中配置了load-on-startup生命周期为从服务器开启到服务器关闭
  • init方法是对servlet初始化的方法,在servlet第一次加载进入内存时执行
  • destroy方法在servlet销毁时执行,也就是服务器关闭时

package com.bjsxt.servlet;

import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServlet;
import java.io.IOException;

public class MyServlet extends HttpServlet {
    @Override
    public void init() throws ServletException {
        System.out.println("servlet初始化完成");
    }

    @Override
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        // 终端输出
        System.out.println("first servlet");
        // 浏览器返回
        res.getWriter().write("hello");
    }

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

web.xml

load-on-startup的数字表示加载顺序

<?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">
    <servlet>
        <servlet-name>HelloWorld</servlet-name>
        <servlet-class>com.bjsxt.servlet.MyServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>HelloWorld</servlet-name>
        <url-pattern>/HelloWorld</url-pattern>
    </servlet-mapping>
</web-app>

Servlet的doGet和doPost方法

  • Service方法:可以处理post和get请求,如果servlet中有Service方法,会优先调用service方法对请求进行处理
  • doGet方法:处理get方式的请求
  • doPost方法:处理post方式的请求

注意:如果覆写的service方法中调用父类的service方法,则service方法处理完后,会再次根据请求方式响应的doGet和doPost方法执行,如果没有复写doGet和doPost方法,会报405错误,所以一般不在service中调用父类的service方法去避免405错误,而是在service中判断。

public class MyServlet extends HttpServlet {
    @Override
    public void init() throws ServletException {
        System.out.println("servlet初始化完成");
    }

    @Override
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        // 终端输出
        System.out.println("first servlet");
        // 浏览器返回
        res.getWriter().write("hello");
        super.service(req, res);
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("我是get");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("我是post");
    }

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

Servlet的Request对象

浏览器发起请求到服务器,会遵循http协议将请求数据发送给服务器。服务器每次接受一个请求,就创建一个Request对象专门的存储此次请求的数据,服务器在调用Servlet时会将创建的Request对象作为实参数传递给Servlet的方法,比如service

public class MyServlet extends HttpServlet {
    @Override
    public void init() throws ServletException {
        System.out.println("servlet初始化完成");
    }

    @Override
    public void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
        //获取请求头数据
            //获取请求方式
            String method = req.getMethod();
            //获取请求url
            StringBuffer url = req.getRequestURL();
            String uri = req.getRequestURI();
            //获取请求协议
            String h = req.getScheme();
        //--------------------------
        //获取请求行数据
            // 获取指定请求行数据
            String value = req.getHeader("User-Agent");
            // 获取所有请求行的枚举
            Enumeration e = req.getHeaderNames();
            while (e.hasMoreElements()) {
                String name = (String)e.nextElement();
                String value2 = req.getHeader(name);
                System.out.println(name+":"+value2);
            }
        //--------------------------
        // 获取用户提交数据 get post都这个方法,没有为Null
            String name = req.getParameter("username");//不能获取多项数据checkbox
            String[] favs = req.getParameterValues("fav"); // 多项
            if (favs != null)
            {
                for (String fav:favs) {
                    System.out.println(fav);
                }
            }
            //获取所有请求数据的键的集合req.getParameterNames()

    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("我是get");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("我是post");
    }

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

Servlet的Response对象

响应数据到浏览器的一个对象,设置响应头,响应实体比较关键,先设置响应编码格式

public class MyServlet2 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setHeader("MOUSE", "TWO BIRDS");//同键,会覆盖
        resp.addHeader("key", "thinkpad");// 同键,不会覆盖,而是追加
        resp.addHeader("key", "thinkpad2");
        //设置响应状态码
        resp.sendError(404,"not found");
        //设置响应实体
        resp.getWriter().write("hello");
        //设置响应编码格式
//        resp.setHeader("content-type", "text/html;charset=utf-8");
        resp.setContentType("text/plain;charset=utf-8");//text/plain,text/xml
    }
}

Servlet请求中文乱码解决

protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html;charset=utf-8");
        String uname = req.getParameter("uname");
        // 浏览器默认编码iso8859-1 下面万能方式改变编码,但是每个都要转很麻烦
        uname = new String(uname.getBytes("iso8859-1"), "utf-8"); 
    }

第二种解决方式,

如果是POST请求,在service方法中req.setCharacterEncoding(“utf-8”),get不行,因为数据在url上

如果是GET请求,不仅要加上req.setCharacterEncoding(“utf-8”),还要配置

在tomcat服务器目录下的conf/server.xml文件,如下配置

 <Connector port="8080" protocol="HTTP/1.1"
                connectionTimeout="20000"
                        redirectPort="8443" useBodyEncodingForURI=true/>

Servlet的请求转发

在服务器接收到浏览器的请求后,仅仅使用一个Servlet进行请求处理,会造成不同的Servlet逻辑代码冗余,Servlet的职责不明确,解决使用请求转发,实现多个servlet联动操作处理请求。

@Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
       //执行了登录逻辑。。。。
        //servletname可以相对路径,直接写servlet别名也可以 jsp也可以
        req.getRequestDispatcher('servletName').forward();
    }

Request对象的作用域

使用请求转发后,不同的servlet之间要怎么共享数据呢?或者说数据(请求数据+其他数据)怎么从一个servlet流向另外一个servlet呢?

解决:使用Request对象的作用域,

req.setAttribute(object name, object value);
req.getAttribute(object obj);

解决了一次请求中不同servlet数据共享的问题,注意,使用Request对象进行数据转,数据只在一次请求内有效。


Servlet的重定向

使用请求转发会造成表单数据重复提交的问题,这时就要使用重定向来处理,不仅如此,还可以让其他servlet来处理当前servlet处理不了的问题。

两次请求,第二次302跳转重定向,

resp.sendRedirect("servlet_name");// /project/servlet_name

Servlet的Cookie使用

设置cookie

protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 设置请求编码格式
        req.setCharacterEncoding("utf-8");
        //设置响应编码格式
        resp.setContentType("text/html;charset=utf-8");
        
        // 创建cookie对象
        Cookie c1 = new Cookie("cookie_name", "cookie_value");
        Cookie c2 = new Cookie("cookie_name2", "cookie_value2");

        // 注意,一个cookie对象存储一条数据,多条数据可以创建多个cookie对象来存储
        
        
        // 响应cookie信息到客户端,临时存储,浏览器关闭就没了,浏览器内存中
        resp.addCookie(c1);
        
        // 定时存储客户端硬盘,秒为单位,每次请求都会携带,除非设置有效路径,指定路径才会携带 c2.setMaxAge(3*24*3600);//设置有效路径 c2.setPath("/cookie/abc")
        resp.addCookie(c2);
    }

获取cookie

Cookie[] cks = req.getCookies();
if (cks != null) {
    for (Cookie c:cks) {
        String name = c.getName();
        String value = c.getValue();
        System.out.println(name+":"+value);
    }
}

Servlet的Session使用

protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 设置请求编码格式
        req.setCharacterEncoding("utf-8");
        //设置响应编码格式
        resp.setContentType("text/html;charset=utf-8");
        
        String name = "张三";

        //存在则获取,否则创建,失效也会重新创建,一句搞定
        //JSESSIONID存储在Cookie的临时存储空间,浏览器关闭会失效,默认存储时间30分,可以修改tomcat的web.xml的session_config配置
        HttpSession hs = req.getSession();
        hs.setMaxInactiveInterval(3600);//session时间 单位秒,一直用,会重新计时
        System.out.println(hs.getId());
        
        //session强制失效退出
        //hs.invalidate();
        
        // SESSION存储数据,共享
        hs.setAttribute("name", name);
        
        //其他请求servlet获取,返回的为object对象,解决了不同请求数据共享的为题,只要sessionid不失效
        hs.getAttribute("name");
        
        //session失效处理
        //将用户请求重的sessionid和后台获取到的session对象的sessionid对比,
    }

Servlet的ServletContext对象

Request对象解决了一次请求内的数据共享问题,session解决了用户不同请求数据共享的问题,不同的用户数据共享该怎么办呢?

ServletContext对象原理:

ServletContext对象由服务器创建,一个项目只有一个对象,不管在项目的任意位置进行获取得到的都是同一个对象,那么不同用户发起的请求获取的也是同一个对象了。

数据共享

protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 获取ServletContext对象的三种方式,获取到的都是同一个对象

        ServletContext sc1 = this.getServletContext(); // 常用
        ServletContext sc2 = req.getSession().getServletContext(); //常用
        ServletContext sc3 = this.getServletConfig().getServletContext();
        System.out.println(sc1 == sc2);
        System.out.println(sc1 == sc3);

        //设置要共享的数据
        sc1.setAttribute("str", "共享的数据");

        // 其他servlet中获取,返回Object类型,不存在为null 先获取对象
        sc2.getAttribute("str");
    }

获取项目web.xml的全局配置数据

<!--配置全局数据,一个context-param只能配置一个键值对>
 <context-param>
     <param-name>name</param-name>
     <param-value>zhangsan</param-value>
 </context-param>
sc.getInitParameter("name");//不存在返回Null,返回String类型

sc.getInitParamterNames();//返回键名的枚举

作用:将静态数据和代码解耦

获取webroot下的资源的绝对路径

String path = sc.getRealPath("/doc/1.txt");

获取的路径为项目的根目录,path参数为项目根目录中的路径。

获取webroot下的资源的流对象

InputStream is = sc.getResourceAsStream(String path);

注意,此种方式只能获取项目根目录下的资源流对象,class文件的流对象需要使用类加载器

使用ServletContext对象完成网页计数器

思路:在用户登录校验中创建计数器并自增,然后存储到ServletContext对象中,在主页面里取出计数器数据并显示给用户


Servlet的ServletConfig对象

使用ServletContext对象可以获取web.xml中的全局配置文件,在web.xml中,每个Servlet也可以单独的配置,就需要用ServletConfig对象来获取这些单独的配置信息了。

作用:ServletConfig对象是Servlet的专属配置对象,每个Servlet都单独拥有一个ServletConfig对象,用来获取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">
<!--    配置全局数据,一个context-param只能配置一个键值对>-->
    <context-param>
        <param-name>name</param-name>
        <param-value>zhangsan</param-value>
    </context-param>
    <servlet>
        <servlet-name>HelloWorld</servlet-name>
        <servlet-class>com.bjsxt.servlet.MyServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
<!--        单独的servlet配置-->
        <init-param>
            <param-name>config</param-name>
            <param-value>utf-8</param-value>
        </init-param>
    </servlet>

    <servlet-mapping>
        <servlet-name>HelloWorld</servlet-name>
        <url-pattern>/HelloWorld</url-pattern>
    </servlet-mapping>
</web-app>
 protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //获取ServletConfig对象
        ServletConfig sc = this.getServletConfig();
        //获取web.xml中单独的配置
        String code = sc.getInitParameter("config");

    }


web.xml文件使用总结

存储项目相关的配置信息,保护servlet,解耦一些数据对程序的依赖。

web项目下面的web.xml文件为局部配置,针对本项目的位置

tomcat下的web.xml文件为全局配置,配置公共信息。

核心组件:

  • 全局上下文配置
  • Servlet配置
  • 过滤器配置
  • 监听器配置

配置顺序无先后,服务器启动的加载有顺序

web容器会按照ServletContext->context-param->listener->filter->servlet这个顺序来加载组件


评论

还没有任何评论,你来说两句吧

粤ICP备17155863号

- 友情链接 - Theme by Qzhai