内嵌服务器
创建项目
打开idae
创建项目
- 等待项目构建成功后,我们来到Pom.xml 添加下方的Jar包
配置类
/**
* @program: CXM-MVC-Framework
* @description: Web默认配置信息类
* @author: 程序梦
* @create: 2024-05-13 15:28
**/
public class ServerConfig {
/**
* 这里的所有的字段命名必须与yml中子节点名字相同
*/
private Integer port = 8080; // 端口号
private String contextPath = "/" //web服务器默认访问路径
private String webRoot ="src/main/webapp" // web项目静态资源目录
/**
Get Set 方法
*/
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;
}
}
- 上面的是 Web 服务器默认配置信息类
- 本类只对Web服务器配置一些信息类
----手动分割线
Configure
/**
* @program: CXM-MVC-Framework
* @description: 配置对象(也是所有配置的根)
* @author: 程序梦
* @create: 2024-05-13 15:28
* 作用是将yml解析的数据封装到这个对象中
**/
public class Configure {
// 这里字段命名不需要与yml文件中的字段一致
private ServerConfig server = new ServerConfig();
// 扫描控制的Class集合,使用 set 集合 不允许重复
private Set<Class<?>> classes;
// 设置路径的Set方法
public void setClasses(Set<Class<?>> classes) {
this.classes = classes;
}
/**
* 执行扫描,根据启动类所在包以及所有的子包开始扫描
*
* @param mianClass
*/
public void scan(Class<?> mianClass) {
/**
* 使用 hutool工具包中的ClassUtil工具类中的scanPackage方法去扫描启动类所在包以及所有的子包
*/
classes = ClassUtil.scanPackage(mianClass.getPackageName());
// 开始过滤,只要Controller注解的类
}
// get set 方法
public ServerConfig getServer() {
return server;
}
public void setServer(ServerConfig server) {
this.server = server;
}
public Set<Class<?>> getClasses() {
return classes;
}
}
- 上面是配置对象是所有配置的根,可以不止配置这些类,还可以配置其他信息
- 作用是将yml解析的数据封装到这个对象中。
内嵌容器
/**
* @description: 抽象的容器接口
* @author: 程序梦
* @create: 2024-05-14 08:38
**/
public interface WebServer {
/**
* 抽象的启动方法,不同的容器有不同启动方法
* @param mainClass
* @throws Exception
*/
void runServer(Class<?> mainClass)throws Exception
}
- 一、之前第一版的时候,我们是直接在 TomCatServer 当中写启动方法,为什么要抽象一个抽象接口呢,因为 Web 服务器不止一个如:Jetty WildFly 等服务器,他们的启动方法式并不相同
- 二、定义接口目的: 定义一个统一的接口,可以为很多 Web服务器实现统一的标准的规范
- 三、五大设计原则
- 单一职责:接口只有一个 runServer 方法,表明了这个方法值做启动Web服务器
- 依赖倒置: runServer 方法只接收一个Class<?>参数,而不是具体服务器实现类,使用实现此接口的类没有直接和WebServer直接产生依赖,通过抽象来进行依赖。
- 开辟原则: 接口对扩展是开方法的,因为可以有很多种 Web 服务器可以实现扩展此接口,不用去修改WebServer
- 接口隔离: 此接口只有一个 RunServer 提供启动服务器的抽象方法
- 里氏替换: 实现 WebServer 的接口必须遵守约定,在使用 WebServer 的方法都可以直接替换。
- 四、使用策略模式
- 因为不同 Web 服务器启动方式不一样,但是此接口提供了启动方法,tomcat 实现了WebServer 去覆写 它的策略启动类就好了,jetty 去复写runServer就好了
-------- 分割线
AbstractWebServer
/**
* @program: CXM-MVC-Framework
* @description: 模版方法, 抽象类, 实现WebServer接口, 实现runServer方法, 解析yml文件, 启动容器, 不同容器有不同的实现
* @author: 程序梦
* @create: 2024-05-14 08:41
* 模版方法
**/
public abstract class AbstractWebServer implements WebServer {
/**
* 模版方法
* 做扫描用户给的包路径
*
* @throws Exception
*/
public void RunServer(Class<?> mainClass) {
// 1.解析 yml 文件返回Configure对象
Configure configure = initConfigure(mainClass);
// 启动容器
try {
// 符合依赖倒置、里氏替换,
strataServer(configure)
} catch (Exception e) {
// 降级重抛异常
throw new ExecutionException("启动容器失败",e);
}
}
/**
*
* 解析 yml 文件
* 为什么要解析 yml 文件 因为用户在yml文件填写了Web服务器基本配置信息,我们要通过yml 解析工具去获取基本配置,因为Web服务器的配置基本一致
*/
private Configure initConfigure(Class<?> mianClass) {
// 获取用户填写yml文件的绝对路径
// 我们去读用户的项目的 编译后的clases下的yml文件
String configFilePath = ClassUtil.getClassPath() +"application.yml";
System.out.println(configFilePath + "yml绝对路径");
// 创建配置对象为局部变量
Configure configure
// 判断 yml 文件是否存在,如果不存在直接创默认的配置
if(FileUtil.exist(configFilePath)) {
// 解析 yml 并赋值
configure = YamlUtil.loadByPath("application.yml",Configure.class);
// 判断 configure 上面内容是否为空,如果为空,则使用默认的配置
if(configure == null) {
// 创建默认的配置
configure = new Configure();
}
}else {
Configure =new Configure();
}
// 执行扫描(读取完 yml文件后,开始扫描使用该框架的项目所有包)
configure.scan(mainCalss);
// 返回配置
return configure;
}
}
/**
* 抽象的启动方法,让子类去实现此方法(不同Web服务器启动方式不同)
* Params: configure -- 配置类
*/
public abstract void strataServer(Configure configure);
- 代码解释 :
- runServer 方法解析: 此方法调用 initConfigure方法对 yml 文件进行解析, 并调用strataServer 方法去启动容器。
- initConfigure 方法解析:此方法接收启动类的 class, 获取到用户项目的 yml 的绝对路径,创建Configure 配置类的成员变量,然后判断用户的 yml 是否存在
- 存在:使用 yml解析工具库对yml进行解析,并将yml填写的配置信息赋值给我们的 Configure 对象,然后判断 Configure 是否为空
- 为空: 创建 Configure 对象
- 不为空: 执行对用户所有项目目录进行扫包存入我们的 Configure 对象里面的Set集合,
- 返回配置给到 runServer 方法
- 不存在:则创建默认的 Configure 配置对象赋值给我们之前创建的成员变量 Configure
- 总结: 本类实现了 WebServer 并覆写 RunServer 这个方法用做Web服务器一些基本步骤RunServer 这个方法是启动Web服务器的方法,但是不同 Web 服务器有着不同的启动方式,所以这里将 RunServer 当作模版方法,将Web服务器相同的工作进行封装,如解析 yml 文件读取配置、和全局扫包 strataServer 方法让子类实现此方法做自己启动方式。
- 设计模式: 使用 模版方法模式:在 AbstractWebServer 抽象类中,定义了模版方法,将一些通用的步骤封装起来,留下一些不同步骤给子类去实现,具体 Web 服务器 通过 strataServer 这个抽象方来实现具体 Web 服务器去实现,可以不违反开辟原则下去进行扩展。
- 总结: 这个抽象类实现了一个通用的 Web 服务器框架,并将一些通用的功能使用模板方法设计模式进行了封装,以便子类可以根据具体的需要进行扩展和实现。未来可以根据需求扩展新的 Web 服务器实现,并且通过模板方法模式,保证了代码的可扩展性和可维护性。
------------
TomCatServer
/**
* @program: CXM-MVC-Framework
* @description: TomCat容器
* @author: 程序梦
* @create: 2024-05-13 11:32
**/
public class TomCatServer extends AbstractWebServer {
public void strataServer(Configure configure) {
// 实例化 TomCat
Tomcat tomcat = new Tomcat();
// 设置端口
tomcat.setPort(configure.getServer().getPort());
// 将静态资源目录构成绝对路径
String WebRoot = Configure.getServer().getWebRoot();
System.out.println(WebRoot + "静态资源目录绝对路径");
// 获取项目的绝对路径
WebRoot = FileUtil.getWebRoot().getAbsoultePath()+ File.separator+WebRoot;
// 设置 contextPath和web资源目录,返回一个Context上下文对像 添加一个Web应用程序
Context context = tomcat.addWebapp(configure.getServer().getContextPath(),WebRoot);
System.out.println("上下文对象" + context);
// 设置应用程序的编译路径,这样容器启动时会从classPath下查找带有Servlet注解的组件
// 列如:@WebServlet @WebFilter @WebListener
// 我们可以把WebResourceRoot 当作一个容器,这里存放成所有资源
WebResoutecRoot resourceRoot = new StandardRoot(context);
// 构建DirResourceSet 对象 这个对象要负责将资源分类:列如查找@WebServlet @WebFilter @WebListener,处理完后,赋值给 resourceRoot
DirResourceSet resourceSet = new DirResourceSet(resourceRoot,"/WEB-INF/classes",ClassUtil.getClassPath(),"
/");
// 将资源放回容器中
resourceRoot.addPostResources(resourceSet);
// 将 ResourceRoot 设置到Tomcat上下文对象的context 中,Tomcat 会根据这些资源查照找 @WebServlet @WebFilter @WebListener 的组件,也会去加载一些其他操作
context.setResources(resourceRoot);
// 获取 Servlet 上下文路径,并配置对象保存到上下文对象中便于在DispatcherServlet中获取
context.getServletContext().setAttribute("configure", configure);
// 获取 tomcat 连接
tomcat.getConnector();
try {
// 启动 Tomcat
tomcat.start();
// 阻塞 Tomcat,保存运行状态
tomcat.getServer().await();
} catch (Exception e) {
e.printStackTrace();
}
}
}
- 代码解释:上面代码只做四件事情
- 1、设置 contextPath和Web资源路径
- 2、设置应用程序的编译路径,这样容器启动时会从classPath下查找带有Servlet注解的组件
- 3、将配置类设置到我们的作用域给 SpringMvc 的 重要组件DispatcherServlet使用
- 4、启动Tomcat服务器
- 问题
- 为什么不直接实现WebServer去实现AbstractWebServer服务器呢?
- 1、不同的容器有不同的方法
- 2、其他服务器可以直接实现此接口来进行扩展,也满足于策略模式
- 3、因为不同服务器的前期准备工作不同,所以AbstractWebServer有一个模版方法去将相同封装在一起,不同交给子类实现
- 4、因为不符合五依赖倒置原则,因抽象不应该依赖细节,细节应该依赖抽象
- 设计模式
- 模版方法:对不同服务器要做相同的事情进行封装,将不同事情使用一个抽象方法,然后交给子类进行实现
- 策略模式:因为不同服务器有不同启动方式,所以交给其他服务器去实现去扩展
我们内嵌容器已经搞定,接下来就是实现SpringMvc啦!
等待更新大家如果觉得样式觉得变扭我把pdf发出来
评论
问答助学
相关内容
0个评论
全部评论
点击登录,快来和大家讨论吧~
表情
图片
暂无评论
