轻量级仿 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;

    @Override
    public 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 {
    @Before
    public void initDb() {
        DataBaseConnection.initDb();
    }

    @After
    public 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 纯注解配置

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/107307.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

刀片式服务器介绍

大家都知道服务器分为机架式服务器、刀片式服务器、塔式服务器三类&#xff0c;今天小编就分别讲一讲这三种服务器&#xff0c;第二篇先来讲一讲刀片式服务器的介绍。 刀片式服务器定义&#xff1a;是一种高密度的服务器架构&#xff0c;通过多个独立服务器单元组成&#xff0c…

windows qemu安装飞腾Aarch64 操作系统 亲测

在win7&#xff08;X86架构CPU&#xff09;下使用QEMU虚拟机运行银河麒麟操作系统&#xff08;ARM架构CPU&#xff09; 1、下载并安装QEMU虚拟机软件 https://qemu.weilnetz.de/w64/2020/ 2、准备好ARM银河麒麟操作系统.iso文件 这里是 Kylin-Desktop-V10-Release-2107-ar…

力扣:141. 环形链表(Python3)

题目&#xff1a; 给你一个链表的头节点 head &#xff0c;判断链表中是否有环。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&#xff0c;评测系统内部使用整数 pos 来表示链表尾连接到链表中的…

CentOS 编译安装 nginx

CentOS 编译安装 nginx 修改 yum 源地址为 阿里云 curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repoyum makecache升级内核和软件 yum -y update安装常用软件和依赖 yum -y install gcc gcc-c make cmake zlib zlib-devel openss…

CRM客户管理系统源码 带移动端APP+H5+小程序

CRM客户管理系统源码 带移动端APPH5小程序 开发环境: thinkphp mysql 功能介绍&#xff1a; 1、 办公管理&#xff1a;审批管理、工作报告、日程管理、办公审批、公告管理 2、 客户管理&#xff1a;我的客户、客户列表、成交客户、行业类别、预查、地区列表、客户状态、客…

ubuntu 安装 gnome 安装 xrdp

先安装xrdp 更新 apt-get sudo apt-get update && apt-get upgrade安装图形包 apt-get install xubuntu-desktop安装 xrdp apt-get install xrdp安装 xfce4 apt-get install xfce4配置 xfce4 Add xfce to the xfce desktop window manager autorun by fixing the …

关于网络安全运营工作与安全建设工作的一些思考

以下内容是个人成长过程中对于网络安全运营工作的理解和思考&#xff0c;希望通过这篇文章帮助大家更好的去做安全运营体系化建设&#xff0c;开始吧&#xff01; 文章目录 一、网络安全运营是什么&#xff1f;二、网络安全运营建设阶段第一阶段&#xff1a;设备限制阶段第二阶…

【软考】13. 结构化开发方法

《系统分析与设计概述》 当前系统的物理模型 ——> 当前系统的逻辑模型 ——> 目标系统的逻辑模型 ——> 目标系统的物理模型系统开发的目的&#xff1a;当前系统的物理模型 ——> 目标系统的物理模型 系统设计基本原理 抽象、模块化&#xff08;逐步分解&#xf…

Spark_SQL-DataFrame数据写出以及读写数据库(以MySQl为例)

一、数据写出 &#xff08;1&#xff09;SparkSQL统一API写出DataFrame数据 二、写出MySQL数据库 一、数据写出 &#xff08;1&#xff09;SparkSQL统一API写出DataFrame数据 统一API写法&#xff1a; 常见源写出&#xff1a; # cording:utf8from pyspark.sql import SparkSes…

Maven - no main manifest attribute(SpringBoot 多模块)

问题描述 no main manifest attribute 解决方案 一个主项目下&#xff0c;多个业务模块&#xff0c;假设 starter 模块作为启动器&#xff0c;以及主项目&#xff08;project&#xff09;最外层父 pom.xml 最关键要关注这 2 个 pom.xml&#xff08;starter - pom.xml & p…

【代码随想录】算法训练计划04

1、24. 两两交换链表中的节点 题目&#xff1a; 给你一个链表&#xff0c;两两交换其中相邻的节点&#xff0c;并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题&#xff08;即&#xff0c;只能进行节点交换&#xff09;。 思路&#xff1a; 链表这种题…

结构体指针的引入

概念引入 结构体指针类似于前面学习过的指针&#xff0c;我们先回忆下指针&#xff1a;指针就是地址&#xff0c;指针变量就是存放地址的变量。而结构体也是变量&#xff0c;变量访问有两种方式:1.变量名&#xff0c; 2.地址&#xff0c;之前案例&#xff0c;是用变量名访问。…

大厂面试题-什么是JVM

JVM全称是Java虚拟机&#xff0c;在聊什么是JVM之前&#xff0c;我们不妨看⼀下这张图。 从这张图中可以看出JVM所处的位置&#xff0c;同时也能看出它两个作用&#xff1a; 1、运⾏并管理Java源码⽂件所⽣成的Class⽂件&#xff0c; 2、在不同的操作系统上安装不同的JVM&#…

PostGreSQL模式schema

问题引入 之前在做数据库设计时&#xff0c;经常会忽略schema模式&#xff0c;直接在数据库下的public模式下建立各类数据表。如果数据表命名不够规范&#xff0c;后期寻找某张表时就会比较麻烦。通过 所幸&#xff0c;PostgreSQL 的模式schema管理&#xff0c;可以对这个问题…

正点原子嵌入式linux驱动开发——Linux INPUT子系统

按键、鼠标、键盘、触摸屏等都属于输入(input)设备&#xff0c;Linux内核为此专门做了一个叫做input子系统的框架来处理输入事件。输入设备本质上还是字符设备&#xff0c;只是在此基础上套上了input框架&#xff0c;用户只需要负责上报输入事件&#xff0c;比如按键值、坐标等…

C++——类和对象(上)

1.面向过程和面向对象初步认识 C语言是面向过程的&#xff0c;关注的是过程&#xff0c;分析出求解问题的步骤&#xff0c;通过函数调用逐步解决问题。 例如手洗衣服 C是基于面向对象的&#xff0c;关注的是对象&#xff0c;将一件事情拆分成不同的对象&#xff0c;靠对象之间…

计算机视觉的相机选型

#你一般什么时候会用到GPT?# 目前市面上的工业相机大多是基于CCD&#xff08;ChargeCoupled Device&#xff09;或CMOS&#xff08;Complementary Metal Oxide Semiconductor&#xff09;芯片的相机。一般CCD制造工艺更加复杂&#xff0c;也会更贵一点&#xff01; 1、CCD工…

底层全部重构,小米澎湃OS完整系统架构公布

上周&#xff0c;雷军发文称小米全新操作系统澎湃 OS 正式版已完成封包&#xff0c;将逐步接替 MIUI。而后&#xff0c;又有网友曝光小米澎湃 OS 界面。 今日&#xff0c;雷军再度发表长文预热小米澎湃 OS&#xff0c;正式公布了完整系统架构。 据介绍&#xff0c;从架构设计之…

统计学习方法 决策树

文章目录 统计学习方法 决策树决策树模型与学习特征选择决策树的生成ID3 算法C4.5 的生成算法 决策树的剪枝CART 算法CART 回归树的生成CART 分类树的生成CART 剪枝 统计学习方法 决策树 阅读李航的《统计学习方法》时&#xff0c;关于决策树的笔记。 决策树模型与学习 决策…

归结原理、归结演绎推理

主要内容 归结演绎推理范式子句与子句集将谓词公式转化为子句集命题逻辑鲁宾逊归结原理 归结演绎推理 定理证明的实质是对前提P和结论Q证明P →Q的永真性应用反证法&#xff0c;欲证明P →Q&#xff0c;只要证明 P∧~Q 等价于 F鲁宾逊归结原理对机械化推理有重大突破鲁宾逊归…
最新文章