• 从使用传统Web框架到切换到Spring Boot后的总结


    1、前言

    其实我接触 Spring Boot 的时间并不长,所以还算一个初学者,这篇文章也算是我对 Spring Boot 学习以及使用过程中的复盘,如果文章出现描述错误或表达不清晰的地方,欢迎大家在评论区留言互动。

    没想到 Spring Boot 这两年竟然普及的这么快, 两年前刚毕业的时候,因为待的是二线城市的小公司,公司的技术栈并不追求新颖,而是追求稳定、能用就行的理念,所以项目上就一直使用传统的 SSM、SSH。

    当搭建后端框架时,需要手动添加 Maven 配置、各种 XML 配置,反正就是一个项目创建就要配置一遍,从我两年前写的这篇 SSM框架搭建,就可以看出该过程是有多么的繁琐,甚至有时候因为配置错误以致于一上午就又可以愉快的划水了...

    而项目部署时也是头大,首先需要安装 Tomcat,然后将项目编译打包成 war 包,再将 war 包放在 Tomcat 的 webapp 目录下部署运行,这个过程就觉得很不方便...

    汇总一下构建一个传统项目需要的步骤:

    • 配置 web.xml,加载 Spring 和 Spring MVC
    • 配置数据库连接、配置 Spring 事务
    • 配置加载配置文件的读取,开启注解
    • 配置日志文件
    • Redis、MQ 等等 …
    • 配置完成之后部署 Tomcat 调试
    • 可能还需要考虑各个版本的兼容性,jar 包冲突的各种可行性。

    因为如上种种问题,当一接触到 Spring Boot 后就被它简单的操作吸引了...

    真羡慕现在的小伙伴,一上来就是用的 Spring Boot 了~ 不用再像我那会一样。

    2、Spring Boot 解决的问题

    Spring Boot 的出现大大简化了传统 Web 框架的搭建过程,提高了开发效率,Spring Boot 总结后有以下几个特点:

    • 可以快速的创建 Spring 应用。
    • 使用嵌入式的Servlet容器,应用无需打成WAR包
    • 整合了大量第三方框架,做到开箱即用。
    • 约定大于配置。

    这几个特点基本上也是面试 Spring Boot 时常问的,也正是因为这几个特点让之前繁琐的搭建过程变的简单。

    我们都知道,特点并不是解决问题的关键,所以我们要了解,Spring Boot 到底是如何解决问题的。

    2.1、自动配置

    之前我们使用 XML 方式时,有相当大的部分就是对 Bean 进行初始化并配置,比如下面这一段就是配置数据库连接信息:

    <!-- 配置 数据源 -->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver" />
        <property name="url" value="jdbc:mysql://10.211.55.4:3306/test" />
        <property name="username" value="root" />
        <property name="password" value="123456" />
    </bean>

    而使用 Spring Boot 后,会根据某些约定的规则对所有配置的 Bean 进行初始化,也就是约定优于配置,然后有的小伙伴就会问,那么什么是约定优于配置呢?

    约定优于配置可以这样理解:

    1. 开发人员仅需要规定应用中不符合约定的部分。
    2. 在没有规定配置的地方,采用默认配置

    举例说明:

    不符合规定的部分:例如,如果模型中有个名为 User 的类,那么数据库中对应的表就会默认命名为 user。只有在偏离这一约定时,例如将该表命名为 “user_info”,才需写有关这个名字的配置。

    规定默认配置的地方:

    1. Maven 的目录结构。项目创建后默认有 resources 文件夹,用于存放资源配置文件。默认的编译生成的类都在targe文件夹下面。
    2. Spring Boot默认的配置文件必须是,也只能是后缀名为 yml 或者 properties 的文件,且名称唯一。
    3. application.yml中默认的属性。数据库连接信息必须是以 spring: datasource: 为前缀;再就是其他环境配置,比如端口号、请求路径等,后面单独写一篇文章关于默认配置文件的。

    是否对这个 yml 或者 properties 文件中配置了信息就实现了配置有点好奇?

    我们还是以数据库连接信息为例:在 Spring Boot 中有一个 DataSourceAutoConfiguration 配置类,这个类会自动查找 application.yml 或者 properties 文件里的 spring.datasource.* 路径,然后相关属性会自动配置到数据源中。这个过程就属于约定大于配置,如果感兴趣的小伙伴可以进入这个类看看。

    2.2、内嵌容器

    Spring Boot 应用程序可以不用部署到外部容器中,比如 Tomcat。Spring Boot 应用可以直接通过 Maven 命令将项目编译成可执行的 jar 包,通过 java -jar 命令启动即可,非常方便。

    怎么不需要 Servlet 容器就启动起来了呢?

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    只要加上这个依赖,Spring Boot 就帮我们默认内嵌了 Tomcat,当然同样也支持修改,比如可以使用 jetty(见标签7修改默认内置的Tomcat容器)。

    内嵌就内嵌了吧,简单看一下 Tomcat 在 Spring Boot 中是如何启动的。

    可以看一下我写的这篇文章:https://www.cnblogs.com/niceyoo/p/14019428.html

    2.3、应用监控

    应用监控是项目部署中必不可少的环节,以前我们怎么知道系统实际运行的情况呢?

    需要我们人为的进行运维监控,比如对 cpu、内存、数据库连接等监控。如果是对系统接口,那么可能会单独写个测试接口来判断当前应用的健康程度,当然,大部分情况接口只要返回200就认为当前应用是健康的。

    但是这种情况会存在很大的问题,首先前者会浪费人力资源,后者则因为固定接口形式无法真正意义上判断应用的健康状态。

    而 Spring Boot 中提供了一个用于监控和管理自身应用信息的模块—Actuator,通过 Actuator,可以实现对程序内部运行情况进行监控,比如 Bean 加载情况、环境变量、日志信息、线程信息等。当然也可以自定义跟业务相关的监控,通过 Actuator 的端点信息进行暴露。这个有点 DevOps 的意思。

    如何集成到 Spring Boot 中呢?

    只需要在 pom.xml 中添加如下依赖即可:

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>

    应用启动则直接可以访问:

    • http://localhost:port/actuator 查看所有端点信息。
    • http://localhost:port/actuator/env 查看该应用全部环境属性。

    application配置文件中可配置的参数:

    management:
      endpoints:
        web:
          # actuator的访问路径,替换默认/actuator
          base-path: /monitor
          # 设置是否暴露端点 默认只有health和info可见
          exposure:
            # include: env   # 方式1: 暴露端点env,配置多个以,隔开
            include: "*"     # 方式2: 包括所有端点,注意需要添加引号
            # 排除端点,如果不排除则只需要访问该应用的/shutdown 端点就能实现关闭该应用的远程操作
            exclude: shutdown
      server:
        port: 8888  #新开监控端口,不和应用用同一个端口,
      endpoint:
        health:
          show-details: always # 显示db、redis、rabbti连接情况等
        shutdown:
          enabled: true  #默认情况下,除shutdown以外的所有端点均已启用。手动开启

    按照如上配置则访问路径为:http://localhost:8888/monitor

    我们可以看到有很多节点(图中省略),这些结点被称作 端点,如下是这些端点的描述:

    ID描述默认启用默认公开
    auditevents 公开当前应用程序的审计事件信息 Yes No
    beans 显示应用程序中所有Spring bean的完整列表 Yes No
    conditions 显示在配置和自动配置类上评估的条件以及它们是否匹配的原因 Yes No
    configprops 显示所有@ConfigurationProperties对照的列表 Yes No
    env 从Spring的ConfigurableEnvironment中公开属性 Yes No
    flyway 显示已应用的任何Flyway数据库迁移 Yes No
    health 显示应用程序健康信息 Yes Yes
    httptrace 显示HTTP跟踪信息(默认情况下,最后100个HTTP请求-响应交互) Yes No
    info 显示任意应用程序信息 Yes Yes
    loggers 显示和修改应用程序中记录器的配置 Yes No
    liquibase 显示已应用的任何Liquibase数据库迁移 Yes No
    metrics 显示当前应用程序的“指标”信息 Yes No
    mappings 显示所有@RequestMapping路径对照的列表 Yes No
    scheduledtasks 显示应用程序中调度的任务 Yes No
    sessions 允许从Spring Session支持的会话存储中检索和删除用户会话 Yes No
    shutdown 让应用程序优雅地关闭 No No
    threaddump 执行线程转储 Yes No

    如上所知,如果想显示应用程序健康信息,那么就访问 health 端点,即:

    http://127.0.0.1:8888/monitor/health

    image-20201122204029210

    其他的大家可以自行尝试看一下,额外需要注意的是 shutdown 端点,项目中一定要排除,否则只需要访问该应用的 /shutdown 端点就能实现该应用的远程关闭操作,十分危险。

    这就完了?

    显然不是,上边这样直接访问端点,然后返回 JSON 数据,显然很不直观,毕竟现在很流行可视化嘛~ 咳咳。

    所以这时候我们可以通过 Spring Boot Admin 来对 actuator 返回的这些数据进行整理。

    关于 Spring Boot Admin 的介绍:

    Spring Boot Admin 是用于管理和监控 Spring Boot 应用程序运行状态的。在 Spring Boot 项目中可以通过集成 Spring Boot Admin Client 向 Spring Boot Admin Server 进行注册(通过 HTTP),这样我们就可以在 Spring Boot Admin Server 统一管理 Spring Boot 应用。可视化是端点之上的 Vue 项目。

    提供功能如下:

    • 显示健康状态及详细信息,如JVM和内存指标、数据源指标、缓存指标
    • 跟踪并下载日志文件
    • 查看jvm系统-和环境属性
    • 查看Spring启动配置属性
    • 方便loglevel管理
    • 查看线程转储
    • 视图http-traces
    • 查看http端点
    • 查看计划任务
    • 查看和删除活动会话(使用spring-session)
    • 状态更改通知(通过电子邮件、Slack、Hipchat…)
    • 状态变化的事件日志(非持久性)
    • ……

    使用 Spring Boot Admin 也是非常的简单,直接添加如下依赖:

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <dependency>
        <groupId>de.codecentric</groupId>
        <artifactId>spring-boot-admin-starter-server</artifactId>
        <version>2.3.0</version>
    </dependency>
    <dependency>
        <groupId>de.codecentric</groupId>
        <artifactId>spring-boot-admin-starter-client</artifactId>
        <version>2.3.0</version>
    </dependency>

    配置文件如下:

    spring:
      boot:
        admin:
          # 修改上下文路径
          context-path: /admin
          client:
            url: http://127.0.0.1:${server.port}/admin

    再在启动类上加上 @EnableAdminServer 注解,齐活,完事。

    通过如上界面,我们可以清楚的看到应用的名称和 IP 端口信息,应用名称是默认的,也可以在配置文件中通过 spring.application.name 来自定义名称。

    我们还可以点击服务信息进去查看详情,左边是对应的功能菜单,右边是数据展示的页面。详情中有健康信息、线程信息、JVM 内存等信息,都通过图形的方式展示,一目了然。

    通过左侧功能菜单可以看到还有 日志、JVM、缓存 等管理功能,大家可以点点看看。

    2.4、Spring Boot Starter 开箱即用

    关于 Spring Boot Starter 相信大家一定不陌生,很多第三方工具的引入都是涉及到 Starter 包,Starter 包可以说是 Spring Boot 中的核心功能,Starter 的出现,简化了 Spring 很多工作。不懂就问环节:什么是 Starter?

    大家可以看一下我转载的这篇文章:

    https://www.cnblogs.com/niceyoo/p/14022406.html

    总之,Starter 包简化框架集成难度,将 Bean 的自动装配逻辑封装在 Starter 包内部,同时也简化了 Maven Jar 包的依赖,对框架的集成只需要加入一个 Starter 包的配置,降低了烦琐配置的出错几率。

    如下是 Spring Boot 提供的开箱即用 Starter 包:

    starterdesc
    spring-boot-starter-web 用于快速构建Web,包含 RESTful 风格框架、SpringMVC和默认的嵌入式容器Tomcat
    spring-boot-starter-test 用于测试
    spring-boot-starter-data-jpa 带有Hibermate的Spring Data JPA,用于操作数据库。
    spring-boot-starter-jdbc 传统的JDBC支持
    spring-boot-starter-thymeleaf 支持Thymeleaf模板
    spring-boot-starter-mail 支持Java Mail、Spring Email 发送邮件
    spring-boot-starter-integration Spring框架创建的一个API,面向企业应用集成(EAI)
    spring-boot-starter-mobile SpringMVC的扩展,用来简化手机上的Web应用程序开发
    spring-boot-starter-data-redis 快速整合并操作 Redis:通过Spring Data Redis、Redis Client使用Redis
    spring-boot-starter-validation Bean Validation是一个数据验证的规范,Hibernate Validator是一个数据验证框架
    spring-boot-starter-websocket 相对于非持久的协议HTTP,Websocket 是一个持久化的协议
    spring-boot-starter-web-services SOAP Web Services
    spring-boot-starter-hateoas 为服务添加HATEOAS功能
    spring-boot-starter-security 用Spring Security进行身份验证和授权
    spring-boot-starter-data-rest 用Spring Data REST公布简单的REST服务

    3、Spring Boot 项目的创建方式

    创建 Spring Boot 有两种方式:

    • 手动创建一个 Maven 项目,然后添加 Spring Boot 需要的依赖;
    • 通过 Spring Initializr 脚手架创建;

    工具环境 IDEA,下文是基于 Spring Initializr 脚手架创建。

    通过 IDEA 创建 Spring Boot 项目实在是太简单了:

    关于下一步之后,无非就是选择创建项目的方式(Maven/Gradle),Spring Boot 版本(2.2.2.RELEASE),引入的依赖(Web/SQL/NoSQL/Security等),在这就不赘述了。

    4、Spring Boot 编译打包

    Spring Boot 项目打包无非就 「打 jar 包、打 war 包」 这两种情况,这两者的应用场景不同,各有优缺点。

    jar 包部署优点:

    • 无须搭建本地web容器,默认内置 Tomcat 容器,可以直接以 java -jar 形式运行;
    • 因为自带web容器,可以避免由于web容器的差异造成不同环境结果不一致问题。
    • 借助容器化,可以进行大规模的部署。

    jar 包部署缺点:

    • jar 应用体积过大「可以参考瘦身指南」
    • 数据源无法通过界面进行管理。
    • 修改web容器相关配置比较困难,需要借助代码实现。

    war 包部署优点:

    • 可以借助web容器管理界面对应用进行管理。
    • web容器配置较为灵活,配置和程序分离。「jar包方式其实也可以」
    • 应用体积较小,甚至可以借助web容器的包管理功能进一步减小应用大小。

    war 包部署缺点:

    • 本地需要搭建web容器
    • 调试较为困难,需要借助web容器。
    • 部署较为困难

    至于最终选择哪个,本文不追究,毕竟都是要根据实际项目来说的。

    以打 jar 包为例

    首先需要在 pom.xml 文件中配置 spring-boot-maven-plugin 打包插件,也就是我们通常看到的:

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

    这样就可以在 Tmerinal 控制台通过 maven 命令进行打包了:mvn package

    或者是可以直接在 IDEA 右侧的 Maven 标签:

    有小伙伴可能好奇,难道不需要设置 <packaging>jar</packaging> 吗?

    <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">
        ...
        <packaging>jar</packaging>
        ...
    </project>

    其实默认的 就是 jar 方式。

    5、Spring Boot 多环境配置

    通常在实际开发中,往往涉及到好几个环境,比如开发环境、本地环境、测试环境等等,通常不同环境下的配置信息是不一样的「比如端口号?数据库连接信息等」,因为我们每次只能使用一个配置环境,如果频繁的修改配置以达到效果,自然是麻烦的不行,且很容易出错。

    而在 Spring Boot 中,我们可以通过 spring.profiles.active 来激活对应的配置文件。

    比如创建如下三个文件:

    application.yml「主文件」

    • application-dev.yml:开发环境
    • application-local.yml:本地环境
    • application-test.yml:测试环境

    我们想要使用 dev 开发环境,只需要在 application.yml 中使用 spring.profiles.active=dev 就可以完成指定:

    spring:
     profiles:
      active: dev

    6、替换内置的 Tomcat 容器

    尽管Spring Boot内置了 Tomcat ,但是在高并发场景下的 Tomcat 相对来说比较弱。比如在相同的机器配置下,模拟相等的请求数,Undertow在性能和内存使用方面都是最优的。并且Undertow新版本默认使用持久连接,这将会进一步提高它的并发吞吐能力。所以,如果是高并发的业务系统,Undertow 是最佳选择。

    问题是怎么替换?

    因为 spring-boot-starter-web 中默认自带的容器是 Tomcat,如果想要替换成 Undertow 也是非常简单的。

    首先需要排除 spring-boot-starter-web 包中的 Tomcat,然后单独增加 undertow 容器的依赖:

    <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-web</artifactId>
      <!-- 排除Tomcat依赖 -->
     <exclusions>
      <exclusion>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-tomcat</artifactId>
      </exclusion>
     </exclusions>
    </dependency>

    <!-- 添加 Undertow依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-undertow</artifactId>
    </dependency>

    7、配置文件的读取

    以前读取 spring 的配置文件,都是通过工具类类读取,其实无非就是读取 xml 文件,比如,通过 ClassPathXmlApplicationContext 加载到文件,然后再通过 上下文拿到 对应的Bean,再取参等。

    而在 Spring Boot 中读取配置文件有三种方式:

    1. Environment

    2. @Value

    3. @ConfigurationProperties

    我们一一看看这三种方式:

    application.yml 中的模拟数据:

    niceyoo:
      name: 张三
      age: 24
    7.1、Environment

    Environment 用于管理当前的应用环境信息,定义了获取 Profile 的方法,同时继承了 PropertyResolver,PropertyResolver 提供了属性访问的相关方法。

    也就是我们可以使用 Environment 的 getProperty() 方法读取指定配置 Key 的内容。

    代码中体现:

    @Controller
    @RequestMapping(value = "/test")
    public class TestController {

        @Autowired
        private Environment environment;

        @PostMapping("/test")
        @ResponseBody
        public void test(@RequestBody User user) {
            String name = environment.getProperty("niceyoo.name");
            Integer age = Integer.valueOf(environment.getProperty("niceyoo.age"));
            System.out.println(name+" " +age);
        }
    }
    7.2、@Value

    @Value 方式就简单多了,直接在接收的属性上加上该注解即可:

    @Controller
    @RequestMapping(value = "/test")
    public class TestController {

        @Value("niceyoo.name")
        private String name;

        @Value("niceyoo.age")
        private Integer age;

        @PostMapping("/test")
        @ResponseBody
        public void test(@RequestBody User user) {
            System.out.println(name+" " +age);
        }
    }
    7.3、@ConfigurationProperties

    使用该注解可以直接注入到实体类中,方便值的同一管理。

    比如我们创建一个 Model 实体类,定义 name、age 属性,然后实现 getset 方法,再在实体类上加上 @Configuration 和 @ConfigurationProperties(prefix="niceyoo") 注解,并指定前缀为 niceyoo。

    @Configuration
    @ConfigurationProperties(prefix="niceyoo")
    public class Model {
        private String name;
        private Integer age;
        省略get、set方法
    }

    这样就可以将 niceyoo 下面的值直接对应到实体上了,其中加入 @Configuration 注解,我们在使用时可以直接通过 @Autowired 进行注入。

    取值代码:

    @Controller
    @RequestMapping(value = "/test")
    public class TestController {

        @Autowired
        private Model model;

        @PostMapping("/test")
        @ResponseBody
        public void test(@RequestBody User user) {
            System.out.println(model.getName()+" " +model.getAge());
        }
    }

    总结

    以前 Spring 主打轻量级应用框架,但随着外界不断地进行扩充,像是 Shiro、Security、MQ、Redis、ElasticSearch 等等等等, 总之就是 Spring 几乎可以做任何事了,但是相应的问题也来了。

    Spring 每集成一个第三方软件,就需要手动增加一些配置,随着新软件的加入,以至于需要引入越来越多的配置,且这些配置各玩各的,难以理解,难以管理,我记得我实习时的那家公司就是这样,各种五花八门的配置文件,以至于经常配置出错,然后解决配置相关的问题就需要好久。

    工欲善其行,必先利其器。

    Spring Boot 的出现可以说真正的颠覆了以前传统的 Web 项目,开发人员再也不用配置繁琐的 xml 文件了,简直是解放了双手,整个项目的配置文件也变得简洁了。

    正所谓简洁并不意味着简单,Spring Boot 只是将众多复杂的功能进行了封装,让我们在使用的时候足够的简单。

    关于 Spring Boot 的知识点可以说太多太多了,文中只是把自己能想到的几点描述了出来。

    欢迎大家在留言区互动,

    博客园持续更新,欢迎关注。希望这篇文章对大家有所帮助。

    博客园:https://www.cnblogs.com/niceyoo

  • 相关阅读:
    Python 数据处理 正则匹配,清洗脏数据
    Pycharm自用激活码
    编程杂谈 集群与分布式
    win10 向右键添加管理员级CMD
    win10 笔记本键盘永久屏蔽Numlk键
    Golong 环境搭建
    python 各种锁机制归纳整理
    MySQL数据库操作
    Web框架本质
    Django框架
  • 原文地址:https://www.cnblogs.com/niceyoo/p/14022503.html
Copyright © 2020-2023  润新知