当前位置: 首页 > news >正文

轻量级仿 Spring Boot=嵌入式 Tomcat+Spring MVC

啥?Spring Boot 不用?——对。就只是使用 Spring MVC + Embedded Tomcat,而不用 Boot。为啥?——因为 Boot 太重了:)

那是反智吗?Spring Boot 好好的就只是因为太重就不用?——稍安勿躁,这里并非说重新写代替 Spring 的轮子,而是继续使用原装的 Spring MVC,进而对其加强升级,——请听我跟你说, 优化后的 Spring MVC 几乎能做到 Spring Boot 的事情,是一个近乎 99% 完成度的平替,而且它更轻量级,何乐不为呢?Yes,让我们试试:Spring Framework without Spring Boot!

为了说明如何打造轻量级的 Spring Boot,本文分为“嵌入式 Tomcat”、“增强 Spring MVC”和“打包/部署”三个小节来介绍。

嵌入式 Tomcat

目的是通过执行main()函数即可启动 Web 程序。在上一篇文章《嵌入式 Tomcat 调校》中已经讨论了如何制定化 Tomcat,但仍未与 Spring 结合。

实际上,从 Spring MVC 时代起就支持通过 Java 注解来配置,代替古老的 XML 方式。笔者在两年之前的文章《Spring MVC 用起来还是很香的》已经介绍过。那时还未摆脱标准 Tomcat 的运行模式,而目前要做的,就是结合嵌入式 Tomcat 与 Spring MVC 两者。

因为是纯手动编码(Programmatically)达成的,所以要了解 Tomcat 加载的生命周期。当为LifecycleState.STARTING_PREP之时,才能有关键的ServletContext ctx对象,以便 Spring 绑定。

在这里插入图片描述
完整代码在这里。

调用例子

一般情况下,要指定的只有 Tomcat 端口和 Context 目录,甚至 Context 目录都可以不传。所以多数情况下你调用 EmbeddedTomcatStarter 的静态方法即可。

另外start() 有 class… 的参数列表,它是个可变长度的数组,表示 Java 配置类,如下例的DemoApp.classDemoConfig.class,第一个 class 是 main 函数的那个类,第二个、第三……第 n 个是带有@Configuration注解的配置类。

import com.ajaxjs.data.sql_controller.ServiceBeanDefinitionRegistry;
import com.ajaxjs.framework.spring.BaseWebMvcConfigure;
import com.ajaxjs.framework.spring.EmbeddedTomcatStarter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;@Configuration
@EnableWebMvc
@ComponentScan("com.ajaxjs.demo")
public class DemoApp extends BaseWebMvcConfigure {public static void main(String[] args) {EmbeddedTomcatStarter.start(8300, DemoApp.class, DemoConfig.class);}
}

配置类是这样的,与 Spring Boot 的无异,还是熟悉的配方。

在这里插入图片描述

增强 SpringMVC

YAML 配置

主流采用 YAML 作为配置文件,properties/xml 文件则不考虑了。在 Spring MVC 中支持 YAML 配置文件,首先引入 yaml 依赖。

<!-- YAML 配置文件 -->
<dependency><groupId>org.yaml</groupId><artifactId>snakeyaml</artifactId><version>1.33</version>
</dependency>

然后初始化加载 YAML。这是封装到框架里面的,位于BaseWebMvcConfigure
在这里插入图片描述
YAML 有个问题,就是没有直接提供静态方法的手段,于是重写PropertySourcesPlaceholderConfigurer.postProcessBeanFactory()方法,获取内部的 Key/Value 结构Properties localProperties,暴露出来给外界获取,传入 key 即可得到的配置 value。源码如下:

package com.ajaxjs.framework.spring;import com.ajaxjs.util.convert.ConvertBasicValue;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;import java.io.IOException;
import java.util.Properties;/*** PropertySourcesPlaceholderConfigurer 是一个由 Spring 提供的用于解析属性占位符的配置类,* 它没有提供直接获取私有属性 localProperties 的公开方法。但是,可以通过以下步骤获取 localProperties 的值*/
public class CustomPropertySources extends PropertySourcesPlaceholderConfigurer {private Properties localProperties;@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {super.postProcessBeanFactory(beanFactory);try {localProperties = mergeProperties();} catch (IOException e) {throw new RuntimeException(e);}}public Properties getLocalProperties() {return localProperties;}/*** 获取配置值** @param key 配置 key* @return 配置值*/public static String getConfig(String key) {CustomPropertySources bean = DiContextUtil.getBean(CustomPropertySources.class);assert bean != null;Object o = bean.getLocalProperties().get(key);if (o != null)return o.toString();else {System.err.println("找不到 " + key + "配置");return null;}}public static <T> T getConfig(String key, Class<T> clz) {String value = getConfig(key);return ConvertBasicValue.basicCast(value, clz);}
}

上述静态的方法就是获取配置的手段。

用户配置

用户来说,具体操作就是在 resources 目录下设置application.yml文件。

在这里插入图片描述

其他

另外,这里有个大神开源的作品 spring-config-ext,也是在 MVC 中实现类似 Boot 的配置,号称“spring mvc config simple extension, make it have the same config abilities as spring boot does.”,大家有兴趣的可去看看。

运行 Web 页面

尽管打包为 JAR 包了,都是弄 API 接口了,也就没什么理由存放那些 Web 页面了。但某些情况下,作为一个前-前端人员,还是觉得有必要打开 JSP 渲染的,可以访问一下 html/css/js/jsp 资源。

按照 Servlet 3.0 规范,有一块地方是专门存放 html/css/js 甚至 JSP 的,即META-INF\resources,在工程的资源目录下,即\src\main\resources\META-INF\resources。所以,以前是在src\main\webapp下面的所有文件,移动到\src\main\resources\META-INF\resources目录下。

在这里插入图片描述

新建一个 index.jsp 设置内容<%=88888%>即可测试之。

存在问题:这个不像以前在 Eclipse 下可以修改了 JSP 重新编译,在 IDEA 下没法那样子玩了,所以每次修改后要手动重启服务器,非常麻烦。如果有懂行的朋友知道怎么搞自动重启,请多告知!

单元测试

单元测试一般都有这两个类,一个是配置,一个是基类。

配置很简单,但是你要修改扫描的包名,@ComponentScan那里的。

package com.ajaxjs.iam.server;import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;@Configuration
@ComponentScan("com.ajaxjs.iam.server")
public class TestConfig {}

基类是个抽象类,主要是绑定配置类和数据库连接跟关闭,方便你不用每次都手动连接数据库。

package com.ajaxjs.iam.server;import com.ajaxjs.data.jdbc_helper.JdbcConn;
import com.ajaxjs.framework.spring.filter.dbconnection.DataBaseConnection;
import org.junit.After;
import org.junit.Before;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;@ContextConfiguration(classes = TestConfig.class)
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
public abstract class BaseTest {@Beforepublic void initDb() {DataBaseConnection.initDb();}@Afterpublic void closeDb() {JdbcConn.closeDb();}
}

一个例子。

在这里插入图片描述

打包与部署

Maven 打包

我们希望打出哪个环境的包,就只需要包含这个环境的配置文件即可,不想包含其他环境的配置文件,这时候可以直接在 maven 中使用 profiles 和 resources 来配置,打包时使用mvn package -P dev即可。

<profiles><!--开发环境--><profile><id>dev</id><properties><spring.profiles.active>dev</spring.profiles.active></properties><activation><activeByDefault>true</activeByDefault></activation></profile><!--测试环境--><profile><id>test</id><properties><spring.profiles.active>test</spring.profiles.active></properties></profile><!--生产环境--><profile><id>prod</id><properties><spring.profiles.active>prod</spring.profiles.active></properties></profile>
</profiles>
<build><resources><resource><directory>src/main/resources</directory><filtering>false</filtering></resource><resource><directory>src/main/resources.${spring.profiles.active}</directory><filtering>false</filtering></resource></resources>
</build>

原理如下:

maven 在构建项目时,默认是把main/resoures目录作为资源文件所在目录的,现在我们在main/conf目录下也存放了资源文件(即application.properites文件),因此需要告诉 maven 资源文件所在的目录有哪些,通过 build 元素中增加 resources 元素就可以达到这一目的。这里告诉 maven 有两个地方存在资源文件,一个是默认的 resources 目录,另一个是在src/main/conf/${env}目录下,而${env}引用的是上面 properties 元素中定义的 env 的值,而它的值引用的又是spring.profiles.active的值(其值为 dev、test 和 online 中的一个),因此,目录要么是src/main/conf/dev,要么是src/main/conf/test,要么是main/conf/online,这最终取决于参数spring.profiles.active的值。因此,根据参数spring.profiles.active的值的不同,在构建打包时最终会选择 dev、test 和 online 这三个目录中的一个中的application.properties打包到项目中来。

将应用打成一个 Fat Jar 的方式,可以用 Spring 的:

<plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><version>1.3.3.RELEASE</version><configuration><mainClass>com.demo.proj.Main</mainClass></configuration><executions><execution><phase>package</phase><goals><goal>repackage</goal></goals></execution></executions>
</plugin>

Profiles

在实际使用环境中,我们同一个应用环境可能需要在不同环境运行(开发、测试、生产等),每个环境的参数都有可能不同(连接参数、日志级别等),使用 Profiles 可以将不同环境下的参数进行拆分,并指定加载。

IDEA 配置,在 src 目录下创建 profiles 目录,安排如下图的配置文件。

在这里插入图片描述
然后 Maven Profile 打勾即可。

在这里插入图片描述

启动参数

开始以为要 run 配置中加入--spring.profiles.active=dev参数,其实不用,还是在 IDEA 里面选 profile 打勾即可。

小结

参考

  • SpringMVC 纯注解配置

相关文章:

轻量级仿 Spring Boot=嵌入式 Tomcat+Spring MVC

啥&#xff1f;Spring Boot 不用&#xff1f;——对。就只是使用 Spring MVC Embedded Tomcat&#xff0c;而不用 Boot。为啥&#xff1f;——因为 Boot 太重了&#xff1a;&#xff09; 那是反智吗&#xff1f;Spring Boot 好好的就只是因为太重就不用&#xff1f;——稍安勿…...

笔记Kubernetes核心技术-之Controller

2、Controller 2.1、概述 在集群上管理和运行容器的对象&#xff0c;控制器(也称为&#xff1a;工作负载)&#xff0c;Controller实际存在的&#xff0c;Pod是抽象的&#xff1b; 2.2、Pod和Controller关系 Pod是通过Controller实现应用运维&#xff0c;比如&#xff1a;弹…...

Azure云工作站上做Machine Learning模型开发 - 全流程演示

目录 本文内容先决条件从“笔记本”开始设置用于原型制作的新环境&#xff08;可选&#xff09;创建笔记本开发训练脚本迭代检查结果 关注TechLead&#xff0c;分享AI全维度知识。作者拥有10年互联网服务架构、AI产品研发经验、团队管理经验&#xff0c;同济本复旦硕&#xff0…...

前端 : 用html ,css,js写一个你画我猜的游戏

1.HTML&#xff1a; <body><div id "content"><div id "box1">计时器</div><div id"box"><div id "top"><div id "box-top-left">第几题:</div><div id "box…...

Illustrator 2024(AI v28.0)

Illustrator 2024是一款功能强大的矢量图形编辑软件&#xff0c;由Adobe公司开发。它是设计师、艺术家和创意专业人士的首选工具&#xff0c;用于创建和编辑各种矢量图形、插图、图标、标志和艺术作品。 以下是Adobe Illustrator的主要功能和特点&#xff1a; 矢量图形编辑&…...

【Git企业开发】第二节.Git 的分支管理

作者简介&#xff1a;大家好&#xff0c;我是未央&#xff1b; 博客首页&#xff1a;未央.303 系列专栏&#xff1a;Git企业级开发 每日一句&#xff1a;人的一生&#xff0c;可以有所作为的时机只有一次&#xff0c;那就是现在&#xff01;&#xff01;&#xff01;&#xff0…...

第三章认识Node.js模块化开发

目录 认识Node.js 概述 作用 基本使用 Node.js的运行 Node.js的组成 Node.js的语法 Node.js全局对象 认识模块化开发 概述 场景 特点 模块成员的导入和导出 Node.js 模块化语法 导入模块 导出模块 ES6 模块化语法 导入模块 导出模块 项目 认识Node.js 概述…...

扩展Nginx的无限可能:掌握常见扩展模块和第三方插件的使用方法

Nginx是一款高性能的开源Web服务器和反向代理服务器。它具有模块化的架构&#xff0c;可以通过扩展模块和插件来增强其功能。在本文中&#xff0c;我将围绕Nginx的扩展模块和插件进行讲解&#xff0c;并提供一些常见的扩展模块和第三方插件的示例。 一、Nginx扩展模块 Nginx的…...

centos遇到的问题

lsof -i :8091 > 查看这个端口的线程 lsof &#xff1a; list open files 列出打开文件 -i &#xff1a; internet linux检测系统进程和服务&#xff1a; top &#xff1a; 实时监视系统的进程和资源的利用情况htop &#xff1a; top的增强版 问题&#xff1a; -bash: …...

本机spark 通idea连接Oracle的坑

1. 报错&#xff1a;Exception in thread "main" java.lang.NoSuchMethodError: scala.Product.$init$(Lscala/Product;)V 查询网上资料&#xff0c;是idea引入的scala运行环境版本与idea默认的scala版本不一样 也就是写的项目中的pom的spark版本与idea默认的版本不…...

网络协议--DNS:域名系统

14.1 引言 域名系统&#xff08;DNS&#xff09;是一种用于TCP/IP应用程序的分布式数据库&#xff0c;它提供主机名字和IP地址之间的转换及有关电子邮件的选路信息。这里提到的分布式是指在Internet上的单个站点不能拥有所有的信息。每个站点&#xff08;如大学中的系、校园、…...

计算机视觉注意力机制小盘一波 (学习笔记)

将注意力的阶段大改分成了4个阶段 1.将深度神经网络与注意力机制相结合&#xff0c;代表性方法为RAM ⒉.明确预测判别性输入特征&#xff0c;代表性方法为STN 3.隐性且自适应地预测潜在的关键特征&#xff0c;代表方法为SENet 4.自注意力机制 通道注意力 在深度神经网络中…...

LVS+keepalive高可用集群

keepalive简介 keepalive为LVS应用延伸的高可用服务。lvs的调度器无法做高可用。但keepalive不是为lvs专门集群服务的&#xff0c;也可以为其他的的代理服务器做高可用。 keepalive在lvs的高可用集群&#xff0c;主调度器和备调度器(可以有多个) 一主两备或一主一备。 VRRP: k…...

Thread 和 Runnable 的区别

Thread 和 Runnable 接口的区别有四个&#xff1a; Thread 是一个类&#xff0c;Runnable 是接口&#xff0c;因为在 Java 语言里面的继承特性&#xff0c;接口可以支持多继承&#xff0c;而类只能单一继承。所以如果在已经存在继承关系的类里面要实现线程的话&#xff0c;只能…...

图神经网络和分子表征:5. Completeness

大家都知道 “两点确定一线&#xff0c;三点确定一平面”&#xff0c;那么多少个变量可以确定一个分子呢&#xff1f;这是最近顶刊们热烈讨论的话题。 &#xff08;据笔者不完全统计&#xff09;最早在 SphereNet &#xff08;2022 ICLR&#xff09;论文里&#xff0c;摘要上就…...

css-渐变色矩形

效果图&#xff1a; 代码&#xff1a; html: <!DOCTYPE html> <html><head><meta charset"utf-8"><meta name"viewport" content"initial-scale1.0, user-scalableno" /><title></title><link …...

使用easypoi-spring-boot-starter 4.1.1导入excel报错NoSuchMethodError和NoSuchMethodError

前言 使用easypoi进行excel的导入遇到的错误以及解决办法 easypoi项目地址&#xff1a;https://gitee.com/lemur/easypoi easypoi的Maven依赖&#xff1a; <dependency><groupId>cn.afterturn</groupId><artifactId>easypoi-spring-boot-starter<…...

matlab中类的分别之handle类和value类——matlab无法修改类属性值的可能原因

写在之前&#xff08;吐槽&#xff09; 最近由于变化了一些工作方向&#xff0c;开始需要使用matlab进行开发&#xff0c;哎哟喂&#xff0c;matlab使用的我想吐&#xff0c;那个matlab编辑器又没代码提示&#xff0c;又没彩色&#xff0c;我只好用vscode进行代码编辑&#xf…...

3. t2t_vit inference

前言 对vit 进行fp16推理 参考链接: https://github.com/open-mmlab/mmpretrain/tree/master/configs/t2t_vit run code : https://mmclassification.readthedocs.io/en/latest/getting_started.html#inference-and-test-a-dataset https://mmclassification.readthedo…...

SpringMVC Day 05 : Spring 中的 Model

前言 欢迎来到 SpringMVC 系列教程的第五天&#xff01;在之前的教程中&#xff0c;我们已经学习了如何使用控制器处理请求和返回视图。今天&#xff0c;我们将深入探讨 Spring 中的 Model。 在 Web 应用程序开发中&#xff0c;数据的传递和展示是非常重要的。SpringMVC 提供…...

redis6.0源码分析:字典扩容与渐进式rehash

文章目录 字典数据结构结构设计dictType字典类型为什么字典有两个哈希表&#xff1f;哈希算法 扩容机制扩容前置知识字典存在几种状态&#xff1f;容量相关的关键字段定义字典的容量都是2的幂次方 扩容机制字典什么时候会扩容&#xff1f;扩容的阈值 & 扩容的倍数哪些方法会…...

【C++迭代器iterator】

迭代器 i t e r a t o r 迭代器iterator 迭代器iterator 在 容器 v e c t o r 容器vector 容器vector 中的使用 迭代器 i t e r a t o r 迭代器iterator 迭代器iterator 一般使用在 容器 v e c t o r 容器vector 容器vector 的 遍历 遍历 遍历&#xff0c;充当 遍历指针 遍…...

基于群居蜘蛛算法的无人机航迹规划

基于群居蜘蛛算法的无人机航迹规划 文章目录 基于群居蜘蛛算法的无人机航迹规划1.群居蜘蛛搜索算法2.无人机飞行环境建模3.无人机航迹规划建模4.实验结果4.1地图创建4.2 航迹规划 5.参考文献6.Matlab代码 摘要&#xff1a;本文主要介绍利用群居蜘蛛算法来优化无人机航迹规划。 …...

火爆全网,Python+Requests+Pytest+YAML+Allure实现接口自动化测试(附源码)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 本项目实现接口自…...

【深度学习】【NLP】如何得到一个分词器,如何训练自定义分词器:从基础到实践

文章目录 什么是分词&#xff1f;分词算法使用Python训练分词器步骤1&#xff1a;选择分词算法步骤2&#xff1a;准备训练语料步骤3&#xff1a;配置分词器参数步骤4&#xff1a;训练分词器步骤5&#xff1a;测试和使用分词器 代码示例&#xff1a;使用SentencePiece训练分词器…...

线程池的线程回收

线程池 线程池里面分为核心线程和非核心线程。 核心线程是常驻在线程池里面的工作线程&#xff0c;它有两种方式初始化&#xff1a; 向线程池里面添加任务的时候&#xff0c;被动初始化 主动调用 prestartAllCoreThreads 方法 当线程池里面的队列满了的情况下&#xff0c;为了增…...

【2023.10.25练习】数据库-函数1

任务描述 本关任务&#xff1a;编写函数fun_1完成学生选课操作。输入参数&#xff1a;学号、课程名&#xff0c;函数返回值&#xff1a;操作结果。 如果该生已有该门课程的选修记录&#xff0c;则函数返回-1&#xff1b;如果该门课程无先修课&#xff0c;则将选课信息添加到选…...

基于水循环算法的无人机航迹规划-附代码

基于水循环算法的无人机航迹规划 文章目录 基于水循环算法的无人机航迹规划1.水循环搜索算法2.无人机飞行环境建模3.无人机航迹规划建模4.实验结果4.1地图创建4.2 航迹规划 5.参考文献6.Matlab代码 摘要&#xff1a;本文主要介绍利用水循环算法来优化无人机航迹规划。 1.水循环…...

JVM调优(10)JVM的运行时数据区

一、概述 对于 C C 来说&#xff0c;在内存管理领域&#xff0c;JVM既拥有最高的权利&#xff0c;但是同时他们又是从事最基础工作的劳动人员&#xff0c;因为他们担负着每一个对象从开始到结束的维护责任。而对于Java来说&#xff0c;再虚拟机自动内存管理的帮助下&#xff0…...

Python网络爬虫介绍

视频版教程&#xff1a;一天掌握python爬虫【基础篇】 涵盖 requests、beautifulsoup、selenium 什么是网络爬虫&#xff1f; 网络爬虫&#xff08;又称为网页蜘蛛&#xff0c;网络机器人&#xff0c;在FOAF社区中间&#xff0c;更经常的称为网页追逐者&#xff09;&#xff…...