1. IDEA 创建 JavaWeb 项目

1.1 选择JavaEE 项目

1.2 Project tempate中选择 Web Application

1.3 一路创建

1.4 在Edit Configurations 设置tomcat, 下面为brew 安装的tomcat地址

/usr/local/Cellar/tomcat@9/9.0.53/libexec

1.5 设置On update action: update resources, On frame deactivation: update resources

1.6 设置第二栏Deployment: Application text: /web_demo

1.7 启动tomcat 即可自动在浏览器上预览

2. 打包JavaWeb 项目

2.1 在本地wepapp文件夹内, 使用jar -cvf myweb.war .打包

jar -cvf myweb.war .

2.2 上传到tomcat服务器中的webapps中, 重启tomcat即可看到发布结果

scp myweb.war webserver:tomcat9.0/webapps/

./shutdown.sh
./startup.sh

3. 更改Tomcat 端口

conf中server.xml 8080更改为80端口

4. 在server.xml中添加其它目录做为tomcat项目

**在Host中间添加

<Context path="/myweb" docBase="/Users/lizicai/IdeaProjects/web_demo/src/main/webapp" />

访问ip:8080/myweb 即可

5. 设置域名访问tomcat

5.1 在server.xml中添加配置

# 在<Engine name="Catalina" defaultHost="localhost"> 下添加
<Host name="tom.lizicai.com" appbase="webapps"
    unpackWARs="true" autoDeploy="true" >
    <Context path="/s" docBase="web_demo" />
</Host>
name 域名
appbase 项目存放路径
unpackWARs 解压
autoDeploy 自动发布

5.2 设置dns, 或者更改本机hosts

# 添加dns
192.168.0.100 tom.lizicai.com

# 更改本机hosts
127.0.0.1 tom.lizicai.com

6. HTTP 协议的响应

  • 常见状态码
200 一切OK
302/307 请求重定向,两次请求,地址栏发生变化
304 请求资源未发生变化,使用缓存
404 请求资源未找到
500 服务器错误
  • HTTP 响应头
Location               请求重定向的地址,常与302,307配合使用
Server                 服务器相关信息
Content-Type           响应正文的MIME类型
Content-Length         响应正文的长度
Content-Dispositio     告知客户端浏览器,以下载的方式打开响应正文
Refresh                定时刷新
Lash-Modified          服务器资源的最后修改时间
Set-Cookie             会放管理相关,非常重要
Expires:-1             服务器资源到客户端浏览器后的缓存时间
Catch-Control:no-catch 不要缓存

Servlet

  • Servlet的介绍
    • Servlet是运行在Java 服务器端的程序,用于接收和响应来自客户端基于HTTP协议的请求。
    • 如果想实现Servlet的功能,可以通过实现javax.servlet.Servlet接口或者继承它的实现类。
    • 核心方法:service(),任何客户端的请求都会经过该方法。

对应项目案例在03_JavaWeb

  • Servlet 的执行流程
    • 浏览器地址
    • web.xml的地址/url
    • /url匹配web.xml的servlet name
    • servlet name 匹配servlet class
    • 执行servlet calss中的service()方法

实现 GenericServlet

package com.lizicai.test;

import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;

public class Demo1GenericServlet extends GenericServlet {
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("Servlet 执行了");
    }
}

实现HttpServlet

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class Demo2HttpServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("DoPost 执行了");
    }
}

servlet 详细执行过程

servlet执行过程

Servlet GenericServlet HttpServlet 之间的关系

Servlet GenericServlet HttpServlet关系

Servlet 实现方式

Servlet 实现方式

  • 第一种
    • 实现Servlet接口,实现所有的抽象方法。该方式支持最大程度的自定义。
  • 第二种
    • 继承 GenericServlet抽象类,必须重写 service方法,其他方法可选择重写。该方式让我们开发Servlet变得简单。
    • 但是这种方式和HTTP协议无关。
  • 第三种
    • 继承 Httpser/let抽象类
    • 需要重写doGet和doPost方法。该方式表示请求和响应都需要和HTTP协议相关。

Servlet 生命周期

  • 出生: 请求第一次到达Servlet时,对象就创建出来, 并且初始化. 只出生一次, 放到内存中.
  • 活着: 服务器提供服务的整个过程中, 该对象一直存在, 每次都执行service() 方法
  • 死亡: 当前服务停止时, 或者服务器宕机时, 对象死亡.

结论: Servlet 对象只会创建一次, 销毁一次, 所以Servlet对象只有一个实例, 如果对象实例在应用中是唯一存在, 就是单例模式

Servlet 线程案例问题

  • 由于Servlet 采用的单例模式, 也就是整个应用中只有一个实例, 这个实例是否是线程案例的

  • 模拟2个用户同时访问HttpServlet时, doGet中打印用户名, 其中一个sleep3000

  • 结论: 2个用户返回同一个用户名, 线程不安全的.

  • 解决:

    • 定义类成员要谨慎. 如果是共用的, 并且只会在初始化时赋值, 其他时间都是获取的话,那没问题的.
    • 如果不是共用的, 或者每次使用都有可能对其赋值, 那就要考虑线程安全问题了.
    • 变量可以定义到doGet doPost方法内
    • 在doGet doPost 使用同步功能, 如 synchronized (this)
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

public class Demo4HttpServletFixed extends HttpServlet {
    private String username;
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String username = null;
        username = req.getParameter("username");
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        PrintWriter pw = resp.getWriter();
        pw.println("welcome   "+username);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        synchronized (this){
            username = req.getParameter("username");
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            PrintWriter pw = resp.getWriter();
            pw.println("welcome   "+username);
        }

    }
}

Serlet 映射方式

  • 第一种
    • 具体名称的方式, 访问的资源路径必须和映射配置安全相同
  • 第二种
    • /开头 + 通配符的方式. 只要符合目录结构即可, 不用是考虑结尾
  • 第三种
    • 通配符 + 固定格式结尾的方式. 只要符合固定结尾格式即可, 不用考虑前面的路径

**优先级的问题, 越时具体优化级越高, 第一种 > 第二种 > 第三种

<!--    Servlet 映射方式1   -->
    <servlet>
        <servlet-name>demo5HttpServlet</servlet-name>
        <servlet-class>com.lizicai.test.Demo5HttpServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>demo5HttpServlet</servlet-name>
        <url-pattern>/demo5HttpServlet</url-pattern>
    </servlet-mapping>

<!--    Servlet 映射方式2 -->
    <servlet>
        <servlet-name>demo5HttpServlet</servlet-name>
        <servlet-class>com.lizicai.test.Demo5HttpServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>demo5HttpServlet</servlet-name>
        <url-pattern>/demo/*</url-pattern>
    </servlet-mapping>

    <!--    Servlet 映射方式3 -->
    <servlet>
        <servlet-name>demo5HttpServlet</servlet-name>
        <servlet-class>com.lizicai.test.Demo5HttpServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>demo5HttpServlet</servlet-name>
        <url-pattern>*.do</url-pattern>
    </servlet-mapping>
  • Servlet 多映射使用的场景
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class Demo6HttpServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req,resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        int sum = 100;
        String url = req.getRequestURI();
        String vUrl = url.substring(url.lastIndexOf("/"));
        if( "/vip".equals(vUrl)){
            sum = 95;
        } else if( "/vvip".equals(vUrl)){
            sum = 90;
        } else {
            sum = 100;
        }
        System.out.println(url+"..."+vUrl+"您的商品价格是"+sum+"元");
    }
}

Servlet 创建时机

  • 第一次访问时创建
    • 优势: 减少对服务器内存的浪费. 提高了服务器启动的效率
    • 弊端: 如果有一些要在应用加载时就做的初始化操作, 无法完成
  • 服务器加载时创建
    • 优势: 提前创建好对象, 提高了首次执行的效率. 可以完成一些应用加载时要做的初始化操作
    • 弊端: 对服务器内存占用较多, 影响了服务器启动的效率.

**修改Servlet 创建时机, 在标签中, 添加标签 **正整数代表服务器加载时间创建, 值越小优先级越高. 负整数或不写代表第一次访问创建.

<servlet>
    <servlet-name>demo6HttpServlet</servlet-name>
    <servlet-class>com.lizicai.test.Demo6HttpServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>demo6HttpServlet</servlet-name>
    <url-pattern>/demo/*</url-pattern>
</servlet-mapping>

默认Servlet

  • 默认Servlet 是由服务器提供的一个Servlet. 它配置在Tomcat的conf目录中的web.xml中.
  • 它的映射路径是/, 我们在发送请求时, 首先会在我们项目中的web中查看映射配置, 找到则执行.
  • 但是当找不到对应的Servlet路径时, 就去找默认的Servlet, 由默认Servlet处理, 一切都Servlet.

ServletConfig

  • ServletConfig 是Servlet 的配置参数对象, 在Servlet的规范中, 允许为每一个Servlet都提供一个初始化的配置. 所以每个Servlet都有一个自己的ServletConfig
  • 作用: 在Servlet的初始化时, 把一些配置信息传递给Servlet
  • 生命周期: 和Servlet 相同.
<servlet>
    <servlet-name>demo7ServletConfig</servlet-name>
    <servlet-class>com.lizicai.test.Demo7ServletConfig</servlet-class>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>UTF-8</param-value>
    </init-param>
</servlet>
<servlet-mapping>
    <servlet-name>demo7ServletConfig</servlet-name>
    <url-pattern>/demo7</url-pattern>
</servlet-mapping>

ServletConfig 常用方法

返回值              方法名                       说明
String              getInitParameter(Stringname) 根据参数名称获取参数值
Enumeration<String> getInitParameterNames()      获取所有参数名称的枚举
String              getServletName()             获取Servlet名称
ServletContext      getServletContext()          获取ServletContext对象
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Enumeration;

public class Demo7ServletConfig 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 {
        this.doPost(req,resp);
    }

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

        String enCodingValue = config.getInitParameter("encoding");
        System.out.println(enCodingValue);

        Enumeration<String> initParameterNames = this.config.getInitParameterNames();

        while ( initParameterNames.hasMoreElements()){
            String key = initParameterNames.nextElement();
            String value = config.getInitParameter(key);
            System.out.println(key+"...."+value);
        }

        String sereletName = config.getServletName();
        System.out.println(sereletName);

        ServletContext servletContext = config.getServletContext();
        System.out.println(servletContext);
    }
}

ServletContext 介绍

  • ServletContext 是应用上下文对象(应用域对象). 每一个应用中只有一个SeverContext对象
  • 作用: 可以配置和获得应用的全局初始化参数, 可以实现Servlet之间的数据共享.
  • 生命周期: 应用一加载则创建, 应用被停止则销毁

域对象

  • 域对象指的是对象有作用域. 也就是有作用范围. 域对象可以实现数据的共享. 不同的作用范围的域对象, 共享数据的能力也不一样
  • 在Servlet规范中, 一共有4个域对象. ServletContext就是其中的一个, 它也是Web应用中最大的作用域, 也
  • 叫application域. 它可以实现整个应用之间的数据共享.

ServletContext与下面Servlet关系

<servlet>
    <servlet-name>demo8ServletContext</servlet-name>
    <servlet-class>com.lizicai.test.Demo8ServletContext</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>demo8ServletContext</servlet-name>
    <url-pattern>/demo8ServletContext</url-pattern>
</servlet-mapping>

<context-param>
    <param-name>globalEncoding</param-name>
    <param-value>UTF-8</param-value>
</context-param>
<context-param>
    <param-name>globalDesc</param-name>
    <param-value>ServletContext cc</param-value>
</context-param>

Servlet 常用方法

返回值 方法名                                  说明
String getInitParameter(String.name)           根据名称获取全局配置的参数
String getContextPath()                        获取虚拟路径,这是run配置的delployment的context路径
String getRealPath(String.path)                根据虚拟目录获取应用部署的磁盘绝对路径
void   setAttribute(String.name,Object.object) 向应用域对象中存储数据
Object getAttribute(String.name)               通过名称获取应用域对象中的数据
void   removeAttribute(String.name)            通过名称移除应用域对象中的数据
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class Demo8ServletContext extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req,resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext servletContext = this.getServletContext();

        String value = servletContext.getInitParameter("globalEncoding");
        System.out.println(value);

        String contextPath = servletContext.getContextPath();
        System.out.println(contextPath);

        String realPath = servletContext.getRealPath("/");
        System.out.println(realPath);

        String realPath2 = servletContext.getRealPath("/b.txt");
        System.out.println(realPath2);
        String realPath3 = servletContext.getRealPath("/WEB-INF/c.txt");
        System.out.println(realPath3);
    }
}

多个Servlet 之间从ServletContext 获取数据

先访问demo8ServletContext2再访问demo8ServletContext3

<!--    多Servlet间共享数据, 通过ServletContext, Demo8ServletContext2和 Demo8ServletContext3之间-->

    <servlet>
        <servlet-name>demo8ServletContext2</servlet-name>
        <servlet-class>com.lizicai.test.Demo8ServletContext2</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>demo8ServletContext2</servlet-name>
        <url-pattern>/demo8ServletContext2</url-pattern>
    </servlet-mapping>

    <servlet>
        <servlet-name>demo8ServletContext3</servlet-name>
        <servlet-class>com.lizicai.test.Demo8ServletContext3</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>demo8ServletContext3</servlet-name>
        <url-pattern>/demo8ServletContext3</url-pattern>
    </servlet-mapping>
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class Demo8ServletContext2 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req,resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext servletContext = this.getServletContext();
        servletContext.setAttribute("name", new String("Zhangsan"));
        System.out.println("访问Demo8ServletContext2...写入Attribute");

    }
}
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class Demo8ServletContext3 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req,resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext servletContext = this.getServletContext();
        String value = (String) servletContext.getAttribute("name");
        System.out.println("从ServletContext 取出之前2存入name"+value);
    }
}

注解开发Servlet

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("/demo9Servlet")
public class Demo9Servlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req,resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("处理请求...");
    }
}

手动创建容器

  • 创建Demo10ManualServle 继承 HttpServlet
  • MyRegister implements ServletContainerInitializer
  • 在src/main/java文件夹下创建包META-INF.services, 创建文件javax.servlet.ServletContainerInitializer, 添加一行com.lizicai.test.MyRegister
  • 在MyRegister中执行
    • Demo10ManualServle demo10ManualServle = new Demo10ManualServle();
    • ServletRegistration.Dynamic registration = servletContext.addServlet (“demo10ManualServle”, demo10ManualServle);
    • registration.addMapping("/demo10ManualServle");
  • 浏览器访问 /demo10ManualServle
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class Demo10ManualServle extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req,resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("Demo10ManualServle处理请求...");
    }
}
public class MyRegister implements ServletContainerInitializer {
    @Override
    public void onStartup(Set<Class<?>> set, ServletContext servletContext) throws ServletException {
        // 创建servlet
        Demo10ManualServle demo10ManualServle = new Demo10ManualServle();

        // ServletContext  对象添加Servlet, 并得到Servlet 的动态配置对象
       ServletRegistration.Dynamic registration = servletContext.addServlet
               ("demo10ManualServle", demo10ManualServle);

        registration.addMapping("/demo10ManualServle");

    }
}
com.lizicai.test.MyRegister

案例, 提交学生信息->保存文件中->同时返回一个保存成功信息给浏览器

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.*;

@WebServlet("/demo11StudentServlet")
public class Demo11StudentServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String name = (req.getParameter("name"));
        String  age =  req.getParameter("age") ;
        String  chenji = req.getParameter("chenji") ;

        File file = new File("/Users/lizicai/workplace/test/b.txt");
        BufferedWriter bw = new BufferedWriter(new FileWriter(file,true));
        String sStr = name+","+age+","+chenji;
        System.out.println(sStr);
        bw.write(sStr);
        bw.close();

        PrintWriter pw2 = resp.getWriter();

        pw2.println("save success");
        pw2.close();
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doGet(req,resp);
    }
}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>学生信息保存页</title>
</head>
<body>
  <form action="/stu/demo11StudentServlet" method="get" autocomplete="on">
    <label for="name">学生姓名:</label>
    <input type="text" name="name" id="name">
    <br>
    <label for="age">学生年龄:</label>
    <input type="text" name="age" id="age">
    <br>
    <label for="chenji">学生成绩:</label>
    <input type="text" name="chenji" id="chenji">
    <br>
    <button type="submit">保存信息</button>
  </form>
</body>
</html>