MVC框架简易实现【精细】

目录

mvc 的架构

MVC 框架

传统web开发的弊端

1.创建一个maven项目

2.添加maven依赖

3.创建TomCatService类

        3.1 创建TomcatService类

        3.2 TomcatService类讲解

        3.3 安装项目到本地仓库,给其他项目使用

4.测试启动Tomcat

         4.1 创建一个新的maven项目

        4.2 引用mvc-framework项目的依赖

        4.3 创建一个webapp包,里面在创建index.html文件

        4.4 创建一个测试类

        4.5 启动项目

        4.6 访问项目

5. 让Tomcat可以访问Servlet

        5.1 创建一个Servlet类

         5.2 修改mvc-framework中的Tomcat配置代码

        5.3 测试访问Servlet

6.Tomcat可以使用YML文件配置

        6.1 添加配置信息类

        6.2 修改TomcatServer类


mvc 的架构

model     ----------> 业务领域的核心对象(Entity,Service,Dao)

View       ----------> 视图层,做数据的提交和渲染 (JSP、HTML.........)

Controller --------> 控制层,专门用于处理Http请求(Servlet)


MVC 框架

简化控制层以及视图渲染的操作,将这些API进行更高层封装,使用起来更加简便,可以隐藏Servlet的操作,同时还可以提供简便的部署方式。


传统web开发的弊端

1.每一个请求都必须要有一个对应的Servlet去处理:

                在传统的Servlet开发中,每个请求都需要对应一个Servlet类来处理。这导致了项目结构的臃肿,且难以维护和扩展。特别是对于大型项目,Servlet的数量可能会非常庞大,给代码的管理和维护带来挑战


2.请求参数的处理相对繁琐 :

                在Servlet开发中,通常需要手动解析请求参数,包括从URL中解析参数或者从请求体中读取参数。这样的处理方式相对繁琐,容易出错,尤其是在处理复杂的参数结构时更加困难。


3.请求响应的统一性比较繁杂:

                在Servlet开发中,通常需要手动设置响应的内容类型、编码等信息,并且手动编写响应内容。这样的操作需要开发者自行管理,容易导致响应的不一致性,增加了维护成本。


4.部署Web应用相对繁杂,肯能存在缓存的问题:

                在传统的Servlet开发中,部署Web应用通常需要手动将Servlet类部署到Servlet容器中,并且需要手动处理依赖关系、配置信息等。这样的部署方式相对繁琐,容易出错。此外,由于Servlet容器会缓存Servlet类,当修改了Servlet类但未正确地刷新缓存时,可能会导致旧版本的Servlet类被执行,从而引发一系列问题。

 为了解决这传统web开发的弊端,我们自己写一个MVC框架简易实现


1.创建一个maven项目


2.添加maven依赖

对应的依赖为:

<?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>com.xr</groupId>
    <artifactId>mvc-framework</artifactId>
    <version>0.9</version>
    <!--打包方式为jar-->
    <packaging>jar</packaging>
 
    <properties>
        <!--maven编译源代码的jdk版本-->
        <maven.compiler.source>21</maven.compiler.source>
        <!--maven编译目标代码的jdk版本-->
        <maven.compiler.target>21</maven.compiler.target>
        <!--项目构建编码格式-->
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
 
 
    <dependencies>
        <!--Tomcat依赖-->
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-core</artifactId>
            <version>10.1.19</version>
        </dependency>
 
        <!--JSP解析器依赖-->
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-jasper</artifactId>
            <version>10.1.19</version>
        </dependency>
 
        <!--EL表达式依赖-->
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-el</artifactId>
            <version>10.1.19</version>
        </dependency>
 
        <!--servlet依赖-->
        <dependency>
            <groupId>jakarta.servlet</groupId>
            <artifactId>jakarta.servlet-api</artifactId>
            <version>6.0.0</version>
                        <!--这个依赖是运行环境提供的,不需要打包到jar中,为什么一定要是provided,如果不是当其他项目中引用,因为Tomcat自带Servlet,如果将这个依赖打包到jar中,
那么tomcat会识别到这个jar中的Servlet,tomcat就不知道使用哪个Servlet-->
            <scope>provided</scope>
        </dependency>
 
        <!--yml文件解析器-->
        <dependency>
            <groupId>org.yaml</groupId>
            <artifactId>snakeyaml</artifactId>
            <version>1.33</version>
        </dependency>
 
        <!--底层工具库(Guava,Hutool)-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.27</version>
        </dependency>
 
    </dependencies>
 
</project>

注意:

        1.这里的项目打包方式一定是jar

        2.servlet依赖范围一定是provided

                如果不是Provided,当其他项目引用这个项目的依赖,那么这个依赖会打包到jar中,而Tomcat自带Servlet,那么tomcat会识别到这个jar中的Servlet,tomcat就不知道使用哪个Servlet


3.创建TomCatService类

        3.1 创建TomcatService类

作用:

        启动一个嵌入式的Tomcat服务器。在Java Web开发中,通常使用Tomcat作为Servlet容器来运行Web应用,而这个类的目的是通过代码的方式启动一个Tomcat服务器,而不是通过传统的部署方式

       

        3.2 TomcatService类讲解

package com.xr.embed;
 
import cn.hutool.core.io.FileUtil;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.startup.Tomcat;
 
import java.io.File;
 
/**
 * Tomcat容器
 * @author xr
 * @Date 2024/5/13 11:32
 */
public class TomcatServer {
    /**
     * 容器的端口号
     */
    private final Integer port = 8080;
 
    /**
     * ContextPath路径,默认值是"/",表示项目的根
     */
    private final String contextPath = "/";
 
    /**
     * web资源的部署目录
     */
    private String webRoot = "src/main/webapp";
 
    /**
     * 启动Tomcat服务器
     */
    public void startServer() {
        // 实例化Tomcat对象
        Tomcat tomcat = new Tomcat();
 
        // 设置端口
        tomcat.setPort(port);
 
        // 将webRoot构建为绝对路径
        webRoot = FileUtil.getWebRoot().getAbsolutePath()+ File.separator + webRoot;
 
        // 设置contextPath和web资源目录
        tomcat.addWebapp(contextPath, webRoot);
 
        // 创建连接
        tomcat.getConnector();
 
 
        try {
            //启动tomcat
            tomcat.start();
 
            //将Tomcat进行阻状态,保持运行状态
            tomcat.getServer().await();
        } catch (LifecycleException e) {
            throw new RuntimeException("启动Tomcat失败!!!",e);
        }
    }
}

属性:

                     port  :     配置Tomcat服务器的端口

        ContextPath :   应用的根路径

              webRoot :   指定了Web资源的部署目录,即Web应用的根目录。在实际运行时,会将这个目录构建为绝对路径,并传递给Tomcat


方法 :

        startServer() :     首先实例化了Tomcat对象,然后设置了端口和Web资源目录。接着,通过调用addWebapp()方法将Web应用部署到Tomcat的ContextPath下。最后,启动了Tomcat服务器并使其保持运行状态

注意 :

           为什么要让Tomcat进入阻塞装填???

        如果Tomcat进入阻塞状态的话,只要一启动,Tomcat就直接结束了,所以要让Tomcat保持运行状态

        3.3 安装项目到本地仓库,给其他项目使用


4.测试启动Tomcat

         4.1 创建一个新的maven项目

        4.2 引用mvc-framework项目的依赖

对应的依赖:

        

<?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>com.xr</groupId>
    <artifactId>webapp-demo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>
 
    <properties>
        <maven.compiler.source>21</maven.compiler.source>
        <maven.compiler.target>21</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
 
    <dependencies>
        <dependency>
            <groupId>com.xr</groupId>
            <artifactId>mvc-framework</artifactId>
            <version>0.9</version>
        </dependency>
    </dependencies>
 
</project>

        4.3 创建一个webapp包,里面在创建index.html文件

有则不用创建,没有则创建

具体代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h2>Hello Tomcat</h2>
</body>
</html>

        4.4 创建一个测试类

具体代码:

package com.xr;
 
import com.xr.embed.TomcatServer;
 
/**
 * 测试方法
 * @author xr
 * @Date 2024/5/13 11:50
 */
 
public class Main {
    public static void main(String[] args) {
        // 创建TomcatServer对象
        TomcatServer server = new TomcatServer();
 
        // 启动Tomcat
        server.startServer();
    }
}

        4.5 启动项目

项目启动成功!!!!

                之前是要配置了Tomcat才能启动项目,我们这里并没有配置Tomcat

发现 :

        我们并没有在Idea中配置Tomcat,按照之前的方法,我们需要在IDEA中配置Tomcat,而这里所使用的,间接让项目内嵌了Tomcat,所以无需配置Tomcat

        4.6 访问项目


5. 让Tomcat可以访问Servlet

前面的使用我们只能访问webapp下的静态页面,那我想servelt也能使用该怎么办呢???

        5.1 创建一个Servlet类

在测webapp-demo项目中创建一个servlet包,在到里面创建一个DemoServlet类(servelt类)

DemoServlet类具体代码:

package com.xr.servlet;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;

/**
 * 测试
 * @author xr
 * @WebServlet :该注解的作用是将一个类声明为Servlet组件, 并指定该Servlet组件的访问路径
 * HttpServlet: 继承了HttpServlet类,该类提供了一些方法,用于处理HTTP请求和响应。
 * @Date 2024/5/13 14:59
 */
@WebServlet("/hello")
public class DemoServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
       // 设置响应类型为HTML和编码格式
        resp.setContentType("text/html;charset=utf-8");
        // 输出响应到页面上
        resp.getWriter().println("<h2>Servlet Page</h2>");
    }
}

启动项目后,通过路径【localhost:8080/hello】访问servelt,发现访问不了,为什么???

        1. 因为我们编写的mvc-framework项目中的Tomcat并没有设置对应的Servlet访问配置


解决思路:

        1.我们声明一个Servlet类的时候,使用@WebServlet注解声明该类是一个servlet类,并给注解赋值有一个访问路径,通过这个访问路径来访问对应Servlet

        2.可以获取@WebServlet注解来确定哪些是Servlet类获取到了Servlet类之后,就知道了有对应的访问路径了

         5.2 修改mvc-framework中的Tomcat配置代码

项目中的所有类编译后会存放在targe/classes目录下,我们通过Hutool工具类来过滤没有@WebServlet的类,过滤完成之后将这些编译好的类存放在webapp/WEB-INF/classes下,

再将这些类设置到TomcatContext中,这时候,Tomcat启动的时候,通过加载Context就知道哪些是Serlvet类了,最后就可以访问了Servlet

改写TomcatService类:

package com.xr.embed.server;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ClassUtil;
import org.apache.catalina.Context;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.WebResourceRoot;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.webresources.DirResourceSet;
import org.apache.catalina.webresources.StandardRoot;

import java.io.File;

/**
 * Tomcat容器
 * @author xr
 * @Date 2024/5/13 11:32
 *  extends AbstractWebServer
 */

public class TomcatServer {
    /**
     * 容器的端口号
     */
    private final Integer port = 8080;

    /**
     * ContextPath路径,默认值是"/",表示项目的根
     */
    private final String contextPath = "/";

    /**
     * web资源的部署目录,静态资源的路径
     */
    private String webRoot = "src/main/webapp";

    /**
     * 启动tomcat
     */
    public void startServer() {
        // 实例化Tomcat对象
        Tomcat tomcat = new Tomcat();

        // 设置端口
        tomcat.setPort(port);

        // 将webRoot构建为绝对路径
        webRoot = FileUtil.getWebRoot().getAbsolutePath()+ File.separator + webRoot;

        // 设置contextPath和web资源目录
        Context context = tomcat.addWebapp(contextPath, webRoot);

        // 设置应用程序的编译路径,这样容器启动时会从classpath路径下查找对应Servlet注解的组件
        // 例如: @webServlet、@WebFilter、@WebListener
        // 管理Web应用中的所有资源(静态资源和类文件)
        WebResourceRoot resourceRoot = new StandardRoot(context);

        // 过滤出有注解的Servlet类,并将这些类放在/WEB-INF/classes目录下
        DirResourceSet resourceSet = new DirResourceSet(resourceRoot, "/WEB-INF/classes", ClassUtil.getClassPath(), "/");

        // 将过滤之后的结果添加到容器中
        resourceRoot.addPreResources(resourceSet);

        // 将容器设置回Tomcat上下文中
        context.setResources(resourceRoot);


        // 创建连接
        tomcat.getConnector();


        try {
            //启动tomcat
            tomcat.start();

            //将Tomcat进行阻状态,保持运行状态
            tomcat.getServer().await();
        } catch (LifecycleException e) {
            throw new RuntimeException("启动Tomcat失败!!!",e);
        }
    }

        5.3 测试访问Servlet

5.3.1

        上面已经重新修改了TomcatServer类,使用maven先clean清理,在install安装到本地仓库

5.3.2

        在webapp-demo项目中重新刷新mvc-framework的依赖        

5.3.3

        启动项目,并访问对应的servlet路径

启动项目:

访问Servlet:

访问Servlet也成功了!!!


6.Tomcat可以使用YML文件配置

之前的TomcatServer中已经有3个属性:

                port:       tomcat端口号,(如:8080)

  contextPath:        项目的根路径,(如: "/index.html"访问静态页面)

        webRoot:       静态资源的路径 (如:“src/main/webapp")


如果其他项目想要修改这些配置怎么办?不可能去这个TomcatService类中修改叭,而且如果直接去修改也违反了开辟原则!!!


解决方法:

        使用yml文件进行对Tomcat的配置


思路:

        1.创建一个ServiceConfig类,这个类定义web容器的配置信息

        2.定义一个Configure类,配置对象(也是所有配置的根),将ServletConfig类作为属性

        3. 通过Hutool工具类获取yml文件里面中的配置信息,在映射到Configure类中的ServiceConfig属性中

        4.在Tomcat创建后,初始化配置对象,并获取对应的配置信息,并设置对应的配置

        6.1 添加配置信息类

mvc-framework项目中创建config包,在这个包下添加2个类ServiceConfig,Configure

ServiceConfig 配置信息类 :

package com.xr.config;
/**
 * web容器的配置信息
 * @author xr
 * @Date 2024/5/13 15:28
 */

public class ServerConfig {
    /**
     * 这里的所有字段名字必须与yml中子节点名字相同
     */
    private Integer port = 8080; // 端口
    private String contextPath = "/"; //访问项目的根路径
    private String webRoot = "src/main/webapp"; // 静态资源根目录

    public Integer getPort() {
        return port;
    }

    public void setPort(Integer port) {
        this.port = port;
    }

    public String getContextPath() {
        return contextPath;
    }

    public void setContextPath(String contextPath) {
        this.contextPath = contextPath;
    }

    public String getWebRoot() {
        return webRoot;
    }

    public void setWebRoot(String webRoot) {
        this.webRoot = webRoot;
    }
}

Configure 配置类:

package com.xr.config;


/**
 * 配置对象(也是所有配置的根),作用是将yml解析的数据库封装到该对象中
 * @author xr
 * @Date 2024/5/13 15:26
 */
public class Configure {
    /**
     * 这里的字段命名必须与yml中节点名称相同
     * 这里的server对应的父节点的名字
     */
    private ServerConfig server;


    public ServerConfig getServer() {
        return server;
    }

    public void setServer(ServerConfig server) {
        this.server = server;
    }

}

        6.2 修改TomcatServer类

package com.xr.embed.server;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.setting.yaml.YamlUtil;
import com.xr.config.Configure;
import org.apache.catalina.Context;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.WebResourceRoot;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.webresources.DirResourceSet;
import org.apache.catalina.webresources.StandardRoot;

import java.io.File;

/**
 * Tomcat容器
 * @author xr
 * @Date 2024/5/13 11:32
 *  extends AbstractWebServer
 */

public class TomcatServer {
    /**
     * 容器的端口号
     */
    private final Integer port = 8080;

    /**
     * ContextPath路径,默认值是"/",表示项目的根
     */
    private final String contextPath = "/";

    /**
     * web资源的部署目录,静态资源的路径
     */
    private String webRoot = "src/main/webapp";


        /**
     * 启动Tomcat容器
     */
    public void startServer() {
        // 实例化Tomcat对象
        Tomcat tomcat = new Tomcat();

        // 创建配置对象
        Configure configure = initConfigure();

        // 获取配置文件中的端口,并设置在Tomcat中
        tomcat.setPort(configure.getServer().getPort());

        // 获取配置文件中的静态资源的路径src/main/webapp
        webRoot = configure.getServer().getWebRoot();

        // 将webRoot构建为绝对路径
        webRoot = FileUtil.getWebRoot().getAbsolutePath()+ File.separator + webRoot;

        // 设置contextPath和web资源目录
        Context context = tomcat.addWebapp(configure.getServer().getContextPath(), webRoot);

        // 设置应用程序的编译路径,这样容器启动时会从classpath路径下查找对应Servlet注解的组件
        // 例如: @webServlet、@WebFilter、@WebListener
        // 管理Web应用中的所有资源(静态资源和类文件)
        WebResourceRoot resourceRoot = new StandardRoot(context);

        // 过滤出有注解的Servlet类
        DirResourceSet resourceSet = new DirResourceSet(resourceRoot, "/WEB-INF/classes", ClassUtil.getClassPath(), "/");

        // 将过滤之后的结果添加到容器中
        resourceRoot.addPreResources(resourceSet);

        // 将容器设置回Tomcat上下文中
        context.setResources(resourceRoot);


        // 创建连接
        tomcat.getConnector();


        try {
            //启动tomcat
            tomcat.start();

            //将Tomcat进行阻状态,保持运行状态
            tomcat.getServer().await();
        } catch (LifecycleException e) {
            throw new RuntimeException("启动Tomcat失败!!!",e);
        }
    }


    /**
     * 初始化配置对象,将yml的配置信息映射到Configure
     */
    private Configure initConfigure() {
        // 获取配置文件路径
        String configFilePath = ClassUtil.getClassPath() + "application.yml";

        // 定义配置对象
        Configure configure;

        // 判断配置文件是否存在
        if (FileUtil.exist(configFilePath)) {
            // 解析yml并映射到Configure对象中
            configure = YamlUtil.loadByPath("application.yml", Configure.class);
            // 有肯能用户只创建一个application.yml文件但未设置任何节点,因此返回的对象肯能是null
            if(configure == null) {
                configure = new Configure();
            }
        } else {
            // 如果配置文件不存在,则创建一个空的Configure对象
            configure = new Configure();
        }
        return configure;
    }
}

startServer()方法中:

                当Tomcat一创建,就调用了initConfigure()方法,这个方法就是初始化配置对象,将yml文件中的配置映射到配置类中

        6.3 测试读取YML文件

6.3.1

        将mcv-framework重新安装

6.3.2

        在webapp-demo项目中刷新依赖

6.3.3

        在webapp-demo项目中resources目录增加application.yml文件

YML对应的设置:

server:
  port: 8082    # 端口号
  contextPath: "/" # 访问项目的根路径
  webRoot: "src/main/webapp" # 静态资源根路径

6.3.4

        启动项目,并访问

启动项目:

通过8082端口访问静态页面:

访问Servlet :

6.3.5 

        修改YML配置文件的端口配置,将端口号修改8088测试!!!

启动项目:

通过8088端口访问静态页面:

通过8088端口访问Servlet :

最后通过yml形式配置Tomcat完成!!!!


7. 扩展代码(扩展不同的容器)

我所做的目前只有Tomcat容器,但是市场的容器有许多种,比如(Jetty容器),但是之前的代码只是专门争对于Tocamt容器进行一个处理,当我想使用其他容器的时候,不肯能又写一套代码叭,所以我们对其扩展


        之前配置的Tomcat是不是都是先初始化Configure对象来获取yml文件的配置,然后在配置Tomcat启动Tomcat方法。

        那其他容器是不是也可以照着这样的方式来配置其他容器,也是先初始化配置,在接着启动容器


       这样多种容器都是先初始化配置,在启动容器,是不是都是同一个步骤,这里我们就可以使模板方法来做处理了


思路:

        1. 定义接口,因为不同的容器有不同的实现

        2.定义抽象类实现接口,抽象类中使用模板方法

        3. TomcatServlet继承抽象类,实现具体的实现

        7.1 在embed包下定义WebServlet接口

WebServlet接口具体代码 :

package com.xr.embed;

/**
 * 抽象的容器接口,不止Tomcat容器,还有其他容器比如Jetty容器
 * @author xr
 * @Date 2024/5/14 8:39
 */

    public interface WebServer {

    /**
     * 抽象的启动方法,不同的容器有不同的实现
     * @throws Exception 启动异常
     * Class<?> mainClass
     */
    void run() throws Exception;


}

        7.2 在定义AbstractWebServer抽象类并实现WebServlet接口

AbstractWebServer具体代码 :

package com.xr.embed;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.setting.yaml.YamlUtil;
import com.xr.config.Configure;

/**
 * 使用模板方法
 * @author xr
 * @Date 2024/5/14 8:42
 */

public abstract class AbstractWebServer implements WebServer{

    /**
     * 启动容器
     * @throws Exception
     */
    @Override
    public void run() throws Exception {
        // 1. 解析配置返回Configure
        Configure configure = initConfigure();
        // 2.启动容器,不同的容器有不同的具体实现
        start(configure);
    }


    /**
     * 抽象的启动方法,由子类不同的实现
     * @param configure
     */
    public abstract void start (Configure configure);

    /**
     * 初始化配置对象,将yml的配置信息映射到Configure
     *
     */
    private Configure initConfigure() {
        // 获取配置文件路径
        String configFilePath = ClassUtil.getClassPath() + "application.yml";

        // 定义配置对象
        Configure configure;

        // 判断配置文件是否存在
        if (FileUtil.exist(configFilePath)) {
            // 解析yml并映射到Configure对象中
            configure = YamlUtil.loadByPath("application.yml", Configure.class);
            // 有肯能用户只创建一个application.yml文件但未设置任何节点,因此返回的对象肯能是null
            if(configure == null) {
                configure = new Configure();
            }
        } else {
            // 如果配置文件不存在,则创建一个空的Configure对象
            configure = new Configure();
        }


        return configure;
    }
}

        7.3 TomcatServer继承AbstractWebServer抽象类

TomcatServer具体代码:

package com.xr.embed.server;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ClassUtil;
import com.xr.config.Configure;
import com.xr.embed.AbstractWebServer;
import org.apache.catalina.Context;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.WebResourceRoot;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.webresources.DirResourceSet;
import org.apache.catalina.webresources.StandardRoot;

import java.io.File;

/**
 * Tomcat容器
 * @author xr
 * @Date 2024/5/13 11:32
 *  extends AbstractWebServer
 */

public class TomcatServer extends AbstractWebServer {
    /**
     * 容器的端口号
     */
    private final Integer port = 8080;

    /**
     * ContextPath路径,默认值是"/",表示项目的根
     */
    private final String contextPath = "/";

    /**
     * web资源的部署目录,静态资源的路径
     */
    private String webRoot = "src/main/webapp";

    //1.1
    @Override
    public void start(Configure configure) {
        // 创建一个Tomcat容器
        Tomcat tomcat = new Tomcat();
        // 获取配置文件中的端口,并设置在Tomcat中
        tomcat.setPort(configure.getServer().getPort());

        // 获取配置文件中静态资源的路径src/main/webapp
        String webRoot = configure.getServer().getWebRoot();


        // 将静态资源设置成绝对路径 比如 D:\store\webapp-demo\src\main\webapp
        webRoot = FileUtil.getWebRoot().getAbsolutePath()+ File.separator + webRoot;
 // 设置contextPath和web资源目录
        Context context = tomcat.addWebapp(configure.getServer().getContextPath(), webRoot);

        // 设置应用程序的编译路径,这样容器启动时会从classpath路径下查找对应Servlet注解的组件
        // 例如: @webServlet、@WebFilter、@WebListener
        // 管理Web应用中的所有资源(静态资源和类文件)
        WebResourceRoot resourceRoot = new StandardRoot(context);

        // 过滤出有注解的Servlet类
        DirResourceSet resourceSet = new DirResourceSet(resourceRoot, "/WEB-INF/classes", ClassUtil.getClassPath(), "/");

        // 将过滤之后的结果添加到容器中
        resourceRoot.addPreResources(resourceSet);

        // 将容器设置回Tomcat上下文中
        context.setResources(resourceRoot);


        // 创建连接
        tomcat.getConnector();


        try {
            //启动tomcat
            tomcat.start();

            //将Tomcat进行阻状态,保持运行状态
            tomcat.getServer().await();
        } catch (LifecycleException e) {
            throw new RuntimeException("启动Tomcat失败!!!",e);
        }

    }

}

        这里tomcatServlet类继承了抽象类,之前的Tomcat是不是创建了Tomcat之后要初始化Configure类来获取yml,但是我们的抽象类已经实现了那一步,这是让运行Tomcat的第一步,接着在将这些配置设置在Tomcat中,而我们的抽象类,有一个的抽象方法叫start方法,我们的tomcatServlet类重写start方法,将对应的设置和启动tomcat是不是对应的实现完成了!!!


最后测试项目中是不是只需要调用run方法,就可以完成tomcat的初始化和启动了,因为run方法里面有两个操作,一个是初始化,一个是启动Tomcat,这是不是一个步骤

        7.4 测试扩展后的代码

7.4.1

        先清理,在安装

7.4.2

        在webapp-demo项目中刷新mvc-framework依赖

7.4.3

        在Main测试类中修改Tomcat启动方式

Main启动类具体代码:

package com.xr;
import com.xr.embed.server.TomcatServer;

/**
 * 测试方法
 * @author xr
 * @Date 2024/5/13 11:50
 */

public class Main {
    public static void main(String[] args) throws Exception {
        // 创建Tomcat
        TomcatServer tomcatServer = new TomcatServer();

        // 启动Tomcat,调用run方法来启动Tomcat
        tomcatServer.run();
    }
}

7.4.4

          启动项目并测试访问servlet和静态资源页面

启动项目:

访问静态资源页面:

访问Servlet:

扩展代码成功,可以实现不同容器的扩展!!!! 


8. 解决多个请求对应多个Servlet问题

之前完成了内嵌Tomcat容器,也可以多个内嵌其他容器。

现在来解决传统的web开发的弊端:

        1. 解决多个请求对应多个Servlet


 原因 :

               传统的web开发的时候,我们每发送一个请求,Tomcat内部会创建一个Servlet,如果项目很大,请求很多,那么我们的程序会不会变得非常臃肿


解决方案 :

                为了解决这一个弊端,我们采用用一个Servlet处理所有请求,并将这些请求分发给对应的控制器去处理,这样解决了频繁创建Servlet的问题

8.1 创建DispatcherServlet类

        创建一个DispatcherServlet(Servlet类),重写Tomcat的DefaultServlet

        将所有请求统一交给该DispatcherServlet类处理

创建Servlet包,在创建DispatcherServlet类

DispatcherServlet类具体代码:

                 1. 多个请求对应多个servlet,简化成多个请求1个servlet处理


                 2.请求总控器,负责接收所有请求,然后更具请求地址分配给不同的后端控制器(Controller)


                 3.当@WebServlet注解中的url-pattern设置为"/"时,会覆盖默认的DefaultServlet


                 4.所有的web服务器都会由一个默认的Servlet,这个Servlet专门用于处理静态资源


                 5.Tomcat有3个自带的servlet:1.DefaultServlet 2.JspServlet 3.ServletFileUpload


                 6.默认的Servlet会处理静态资源,例如:html、css、js、图片等


                 7.jspServlet: 处理jsp页面


                8.ServletFileUpload: 处理文件上传

package com.xr.servlet;


import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;

/**
 * 多个请求对应多个servlet,简化成多个请求1个servlet处理
 * 请求总控器,负责接收所有请求,然后更具请求地址分配给不同的后端控制器(Controller)
 * 当url-pattern设置为"/"时,会覆盖默认的DefaultServlet,
 * 所有的web服务器都会由一个默认的Servlet,这个Servlet专门用于处理静态资源
 * Tomcat有3个自带的servlet:1.DefaultServlet 2.JspServlet 3.ServletFileUpload
 * 默认的Servlet会处理静态资源,例如:html、css、js、图片等
 * jspServlet: 处理jsp页面
 * ServletFileUpload: 处理文件上传
 * @author xr
 * @Date 2024/5/14 9:09
 */

@WebServlet("/")
public class DispatcherServlet extends HttpServlet {

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

        System.out.println("进入了统一处理请求的Servlet");
        // 获取请求路径
        String uri = req.getRequestURI();
        System.out.println("请求路径:"+uri);
    }


}

8.2 测试处理请求

8.2.1

        先清理,在安装

8.2.2

        在webapp-demo中刷新mvc-framework依赖

        

8.2.3

        在webapp文件创建WEB-INF文件,在创建web.xml文件配置Tomcat默认的Servlet

作用:

        将多个请求统一交给该Serlvet处理

web.xml文件具体配置:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <!--配置DispatcherServlet,覆盖默认的Servlet类,将所有请求交给该Servlet类处理-->
    <servlet>
        <servlet-name>dispatcherServlet</servlet-name>
        <servlet-class>com.xr.servlet.DispatcherServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

</web-app>

8.2.4

        启动测试项目,测试!!!

启动:

注意:

我们原本在测试项目中创建了一个Servlet,而我们在mvc-framework中也创建了一个Servlet类(DispatcherServlet),所以该测试项目中一共有两个Servlet,他们两个互不相关,都可以访问

访问测试项目创建的Serlvet:

访问静态资源页面:

访问其他任意请求:

控制台输出:

访问任意请求:

控制台输出:

发现:

                除了自己创建的Servlet类和静态资源页面,我们不管访问其他任意请求都会被DispatcherServlet(默认Servlet)类处理

多个请求被一个Servlet处理完成


8.3  将接收的请求进行处理

上面已经解决了多个请求需要创建了多个Servlet进行处理,但是还没有做具体的处理。

------------------------------------------------------待更新------------------------------------------------------------------

  • 17
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

꧁惜若༒奔已꧂

争取做到更好

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值