Tomcat & Servlet & Request

Tomcat & Servlet

Web相关概念回顾

软件架构

  1. C/S:客户端/服务器端
  2. B/S:浏览器/服务器端

资源分类

  1. 静态资源:所有用户访问后,得到的结果都是一样的,静态资源可以被浏览器直接解析,如:html、css、js

  2. 动态资源:每个用户访问相同资源后,得到的结果可能不一样。动态资源被访问后,需要先转换为静态资源,再返回给浏览器。

    如:servlet/jsp、php、asp…

网络通信三要素

  1. IP:计算机在网络上的唯一标识。

  2. 端口:应用程序在计算机中的唯一标识。0~65536

  3. 传输协议

    tcp:可靠传输协议

    udp:不可靠

web服务器软件

服务器:安装了服务器软件的计算机

服务器软件:接收用户的请求,处理请求,做出响应

web服务器软件:接收用用户的请求,处理请求,做出响应

  • 在web服务器软件中,可以部署web项目,让用户通过浏览器来访问这些项目
  • web容器

常见的web服务器软件

  • weblogic:oracle公司,大型JavaEE服务器,支持JavaEE规范,收费。
  • webSphere:IBM公司,大型JavaEE服务器,支持JavaEE规范,收费。
  • JBOSS:JBOSS公司,大型JavaEE服务器,支持JavaEE规范,收费。
  • Tomcat:Apache基金组织,中小型的JavaEE服务器,仅仅支持少量的JavaEE规范,免费。

JavaEE:Java语言在企业级开发中使用的技术规范和综合,一共规定了13项大的规范。

Tomcat

Tomcat配置

  1. 下载:https://tomcat.apache.org/download-80.cgi

  2. 安装:解压压缩包即可,注意:安装目录建议不要有中文

  3. 卸载:删除目录即可

  4. 启动:配置jdk环境变量,配置tomcat环境变量。然后cmd命令行直接startup启动tomcat服务器

    可能遇到的问题:

    黑窗口一闪而过:

    • 原因:没有正确配置JAVA_HOME环境变量
    • 解决方案:重新配置JAVA_HOME

    启动报错:

    • 暴力:找到占用的端口号,并且找到对应的进程,杀死该进程

      netstat -ano

    • 温柔:修改tomcat的端口号

      在配置文件conf/server.xml中修改

      <Connect prot=“xxxx”

  5. 关闭

    • 正常关闭:bin/shutdown.bat或者ctrl+c
    • 强制关闭:点击启动窗口的X
  6. 目录结构

    • bin:可执行文件
    • conf:配置文件
    • lib:依赖jar包
    • logs:日志文件
    • temp:临时文件
    • webapps:存放web项目
    • work:存放运行时数据

部署项目的方式

  1. 直接将项目放到webapps目录下即可

    /hello:项目的访问路径–>虚拟目录

    简化部署:将项目打成一个war包,再将war包放置到webapps目录下

    • war包会自动解压缩,删除项目时,只需要删除项目的war包即可,项目会自动删除。
  2. 配置conf/server.xml文件

    在<Host>标签体中配置

    <Context docBase=“E:\hello” path="/hehe" />

    • docBase:项目存放的路径
    • path:虚拟目录

    问题:不安全

  3. **推荐,**在conf/Catalina/localhost创建任意名称的xml文件。在文件中编写

    <Context docBase=“E:\hello” />

    虚拟目录:xml文件的名称

静态项目和动态项目

目录结构

Java动态项目的目录结构:

  • 项目的根目录
    • WEB-INF目录
      • web.xml:web项目的核心文件
      • classes目录:放置字节码文件的目录
      • lib目录:放依赖jar包

将Tomcat集成到IDEA中,并且创建JavaEE的项目,部署项目。

Servlet入门

概念

概念:运行在服务器端的小程序

  • Servlet就是一个接口,定义了Java类被浏览器访问到(tomcat识别)的规则。
  • 以后我们自定义一个类,实现Servlet接口,复写方法

快速入门

  1. 创建JavaEE的项目

  2. 定义一个类,实现Servlet接口

    public class ServletDemo1 implements Servlet

  3. 实现接口中的抽象方法

  4. 配置Servlet

<!-- 配置Servlet -->
<servlet>
    <servlet-name>demo1</servlet-name>
    <servlet-class>cn.myz.web.servlet.Demo1</servlet-class>
</servlet>

<servlet-mapping>
    <servlet-name>demo1</servlet-name>
    <url-pattern>/demo1</url-pattern>
</servlet-mapping>

执行原理

  1. 当服务器接收到客户端浏览器的请求后,会解析这个请求的url路径,获取访问的Servlet的资源路径
  2. 查找web.xml文件,是否有对应的<url-pattern>标签体内容,与请求资源路径一致。
  3. 如果有,则根据servlet-name找到对应的<servlet-class>的全类名。
  4. tomcat会将该类的字节码文件加载进内存,并创建其对象
  5. 调用其方法

Servlet生命周期

创建:执行init方法,只执行一次

  • Servlet什么时候被创建?

    默认情况下,第一次被访问时,Servlet被创建。但也可以去配置指定Servlet的创建时机。在servlet标签下配置

    • 第一次被访问时,创建:<load-on-startup>值为负数
    • 在服务器启动时,创建:<load-on-startup>值为0或正整数
  • Servlet的init方法,只执行一次,说明一个Servlet在内存中只存在一个对象,Servlet是单例的

    多个用户同时访问时,可能存在线程安全问题。

    解决方法:尽量不要在Servlet里定义成员变量。即使定义了成员变量也不要对其修改值。

提供服务:执行service方法,执行多次

  • 每次访问Servlet时,Service方法都会被调用一次

销毁:执行destroy方法,只执行一次

  • Servlet被销毁时执行。服务器关闭时,Servlet被销毁。
  • 注意:只有服务器正常关闭时,才会执行destroy方法。

Servlet3.0

优点

  • 支持注解配置。可以不需要web.xml

步骤

  1. 创建JavaEE项目,选择Servlet的版本3.0以上即可,可以不创建web.xml

  2. 定义一个类,实现Servlet接口

  3. 复写方法

  4. 在类上使用注解@WebServlet注解配置

    @WebServlet(“资源路径”)

java.lang.String[] value() default {};//代表urlPatterns()属性的配置
java.lang.String[] urlPatterns() default {};//相当于<url-pattern>
int loadOnStartup() default -1;//相当于<load-on-startup>

javax.servlet.annotation.WebInitParam[] initParams() default {};

boolean asyncSupported() default false;

java.lang.String smallIcon() default "";

java.lang.String largeIcon() default "";

java.lang.String description() default "";

java.lang.String displayName() default "";

IDEA与tomcat的相关配置

IDEA会为每一个tomcat部署的项目单独建立一个配置文件。

  • 配置文件路径可以通过查看控制台logCATALINA_BASE获得:Using CATALINA_BASE: “C:\Users\hp.IntelliJIdea2019.2\system\tomcat\Tomcat_8_5_70_20210827_servlet”

工作空间项目和tomcat部署的web项目

  • tomcat真正访问的是“tomcat部署的web项目”,“tomcat部署的web项目”对应着“工作空间项目”的web目录下的所有资源
  • WEB-INF目录下的资源不能被浏览器直接访问。
  • 断点调试

Servlet详解

Servlet体系结构

Servlet–接口

​ |

GenericServlet – 抽象类

​ |

HttpServlet --抽象类

GenericServlet
  • 将Servlet接口中其它方法做了默认空实现,只将service()方法作为抽象
  • 将来定义Servlet类时,可以继承GenericServlet,实现service()方法即可
*HttpServlet

对http协议的一种封装,简化操作。使用步骤:

  1. 定义类继承HttpServlet
  2. 复写doGet/doPost方法

推荐使用继承HttpServlet复写doget/dopost方法

Servlet相关配置

urlpartten:Servlet访问路径

  • 一个Servlet可以定义多个访问路径:@WebServlet({“d1”,“dd1”,“ddd1”})
  • 路径定义规则:
    1. /xxx
    2. /xxx/xxx:多层路径,目录结构
    3. *.后缀名

HTTP

概念

概念:Hyper Text Transfer Protocol 超文本传输协议

传输协议:定义了客户端和服务器端通信时发送数据的格式。

特点:

  1. 基于TCP/IP的高级协议
  2. HTTP默认端口号:80
  3. 基于请求/响应模型:一次请求对应一次响应
  4. Http是一种无状态的协议:每次请求之间相互独立,不能交互数据

历史版本:

  • 1.0:每一次请求响应都会建立新的连接
  • 1.1:复用连接

请求消息数据格式

请求行

  • 请求方式 请求url 请求协议/版本

  • GET /login.html HTTP/1.1

  • 请求方式

    http协议有7种请求方式,常用的有2种

    GET:

    • 请求参数在请求行中,在url后。
    • 请求的url长度是有限制的。
    • 不安全

    POST:

    • 请求参数在请求体中。
    • 请求的url长度没有限制
    • 安全

请求头

  • 请求头名称:请求头值

  • 常见的请求头:

    User-Agent:浏览器告诉服务器,访问服务器使用的浏览器版本

    • 可以在服务器端获得不同浏览器的浏览器品牌和版本,返回给它们不同的代码,以解决不同的浏览器的兼容问题。

    Referer:http://localhost/login.html

    • 告诉服务器,当前请求从哪里来

    • 作用

      1.防盗链

      2.做统计工作

请求空行

  • 空行,就是用于分隔POST请求的请求头和请求体的。

请求体(正文)

  • 封装POST请求消息的请求参数的。

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,/;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
Cache-Control: max-age=0
Connection: keep-alive
Host: localhost
If-Modified-Since: Sat, 28 Aug 2021 03:00:59 GMT
If-None-Match: W/“264-1630119659545”
sec-ch-ua: “Chromium”;v=“92”, " Not A;Brand";v=“99”, “Microsoft Edge”;v=“92”
sec-ch-ua-mobile: ?0
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36 Edg/92.0.902.78

Request

  1. tomcat服务器会根据请求url中的资源路径,创建对应的ServletDemo对象
  2. tomcat服务器,会创建request和response对象,request对象中封装请求消息数据。
  3. tomcat将request和response两个对象传递给service方法,并调用service方法。
  4. 程序员可以通过request对象获取请求消息数据,通过response对象设置响应消息数据
  5. 服务器在给浏览器做出响应之前,会从response对象中拿程序员设置的响应消息数据。

Request对象和response对象的原理

  1. request和response对象是由服务器创建,我们来使用它们。
  2. request对象是来获取请求信息,response对象是来设置响应信息

继承体系结构

request对象继承体系结构

ServletRequest --接口

​ |继承

HttpServletRequest --接口

​ |实现

org.apache.catalina.connector.RequestFacade类

Request功能

获取请求消息数据

获取请求行数据

  • GET /day14/demo1?name=zhangsan HTTP/1.1

  • 方法

    1. 获取请求方式:GET

      String getMethod()

    2. (*)获取虚拟目录:/day14

      String getContextPath()

    3. 获取Servlet路径:/demo1

      String getServletPath()

    4. 获取get方式请求参数:name=zhangsan

      String getQueryString()

    5. (*)获取请求URI:

      String getRequestURI() /day14/demo1

      StringBuffer getRequestURL() http://localhost/day14/demo1

      URL:统一资源定位符,http://localhost/day14/demo1

      URI:统一资源标识符,/day14/demo1

    6. 获取协议及版本:HTTP/1.1

      String getProtocol()

    7. 获取客户机的IP地址:

      String getRemoteAddr()

获取请求头数据

  • 方法
  1. (*)String getHeader(String name):通过请求头的名称获取请求头的值
  2. Enumeration<String> getHeaderNames():获取所有的请求头名称

获取请求体数据

  • 请求体:只有POST请求方式,才有请求体,在请求体中封装了POST请求的请求参数

  • 步骤

    1. 获取流对象

      BufferedReader getReader():获取字符输入流,只能操作字符数据

      ServletInputStream getInputStream():获取字节输入流,可以操作所有类型的数据

      • 在文件上传知识点后详解
    2. 再从流对象中拿数据

其它功能

获取请求参数通用方式:不论get还是post请求方式都可以使用下列方法来获取请求参数

  1. String getParameter(String name):根据参数名称获取参数值 username=zs&password=123

  2. String[] getParemeterValues(String name):根据参数名称获取参数值的数组 hobby=xx&hobby=game

  3. Enumeration<String> getParameterName():获取所有请求的参数名称

  4. Map<String,String[]> getParameterMap():获取所有参数的map集合

  • 中文乱码问题:

    • get方式:tomcat8已经将get方式乱码entity解决了

    • post方式:会乱码

      解决方法:在获取参数前,设置request的编码request.setCharacterEncoding(“utf-8”);

    package request;
    
    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.BufferedReader;
    import java.io.IOException;
    import java.util.Enumeration;
    import java.util.Map;
    import java.util.Set;
    
    /**
     * @author: 小码农
     * @create: 2021-08-28 21:36
     **/
    @WebServlet("/RequestDemo6")
    public class RequestDemo6 extends HttpServlet {
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            // post 获取请求参数
    
            // 根据参数名获取参数值
            String username = req.getParameter("username");
            /*System.out.println("post");
            System.out.println(username);*/
            // 根据参数名称获取参数值的数组
            /*String[] hobbies = req.getParameterValues("hobby");
            for (String hobby:hobbies){
                System.out.println(hobby);
            }*/
    
            // 获取所有请求的参数名称
            /*Enumeration<String> parameterNames = req.getParameterNames();
            while (parameterNames.hasMoreElements()){
                String name = parameterNames.nextElement();
                System.out.println(name);
                String parameter = req.getParameter(name);
                System.out.println(parameter);
                System.out.println("---------------");
            }*/
    
            // 获取所有参数的map集合
            Map<String, String[]> parameterMap = req.getParameterMap();
            // 遍历
            Set<String> strings = parameterMap.keySet();
            for (String name:strings){
                // 根据键获取值
                String[] strings1 = parameterMap.get(name);
                System.out.println(name);
                for (String value:strings1){
                    System.out.println(value);
                }
                System.out.println("----------------");
            }
        }
    
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            // get 获取请求参数
    
            // 根据参数名获取参数值
            /*String username = req.getParameter("username");
            System.out.println("get");
            System.out.println(username);*/
            this.doPost(req,resp);
        }
    }
    

请求转发:一种在服务器内部的资源跳转方式

  • 步骤:
  1. 通过request对象获取请求转发器对象:RequestDispatcher getRequestDispatcher(String path)
  2. 使用RequestDispatcher对象来进行转发:forward(ServletRequest request,ServletResponse response)
  • 特点
  1. 浏览器地址栏路径不发生变化
  2. 转发只能转发到当前服务器内部资源中。
  3. 转发是一次请求
package request;

import javax.servlet.RequestDispatcher;
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;

/**
 * @author: 小码农
 * @create: 2021-08-28 21:36
 **/
@WebServlet("/RequestDemo8")
public class RequestDemo8 extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("demo8888被访问了...");

        // 存储数据到request域中
        req.setAttribute("msg","hello");

        // 转发到demo9资源
        req.getRequestDispatcher("/RequestDemo9").forward(req,resp);
    }

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

        this.doPost(req,resp);
    }
}
package request;

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;

/**
 * @author: 小码农
 * @create: 2021-08-28 21:36
 **/
@WebServlet("/RequestDemo9")
public class RequestDemo9 extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 获取数据
        Object msg = req.getAttribute("msg");
        System.out.println(msg);

        System.out.println("demo9999被访问了...");
    }

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

        this.doPost(req,resp);
    }
}

共享数据

域对象:一个有作用范围的对象,可以在范围内共享对象

request域:代表一次请求的范围,一般用于请求转发的多个资源中共享数据。

方法:

  1. setAttribute(String name,Object obj):存储数据
  2. Object getAttribute(String name):通过键获取值
  3. void removeAttribute(String name):通过键移除键值对

获取ServletContext

  • ServletContext getServletContext()

案例:用户登录

需求:

  1. 编写login.html登录页面

    username&password 两个输入框

  2. 使用Druid数据库连接池技术,操作mysql,day14数据库中的user表

  3. 使用jdbcTemplate技术封装JDBC

  4. 登录成功跳转到SuccessServlet展示:登陆成功!用户名,欢迎您

  5. 登陆失败挑战到FailServlet展示:登录失败,用户名或密码错误

开发步骤

  1. 创建项目,导入html页面,配置文件,导入jar包

    driverClassName=com.mysql.cj.jdbc.Driver
    url=jdbc:mysql://localhost:3306/db2
    username=root
    password=123456
    # 初识化连接
    initialSize=5
    # 最大连接数
    maxActive=10
    # 超时时间
    maxWait=3000
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hM3fZiAk-1630236204261)(image/image-20210829163357548.png)]

  2. 创建数据库环境

    // 创建LOGIN数据库
    CREATE DATABASE LOGIN;
    USE LOGIN;
    // 创建USER表
    CREATE TABLE USER(
    id INT PRIMARY KEY AUTO_INCREMENT,
    username VARCHAR(32) UNIQUE NOT NULL,
    password VARCHAR(32) NOT NULL
    );
    
  3. 创建com.myz.domain,创建User实体类

    package com.myz.domain;
    
    /**
     * @author: 小码农
     * @create: 2021-08-29 16:35
     * 用户实体类
     **/
    public class User {
        private int id;
        private String username;
        private String password;
    
        public int getId() {
            return id;
        }
    
        public void setId(int id) {
            this.id = id;
        }
    
        public String getUsername() {
            return username;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
        public String getPassword() {
            return password;
        }
    
        public void setPassword(String password) {
            this.password = password;
        }
    
        @Override
        public String toString() {
            return "User{" +
                    "id=" + id +
                    ", username='" + username + '\'' +
                    ", password='" + password + '\'' +
                    '}';
        }
    }
    
  4. 创建包com.myz.dao,创建类UserDao,提供login方法

    创建JDBC工厂类

    package com.myz.utils;import com.alibaba.druid.pool.DruidDataSourceFactory;import javax.sql.DataSource;import java.io.IOException;import java.io.InputStream;import java.sql.Connection;import java.sql.SQLException;import java.util.Properties;/** * @author: 小码农 * @create: 2021-08-29 16:40 * JDBC工具类,使用Durid连接池 **/public class JDBCUtils {    private static DataSource ds;    static {        // 1.加载配置文件        Properties pro = new Properties();        // 2.使用ClassLoader加载配置文件,获取字节输入流        InputStream resourceAsStream = JDBCUtils.class.getClassLoader().getResourceAsStream("druid.properties");        try {            pro.load(resourceAsStream);            // 3.初始化连接池对象            ds = DruidDataSourceFactory.createDataSource(pro);        } catch (IOException e) {            e.printStackTrace();        } catch (Exception e) {            e.printStackTrace();        }    }    /*    * 获取连接池对象    * */    public static DataSource getDataSource(){        return ds;    }    /*    * 获取连接Connection对象    * */    public static Connection getConnection() throws SQLException {        return ds.getConnection();    }}
    

    UserDao类

    package com.myz.dao;
    
    import com.alibaba.druid.util.JdbcUtils;
    import com.myz.domain.User;
    import com.myz.utils.JDBCUtils;
    import org.springframework.dao.DataAccessException;
    import org.springframework.jdbc.core.BeanPropertyRowMapper;
    import org.springframework.jdbc.core.JdbcTemplate;
    
    /**
     * @author: 小码农
     * @create: 2021-08-29 16:37
     * 操作数据库中user表的类
     **/
    public class UserDao {
    
        //声明JDBCTemplate对象共用
        private JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDataSource());
    
        /*
        * 登录方法
        * loginUser只有用户名密码
        * user包含用户全部数据
        *
        * */
        public User login(User loginUser){
            try {
                // 1.编写sql
                String sql = "select * from user where username=? and password=?";
                // 2.调用query方法
                User user = template.queryForObject(sql, new BeanPropertyRowMapper<User>(User.class), loginUser.getUsername(), loginUser.getPassword());
                return user;
            }catch (DataAccessException e){
                e.printStackTrace();
                return null;
            }
        }
    }
    
  5. 编写com.myz.servlet.LoginServlet类

  6. login.html中的form表单的action路径写法

    • 虚拟目录+Servlet的资源路径
  7. BeanUtils工具类,简化数据封装

    • 用于封装JavaBean的
    1. JavaBean:标准的Java类

      要求

      1. 类必须被public修饰

      2. 必须提供空参的构造器

      3. 成员变量必须使用private修饰

      4. 提供公共setter和getter方法

      功能:封装数据

    2. 概念:

      成员变量

      属性:setter和getter方法截取后的产物

      ​ 例如:getUsername() --> Username ——>username

    3. 方法:

      1. setProperty(Object obj,String name,String value):把value值按照obj对象有关于name的set方法,对obj赋值
      2. getProperty(Object obj,String name):根据name调用obj对象的getname方法,返回value值。
      3. populate(Object obj,Map map):封装JavaBean,它会把map集合的键值对信息,封装到对应的JavaBean对象中。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值