文章目录


1、Web概述

Web是全球广域网,也称为万维网(www),能够通过浏览器访问的网站。

2、JavaWeb技术栈

2.1、B/S架构

B/S 架构: Browser/Server,浏览器/服务器 架构模式,它的特点是,客户端只需要浏览器,应用程序的逻辑和数据都存储在服务器端。浏览器只需要请求服务器,获取Web资源,服务器把Web资源发送给浏览器即可。

优点:服务器端升级后,客户端无需任何部署就可以使用到新的版本。

2.2、静态资源

静态资源主要包含HTML、CSS、JavaScript、图片等,主要负责页面的展示。

2.3、动态资源

  • 动态资源主要包含Servlet、JSP等,主要用来负责逻辑处理。
  • 动态资源处理完逻辑后会把得到的结果交给静态资源来进行展示,动态资源和静态资源要结合一起使用。

2.4、数据库

  • 数据库主要负责存储数据。

2.5、HTTP协议

2.6、Web服务器

3、HTTP

3.1、简介

HTTP概念

HyperText Transfer Protocol,超文本传输协议,规定了浏览器和服务器之间数据传输的规则。

HTTP协议特点

  • 基于TCP协议: 面向连接,安全

    TCP是一种面向连接的(建立连接之前是需要经过三次握手)、可靠的、基于字节流的传输层通信协议,在数据传输方面更安全。

  • 基于请求-响应模型的:一次请求对应一次响应

  • HTTP协议是无状态协议:对于事物处理没有记忆能力。每次请求-响应都是独立的

    无状态指的是客户端发送HTTP请求给服务端之后,服务端根据请求响应数据,响应完后,不会记录任何信息。这种特性有优点也有缺点,

    • 缺点:多次请求间不能共享数据。Java中使用会话技术(Cookie、Session)来解决这个问题。
    • 优点:速度快

3.2、请求数据格式

在这里插入图片描述

格式介绍

  • 请求数据总共分为三部分内容,分别是请求行、请求头、请求体。
  • 请求行: HTTP请求中的第一行数据,请求行包含三块内容, GET表示请求方式, /请求资源路径] ,HTTP/1.1表示HTTP协议及版本。
  • 请求方式有七种,最常用的是GET和POST。
  • 请求头: 第二行开始,格式为key: value形式。
  • 请求头中会包含若干个属性,常见的HTTP请求头有:

    Host: 表示请求的主机名
    User-Agent: 浏览器版本,例如Chrome浏览器的标识类似Mozilla/5.0 …Chrome/79,IE浏览器的标识类似Mozilla/5.0 (Windows NT …)like Gecko;
    Accept:表示浏览器能接收的资源类型,如text/*,image/或者/*表示所有;
    Accept-Language:表示浏览器偏好的语言,服务器可以据此返回不同语言的网页;
    Accept-Encoding:表示浏览器可以支持的压缩类型,例如gzip, deflate等。

  • 请求体: POST请求的最后一部分,存储请求参数
    在这里插入图片描述
    请求体和请求头之间是一个空行隔开。

GET和POST两个请求之间的区别

  • GET请求请求参数在请求行中,没有请求体,POST请求请求参数在请求体中。
  • GET请求请求参数大小有限制,POST没有。

3.3、响应数据格式

3.3.1、格式介绍

响应数据总共分为三部分内容,分别是响应行响应头响应体
在这里插入图片描述

  • 响应行:响应数据的第一行,响应行包含三块内容, HTTP/1.1表示HTTP协议及版本,200表示响应状态码,ok表示状态码的描述。

  • 响应头:第二行开始,格式为key:value形式。

    常见的HTTP响应头有:

    Content-Type:		表示该响应内容的类型,例如text/html,image/jpeg;
    Content-Length:	表示该响应内容的长度(字节数);
    Content-Encoding:	表示该响应压缩算法,例如gzip;
    Cache-Control:		指示客户端应如何缓存,例如max-age=300表示可以最多缓存300
  • 响应体: 最后一部分。存放响应数据

上图中…这部分内容就是响应体,它和响应头之间有一个空行隔开。

3.3.2、响应状态码

状态码 英文描述 解释
200 OK 客户端请求成功,即处理成功,这是我们最想看到的状态码
302 Found 指示所请求的资源已移动到由Location响应头给定的 URL,浏览器会自动重新访问到这个页面
304 Not Modified 告诉客户端,你请求的资源至上次取得后,服务端并未更改,你直接用你本地缓存吧。隐式重定向
400 Bad Request 客户端请求有语法错误,不能被服务器所理解
403 Forbidden 服务器收到请求,但是拒绝提供服务,比如:没有权限访问相关资源
404 Not Found 请求资源不存在,一般是URL输入有误,或者网站资源被删除了
428 Precondition Required 服务器要求有条件的请求,告诉客户端要想访问该资源,必须携带特定的请求头
429 Too Many Requests 太多请求,可以限制客户端请求某个资源的数量,配合 Retry-After(多长时间后可以请求)响应头一起使用
431 Request Header Fields Too Large 请求头太大,服务器不愿意处理请求,因为它的头部字段太大。请求可以在减少请求头域的大小后重新提交。
405 Method Not Allowed 请求方式有误,比如应该用GET请求方式的资源,用了POST
500 Internal Server Error 服务器发生不可预期的错误。服务器出异常了,赶紧看日志去吧
503 Service Unavailable 服务器尚未准备好处理请求,服务器刚刚启动,还未初始化好
511 Network Authentication Required 客户端需要进行身份验证才能获得网络访问权限

3.3.3、状态码大类

状态码分类 说明
1xx 响应中——临时状态码,表示请求已经接受,告诉客户端应该继续请求或者如果它已经完成则忽略它
2xx 成功——表示请求已经被成功接收,处理已完成
3xx 重定向——重定向到其它地方:它让客户端再发起一个请求以完成整个处理。
4xx 客户端错误——处理发生错误,责任在客户端,如:客户端的请求一个不存在的资源,客户端未被授权,禁止访问等
5xx 服务器端错误——处理发生错误,责任在服务端,如:服务端抛出异常,路由出错,HTTP版本不支持等

状态码大全:https://cloud.tencent.com/developer/chapter/13553

3.3.4、模拟服务器响应

package com.chuhe;

import sun.misc.IOUtils;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
/*
    自定义服务器
 */
public class Server {
    public static void main(String[] args) throws IOException {
        ServerSocket ss = new ServerSocket(8080); // 监听指定端口
        System.out.println("server is running...");
        while (true){
            Socket sock = ss.accept();
            System.out.println("connected from " + sock.getRemoteSocketAddress());
            Thread t = new Handler(sock);
            t.start();
        }
    }
}

class Handler extends Thread {
    Socket sock;

    public Handler(Socket sock) {
        this.sock = sock;
    }

    public void run() {
        try (InputStream input = this.sock.getInputStream()) {
            try (OutputStream output = this.sock.getOutputStream()) {
                handle(input, output);
            }
        } catch (Exception e) {
            try {
                this.sock.close();
            } catch (IOException ioe) {
            }
            System.out.println("client disconnected.");
        }
    }

    private void handle(InputStream input, OutputStream output) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8));
        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(output, StandardCharsets.UTF_8));
        // 读取HTTP请求:
        boolean requestOk = false;
        String first = reader.readLine();
        if (first.startsWith("GET / HTTP/1.")) {
            requestOk = true;
        }
        for (;;) {
            String header = reader.readLine();
            if (header.isEmpty()) { // 读取到空行时, HTTP Header读取完毕
                break;
            }
            System.out.println(header);
        }
        System.out.println(requestOk ? "Response OK" : "Response Error");
        if (!requestOk) {
            // 发送错误响应:
            writer.write("HTTP/1.0 404 Not Found\r\n");
            writer.write("Content-Length: 0\r\n");
            writer.write("\r\n");
            writer.flush();
        } else {
            // 发送成功响应:

            //读取html文件,转换为字符串
            BufferedReader br = new BufferedReader(new FileReader("http/html/a.html"));
            StringBuilder data = new StringBuilder();
            String line = null;
            while ((line = br.readLine()) != null){
                data.append(line);
            }
            br.close();
            int length = data.toString().getBytes(StandardCharsets.UTF_8).length;

            writer.write("HTTP/1.1 200 OK\r\n");
            writer.write("Connection: keep-alive\r\n");
            writer.write("Content-Type: text/html\r\n");
            writer.write("Content-Length: " + length + "\r\n");
            writer.write("\r\n"); // 空行标识Header和Body的分隔
            writer.write(data.toString());
            writer.flush();
        }
    }
}

4、Tomcat

4.1、Web服务器

Web服务器是一个应用程序(软件),对HTTP协议的操作进行封装,使得程序员不必直接对协议进行操作,让Web开发更加便捷。

在这里插入图片描述
Web服务器作用:

  • 封装HTTP协议操作,简化开发。
  • 可以将web项目部署到服务器中,对外提供网上浏览服务。

4.2、Tomcat简介

  • Tomcat是Apache软件基金会一个核心项目,是一个开源免费的轻量级Web服务器,支持Servlet/JSP少量JavaEE规范。
  • JavaEE: Java Enterprise Edition,Java企业版。指Java企业级开发的技术规范总和。包含13项技术规范:JDBC、JNDI、EJB、RMI、JSP、Servlet、XML、JMS、Java IDL、JTS、JTA、JavaMail、JAF。
  • 因为Tomcat支持Servlet/JSP规范,所以Tomcat也被称为Web容器、Servlet容器。Servlet需要依赖Tomcat才能运行。
  • Tomcat的官网: https://tomcat.apache.org/

4.3、Tomcat基本使用

在这里插入图片描述

  • 启动:双击: bin\startup.bat
    在这里插入图片描述

  • 控制台中文乱码解决方法:修改conf/logging.properties

    java.util.logging.ConsoleHandler.encoding = GBK
    

关闭:

  1. 直接x掉运行窗口:强制关闭
  2. bin\shutdown.bat:正常关闭
  3. Ctrl+C:正常关闭

4.4、Tomcat配置

  • 修改启动端口号:conf/server.xml
    http协议默认端口号为80,如果将端口号改为80,则将来访问tomcat时不用输入端口。
        <Connector port="8080" protocol="HTTP/1.1"
                   connectionTimeout="20000"
                   redirectPort="8443" />
    
  • 启动时可能出现的问题:
    1. 端口号冲突:找到对应程序,将其关闭
    2. 启动一闪而过:检查JAVA_HOME环境变量是否正确配置。

4.5、Tomcat部署项目

  • 将项目放置到webapps目录下,即部署完成
  • 一般JavaWeb项目会被打成war包,然后将war包放到webapps目录下,Tomcat会自动解压缩war文件。

4.6、IDEA中创建Maven Web项目

4.6.1、Web项目结构

在这里插入图片描述

  • 开发项目通过执行Maven打包命令package,可以获取到部署的Web项目目录。
  • 编译后的Java字节码文件和resources的资源文件,会被放到WEB-INF下的classes目录下。
  • pom.xml中依赖坐标对应的jar包,会被放入WEB-INF下的lib目录下。

4.6.2、使用骨架

  • 一、创建Maven项目

  • 在这里插入图片描述

  • 在这里插入图片描述
    在这里插入图片描述

  • 二、删除pom.xml中多余内容,只留下面的这些内容,注意打包方式 jar和war的区别

<packaging>:打包方式

  • jar:默认值
  • war:web项目
<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>org.example</groupId>
  <artifactId>TocmcatDemo</artifactId>
  <version>1.0-SNAPSHOT</version>
<!--  
<packaging>:打包方式
     jar:默认值
     war:web项目
  -->
  <packaging>war</packaging>

  
</project>

三、补齐Maven Web项目缺失的目录结构,默认没有java和resources目录,需要手动完成创建补齐,最终的目录结果如下
在这里插入图片描述

4.6.3、不使用骨架

  • 一、创建Maven项目
    在这里插入图片描述

在这里插入图片描述
二、在pom.xml设置打包方式为war,默认是不写代表打包方式为jar

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>TomcatDemo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

</project>

三、补齐Maven Web项目缺失webapp的目录结构
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

四、最终目录
在这里插入图片描述

4.7、IDEA中使用Tomcat

4.7.1、集成本地Tomcat

  • 打开添加本地Tomcat的面板
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
  • 将开发项目部署到Tomcat中
    在这里插入图片描述
  • war模式是将WEB工程打成war包,把war包发布到Tomcat服务器上
  • war exploded模式是将WEB工程以当前文件夹的位置关系发布到Tomcat服务器上
  • war模式部署成功后,Tomcat的webapps目录下会有部署的项目内容
  • war exploded模式部署成功后,Tomcat的webapps目录下没有,而使用的是项目的target目录下的内容进行部署
  • 建议大家都选war模式进行部署,更符合项目部署的实际情况
  • 启动Tomcat
    在这里插入图片描述

4.7.2、IDEA中使用Tomcat Maven插件

  1. pox.xml添加Tomcat插件
    AIt+insert
    在这里插入图片描述
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>

   <groupId>org.example</groupId>
   <artifactId>TomcatDemo</artifactId>
   <version>1.0-SNAPSHOT</version>
   <packaging>war</packaging>
   <properties>
       <maven.compiler.source>11</maven.compiler.source>
       <maven.compiler.target>11</maven.compiler.target>
   </properties>
   
   <build>
       <plugins>
           <plugin>
               <groupId>org.apache.tomcat.maven</groupId>
               <artifactId>tomcat7-maven-plugin</artifactId>
               <version>2.2</version>
               <port>80</port><!--访问端口号 -->
               <path>/</path><!--访问路径 -->
           </plugin>
       </plugins>
   </build>
  1. 使用Maven Helper插件快速启动项目,选中项目,右键–>Run Maven --> tomcat7:run
    在这里插入图片描述
  • 如果选中项目并右键点击后,看不到Run Maven和Debug Maven,这个时候就需要在IDEA中下载Maven Helper插件,具体的操作方式为: File --> Settings --> Plugins --> Maven Helper —> Install,安装完后按照提示重启IDEA,就可以看到了。
  • Maven Tomcat插件目前只有Tomcat7版本,没有更高的版本可以使用
  • 使用Maven Tomcat插件,要想修改Tomcat的端口和访问路径,可以直接修改pom.xml
<build>
    <plugins>
    	<!--Tomcat插件 -->
        <plugin>
            <groupId>org.apache.tomcat.maven</groupId>
            <artifactId>tomcat7-maven-plugin</artifactId>
            <version>2.2</version>             
            <configuration>
            	<port>80</port><!--访问端口号 -->
                <!--项目访问路径
					未配置访问路径: http://localhost:80/tomcat-demo2/a.html
					配置/后访问路径: http://localhost:80/a.html
					如果配置成 /hello,访问路径会变成什么?
						答案: http://localhost:80/hello/a.html
				-->
                <path>/</path>
            </configuration>
        </plugin>
    </plugins>
</build>

5、Servlet

  • Servlet是JavaWeb最为核心的内容,它是Java提供的一门动态web资源开发技术。

  • 使用Servlet就可以实现,根据不同的登录用户在页面上动态显示不同内容。

  • Servlet是JavaEE规范之一,其实就是一个接口,将来我们需要定义Servlet类实现Servlet接口,并由web服务器运行Servlet。.

5.1、快速入门

具体的实现步骤为:

  1. 创建Web项目web-demo,导入Servlet依赖坐标。

    <dependencies>
    	<dependency>
    	    <groupId>javax.servlet</groupId>
    	    <artifactId>javax.servlet-api</artifactId>
    	    <version>3.1.0</version>
    	    <!--
    	      此处为什么需要添加该标签?
    	      provided指的是在编译和测试过程中有效,最后生成的war包时不会加入
    	       因为Tomcat的lib目录中已经有servlet-api这个jar包,如果在生成war包的时候生效就会和Tomcat中的jar包冲突,导致报错
    	    -->
    	    <scope>provided</scope>
    	</dependency>
    </dependencies>
    
  2. 创建:定义一个类,实现Servlet接口,并重写接口中所有方法,并在service方法中输入一句话
    在这里插入图片描述

package com.chuhe.web;

import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;

@WebServlet("/aaa")//配置:在类上使用@WebServlet注解,配置该Servlet的访问路径
public class ServletDemo implements Servlet {



    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("servlet hello world");
    }

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

    @Override
    public void destroy() {

    }

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

    }

    @Override
    public ServletConfig getServletConfig() {
        return null;
    }
}

  1. 配置:在类上使用@WebServlet注解,配置该Servlet的访问路径

    @WebServlet("/aaa")
    
  2. 访问:启动Tomcat,浏览器中输入URL地址访问该Servlet

    http://localhost:8080/web-demo/aaa
    

5.2、Servlet执行流程

在这里插入图片描述

5.3、Servlet生命周期

Servlet运行在Servlet容器(web服务器)中,其生命周期由容器来管理,分为4个阶段:

  1. 加载和实例化:默认情况下,当Servlet第一次被访问时,由容器创建Servlet对象

    • 默认情况,Servlet会在第一次访问被容器创建,但如果创建Servlet比较耗时的话,那么第一个访问的人等待的时间就比较长,用户的体验就比较差,我们可以把Servlet的创建放到服务器启动的时候来创建,配置如下:

    • @WebServlet(urlPatterns = "/aaa",loadOnStartup = 1)

    • loadOnstartup的取值有两类情况

    1. 负整数:第一次访问时创建Servlet对象
    2. 0或正整数:服务器启动时创建Servlet对象,数字越小优先级越高
  2. 初始化: 在Servlet实例化之后,容器将调用Servlet的init()方法初始化这个对象,完成一些如加载配置文件、创建连接等初始化的工作。该方法只调用一次。

  3. 请求处理: 每次请求Servlet时,Servlet容器都会调用Servlet的service()方法对请求进行处理

  4. 服务终止: 当需要释放内存或者容器关闭时,容器就会调用Servlet实例的destroy()方法完成资源的释放。在destroy()方法调用之后,容器会释放这个Servlet实例,该实例随后会被Java的垃圾收集器所回收。

Servlet生命周期涉及到三个方法,分别是 init()、service()、destroy()

  • init方法在Servlet对象被创建的时候执行,只执行1次
  • service方法在Servlet被访问的时候调用,每访问1次就调用1次
  • destroy方法在Servlet对象被销毁的时候调用,只执行1次

5.4、方法介绍

Servlet中总共有5个方法

  • 初始化方法,在Servlet被创建时执行,只执行一次

    void init(ServletConfig config) 
    
  • 提供服务方法, 每次Servlet被访问,都会调用该方法

    void service(ServletRequest req, ServletResponse res)
    
  • 销毁方法,当Servlet被销毁时,调用该方法。在内存释放或服务器关闭时销毁Servlet

    void destroy() 
    
  • 获取Servlet信息

    String getServletInfo() 
    //该方法用来返回Servlet的相关信息,没有什么太大的用处,一般我们返回一个空字符串即可
    public String getServletInfo() {
        return "";
    }
    
  • 获取ServletConfig对象

    ServletConfig getServletConfig()
    

    ServletConfig对象,在init方法的参数中有,而Tomcat Web服务器在创建Servlet对象的时候会调用init方法,必定会传入一个ServletConfig对象,我们只需要将服务器传过来的ServletConfig进行返回即可。操作如下:

package com.chuhe.web;

import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;


@WebServlet(urlPatterns = "/aaa",loadOnStartup = 1)
public class ServletDemo3 implements Servlet {

    private ServletConfig servletConfig;//添加一个成员变量
    /**
     *  初始化方法
     *  1.调用时机:默认情况下,Servlet被第一次访问时,调用
     *      * loadOnStartup: 默认为-1,修改为0或者正整数,则会在服务器启动的时候,调用
     *  2.调用次数: 1次
     * @param config
     * @throws ServletException
     */
    public void init(ServletConfig config) throws ServletException {
        this.servletConfig = config;//赋值给成员变量
        System.out.println("init...");
    }
    public ServletConfig getServletConfig() {
        return servletConfig;
    }
    
    /**
     * 提供服务
     * 1.调用时机:每一次Servlet被访问时,调用
     * 2.调用次数: 多次
     * @param req
     * @param res
     * @throws ServletException
     * @throws IOException
     */
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        System.out.println("servlet hello world~");
    }

    /**
     * 销毁方法
     * 1.调用时机:内存释放或者服务器关闭的时候,Servlet对象会被销毁,调用
     * 2.调用次数: 1次
     */
    public void destroy() {
        System.out.println("destroy...");
    }
    
    public String getServletInfo() {
        return "";
    }
}

5.4、Servlet体系结构

Servlet的体系结构
在这里插入图片描述
我们将来开发B/S架构的web项目,都是针对HTTP协议,所以我们自定义Servlet,会通过继承HttpServlet。

具体的编写格式如下:

@WebServlet("/bbb")
public class ServletDemo4 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //TODO GET 请求方式处理逻辑
        System.out.println("get...");
    }
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //TODO Post 请求方式处理逻辑
        System.out.println("post...");
    }
}
  • 要想发送一个GET请求,请求该Servlet,只需要通过浏览器发送http://localhost:8080/TomcatDemo/bbb,就能看到doGet方法被执行了
  • 要想发送一个POST请求,请求该Servlet,单单通过浏览器是无法实现的,这个时候就需要编写一个form表单来发送请求,在webapp下创建一个a.html页面,内容如下:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="/TomcatDemo/bbb" method="post">
        <input name="username"/><input type="submit"/>
    </form>
</body>
</html>

启动测试,访问http://localhost:8080/TomcatDemo/a.html即可看到doPost方法被执行了。

HttpServlet中要根据请求方式的不同,如何调用?

package com.chuhe.web;

import javax.servlet.*;
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("/bbb")
public class ServletDemo2 implements Servlet {

    public void init(ServletConfig config) throws ServletException {

    }

    public ServletConfig getServletConfig() {
        return null;
    }

    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        //如何调用?
        //获取请求方式,根据不同的请求方式进行不同的业务处理
        HttpServletRequest request = (HttpServletRequest)req;
       //1. 获取请求方式
        String method = request.getMethod();
        //2. 判断
        if("GET".equals(method)){
            // get方式的处理逻辑
        }else if("POST".equals(method)){
            // post方式的处理逻辑
        }
    }

    public String getServletInfo() {
        return null;
    }

    public void destroy() {

    }
}

为了能处理不同的请求方式,得在service方法中进行判断,然后写不同的业务处理,这样能实现,但是每个Servlet类中都将有相似的代码。解决这个问题可以如下优化:

package com.chuhe.web;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

public class MyHttpServlet implements Servlet {
    public void init(ServletConfig config) throws ServletException {

    }

    public ServletConfig getServletConfig() {
        return null;
    }

    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        HttpServletRequest request = (HttpServletRequest)req;
        //1. 获取请求方式
        String method = request.getMethod();
        //2. 判断
        if("GET".equals(method)){
            // get方式的处理逻辑
            doGet(req,res);
        }else if("POST".equals(method)){
            // post方式的处理逻辑
            doPost(req,res);
        }
    }

    protected void doPost(ServletRequest req, ServletResponse res) {
    }

    protected void doGet(ServletRequest req, ServletResponse res) {
    }

    public String getServletInfo() {
        return null;
    }

    public void destroy() {

    }
}

有了MyHttpServlet这个类,以后我们再编写Servlet类的时候,只需要继承MyHttpServlet,重写父类中的doGet和doPost方法,就可以用来处理GET和POST请求的业务逻辑。把ServletDemo1代码进行改造。

@WebServlet("/bbb")
public class ServletDemo1 extends MyHttpServlet {

    @Override
    protected void doGet(ServletRequest req, ServletResponse res) {
        System.out.println("get...");
    }

    @Override
    protected void doPost(ServletRequest req, ServletResponse res) {
        System.out.println("post...");
    }
}

5.5、Servlet urlPattern配置

Servlet类编写好后,要想被访问到,就需要配置其访问路径(urlPattern)。

  • 一个Servlet,可以配置多个urlPattern

    @WebServlet(urlPatterns={"/aaa","/bbb"})
    
    package com.chuhe.web;
    
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    import javax.servlet.annotation.WebServlet;
    
    /**
    * urlPattern: 一个Servlet可以配置多个访问路径
    */
    @WebServlet(urlPatterns = {"/aaa","/bbb"})
    public class ServletDemo7 extends MyHttpServlet {
    
        @Override
        protected void doGet(ServletRequest req, ServletResponse res) {
            
            System.out.println("demo7 get...");
        }
        @Override
        protected void doPost(ServletRequest req, ServletResponse res) {
        }
    }
    

    在浏览器上输入http://localhost:8080/TomcatDemo/aaa,http://localhost:8080/TomcatDemo/bbb这两个地址都能访问到ServletDemo1的doGet方法。

urlPattern配置规则

  • 精确匹配

    //配置路径
    @WebServlet(urlPatterns={"/user/select"})
    

    访问路径:http://localhost:8080/TomcatDemo/user/select

  • 目录匹配

    //配置路径
    @WebServlet(urlPatterns={"/user/*"})
    

    访问路径:
    http://localhost:8080/TomcatDemo/user/aaa
    http://localhost:8080/TomcatDemo/user/bbb
    http://localhost:8080/TomcatDemo/user/a/b

  • 扩展名匹配

    //配置路径
    @WebServlet(urlPatterns={"*.do"})//注意没有/
    

    访问路径:
    http://localhost:8080/TomcatDemo/aaa.do
    http://localhost:8080/TomcatDemo/bbb.do

    • 如果路径配置的不是扩展名,那么在路径的前面就必须要加/否则会报错
    • 如果路径配置的是*.do,那么在*.do的前面不能加/,否则会报错
  • 任意匹配

    //配置路径
    @WebServlet(urlPatterns={"/"})@WebServlet(urlPatterns={"/"})
    

    访问路径:
    http://localhost:8080/TomcatDemo/aaa
    http://localhost:8080/TomcatDemo/bbb

注意://*的区别?

  1. 当我们的项目中的Servlet配置了 “/”,会覆盖掉tomcat中的DefaultServlet,当其他的url-pattern都匹配不上时都会走这个Servlet

  2. 当我们的项目中配置了"/*",意味着匹配任意访问路径

  3. DefaultServlet是用来处理静态资源,如果配置了"/"会把默认的覆盖掉,就会引发请求静态资源的时候没有走默认的而是走了自定义的Servlet类,最终导致静态资源不能被访问。

优先级:
五种配置的优先级为 精确匹配 > 目录匹配> 扩展名匹配 > /* > /

5.6、XML配置方式编写Servlet

Servlet从3.0版本后开始支持注解配置,3.0版本前只支持XML配置文件的配置方法。
对于XML的配置步骤有两步:

  • 编写Servlet类
package com.chuhe;

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 ServletDemo extends HttpServlet {
    @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 {

    }
}

  • 在web.xml中配置该Servlet
<?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>
            <!--servlet的名称,名字任意-->
            <servlet-name>aaa</servlet-name>
            <!--servlet的类全名-->
            <servlet-class>com.chuhe.ServletDemo</servlet-class>
        </servlet>
        <!--
           Servlet访问路径
       -->
        <servlet-mapping>
            <!-- servlet的名称,要和上面的名称一致-->
            <servlet-name>aaa</servlet-name>
            <!-- servlet的访问路径-->
            <url-pattern>/aaa</url-pattern>
        </servlet-mapping>


</web-app>

6、Request

6.1、Request继承体系

Request的继承体系:

  • ServletRequest和HttpServletRequest都是Java提供的。
    在这里插入图片描述

Request继承体系中的RequestFacade:

  • 该类实现了HttpServletRequest接口,也间接实现了ServletRequest接口。
  • Servlet类中的service方法、doGet方法或者是doPost方法最终都是由Web服务器Tomcat来调用的,所以Tomcat提供了方法参数接口的具体实现类,并完成了对象的创建。
  • RequestFacade中都提供的方法,我们可以直接查看JavaEE的API文档中关于ServletRequest和HttpServletRequest的接口文档,因为RequestFacade实现了其接口就需要重写接口中的方法。

6.2、Request获取请求数据

6.2.1、获取请求行

  • 请求行包含三块内容,分别是请求方式请求资源路径、`HTTP协议及版本。
    在这里插入图片描述

对于这三部分内容,request对象都提供了对应的API方法来获取,具体如下:

  • 获取请求方式: GET

    String getMethod()
    
  • 获取虚拟目录(项目访问路径): /requestDemo

    String getContextPath()
    
  • 获取URL(统一资源定位符): http://localhost:8080/requestDemo/req

    StringBuffer getRequestURL()
    
  • 获取URI(统一资源标识符): /requestDemo/req

    String getRequestURI()
    
  • 获取请求参数(GET方式): username=chuhe&password=123

    String getQueryString()
    

代码演示如下:
在这里插入图片描述

package com.chuhe;

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("/req")
public class ServletDemo extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //获取请求方式
        String method=req.getMethod();
        System.out.println(method);

        //获取虚拟目录(项目访问路径)
        String contextPath=req.getContextPath();
        System.out.println(contextPath);

        //获取URL(统一资源定位符)
        StringBuffer url=req.getRequestURL();
        System.out.println(url.toString());

        //获取URI(统一资源标识符)
        String uri=req.getRequestURI();
        System.out.println(uri);

        //获取请示参数
        String queryString=req.getQueryString();
        System.out.println(queryString);

    }

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

    }
}

访问路径:http://localhost:8080/requestDemo/req?username=chuhe&psw=123
输出:
在这里插入图片描述

6.2.2、获取请求头

请求头的数据,格式为key: value如下:
在这里插入图片描述

获取请求头名称对应值的方法为:

String getHeader(String name)
package com.chuhe;

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("/req")
public class ServletDemo extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        //获取请求头:user-agent:浏览器的版本信息
        String agent=req.getHeader("user-agent");
        System.out.println(agent);

    }

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

    }
}

Edge浏览器访问:在这里插入图片描述
Chrome浏览器访问:
在这里插入图片描述

6.2.3、获取请求体

浏览器在发送GET请求时,是没有请求体的,只有发送POST请求时才有。
对于请求体中的数据,Request对象提供了两种方式来获取其中的数据,分别是:

  • 获取字节输入流,如果前端发送的是字节数据,比如传递的是文件数据,则使用该方法

    ServletInputStream getInputStream()
    该方法可以获取字节
    
  • 获取字符输入流,如果前端发送的是纯文本数据,则使用该方法

    BufferedReader getReader()
    

代码演示:

  • 在项目的web目录下添加一个html页面,名称为:req.html
    在这里插入图片描述
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="/requestDemo/req" method="post">

        <input type="text" name="username"/>
        <input type="password" name="pwd"/>
        <input type="submit">
    </form>
</body>
</html>
  • 调用getReader()或者getInputStream()方法,因为目前前端传递的是纯文本数据,所以我们采用getReader()方法来获取
package com.chuhe;

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;

@WebServlet("/req")
public class ServletDemo extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //获取POST请求体
        //1.获取字符输入流
        BufferedReader br=req.getReader();
        //2.读取数据
        String line=br.readLine();
        System.out.println(line);
    }
}

  • 访问http://localhost:8080/requestDemo/req.html
    在这里插入图片描述
    输出结果
    在这里插入图片描述

6.2.4、请求参数的通用方式

  • 获取所有参数Map集合

    Map<String,String[]> getParameterMap()
    
  • 根据名称获取参数值(数组)

    String[] getParameterValues(String name)
    
  • 根据名称获取参数值(单个值)

    String getParameter(String name)
    

代码演示:

  • req.html页面,添加爱好选项,爱好可以同时选多个

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <form action="/requestDemo/req" method="get">
    
        <input type="text" name="username"/><br>
        <input type="password" name="password"/><br>
        <input type="checkbox" name="hobby" value="1"/> 游泳
        <input type="checkbox" name="hobby" value="2"/> 爬山 <br>
        <input type="submit">
    </form>
    </body>
    </html>
    

    -

  • 获取所有参数Map集合的代码演示

    package com.chuhe;
    
    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.Map;
    
    @WebServlet("/req")
    public class ServletDemo extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            //获取所有参数Map集合
            Map<String,String[]> map=req.getParameterMap();
            for(String key:map.keySet()){
                System.out.print(key+":");
                String[] values=map.get(key);
                for(String value:values){
                    System.out.print(value+" ");
                }
                System.out.println();
            }  
    
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    		this.doGet(req,resp);
        }
    }
    
    
  • 根据名称获取参数值(数组)的代码演示

    package com.chuhe;
    
    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.Map;
    
    @WebServlet("/req")
    public class ServletDemo extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            //根据名称获取参数值(数组)
            String[] hobbies=req.getParameterValues("hobby");
            for(String hobby:hobbies){
                System.out.println(hobby);
            }       
    
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    		this.doGet(req,resp);
        }
    }
    
    
  • 根据名称获取参数值(单个值)的代码演示

    package com.chuhe;
    
    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.Map;
    
    @WebServlet("/req")
    public class ServletDemo extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            //根据名称获取参数值(单个值)
            String username=req.getParameter("username");
            System.out.println(username);
            String password=req.getParameter("password");
            System.out.println(password);
    
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    		this.doGet(req,resp);
        }
    }
    
    

GET、POST虽然请求方式不同,但都要获取参数,服务端处理参数的逻辑是相同的,所以POST可以直接调用doGet方法里的参数,简化代码。

6.2.5、IDEA模板快速创建Servlet

  • New–Servlet,即可快速创建Servlet
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
  • 按照自己的需求,修改Servlet创建的模板内容
    File–Settings
    在这里插入图片描述

6.2.5、Request请求参数中文乱码处理

请求参数如果存在中文数据,则有可能会乱码。

6.2.5.1、POST请求乱码

解决方案:

  • POST:设置输入流的编码

    req.setCharacterEncoding("UTF-8");//设置字符输入流的编码,这里的编码要与html页面的编码一致
    

解决POST请求中文乱码,代码演示

package com.chuhe;

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("/req")
public class ServletDemo extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //解决乱码:POST
        req.setCharacterEncoding("UTF-8");//设置字符输入流的编码,这里的编码要与html页面的编码一致
        //获取username
        String username=req.getParameter("username");
        System.out.println(username);


    }

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

6.2.5.2、URL编码

URL编码

  1. 将字符串按照编码方式转为二进制
  2. 每个字节转为2个16进制数并在前边加上%
    在这里插入图片描述
package com.chuhe;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;

public class URLDemo {
    public static void main(String[] args) throws UnsupportedEncodingException {
        String username="张三";
        //URL编码
        String encode= URLEncoder.encode(username,"utf-8");
        System.out.println(encode);

        //URL解码
        //String deCode= URLDecoder.decode(encode,"utf-8");//打印:张三
        String deCode= URLDecoder.decode(encode,"ISO-8859-1");//打印:å¼ ä¸ 
        System.out.println(deCode);
    }
}

6.2.5.3、Get请求乱码

POST请求的中文乱码解决方案为什么不适用GET请求?

  • GET请求获取请求参数的方式是request.getQueryString()
  • POST请求获取请求参数的方式是request.getReader()
  • request.setCharacterEncoding("utf-8")是设置request处理流的编码。
  • getQueryString方法并没有通过流的方式获取数据。
    在这里插入图片描述

GET请求中文参数出现乱码的原因:

  • 浏览器把中文参数按照UTF-8进行URL编码
  • Tomcat对获取到的内容进行了ISO-8859-1的URL解码
  • 在控制台就会出现类上å¼ ä¸的乱码,最后一位是个空格

解决方法:

  1. 按照ISO-8859-1编码获取乱码å¼ ä¸对应的字节数组(编码)
  2. 按照UTF-8编码获取字节数组对应的字符串(解码)
package com.chuhe;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;

public class URLDemo {
    public static void main(String[] args) throws UnsupportedEncodingException {
        String username="张三";
        //URL编码
        String encode= URLEncoder.encode(username,"utf-8");
        System.out.println(encode);

        //URL解码
        //String deCode= URLDecoder.decode(encode,"utf-8");//打印:张三
        String deCode= URLDecoder.decode(encode,"ISO-8859-1");//打印:`å¼ ä¸
        System.out.println(deCode);

        //转换成字节数据,编码
        byte[] bytes=deCode.getBytes("ISO-8859-1");
        for(byte by:bytes){
            System.out.print(by+" ");//此处打印的是:-27 -68 -96 -28 -72 -119
        }
        System.out.println();

        //将字节数组转换为字符串,解码
        username=new String(bytes,"UTF-8");
        System.out.println(username);
    }
}

package com.chuhe;

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;
import java.nio.charset.StandardCharsets;


@WebServlet("/req")
public class ServletDemo extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {


        //获取username
        String username=req.getParameter("username");
        System.out.println("乱码前:"+username);
        //解决乱码:GET
        byte[] bytes=username.getBytes(StandardCharsets.ISO_8859_1);
        username=new String(bytes,"UTF-8");
        System.out.println("乱码后:"+username);


    }

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

GET请求在Tomcat8.0以后的版本就不会出现了,Tomcat已将URL编码从ISO-8859-1改成了UTF-8

6.3、Request请求转发

**请求转发(forward)😗*一种在服务器内部的资源跳转方式。
在这里插入图片描述
请求转发的实现方式:

req.getRequestDispatcher("资源B路径").forward(req,resp);

请求转发资源间共享数据:使用Request对象

  • 存储数据到request域[范围,数据是存储在request对象]中

    void setAttribute(String name,Object o);
    
  • 根据key获取值

    Object getAttribute(String name);
    
  • 根据key删除该键值对

    void removeAttribute(String name);
    

代码演示:
请求req1转发至req2

package com.chuhe;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;

@WebServlet( "/req1")
public class ServletDemo1 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("demo1");
        //存储数据
        request.setAttribute("msg","hello");
        //请求转发
        request.getRequestDispatcher("/req2").forward(request,response);
    }

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

package com.chuhe;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;

@WebServlet( "/req2")
public class ServletDemo2 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("Demo2...");
        Object msg=  request.getAttribute("msg");
        System.out.println(msg);
    }

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

7、Response

Respone:使用Response对象来设置响应数据。
在这里插入图片描述

7.1、Response设置响应数据功能介绍

HTTP响应数据总共分为三部分内容:

  1. 响应行
    void setStatus(int sc);//设置响应状态码
    

在这里插入图片描述

  1. 响应头

    void setHeader(String name,String value);//设置响应头键值对
    

    在这里插入图片描述

  2. 响应体

    在这里插入图片描述
    对于响应体,是通过字符、字节输出流的方式往浏览器写,
    获取字符输出流:

    PrintWriter getWriter();
    

    获取字节输出流

    ServletOutputStream getOutputStream();
    

7.2、Response完成重定向

一、重定向(Redirect):一种资源跳转方式。
在这里插入图片描述
二、重定向的实现方式:

resp.setStatus(302);
resp.setHeader("location","资源B的访问路径");
             可以简写成
response.sendRedirect("资源B的访问路径");

代码示例:

package com.chuhe.response;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;

@WebServlet("/resp1")
public class ResponseDemo1 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("resp1...");
        //重定向
        //1.设置响应状态码
       // response.setStatus(302);
        //2.设置响应头location
        //response.setHeader("Location","/requestDemo/resp2");
        
        //可以简写成
        response.sendRedirect("/requestDemo/resp2");

    }

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

package com.chuhe.response;

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("/resp2")
public class ResponseDemo2 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("resp2");
    }

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

三、重定向与请求转发的区别:

重定向特点:

  • 浏览器地址栏路径发生变化。
  • 可以重定向到任意位置的资源(服务器内部、外部均可),
    response.sendRedirect("https://www.baidu.com");
  • 两次请求,不能在多个资源使用request共享数据

请求转发特点:

  • 浏览器地址路径不发生变化。
  • 只能转发当前服务器的内部资源。
  • 一次请求,可以在转 的资源间使用request共享数据。

四、路径问题

明确路径谁使用:

  • 浏览器使用:需要加虚拟目录(项目访问路径)
  • 服务端使用:不需要加虚拟目录。

如:

  • <a href='路劲'>超链接,从浏览器发送,需要加
  • <form action='路径'>表单,从浏览器发送,需要加
  • req.getRequestDispatcher("路径")转发,是从服务器内部跳转,不需要加
  • resp.sendRedirect("路径")重定向,是由浏览器进行跳转,需要加。

五、动态获取项目访问的虚拟目录
在重定向的代码中,/requestDemo是固定编码的,如果后期通过Tomcat插件配置了项目的访问路径,那么所有需要重定向的地方都需要重新修改,该如何优化?
在这里插入图片描述

//动态获取虚拟目录
 String contextPath = request.getContextPath();

如:

package com.chuhe.response;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;

@WebServlet("/resp1")
public class ResponseDemo1 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("resp1...");
        //可以简写成
        String contextPath=request.getContextPath();
        response.sendRedirect("/requestDemo/resp2");
        System.out.println(contextPath);//输出/requestDemo

    }

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

7.3、Response响应字符数据

一、步骤

  • 通过Response对象获取字符输出流:

    PrintWriter writer = resp.getWriter();
    
  • 通过字符输出流写数据:

     writer.write("aaa");
    

二、代码示例

package com.chuhe.response;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
import java.io.PrintWriter;

@WebServlet("/resp3")
public class ResponseDemo3 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //获取字符输出流
        PrintWriter writer = response.getWriter();
        //content-type,告诉浏览器返回的数据类型是HTML类型数据,这样浏览器才会解析HTML标签
        //否则会默认当成普通文本处理
        response.setHeader("content-type","text/html");
        writer.write("aaa");//如果输中文会乱码
        writer.write("<h1>bbb</h1>");
    }

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

三、解决中文乱码
中文乱码原因:通过Response获取的字符输出流默认编码:ISO-8859-1

package com.chuhe.response;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
import java.io.PrintWriter;

@WebServlet("/resp3")
public class ResponseDemo3 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        response.setContentType("text/html;charset=utf-8");//设置响应的数据格式及数据的编码
        PrintWriter writer = response.getWriter();
        writer.write("你好");
        writer.write("<h1>bbb</h1>");
    }

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

7.4、Response响应字节数据

一、步骤

  • 通过Response对象获取字节输出流:

    ServletOutputStream outputStream = resp.getOutputStream();
    
  • 通过字节输出流写数据:

    outputStream.write(字节数据);
    

二、代码示例
返回一个图片文件到浏览器

package com.chuhe.response;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.FileInputStream;
import java.io.IOException;

@WebServlet("/resp4")
public class ResponseDemo4 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1.读取文件
        FileInputStream fis=new FileInputStream("D://a.png");
        //2.获取response字节输出流
        ServletOutputStream os=response.getOutputStream();
        //3.完成流的copy
        byte[] bytes=new byte[1024];
        int len=0;
        while((len=fis.read(bytes))!=-1){
            os.write(bytes,0,len);
        }
        fis.close();
    }

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

    }
}

三、IOUtils工具类使用

  1. 导入坐标

    <dependencies>
    	<dependency>
    	    <groupId>commons-io</groupId>
    	    <artifactId>commons-io</artifactId>
    	    <version>2.6</version>
    	</dependency>
    </dependencies>
    
  2. 调用工具类方法

    //fis:输入流
    //os:输出流
    IOUtils.copy(fis,os);
    

代码示例:

package com.chuhe.response;

import org.apache.commons.io.IOUtils;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.FileInputStream;
import java.io.IOException;

@WebServlet("/resp4")
public class ResponseDemo4 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1.读取文件
        FileInputStream fis=new FileInputStream("D://a.png");
        //2.获取response字节输出流
        ServletOutputStream os=response.getOutputStream();
        //3.完成流的copy
        IOUtils.copy(fis, os);
        fis.close();
    }

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

    }
}

8、登录注册项目实战

8.1、环境准备

  1. 创建Maven项目

  2. 设置Maven:File–Settings
    在这里插入图片描述

  3. 补齐webapp文件夹File–Project Structure
    在这里插入图片描述

  4. pom.xml中配置打包方式

    <packaging>war</packaging>
    
  5. pom.xml中配置依赖
    mybatis 坐标

            <!--mybatis 依赖-->
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis</artifactId>
                <version>3.5.5</version>
            </dependency>
    

    mysql坐标

            <!--mysql 驱动-->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>5.1.46</version>
            </dependency>
    

    junit 单元测试坐标

            <!--junit 单元测试-->
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.13</version>
                <scope>test</scope>
            </dependency>
    

    servlet坐标

    	<dependency>
    	    <groupId>javax.servlet</groupId>
    	    <artifactId>javax.servlet-api</artifactId>
    	    <version>3.1.0</version>
    	    <!--
    	      此处为什么需要添加该标签?
    	      provided指的是在编译和测试过程中有效,最后生成的war包时不会加入
    	       因为Tomcat的lib目录中已经有servlet-api这个jar包,如果在生成war包的时候生效就会和Tomcat中的jar包冲突,导致报错
    	    -->
    	    <scope>provided</scope>
    	</dependency>
    
  6. 静态页面放入webapp下

  7. Tomcat配置
    maven project里点击package生成target文件夹,而.war文件在target文件夹中。
    配置Tomcat
    在这里插入图片描述

在这里插入图片描述
8. 创建mybatis-config.xml核心配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <!--数据库连接信息-->
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://127.0.0.1:3306/db1?useSSL=false&amp;userServerPreStmts=true"/>
                <property name="username" value="root"/>
                <property name="password" value="1234"/>
            </dataSource>
        </environment>   
        
        
    </environments>
        <mappers>
        	<!--扫描mapper -->
            <package name="com.chuhe.mapper"/>
        </mappers>
</configuration>
  1. com.chuhe.mapper包下创建UserMapper接口
  2. resources下创建com/chuhe/mapper包下创建UserMapper.xml

8.2、代码实现

UserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace设置的是名称空间,这里起名为test-->
<!--namespace设置的是名称空间,必须是对应接口的全限定名-->
<mapper namespace="com.chuhe.mapper.UserMapper">
    <!--
    id设置的名称是该处设置的sql唯一名称;
    resultType设置的为该sql的返回值类型,这里我们返回的ResultSet最终封装成User对象
    -->
    <select id="select" resultType="com.chuhe.pojo.User">
        select * from tb_user
        where username=#{username}
        and password=#{password};
    </select>
</mapper>

UserMapper.java

package com.chuhe.mapper;

import com.chuhe.pojo.User;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;

public interface UserMapper {
    User select(@Param("username")String username, @Param("password")String password);

    @Select("select * from tb_user where username=#{username}")
    User selectByUsername(@Param("username")String username);

    @Insert("insert into tb_user values(null,#{username},#{password})")
    void add(User user);
}

LoginServlet .java

package com.chuhe.web;

import com.chuhe.mapper.UserMapper;
import com.chuhe.pojo.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;

@WebServlet("/loginServlet")
public class LoginServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //接收用户名和密码
        String username=request.getParameter("username");
        String password=request.getParameter("password");

        //调用mybatis完成查询
        String resource="mybatis-config.xml";
        InputStream inputStream= Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory=  new SqlSessionFactoryBuilder().build(inputStream);
        //获取Sqlsession对象
        SqlSession sqlSession=sqlSessionFactory.openSession();
        //获取UserMapper接口代理对象
        UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
        User user=userMapper.select(username,password);
        //释放资源
        sqlSession.close();

        //响应
        response.setContentType("text/html;charset=utf-8");
        PrintWriter writer=response.getWriter();
        if (user != null) {
            writer.write("登录成功");
        }else{
            writer.write("登录失败");
        }
    }

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

package com.chuhe.web;

import com.chuhe.mapper.UserMapper;
import com.chuhe.pojo.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;

@WebServlet("/register")
public class RegisterServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //接收用户名和密码
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        //封装User对象
        User user = new User();
        user.setUsername(username);
        user.setPassword(password);
        //调用mybatis
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        //获取SqlSession对象
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //获取UserMapper对象
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        User u = userMapper.selectByUsername(username);

        response.setContentType("text/html;charset=utf-8");
        PrintWriter writer = response.getWriter();

        if (u == null) {
            //username不存在,新增
            userMapper.add(user);
            sqlSession.commit();
        } else {
            //username存在,不允许新增
            writer.write("用户名已存在");
        }
        //释放资源
        sqlSession.close();

    }

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

8.3、SqlSessionFactory工具类抽取

上面两个功能在写Servlet的时候,因为需要使用Mybatis来完成数据库的操作,所以对于Mybatis的基础操作就出现了些重复代码,如下:

String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

这些重复代码存在的问题:

  • 重复代码不利于后期的维护。
  • SqlSessionFactory工厂类进行重复创建。每个SqlSessionFactory都有连接池,资源消耗很大。最好的方法是只创建一次。

改进方案:
在这里插入图片描述

SqlSessionFactorUtils .java

package com.chuhe.utils;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;

public class SqlSessionFactorUtils {
    private static SqlSessionFactory sqlSessionFactory;
    static{
        //调用mybatis
        String resource = "mybatis-config.xml";
        InputStream inputStream = null;
        try {
            inputStream = Resources.getResourceAsStream(resource);
        } catch (IOException e) {
            e.printStackTrace();
        }
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    }
    public static SqlSessionFactory getSqlSessionFactor(){
        return sqlSessionFactory;
    }
}

使用工具类:

	...
     SqlSessionFactory sqlSessionFactory=SqlSessionFactoryUtils.getSqlSessionFactory();
     //获取SqlSession对象
     SqlSession sqlSession = sqlSessionFactory.openSession();
     ...

如:

package com.chuhe.web;

import com.chuhe.mapper.UserMapper;
import com.chuhe.pojo.User;
import com.chuhe.utils.SqlSessionFactoryUtils;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;


import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;

@WebServlet("/register")
public class RegisterServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //接收用户名和密码
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        //封装User对象
        User user = new User();
        user.setUsername(username);
        user.setPassword(password);

        SqlSessionFactory sqlSessionFactory=SqlSessionFactoryUtils.getSqlSessionFactory();
        //获取SqlSession对象
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //获取UserMapper对象
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        User u = userMapper.selectByUsername(username);

        response.setContentType("text/html;charset=utf-8");
        PrintWriter writer = response.getWriter();

        if (u == null) {
            //username不存在,新增
            userMapper.add(user);
            sqlSession.commit();
        } else {
            //username存在,不允许新增
            writer.write("用户名已存在");
        }
        //释放资源
        sqlSession.close();

    }

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

9、JSP

9.1、JSP概述

  • JSP(全称:Java Server Pages):Java 服务端页面。
  • 是一种动态的网页技术。
  • 既可以定义 HTML、JS、CSS等静态内容,还可以定义 Java代码的动态内容。
  • JSP = HTML + Java,用于简化开发。

如下就是jsp代码.

<html>
    <head>
        <title>Title</title>
    </head>
    <body>
        <h1>JSP,Hello World</h1>
        <%
        	System.out.println("hello,jsp~");
        %>
    </body>
</html>

9.2、JSP快速入门

步骤:

  1. 导入JSP坐标

    <dependency>
        <groupId>javax.servlet.jsp</groupId>
        <artifactId>jsp-api</artifactId>
        <version>2.2</version>
        <scope>provided</scope>
    </dependency>
    

    该依赖的 scope 必须设置为 provided,因为 tomcat 中有这个jar包了,所以在打包时我们是不希望将该依赖打进到我们工程的war包中。

  2. 创建JSP文件
    在这里插入图片描述

  3. 编写HTMl签和Java代码

    <body>
        <h1>hello jsp</h1>
        <%
            System.out.println("hello,jsp~");
        %>
    </body>
    

9.3、JSP原理

  • JSP 本质上就是一个 Servlet。
  • JSP在被访问时,由JSP容器(Tomcat)将其转换为Java文件(Servlet),外部提供服务的其实就是这个字节码文件。

在这里插入图片描述

9.4、JSP脚本

JSP脚本用于在 JSP页面内定义 Java代码。

JSP 脚本有如下三个分类:

  • <%…%>:内容会直接放到_jspService()方法之中
  • <%=…%>:内容会放到out.print()中,作为out.print()的参数
  • <%!…%>:内容会放到_jspService()方法之外,被类直接包含

代码演示:
hello.jsp 中书写

<%
    System.out.println("hello,jsp~");
    int i = 3;
%>

通过浏览器访问 hello.jsp 后,查看转换的 hello_jsp.java 文件,i 变量定义在了 _jspService() 方法中
在这里插入图片描述

<%="hello"%>
<%=i%>

通过浏览器访问 hello.jsp 后,查看转换的 hello_jsp.java 文件,该脚本的内容被放在了 out.print() 中,作为参数
在这里插入图片描述

<%!
    void  show(){}
	String name = "zhangsan";
%>

通过浏览器访问 hello.jsp 后,查看转换的 hello_jsp.java 文件,该脚本的内容被放在了成员位置
在这里插入图片描述

9.5、EL表达式

9.5.1、概述

  • EL(全称Expression Language )表达式语言,用于简化 JSP 页面内的 Java 代码。

  • EL 表达式的主要作用是 获取数据。其实就是从域对象中获取数据,然后将数据展示在页面上。

  • EL 表达式的语法也比较简单, e x p r e s s i o n 。 例 如 : {expression} 。例如: expression{brands} 就是获取域中存储的 key 为 brands 的数据。

9.5.2、代码演示

  • 定义servlet,在 servlet 中封装一些数据并存储到 request 域对象中并转发到 el-demo.jsp 页面。

    @WebServlet("/demo1")
    public class ServletDemo1 extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            //1. 准备数据
            List<Brand> brands = new ArrayList<Brand>();
            brands.add(new Brand(1,"三只松鼠","三只松鼠",100,"三只松鼠,好吃不上火",1));
            brands.add(new Brand(2,"优衣库","优衣库",200,"优衣库,服适人生",0));
            brands.add(new Brand(3,"小米","小米科技有限公司",1000,"为发烧而生",1));
    
            //2. 存储到request域中
            request.setAttribute("brands",brands);
    
            //3. 转发到 el-demo.jsp
            request.getRequestDispatcher("/el-demo.jsp").forward(request,response);
        }
    
        @Override
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            this.doGet(request, response);
        }
    }
    

注意: 此处需要用转发,因为转发才可以使用 request 对象作为域对象进行数据共享

  • el-demo.jsp 中通过 EL表达式 获取数据

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>Title</title>
    </head>
    <body>
        ${brands}
    </body>
    </html>
    
  • 在浏览器的地址栏输入 http://localhost:8080/jsp-demo/demo1 ,页面效果如下:
    在这里插入图片描述

9.5.3、域对象

JavaWeb中有四大域对象,分别是:

  • page:当前页面有效
  • request:当前请求有效
  • session:当前会话有效
  • application:当前应用有效

el 表达式获取数据,会依次从这4个域中寻找,直到找到为止。而这四个域对象的作用范围如下图所示
在这里插入图片描述

例如: ${brands},el 表达式获取数据,会先从page域对象中获取数据,如果没有再到 requet 域对象中获取数据,如果再没有再到 session 域对象中获取,如果还没有才会到 application 中获取数据。

9.6、JSTL标签

9.6.1、概述

  • JSP标准标签库(Jsp Standarded Tag Library) ,使用标签取代JSP页面上的Java代码。如下代码就是JSTL标签。
<c:if test="${flag == 1}"></c:if>
<c:if test="${flag == 2}"></c:if>

上面代码看起来是不是比 JSP 中嵌套 Java 代码看起来舒服好了。而且前端工程师对标签是特别敏感的,他们看到这段代码是能看懂的。

JSTL 提供了很多标签,如下图
-

9.6.2、使用

步骤:

  1. 导入坐标

    <dependency>
        <groupId>jstl</groupId>
        <artifactId>jstl</artifactId>
        <version>1.2</version>
    </dependency>
    <dependency>
        <groupId>taglibs</groupId>
        <artifactId>standard</artifactId>
        <version>1.1.2</version>
    </dependency>
    
    1. 在JSP页面上引入JSTL标签库
    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 
    
    1. 使用标签

9.6.3、if 标签

<c:if>:相当于 if 判断

  • 属性:test,用于定义条件表达式

    <c:if test="${flag == 1}"></c:if>
    <c:if test="${flag == 2}"></c:if>
    

代码演示:

  • 定义一个 servlet ,在该 servlet 中向 request 域对象中添加 键是 status ,值为 1 的数据
@WebServlet("/demo2")
public class ServletDemo2 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1. 存储数据到request域中
        request.setAttribute("status",1);

        //2. 转发到 jstl-if.jsp
        数据request.getRequestDispatcher("/jstl-if.jsp").forward(request,response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doGet(request, response);
    }
}
  • 定义 jstl-if.jsp 页面,在该页面使用 <c:if> 标签
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <%--
        c:if:来完成逻辑判断,替换java  if else
    --%>
    <c:if test="${status ==1}">
        启用
    </c:if>

    <c:if test="${status ==0}">
        禁用
    </c:if>
</body>
</html>

注意: 在该页面已经要引入 JSTL核心标签库
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

9.6.4、forEach 标签

9.6.4.1、用法一

类似于 Java 中的增强for循环。涉及到的 <c:forEach> 中的属性如下

  • items:被遍历的容器

  • var:遍历产生的临时变量

  • varStatus:遍历状态对象

如下代码,是从域对象中获取名为 brands 数据,该数据是一个集合;遍历遍历,并给该集合中的每一个元素起名为 brand,是 Brand对象。在循环里面使用 EL表达式获取每一个Brand对象的属性值

<c:forEach items="${brands}" var="brand">
    <tr align="center">
        <td>${brand.id}</td>
        <td>${brand.brandName}</td>
        <td>${brand.companyName}</td>
        <td>${brand.description}</td>
    </tr>
</c:forEach>

代码演示:

  • servlet 还是使用之前的名为 ServletDemo1

  • 定义名为 jstl-foreach.jsp 页面,内容如下:

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
    
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <input type="button" value="新增"><br>
    <hr>
    <table border="1" cellspacing="0" width="800">
        <tr>
            <th>序号</th>
            <th>品牌名称</th>
            <th>企业名称</th>
            <th>排序</th>
            <th>品牌介绍</th>
            <th>状态</th>
            <th>操作</th>
        </tr>
    
        <c:forEach items="${brands}" var="brand" varStatus="status">
            <tr align="center">
                <%--<td>${brand.id}</td>--%>
                <td>${status.count}</td>
                <td>${brand.brandName}</td>
                <td>${brand.companyName}</td>
                <td>${brand.ordered}</td>
                <td>${brand.description}</td>
                <c:if test="${brand.status == 1}">
                    <td>启用</td>
                </c:if>
                <c:if test="${brand.status != 1}">
                    <td>禁用</td>
                </c:if>
                <td><a href="#">修改</a> <a href="#">删除</a></td>
            </tr>
        </c:forEach>
    </table>
    </body>
    </html>
    
9.6.4.2、 用法二

类似于 Java 中的普通for循环。涉及到的 <c:forEach> 中的属性如下

  • begin:开始数

  • end:结束数

  • step:步长

实例代码:

从0循环到10,变量名是 i ,每次自增1

<c:forEach begin="0" end="10" step="1" var="i">
    ${i}
</c:forEach>

9.7、MVC模式和三层架构

9.7.1、MVC模式

MVC 是一种分层开发的模式,其中:

  • M:Model,业务模型,处理业务

  • V:View,视图,界面展示

  • C:Controller,控制器,处理请求,调用模型和视图
    控制器(serlvlet)用来接收浏览器发送过来的请求,控制器调用模型(JavaBean)来获取数据,比如从数据库查询数据;控制器获取到数据后再交由视图(JSP)进行数据展示。

在这里插入图片描述

MVC 好处:

  • 职责单一,互不影响。每个角色做它自己的事,各司其职。

  • 有利于分工协作。

  • 有利于组件重用

9.7.2、三层架构

三层架构是将我们的项目分成了三个层面,分别是 表现层业务逻辑层数据访问层
在这里插入图片描述

  • 数据访问层:对数据库的CRUD基本操作
  • 业务逻辑层:对业务逻辑进行封装,组合数据访问层层中基本功能,形成复杂的业务逻辑功能。例如 注册业务功能 ,我们会先调用 数据访问层selectByName() 方法判断该用户名是否存在,如果不存在再调用 数据访问层insert() 方法进行数据的添加操作
  • 表现层:接收请求,封装数据,调用业务逻辑层,响应数据

而整个流程是,浏览器发送请求,表现层的Servlet接收请求并调用业务逻辑层的方法进行业务逻辑处理,而业务逻辑层方法调用数据访问层方法进行数据的操作,依次返回到serlvet,然后servlet将数据交由 JSP 进行展示。

三层架构的每一层都有特有的包名称:

  • 表现层: com.itheima.controller 或者 com.itheima.web
  • 业务逻辑层:com.itheima.service
  • 数据访问层:com.itheima.dao 或者 com.itheima.mapper

在这里插入图片描述

9.7.3、MVC 和 三层架构

如上图上半部分是 MVC 模式,上图下半部分是三层架构。 MVC 模式 中的 C(控制器)和 V(视图)就是 三层架构 中的表现层,而 MVC 模式 中的 M(模型)就是 三层架构 中的 业务逻辑层 和 数据访问层。

可以将 MVC 模式 理解成是一个大的概念,而 三层架构 是对 MVC 模式 实现架构的思想。 那么我们以后按照要求将不同层的代码写在不同的包下,每一层里功能职责做到单一,将来如果将表现层的技术换掉,而业务逻辑层和数据访问层的代码不需要发生变化。

在这里插入图片描述

9.8、案例

9.8.1、环境准备

  • 创建新的模块brandDemo
  • 创建三层架构的包结构
  • 数据库表tb_brand
  • 实体类Brand
  • MyBatis基础环境
    • MybatisConfig.xml
    • BrandMapper.xml
    • BrandMapper.java接口
      在这里插入图片描述

9.8.2、查询

SelectAllServlet .java

package com.chuhe.web;

import com.chuhe.pojo.Brand;
import com.chuhe.service.BrandService;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
import java.util.List;

@WebServlet("/selectAllServlet")
public class SelectAllServlet extends HttpServlet {
    private BrandService service=new BrandService();
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //调用BrandService完成查询
        List<Brand> brands=service.selectAll();
        //存入request域中
        request.setAttribute("brands",brands);
        //转发到brand.jsp
        request.getRequestDispatcher("/brands.jsp").forward(request,response);
    }

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

9.8.3、新增

AddServlet .java

package com.chuhe.web;

import com.chuhe.pojo.Brand;
import com.chuhe.service.BrandService;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;

@WebServlet( "/addServlet")
public class AddServlet extends HttpServlet {
    private BrandService service=new BrandService();
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setCharacterEncoding("utf-8");

        String brandName = request.getParameter("brandName");
        String companyName=request.getParameter("companyName");
        String ordered=request.getParameter("ordered");
        String description=request.getParameter("description");
        String status=request.getParameter("status");

        Brand brand=new Brand();
        brand.setBrandName(brandName);
        brand.setCompanyName(companyName);
        brand.setOrdered(Integer.parseInt(ordered));
        brand.setDescription(description);
        brand.setStatus(Integer.parseInt(status));

        service.add(brand);

        //转发到查询所有
        request.getRequestDispatcher("/selectAllServlet").forward(request,response);

    }

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

9.8.4、修改

9.8.4.1、回显
package com.chuhe.web;

import com.chuhe.pojo.Brand;
import com.chuhe.service.BrandService;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;

@WebServlet("/selectByIdServlet")
public class selectByIdServlet extends HttpServlet {
    private BrandService service=new BrandService();
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setCharacterEncoding("utf-8");
        String id=request.getParameter("id");
        Brand brand=service.selectById(Integer.parseInt(id));
        request.setAttribute("brand",brand);
        request.getRequestDispatcher("/updateBrand.jsp").forward(request,response);

    }

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

    }
}

9.8.4.2、修改

UpdateServlet.java

package com.chuhe.web;

import com.chuhe.pojo.Brand;
import com.chuhe.service.BrandService;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;

@WebServlet("/updateServlet")
public class UpdateServlet extends HttpServlet {
    private BrandService service=new BrandService();
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setCharacterEncoding("utf-8");


        String id=request.getParameter("id");
        String brandName=request.getParameter("brandName");
        String companyName=request.getParameter("companyName");
        String ordered=request.getParameter("ordered");
        String description=request.getParameter("description");
        String status=request.getParameter("status");

        Brand brand=new Brand();
        brand.setId(Integer.parseInt(id));
        brand.setBrandName(brandName);
        brand.setCompanyName(companyName);
        brand.setOrdered(Integer.parseInt(ordered));
        brand.setDescription(description);
        brand.setStatus(Integer.parseInt(status));

        service.update(brand);
        request.getRequestDispatcher("/selectAllServlet").forward(request,response);

    }

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

9.8.5、删除

DeleteByIdServlet.java

package com.chuhe.web;

import com.chuhe.service.BrandService;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;

@WebServlet( "/deleteByIdServlet")
public class DeleteByIdServlet extends HttpServlet {
    private BrandService service=new BrandService();
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setCharacterEncoding("utf-8");
        String id=request.getParameter("id");
        service.deleteById(Integer.parseInt(id));
        request.getRequestDispatcher("/selectAllServlet").forward(request,response);
    }

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

9.8.6、JSP文件

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <a href="/brand/selectAllServlet">查询所有</a>
</body>
</html>

addBrand.jsp

<%@ page contentType="text/html; charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>添加品牌</title>
</head>
<body>
<h3>添加品牌</h3>
<form action="/brand/addServlet" method="post">
    品牌名称:<input name="brandName"><br>
    企业名称:<input name="companyName"><br>
    排序:<input name="ordered"><br>
    描述信息:<textarea rows="5" cols="20" name="description"></textarea><br>
    状态:
    <input type="radio" name="status" value="0">禁用
    <input type="radio" name="status" value="1">启用<br>

    <input type="submit" value="提交">
</form>
</body>
</html>

brands.jsp


<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <input type="button" value="新增" id="add"><br>
    <table border="1" cellspacing="0" width="80%">
        <tr>
            <th>序号</th>
            <th>品牌名称</th>
            <th>企业名称</th>
            <th>排序</th>
            <th>品牌介绍</th>
            <th>状态</th>
            <th>操作</th>
        </tr>
        <c:forEach items="${brands}" var="brand" varStatus="status">
            <tr align="center">
                <td>${status.count}</td>
                <td>${brand.brandName}</td>
                <td>${brand.companyName}</td>
                <td>${brand.ordered}</td>
                <td>${brand.description}</td>
                <c:if test="${brand.status==1}">
                    <td>启用</td>
                </c:if>
                <c:if  test="${brand.status==0}">
                    <td>禁用</td>
                </c:if>
                <td><a href="/brand/selectByIdServlet?id=${brand.id}">修改 </a><a href="/brand/deleteByIdServlet?id=${brand.id}"> 删除</a></td>

            </tr>
        </c:forEach>
    </table>
<script>
    document.getElementById("add").onclick=function(){
        location.href="/brand/addBrand.jsp";
    }

</script>
</body>
</html>

updateBrand.jsp

<%@ page contentType="text/html; charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>修改品牌</title>
</head>
<body>
<h3>修改品牌</h3>
<form action="/brand/updateServlet" method="post">
    <input type="hidden" name="id" value="${brand.id}">
    品牌名称:<input name="brandName" value="${brand.brandName}"><br>
    企业名称:<input name="companyName" value="${brand.companyName}"><br>
    排序:<input name="ordered" value="${brand.ordered}"><br>
    描述信息:<textarea rows="5" cols="20" name="description" >${brand.description}</textarea><br>
    状态:
    <input type="radio" name="status" value="0" checked>禁用
    <input type="radio" name="status" value="1">启用<br>

    <input type="submit" value="提交">
</form>
</body>
</html>

9.8.7、其它文件

BrandMapper.java

package com.chuhe.mapper;

import com.chuhe.pojo.Brand;
import org.apache.ibatis.annotations.*;

import java.util.List;

public interface BrandMapper {
    @Select("select * from tb_brand;")
    @ResultMap("brandResultMap")
    List<Brand> selectAll();

    @Select("select * from tb_brand where id=#{id};")
    @ResultMap("brandResultMap")
    Brand selectById(int id);

    @Insert("insert into tb_brand values(null,#{brandName},#{companyName},#{ordered},#{description},#{status})")
    @ResultMap("brandResultMap")
    void add(Brand brand);

    @Update("update tb_brand " +
            "set brand_name=#{brandName}," +
            "company_name=#{brandName}," +
            "ordered=#{ordered}," +
            "description=#{description}," +
            "status=#{status} " +
            "where id=#{id};")
    @ResultMap("brandResultMap")
    void update(Brand brand);

    //删除
    @Delete("delete from tb_brand where id=#{id};")
    void deleteById(int id);
}

BrandService.java

package com.chuhe.service;

import com.chuhe.mapper.BrandMapper;
import com.chuhe.pojo.Brand;
import com.chuhe.util.SqlSessionFactoryUtils;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;

import java.util.List;

public class BrandService {
    private SqlSessionFactory sqlSessionFactory= SqlSessionFactoryUtils.getSqlSessionFactory();
    /**
     * 查询全部
     * @return
     */
    public List<Brand> selectAll(){
        SqlSession sqlSession=sqlSessionFactory.openSession();
        BrandMapper mapper = sqlSession.getMapper(BrandMapper.class);
        List<Brand> brands=mapper.selectAll();
        System.out.println(brands.size());
        System.out.println(brands);
        //释放资源
        sqlSession.close();
        return brands;
    }

    /**
     * 根据id查询
     * @param id
     * @return
     */
    public Brand selectById(int id){
        SqlSession sqlSession=sqlSessionFactory.openSession();
        BrandMapper mapper = sqlSession.getMapper(BrandMapper.class);
        Brand brand=mapper.selectById(id);
        return brand;
    }

    /**
     * 添加
     * @param brand
     */
    public void add(Brand brand){
        SqlSession sqlSession = sqlSessionFactory.openSession();
        BrandMapper mapper=sqlSession.getMapper(BrandMapper.class);
        mapper.add(brand);
        sqlSession.commit();
        sqlSession.close();

    }

    /**
     * 修改
     * @param brand
     */
    public void update(Brand brand){
        SqlSession sqlSession=sqlSessionFactory.openSession();
        BrandMapper mapper = sqlSession.getMapper(BrandMapper.class);
        mapper.update(brand);
        //提交事务
        sqlSession.commit();
        //释放资源
        sqlSession.close();

    }

    /**
     * 删除
     * @param id
     */
    public void deleteById(int id){
        SqlSessionFactory sqlSessionFactory=SqlSessionFactoryUtils.getSqlSessionFactory();
        SqlSession sqlSession=sqlSessionFactory.openSession();
        BrandMapper mapper=sqlSession.getMapper(BrandMapper.class);
        mapper.deleteById(id);
        sqlSession.commit();
        //释放资源
        sqlSession.close();
    }
}

MybatisConfig.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <typeAliases>
        <!--
        name属性的值是实体类所在包
        起别名
        可以简化映射配置文件中 resultType 属性值的编写
        -->
        <package name="com.chuhe.pojo"/>
    </typeAliases>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <!--数据库连接信息-->
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql:///db1?useSSL=false"/>
                <property name="username" value="root"/>
                <property name="password" value="1234"/>
            </dataSource>
        </environment>

    </environments>
    <!--扫描mapper-->
    <mappers>
        <package name="com.chuhe.mapper"/>
    </mappers>
</configuration>

BrandMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace设置的是名称空间,必须是对应接口的全限定名-->
<mapper namespace="com.chuhe.mapper.BrandMapper">

    <resultMap id="brandResultMap" type="com.chuhe.pojo.Brand">
        <result column="brand_name" property="brandName"/>
        <result column="company_name" property="companyName"/>

    </resultMap>

</mapper>

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐