XML + Tomcat + Servlet + Http

目录

XML

Tomcat 

配置

查看端口监听

tomcat目录结构

启动 Tomcat

停止Tomcat

部署Web应用 

浏览器访问web服务器文件UML时序图

注意 ​

Tomcat底层机制

​第一阶段:给浏览器返回数据

第二阶段:多线程

第三阶段:处理Servlet

HspRequestHandler类(线程对象)

HspRequest类 

HspResponse类

HspServlet接口

HspHttpServlet抽象类

自定义HspCalServlet类

启动tomcat

 

Servlet

查看版本

引入

Servlet 接口的五种方法

浏览器调用Servlet流程分析

注解流程

Servlet 生命周期

初始化阶段

处理浏览器请求阶段(service方法)

终止阶段 destory方法(体现Servlet完整的生命周期)

通过继承HttpServlet开发Servlet

如何执行到doPost和doGet

Servlet 注意事项和细节

Servlet-注解方式

模拟注解流程

注解嵌套

urlPattern配置

精确匹配

目录匹配

扩展名匹配

任意匹配

注意事项

HTTP

浏览器显示信息

GET请求头和请求行分析

 POST分析

GET请求和POST请求

HTTP响应包分析

状态码 

302

304

 MIME

Servlet

ServletConfig

基本介绍

作用 

应用

ServletContext(域对象)

基本介绍

 作用

应用

HttpServletRequest(域对象)

介绍

常用方法 

应用

注意

请求转发 

应用

注意事项和细节

HttpServletResponse 

类图

​向客户端返回数据

中文乱码 

请求重定向 

注意事项和细节 

动态获取到application context


XML

public class Dom4j {
    public static void main(String[] args) throws DocumentException {
        SAXReader reader = new SAXReader(); //得到解析器
        Document document = reader.read(new File("src/student.xml"));

        //1、得到rootElement
        Element rootElement = document.getRootElement();
            //2、得到rootElement的student所有的Elements
            List<Element> students = rootElement.elements("student");
            System.out.println(students.size());
            for(Element student : students) {
                //获取student元素的name Element
                Element name = student.element("name");
                Element age = student.element("age");
                Element gender = student.element("gender");
                System.out.println("输出学生信息" + name.getText());
            }

        //指定读取第一个学生的信息
        Element student = (Element) rootElement.elements("student").get(0);//返回的是Object,强转
        //输出信息
        System.out.println(student.element("name").getText());
        //获取student元素的属性
        System.out.println(student.attributeValue("id"));
    }
}

Tomcat 

配置

  • 配置JAVA_HOME        java的jdk
  • 端口8080是否被占用

查看端口监听

管理员身份
netstat -anb
netstat -anb |more

tomcat目录结构

  • server.xml 用于配置 tomcat 的基本设置(启动端口,关闭端口, 主机名),可以修改端口号
  • wex.xml 用于指定tomcat运行时配置(比如servlet等..)
  • webapps 目录是存放 web应用,就是网站

启动 Tomcat

进入到 Tomcat 的 bin 目录下,执行命令: catalina run

停止Tomcat

进入到 Tomcat 的 bin 目录下,执行命令: shutdown.bat

部署Web应用 

将 web 工程的目录拷贝到 Tomcat 的 webapps 目录下
浏览器输入: http://ip[域名]:port/news/子目录../文件名

浏览器访问web服务器文件UML时序图

  • 左边图片:在GET路径下找资源,HOST翻译成IP
  • 右边图片:根据资源,比如js、css、图片,浏览器分别tomcat发出请求,

注意 

 

Tomcat底层机制

 第一阶段:给浏览器返回数据

缺点:不能实现多线程,性能差

public class Tomcatv1 {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(8080);
        System.out.println("tomcat在8080监听... ");

        while(!serverSocket.isClosed()) {
            Socket socket = serverSocket.accept();
            InputStream inputStream = socket.getInputStream();
            BufferedReader bufferedReader =
                    new BufferedReader(new InputStreamReader(inputStream, "utf-8"));
            
            //接收浏览器的信息
            String mes =null;
            while((mes = bufferedReader.readLine()) != null) {
                if(mes.length() == 0)
                    break;
                System.out.println(mes);
            }
            
            //数据返回给浏览器
            OutputStream outputStream = socket.getOutputStream();
            //设置响应行响应头空两行之后是响应体
            String respHeader = "HTTP/1.1 200 OK\r\n" +
                    "Content-Type: text/html;charset=utf-8\r\n\r\n";
            String resp = respHeader + "你好";
            outputStream.write(resp.getBytes());
            
            
            outputStream.close();
            bufferedReader.close();
            socket.close();
            serverSocket.close();
        }
    }
}

第二阶段:多线程

缺点:只是简单返回结果,没有和Servlet和web.xml关联

将socket分发给线程

class Tomcatv1 {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(8080);
        while(! serverSocket.isClosed()) {
            //分发给线程
            Socket socket = serverSocket.accept();
            new Thread(new RequestHandler(socket)).start();
        }
    }
}


public class RequestHandler implements Runnable{
    //定义Socket
    private Socket socket = null;

    public RequestHandler(Socket socket) {
        this.socket = socket;
    }

    public void run() {
        //与客户端/浏览器进行交互
        //接收数据
        //返回数据,封装成http响应
        //关闭资源
    }
}

第三阶段:处理Servlet

HspRequestHandler类(线程对象)

public class HspRequestHandler implements Runnable {

    //定义Socket
    private Socket socket = null;

    public HspRequestHandler(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {

        //这里我们可以对客户端/浏览器进行IO编程/交互
        try {

            HspRequest hspRequest = new HspRequest(socket.getInputStream());
            HspResponse hspResponse = new HspResponse(socket.getOutputStream());

            //创建HspCalServlet对象-> 一会我们再用反射来构建对象
            // HspCalServlet hspCalServlet = new HspCalServlet();
            // hspCalServlet.doGet(hspRequest, hspResponse);

            //1. 得到 uri => 就是 servletUrlMapping 的 url-pattern
            String uri = hspRequest.getUri();


            //(1) 判断uri是什么资源 => 工具方法
            //(2) 如果是静态资源,就读取该资源,并返回给浏览器 content-type text/html
            //(3) 因为目前老师并没有起到tomcat, 不是一个标准的web项目
            //(4) 把读取的静态资源放到 target/classes/cal.html
            //过滤,拦截 , 权限等待 => Handler.... => 分发
            if(WebUtils.isHtml(uri)) {//就是静态页面
                String content = WebUtils.readHtml(uri.substring(1));
                content = HspResponse.respHeader + content;
                //得到outputstream , 返回信息(静态页面)给浏览器
                OutputStream outputStream = hspResponse.getOutputStream();
                outputStream.write(content.getBytes());
                outputStream.flush();
                outputStream.close();
                socket.close();
                return;
            }


            String servletName = HspTomcatV3.servletUrlMapping.get(uri);
            //2. 通过uri->servletName->servlet的实例 , 真正的运行类型是其子类 HspCalServlet
            HspHttpServlet hspHttpServlet =
                    HspTomcatV3.servletMapping.get(servletName);
            //3. 调用service , 通过OOP的动态绑定机制,调用运行类型的 doGet/doPost
                hspHttpServlet.service(hspRequest, hspResponse);

            socket.close();

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //最后一定确保socket要关闭
            if (socket != null) {
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}

HspRequest类 

在构造器传入与socket对应inputStream,将http请求的相关数据,进行封装,然后提供相关的方法,进行获取

package com.hspedu.tomcat.http;

/**
 * @author 韩顺平
 * @version 1.0
 */

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.HashMap;

/**
 * 老韩解读
 * 1. HspRequest 作用是封装http请求的数据
 * get /hspCalServlet?num1=10&num2=30
 * 2. 比如 method(get) 、 uri(/hspCalServlet) 、 还有参数列表 (num1=10&num2=30)
 * 3. HspRequest 作用就等价原生的servlet 中的HttpServletRequest
 * 4. 一会老韩在走代码
 * 5. 老韩这里考虑的是GET请求
 */
public class HspRequest {

    private String method;
    private String uri;
    //存放参数列表 参数名-参数值 => HashMap
    private HashMap<String, String> parametersMapping =
            new HashMap<>();
    private InputStream inputStream = null;


    //构造器=> 对http请求进行封装 => 可以将老师写的代码封装成方法
    //inputStream 是和 对应http请求的socket关联
    public HspRequest(InputStream inputStream) {
        this.inputStream = inputStream;
        //完成对http请求数据的封装..
        encapHttpRequest();
    }

    /**
     * 将http请求的相关数据,进行封装,然后提供相关的方法,进行获取
     */
    private void encapHttpRequest() {
        System.out.println("HspRequest init()");
        try {
            //inputstream -> BufferedReader
            BufferedReader bufferedReader =
                    new BufferedReader(new InputStreamReader(inputStream, "utf-8"));

            //读取第一行
            /**
             * GET /hspCalServlet?num1=10&num2=30 HTTP/1.1
             * Host: localhost:8080
             * User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:97.0) Gecko/20100101 Fi
             */
            String requestLine = bufferedReader.readLine();
            //GET - /hspCalServlet?num1=10&num2=30 - HTTP/1.1
            String[] requestLineArr = requestLine.split(" ");
            //得到method
            method = requestLineArr[0];
            //解析得到 /hspCalServlet
            //1. 先看看uri 有没有参数列表
            int index = requestLineArr[1].indexOf("?");
            if (index == -1) { //说明没有参数列表
                uri = requestLineArr[1];
            } else {
                //[0,index)
                uri = requestLineArr[1].substring(0, index);
                //获取参数列表->parametersMapping
                //parameters => num1=10&num2=30
                String parameters = requestLineArr[1].substring(index + 1);
                //num1=10 , num2=30 .... parametersPair= ["num1=10","num2=30" ]
                String[] parametersPair = parameters.split("&");
                //防止用户提交时 /hspCalServlet?
                if (null != parametersPair && !"".equals(parametersPair)) {
                    //再次分割 parameterPair = num1=10
                    for (String parameterPair : parametersPair) {
                        //parameterVal ["num1", "10"]
                        String[] parameterVal = parameterPair.split("=");
                        if (parameterVal.length == 2) {
                            //放入到 parametersMapping
                            parametersMapping.put(parameterVal[0], parameterVal[1]);
                        }
                    }
                }
            }
            //这里不能关闭流 inputStream 和 socket关联
            //inputStream.close();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //request对象有一个特别重要方法
    public String getParameter(String name) {
        if (parametersMapping.containsKey(name)) {
            return parametersMapping.get(name);
        } else {
            return "";
        }
    }

    public String getMethod() {
        return method;
    }

    public void setMethod(String method) {
        this.method = method;
    }

    public String getUri() {
        return uri;
    }

    public void setUri(String uri) {
        this.uri = uri;
    }

    @Override
    public String toString() {
        return "HspRequest{" +
                "method='" + method + '\'' +
                ", uri='" + uri + '\'' +
                ", parametersMapping=" + parametersMapping +
                '}';
    }
}

HspResponse类

/**
 * 老师解读
 * 1. HspResponse对象可以封装OutputStream(是socket关联)
 * 2. 即可以通过 HspResponse对象 返回Http响应给浏览器/客户端
 * 3. HspResponse对象 的作用等价于原生的servlet的 HttpServletResponse
 */
public class HspResponse {

    private OutputStream outputStream = null;

    //写一个http的响应头 => 先死后活
    public static final String respHeader = "HTTP/1.1 200 OK\r\n" +
            "Content-Type: text/html;charset=utf-8\r\n\r\n";

    //说明同学们如果有兴趣, 在编写更多的方法
    //比如 setContentType

    //在创建 HspResponse 对象时,传入的outputStream是和Socket关联的
    public HspResponse(OutputStream outputStream) {
        this.outputStream = outputStream;
    }
    //当我们需要给浏览器返回数据时,可以通过HspResponse 的输出流完成
    //
    public OutputStream getOutputStream() {
        return outputStream;
    }
}

HspServlet接口

public interface HspServlet {
    void init() throws Exception;
    void service(HspRequest request, HspResponse response) throws IOException;
    void destroy();
}

HspHttpServlet抽象类

public abstract class HspHttpServlet implements HspServlet {

    @Override
    public void service(HspRequest request, HspResponse response) throws IOException {
        //老师说明 equalsIgnoreCase 比较字符串内容是相同,不区别大小写
        if("GET".equalsIgnoreCase(request.getMethod())) {
            //这里会有动态绑定
            this.doGet(request,response);
        } else if("POST".equalsIgnoreCase(request.getMethod())) {
            this.doPost(request,response);
        }
    }

    //这里我们使用的了模板设计模式 => java 基础的 抽象类专门讲过模板设计模式
    //让HspHttpServlet 子类 HspCalServlet 实现

    public abstract void doGet(HspRequest request, HspResponse response);
    public abstract void doPost(HspRequest request, HspResponse response);
}

自定义HspCalServlet类

public class HspCalServlet extends HspHttpServlet {
    @Override
    public void doGet(HspRequest request, HspResponse response) {
        //java基础的 OOP 的动态绑定机制..
        //写业务代码,完成计算任务
        int num1 = WebUtils.parseInt(request.getParameter("num1"), 0);
        int num2 = WebUtils.parseInt(request.getParameter("num2"), 0);

        int sum = num1 + num2;

        //返回计算结果给浏览器
        //outputStream 和 当前的socket关联
        OutputStream outputStream = response.getOutputStream();
        String respMes = HspResponse.respHeader
                + "<h1>" + num1 + " + " + num2 + " = " + sum + " HspTomcatV3 - 反射+xml创建</h1>";
        try {
            outputStream.write(respMes.getBytes());
            outputStream.flush();
            outputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void doPost(HspRequest request, HspResponse response) {
        this.doGet(request, response);
    }

    @Override
    public void init() throws Exception {

    }

    @Override
    public void destroy() {

    }
}

启动tomcat

public class HspTomcatV3 {

    //1. 存放容器 servletMapping
    // -ConcurrentHashMap
    // -HashMap
    // key            - value
    // ServletName    对应的实例

    public static final ConcurrentHashMap<String, HspHttpServlet>
            servletMapping = new ConcurrentHashMap<>();


    //2容器 servletUrlMapping
    // -ConcurrentHashMap
    // -HashMap
    // key                    - value
    // url-pattern       ServletName

    public static final ConcurrentHashMap<String, String>
            servletUrlMapping = new ConcurrentHashMap<>();

    
    

    public static void main(String[] args) {
        HspTomcatV3 hspTomcatV3 = new HspTomcatV3();
        hspTomcatV3.init();
        //启动hsptomcat容器
        hspTomcatV3.run();
    }


    //启动HspTomcatV3容器
    public void run() {

        try {
            ServerSocket serverSocket = new ServerSocket(8080);
            System.out.println("=====hsptomcatv3在8080监听======");
            while (!serverSocket.isClosed()) {
                Socket socket = serverSocket.accept();
                HspRequestHandler hspRequestHandler =
                        new HspRequestHandler(socket);
                new Thread(hspRequestHandler).start();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    //直接对两个容器进行初始化
    public void init() {
        //读取web.xml => dom4j =>
        //得到web.xml文件的路径 => 拷贝一份.
        String path = HspTomcatV3.class.getResource("/").getPath();
        //System.out.println("path= " + path);
        //使用dom4j技术完成读取
        SAXReader saxReader = new SAXReader();
        //困难->真的掌握
        try {
            Document document = saxReader.read(new File(path + "web.xml"));
            System.out.println("document= " + document);
            //得到根元素
            Element rootElement = document.getRootElement();
            //得到根元素下面的所有元素
            List<Element> elements = rootElement.elements();
            //遍历并过滤到 servlet servlet-mapping
            for (Element element : elements) {
                if ("servlet".equalsIgnoreCase(element.getName())) {
                    //这是一个servlet配置
                    //System.out.println("发现 servlet");
                    //使用反射将该servlet实例放入到servletMapping
                    Element servletName = element.element("servlet-name");
                    Element servletClass = element.element("servlet-class");
                    servletMapping.put(servletName.getText(),
                            (HspHttpServlet) Class.forName(servletClass.getText().trim()).newInstance());
                } else if ("servlet-mapping".equalsIgnoreCase(element.getName())) {
                    //这是一个servlet-mapping
                    //System.out.println("发现 servlet-mapping");

                    Element servletName = element.element("servlet-name");
                    Element urlPatter = element.element("url-pattern");
                    servletUrlMapping.put(urlPatter.getText(), servletName.getText());

                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        //老韩验证,这两个容器是否初始化成功
        System.out.println("servletMapping= " + servletMapping);
        System.out.println("servletUrlMapping= " + servletUrlMapping);
    }
}

Servlet

特点: 

  • 本质就是java类
  • 由Tomcat解析和执行的
  • 1、支持web.xml 配置        2、3.0版本以后也支持注解

查看版本

引入

tomcat/lib下,添加servlet-api.jar

在src下java类实现Servlet接口

Servlet 接口的五种方法

public interface Servlet {
    void init(ServletConfig var1) throws ServletException;
 
    ServletConfig getServletConfig();
 
    void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
 
    String getServletInfo();
 
    void destroy();
}
import javax.servlet.*;
import java.io.IOException;

public class HelloServlet implements Servlet {

    @Override
    /**
     * 1.初始化 servlet
     * 2.当创建HelloServlet 实例时,会调用init方法,即访问这个网站
     * 3. 该方法只会被调用一次
    */
    public void init(ServletConfig servletConfig) throws ServletException {
        System.out.println("init() run");
    }

    @Override
    //返回ServletConfig 也就是返回Servlet的配置
    public ServletConfig getServletConfig() {
        return null;
    }

    @Override
    //service 方法是专门用来处理请求和响应的
    /**
     * 1. service方法处理浏览器的请求(包括get/post)
     * 2. 当浏览器每次请求Servlet时,就会调用一次service
     * 3. 当tomcat调用该方法时,会把http请求的数据封装成实现ServletRequest接口的request对象
     * 4. 通过servletRequest 对象,可以得到用户提交的数据
     * 5. servletResponse 对象可以用于返回数据给tomcat->浏览器
    */
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("hello world");
    }

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

    @Override
    /**
     * 1. 该方法是在servlet销毁时,被调用
     * 2. 只会调用一次
    */
    public void destroy() {
        System.out.println("Servlet正在销毁");

    }
}

在web.xml配置HelloServlet,即:给HelloServlet 提供对外访问地址
web.xml主要用来配置该web应用使用到的Servlet 

    <servlet>
        //给Servlet取名,名字唯一
        <servlet-name>Helloservlet</servlet-name>
        //Servlet程序的全类名,反射需要
        <servlet-class>HelloServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>Helloservlet</servlet-name>
        //url路径
        <url-pattern>/HelloServlet</url-pattern>
    </servlet-mapping>

浏览器调用Servlet流程分析

id就是Servlet-name 
Servlet默认情况下是单例的

注解流程

       第1步,对包(从src开始)进行扫描,如果某个类是@Webservlet,就说明该类是Servlet,读取urlPatterns
        第2步,读取注解信息
        第6步,能得到@Webservlet,就肯定能得到类的全路径,进行反射实例化

Servlet 生命周期

主要有三个方法

  • init()初始化阶段
  • service()处理浏览器请求阶段
  • destroy()终止阶段 

在哈希map中找实例

初始化阶段

Servlet 容器(比如:Tomcat)加载 Servlet,加载完成后,Servlet 容器会创建一个Servlet实例 并调用init()方法,init()方法只会调用一次,Servlet容器在下面的情况装载Servlet: 

1. Servlet 容器(Tomcat)启动时自动装载某些servlet,实现这个需要在web.xml文件中添加 
        <load-on-startup>1<load-on-startup>   1 表示装载的顺序   
        已经把Servlet实例存进hashmap,init()在tomcat启动时被调用,再去访问网站不会再输出
2. 在Servlet 容器启动后,浏览器首次向Servlet发送请求

3. Servlet 重新装载时(比如tomcat 进行redeploy【redeploy 会销毁Hashmap中的所有Servlet 实例】), 浏览器再向Servlet发送请求的第1次

处理浏览器请求阶段(service方法)

1. 每收到一个http请求,服务器就会产生一个新的线程去 处理 

Thread.currentThread().getId();

2. 创建一个用于封装HTTP请求消息的ServletRequest对象和一个代表HTTP响应消息的 ServletResponse 对象 
3. 然后调用Servlet的service()方法并将请求和响应对象作为参数传递进去

public void service(ServletRequest servletRequest, ServletResponse servletResponse)
tomcat把http请求的信息放入一个实现了ServletRequest接口的类,再把这个类传给servletRequest,进行封装 
然后tomcat会调用service(),就能使用http请求的信息

ServletResponse同理

终止阶段 destory方法(体现Servlet完整的生命周期)

当web应用被终止,或者Servlet容器终止运行,或者Servlet类重新装载时,会调用destroy() 方法 , 比如重启tomcat,或者 redeployweb应用

通过继承HttpServlet开发Servlet

GenericServlet是个抽象类,让HttpServlet去继承

开发步骤:

  1. 编写一个类去继承HttpServlet类
  2. 根据业务需要重写doGet或doPost方法 
  3. 到web.xml中的配置Servlet程序

如何执行到doPost和doGet

先执行service,然后再回到自己的doGet、doPost

根据动态绑定机制,当前this就是自己定义的类,运行类型就是这个类,所以执行定义类的doGet和doPost 

Servlet 注意事项和细节

 Servlet-注解方式

import javax.jws.WebService;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;


@WebServlet(urlPatterns = {"/ok1", "/ok2"}) //代替了web.xml的配置
public class OkServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("注解方式 OkServlet doPost()");
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("注解方式 OkServlet doGet()");
    }
}

模拟注解流程

import com.sun.net.httpserver.HttpServer;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import java.lang.annotation.Annotation;
import java.util.HashMap;

public class TestAnnotationServlet {
    private static final HashMap<String, Object> hm = new HashMap<>();
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        //得到扫描的包 路径,进而得到类的全路径
        String path = "OkServlet";
        //得到Class对象
        Class<?> aClass = Class.forName(path);
        //通过Class对象,得到Annotation
        WebServlet annotation = aClass.getAnnotation(WebServlet.class);
        System.out.println(annotation);//得到WebServlet的所有信息
        String[] strings = annotation.urlPatterns();//再取出url的信息
        for(String url : strings)
            System.out.println("url= " + url);

        //如果匹配url,如果是第一次,tomcat就会创建一个OkServlet实例,放入hashmap
        Object instance = aClass.newInstance();
        System.out.println("instance= " + instance);

        hm.put("Okservlet",instance);
        System.out.println(hm);
    }
}

注解嵌套

@WebServlet(urlPatterns = {"/ok1", "/ok2"}, initParams = @WebInitParam(name = "xx", value = "yy")

WebInitParam是数组,多组可以用{ }包起来

initParams = {@WebInitParam(name = "xx", value = "yy"),@WebInitParam(name = "zz", value = "kk")}

urlPattern配置

精确匹配
@WebServlet(urlPatterns = {"/ok/zs", "/ok2"})
目录匹配
@WebServlet(urlPatterns = {"/ok/*", "/ok2/*"})
//多级目录也可以,/ok/aa/bb/cc,/ok/ok2/aa
扩展名匹配
@WebServlet(urlPatterns = {"*.action"})
//不能带/
任意匹配
@WebServlet(urlPatterns = {"/"})
注意事项

当Servlet配置了"/",会覆盖tomcat的DefaultServlet,当其他的utl-pattern都匹配 不上时,都会走这个Servlet,这样可以拦截到其它静态资源

优先级遵守:精确路径>目录路径>扩展名路径>/*>/

HTTP

浏览器显示信息

public class LoginServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //输出一句话,返回给浏览器
        //通过response获取流PrintWriter,可以给浏览器回复数据
        //为了让浏览器显示中文,需要设置utf-8
        //text/html这个是MIME即告诉浏览器返回的数据是text类型下的html格式数据
        response.setContentType("text/html;charset=utf-8");
        PrintWriter writer = response.getWriter();
        writer.print("登录成功");
        //为了确保数据返回,可以加flush()和close()
        writer.flush();//刷新缓存数据
        writer.close();;//关闭流,释放资源
    }
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    }
}

GET请求头和请求行分析

 POST分析

host是目标,origin是来源
content-type:如果用户名输入中文,他就会进行url编码
 

GET请求和POST请求

数据大POST,封装到http请求体,可以携带更多的数据,安全隐私POST
数据小GET,复制分享链接GET

HTTP响应包分析

状态码 

200 请求成功
301资源(网页等)被永久转移到其他URL
404请求的资源(网页等)不 存在
500内部服务器错误
302(Found)重定向,资源临时移动,仍使用原来的URI
304(Not Modified)请求的没有修改过,不返回资源,仍然使用缓存数据
302

302重定向到hi.html

浏览器有两次请求

第一次,浏览器访问某个资源,资源返回302并且给你新的信息,告诉你要访问哪个资源

第二次,浏览器访问新的资源
1、返回302状态码        2、响应头Location:/hi.html
response.senRedirect("/servlet/hi.html");

304

 MIME

 格式:大类型/小类型

Servlet

ServletConfig

基本介绍

1.ServletConfig类是为Servlet程序的配置信息的类
2.Servlet程序和ServletConfig对象都是由Tomcat负责创建
3.Servlet程序默认是第1次访问的时候创建;ServletConfig在Servlet程序创建时,就创建一个对应的ServletConfig对象

作用 

1.获取Servlet程序的servlet-name的值 
2. 获取初始化参数 init-param
3. 获取 ServletContext 对象

应用

获取web.xml中配置的用户名和密码

 <servlet>
        <servlet-name>DBServlet</servlet-name>
        <servlet-class>DBServlet</servlet-class>
        <init-param>
            <param-name>username</param-name>
            <param-value>hsp</param-value>
        </init-param>
        <init-param>
            <param-name>pwd</param-name>
            <param-value>123456</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>DBServlet</servlet-name>
        <url-pattern>/db</url-pattern>
    </servlet-mapping>
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;

public class DBServlet extends HttpServlet {


    /**
     * 梳理ServletConfig config 使用流程
     * 1. 当DBServlet对象初始化时, tomcat会同时创建一个 ServletConfig对象
     * 2. 这时如果DBServlet init() 方法中你调用 super.init(config);
     * 3. 调用 父类 GenericServlet
     * public void init(ServletConfig config) throws ServletException {
     * this.config = config;
     * this.init();
     * }
     * 这时就会把 Tomcat创建的 ServletConfig对象赋给 GenericServlet的属性 config
     * 4. 因此如果你重写init()方法,记住如果你想在其它方法通过 getServletConfig() 方法获取ServletConfig
     * , 则一定要记住 调用  super.init(config);
     */

    @Override
    public void init(ServletConfig config) throws ServletException {

        //ConcurrentHashMap, 是一个线程安全的容器.
        System.out.println("init" + config);
        super.init(config);
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request,response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        ServletConfig servletConfig = getServletConfig();
        System.out.println(servletConfig.getServletName());
        String username = servletConfig.getInitParameter("username");
        String pwd = servletConfig.getInitParameter("pwd");
        System.out.println("username= " + username);
        System.out.println("pwd= " + pwd);
    }
}
  • HttpServelt的父类GenericServlet存在getServletConfig(),可以返回ServletConfig对象
  • 通过ServletConfig接口中的方法可以返回数据
public abstract class GenericServlet implements Servlet, ServletConfig, Serializable {
    //当一个属性被 transient 修饰,表示该属性不会被串行化(有些重要信息,不希望保存到文件)
    private transient ServletConfig config;
    public ServletConfig getServletConfig() {
        return this.config;//这个config就是这个类的config
    }
}


public interface ServletConfig {
    String getServletName();
    ServletContext getServletContext();
    String getInitParameter(String var1);
    Enumeration<String> getInitParameterNames();
}

ServletContext(域对象)

为什么需要ServletContext
1. 先看一个需求: 如果我们希望统计某个web应用的所有Servlet被访问的次数,怎么办?

方案1-DB

方案2-ServletContext 

基本介绍

1. ServletContext 是一个接口,它表示 Servlet 上下文对象
2. 一个 web 工程,只有一个 ServletContext 对象实例,ServletContext 对象是一个域对象
3. ServletContext 对象 是在 web 工程启动的时候创建,在 web 工程停止的时销毁 
4. ServletContext 对象可以通过 ServletConfig.getServletContext 方法获得对 ServletContext 对象的引用,也可以通过this.getServletContext()来获得其对象的引用。
5. 由于一个WEB应用中的所有Servlet共享同一个ServletContext对象,因此Servlet对象 之间可以通过ServletContext 对象来实现多个Servlet间通讯。ServletContext对象通常也被称之为域对象。

 作用

1. 获取 web.xml 中配置的上下文参数 context-param[信息和整个web应用相关,而不是 属于某个Servlet]
2. 获取当前的工程路径,格式:/工程路径 =》 比如 /servlet
3. 获 取 工 程 部 署 后 在 服 务 器 硬 盘 上 的 绝 对 路 径 (比如: D:\hspedu_javaweb\servlet\out\artifacts\servlet_war_exploded)
4. 像 Map 一样存取数据, 多个Servlet共享数据,域对象

    <servlet>
        <servlet-name>Servlet</servlet-name>
        <servlet-class>Servlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>Servlet</servlet-name>
        <url-pattern>/servlet</url-pattern>
    </servlet-mapping>    
    <context-param>
        <param-name>website</param-name>
        <param-value>http://www.abc.com</param-value>
    </context-param>
    <context-param>
        <param-name>company</param-name>
        <param-value>abc教育</param-value>
    </context-param>
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //获取web.xml的context-parameter

        //1.获取到ServletContext对象
        ServletContext servletContext = getServletContext();
        //也可以根据ServletConfig获取ServletContext对象
        //    getServletConfig().getServletContext();
        //2. 获取website
        String website = servletContext.getInitParameter("website");
        String company = servletContext.getInitParameter("company");
        //3. 获取项目的工程路径
        String contextPath = servletContext.getContextPath();
        //4. 获取项目发布会,正在的工作路径
        //   /表示我们的项目(发布后)的 根路径 D:\hspedu_javaweb\servlet\out\artifacts\servlet_war_exploded
        String realPath = servletContext.getRealPath("/");
        System.out.println("项目路径= " + contextPath);// /servlet
        System.out.println("website= " + website);
        System.out.println("company= " + company);
        System.out.println("项目发布后的绝对路径= " + realPath);

    }
应用

统计网站访问次数

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;
import java.io.PrintWriter;
class WebUtils {
    public static Integer visitCount(ServletContext servletContext) {
        // //从servletContext获取 visit_count 属性 k-v
        Object visit_count = servletContext.getAttribute("visit_count");
        // //判断visit_count是否为null
        if (visit_count == null) {//说明是第1次访问网站
            servletContext.setAttribute("visit_count", 1);
            visit_count = 1;
        } else { //是第二次或以后
            //     //取出visit_count属性的值+1
            visit_count = Integer.parseInt(visit_count + "") + 1;
            //     //放回到servletContext
            servletContext.setAttribute("visit_count", visit_count);
        }
        return Integer.parseInt(visit_count + "");
    }
}
public class OrderServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request, response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        ServletContext servletContext = getServletContext();
        Integer visit_count = WebUtils.visitCount(servletContext);
        response.setContentType("text/html;charset=utf-8");
        PrintWriter writer = response.getWriter();
        writer.print("次数是" + visit_count);
        writer.flush();
        writer.close();
    }
}

class PayServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request, response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //都是同一个servletContext
        ServletContext servletContext = getServletContext();
        Integer visit_count = WebUtils.visitCount(servletContext);
        response.setContentType("text/html;charset=utf-8");
        PrintWriter writer = response.getWriter();
        writer.print("次数是" + visit_count);
        writer.flush();
        writer.close();
    }
}

HttpServletRequest(域对象)

介绍

1. HttpServletRequest 对象代表客户端的请求
2. 当客户端/浏览器通过HTTP协议访问服务器时,HTTP请求头中的所有信息都封装在Request这个对象中,然后传递到service方法(doGet、doPost)中给我们使用。
3. 通过这个对象的方法(HttpServletRequest ),可以获得客户端这些信息。

常用方法 

post中文会出现乱码,设置request.setCharacterEncoding("utf-8");

应用

在一个表单提交数据给Servlet, 然后在Servlet通过HttpServletRequest 对象获取相关数据

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //这里我们使用request对象,获取表单提交的各种数据
        System.out.println("HttpServletRequestMethods doPost() 被调用..");

        /***********************************
         *  获取和http请求头相关信息
         ***********************************/

        System.out.println("请求的资源路径URI= " + request.getRequestURI());
        //http://主机/uri
        System.out.println("请求的统一资源定位符(绝对路径)URL= " + request.getRequestURL());
        System.out.println("请求的客户端ip 地址= " + request.getRemoteAddr());//本地就是127.0.01
        //思考题:如发现某个ip 在10s中,访问的次数超过 100次,就封ip
        //实现思路: 1用一个集合concurrentHashmap[ip:访问次数] 2[线程/定时扫描] 3 做成处理
        // 获取http请求头的信息,可以指定其他,比如 User-Agent , Host等待 老师就举一个例子
        System.out.println("http请求头HOST= " + request.getHeader("Host"));
        // 说明,如果我们希望得到请求的头的相关信息,可以使用request.getHeader("请求头字段")
        System.out.println("该请求的发起地址是= " + request.getHeader("Referer"));
        // 请获取访问网站的浏览器是什么?
        String userAgent = request.getHeader("User-Agent");
        System.out.println("User-Agent= " + userAgent);
        // 取出FireFox, 取出最后
        String[] s = userAgent.split(" ");
        System.out.println("浏览器=" + s[s.length - 1].split("\\/")[0]);
        //获取 Cookie
        // 	JSESSIONID=8CBBD23BDE01BAE6705E03C5C8916BD1

        String cookie = request.getHeader("Cookie");
        String JSESSIONID = cookie.split("=")[1];
        System.out.println("取出JSESSIONID= " + JSESSIONID);

        取出  Windows NT 10.0  和 Win64
        // 主要是Get / Post
        System.out.println("http请求方式~= " + request.getMethod());
        /***********************************
         *  获取和请求参数相关信息, 注意要求在返回数据前,获取参数
         ***********************************/



        request.setCharacterEncoding("utf-8");
        //1. 获取表单的数据[单个数据]
        //username=tom&pwd=&hobby=hsp&hobby=spls
        String username = request.getParameter("username");
        String pwd = request.getParameter("pwd");


        //2. 获取表单一组数据
        String[] hobbies = request.getParameterValues("hobby");
        System.out.println("username= " + username);
        System.out.println("pwd= " + pwd);
        //增强for循环的快捷键 iter->回车即可 , 能使用快捷键,就使用快捷键
        for (String hobby : hobbies) {
            System.out.println("hobby=" + hobby);
        }

        //推而广之, 如果是 单选 , 下拉框 等等. => 作业布置

        //返回接收到的信息, 给浏览器回显
        //本质就是在http响应头,加上 Content-Type: text/html;charset=utf-8
        //说 text/html 表示返回的数据类型,浏览器会根据这个类型来解析数据
        // text/plain 表示返回的数据,请浏览器使用文本方式解析
        // application/x-tar 表示返回的是文件,浏览器就会以下载文件的方式处理
        response.setContentType("text/html;charset=utf-8");
        PrintWriter writer = response.getWriter();
        writer.print("<h1>提交的用户名= " + username + "</h1>");//h1会根据text/html解析
        writer.flush();
        writer.close();

    }

注意

解决接收参数的中文乱码问题, 老师提示,写在 getParameter前.
username写中文,url格式,浏览器查看显示乱码,因为tomcat格式不一致
request.setCharacterEncoding("utf-8");

请求转发 

应用

根据用户名来确定用户是什么身份
始终是同一个request对象

public class CheckServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String username = request.getParameter("username");
        //注意:如果是同一个request对象(请求转发),那么可以在不同的servlet中,是getParameter
        if("tom".equals(username)) {
            request.setAttribute("role", "管理员");
        } else {
            request.setAttribute("role", "普通用户");
        }
        //获取分发器
        //        1. /manageServlet写的是 要转发的servlet的url
        //        2. / 会被解析成 /资源
        //        3. forward(request, response) 表示把当前servlet的request对象和response对象,传递给下一个servlet使用
        RequestDispatcher requestDispatcher = request.getRequestDispatcher("/manageServlet");
        requestDispatcher.forward(request, response);
    }
}


public class ManageServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String username = request.getParameter("username");
        String role = (String)request.getAttribute("role");
        response.setContentType("text/html;charset=utf-8");
        PrintWriter writer = response.getWriter();
        writer.print("用户名" + username + "<br/>");
        writer.print("角色" + role);
        writer.flush();
        writer.close();
    }
}
注意事项和细节

没有跟浏览器发生任何关系,只会保留第一个servlet的url,只有一次http请求
不能直接访问WEB-INF目录下的资源,只能转发

HttpServletResponse 

  1. 每次HTTP请求,Tomcat会创建一个 HttpServletResponse 对象传递给 Servlet 程序去 使用。
  2. HttpServletRequest 表示请求过来的信息,HttpServletResponse 表示所有响应的信息, 如果需要设置返回给客户端的信息,通过HttpServletResponse 对象来进行设置即可

类图

 向客户端返回数据

中文乱码 

请求重定向 

注意事项和细节 

重定向是在浏览器中解析的
第一个/会被浏览器解析成http://localhost:8080/

动态获取到application context
String contextPath = getServletContext().getContextPath();
response.sendRedirect(contextPath + "/downServletNew");

  • 19
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值