系列文章:
【轻松入门SpringBoot】从0到1搭建web 工程(上)-使用SpringBoot框架
【轻松入门SpringBoot】从0到1搭建web 工程(中) -使用Spring框架
一、前言
前面用了两篇文章讲述了如何使用 Spring 框架、SpringBoot 框架分别搭建 web 工程,我们发现在业务实现、工能测试中,两种框架的实现方案大致相同,但配置方面却大相径庭。这篇文章,我将基于实践对比这两种框架的核心差异、设计理念,说明 SpringBoot 为何成为主流。
二、核心共识
SpringBoot 框架是在Spring 框架的基础上封装和配置的框架,是Spring 框架的加强版,而非替代版。SpringBoot 框架 = Spring 框架+ 自动配置 + 起步依赖 + 嵌入式服务器,SpringBoot 框架也是遵循 Spring 的 AOP、IOC 的核心思想的。
三、对比
一张图胜过千言万语
|---------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 对比维度 | Spring | SpringBoot | 核心差别 |
| 业务层代码 | 使用@Controller、@Service 标识 bean;使用@RequestMapping 标识映射;使用@Autowired引入依赖等 | 完全一样 | 无差别 |
| 测试-非web | @RunWith(SpringRunner.class) @ContextConfiguration(locations = {"classpath:spring-context.xml"}) | @RunWith(SpringRunner.class) @SpringBootTest | 1、都需要@RunWith注解表示加载 Spring 容器,扫描 bean; 2、@ContextConfiguration(locations = {"classpath:spring-context.xml"})手动指定需扫描的配置文件地址; 3、@SpringBootTest自动扫描主启动类所在包,加载所有配置,无需手动指定。 |
| 测试-web | @RunWith(SpringRunner.class) @ContextConfiguration(locations = {"classpath:spring-context.xml", "classpath:spring-mvc.xml"}) @WebAppConfiguration - 额外操作:手动构建 MockMvc(MockMvcBuilders.webAppContextSetup(webApplicationContext).build()) | @RunWith(SpringRunner.class) @SpringBootTest @AutoConfigureMockMvc | 核心逻辑一致。 1、Spring需要显式添加扫描 SpringMVC 相关配置路径,手动构建 mockMVC; 2、SpringBoot不用手动指定多个 XML 配置,不用手动构建 MockMvc; |
| 核心配置 | spring-context.xml, spring-mvc.xml, web.xml(web 工程需要) | application.yml | Spring框架需要手动配置配置文件 Spring-context,Spring-mvc,web.xml来管理数据源配置、组件扫描、事务管理、JPA 管理、DispatcherServlet。并且需要手动配置每一个maven 依赖以及maven 版本,需要开发自己解决依赖冲突问题。 SpringBoot 框架,"约定大于配置",无 xml 配置,使用 application.yml 或 application.properties。 自动扫描根目录路径下的Spring 资源,自动配置 dispatcherServlet等,只需要配置数据库的 URL,密码等需要自定义的信息。 且有起步依赖,如 spring-boot-starter-web、spring-boot-starter-data-jpa,一站式配齐,减少"缺这少那"的可能,并且自动管理依赖版本,无需手动匹配等。 |
| 服务启动 | -web 工程 1、打成 war 包 2、安装并配置 Tomcat,将 war 部署到tomcat | -web 工程 1、打成 jar 包(可执行 jar) 2、内置Tomcat+自动配置。 本地执行SpringBootUniqApplication的 main 方法,直接启动 | SpringBoot 内置 web 服务器+自动配置,无需手动安装和配置 web 服务器。 |
四、本质
经过前面的对比,我们可以体会到SpringBoot 框架的三大特点:
1、起步依赖-Starter Dependencies
Spring需要挨个引入每个依赖,上篇文章一个简单的 web 服务搭建完,手动引入了 13 个核心依赖,同时还需要找合适的版本,手动解决版本冲突。使用 SpringBoot 一站式配套依赖spring-boot-starter-XX,可以一键引入搭配好的依赖包,既解决了丢三落四的问题,又调试了好了版本依赖。
2、自动检测+默认配置
Spring 核心配置文件 spring-context.xml,spring-mvc.xml,web.xml
2.1 src/main/resources/spring-context.xml
XML
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">
<!--Spring容器配置文件 配置 JPA、数据源、事务管理、Bean 扫描-->
<!-- 1.扫描 service、Repository 等 bean(排除 Controller,由 Spring mvc 扫描) -->
<context:component-scan base-package="com.example.uniq">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!-- 2.配置数据源(Spring Boot 用 application.yml配置,自动创建 DataSource) -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<!--使用 MySQL 驱动-->
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3307/boot_demo?useSSL=false&serverTimezone=Asia/Shanghai&createDatabaseIfNotExist=true&allowPublicKeyRetrieval=true"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
<!-- 3.配置 JPA 实体管理器工厂 -->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="packagesToScan" value="com.example.uniq.entity"/> <!-- 扫描JPA实体类 -->
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="true"/> <!-- 日志中打印 SQL -->
<property name="generateDdl" value="true"/> <!-- 自动创建表结构 (类似 JPA ddl-auto: update)-->
<!-- <property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect"/> <!– 数据库方言 –>-->
</bean>
</property>
<!-- 新增:Hibernate 额外配置(解决 MySQL 8.0 建表语法错误) -->
<property name="jpaProperties">
<props>
<!-- 强制使用 InnoDB 引擎(MySQL 8.0 默认引擎),避免生成 type=MyISAM -->
<prop key="hibernate.dialect.storage_engine">innodb</prop>
<!-- 可选:明确指定建表引擎为 InnoDB,进一步兼容 -->
<prop key="hibernate.hbm2ddl.auto">update</prop>
</props>
</property>
</bean>
<!-- 4.配置JPA事务管理器 -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<!-- 5.开启事务注解 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
<!-- 6.配置 Spring Data JPA (扫描 Repository 接口) -->
<jpa:repositories base-package="com.example.uniq.repository"
entity-manager-factory-ref="entityManagerFactory"
transaction-manager-ref="transactionManager"/>
</beans>
2.2 src/main/resources/spring-mvc.xml
XML
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 1.SpringMVC 配置文件 配置 controller 扫描、 视图解析、json 消息转换器-->
<context:component-scan base-package="com.example.uniq.controller" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!-- 2.启动 Springmvc 注解驱动(支持@RequestMapping @ResponseBody) -->
<mvc:annotation-driven>
<!-- 配置 json 消息转换器 controller返回 json 必须 -->
<mvc:message-converters>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/>
</mvc:message-converters>
</mvc:annotation-driven>
<!-- 3.静态资源处理 -->
<mvc:default-servlet-handler/>
</beans>
2.3 src/main/webapp/WEB-INF/web.xml
XML
<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_3_1.xsd"
version="3.1">
<!--1.加载 Spring 容器配置文件(spring-context.xml) -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-context.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--2.Spring MVC 配置 前端控制器 (DispatcherServlet)-->
<servlet>
<servlet-name>springMvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value> <!-- Spring MVC 容器配置文件 -->
</init-param>
<load-on-startup>1</load-on-startup> <!-- 启动时加载 -->
</servlet>
<servlet-mapping>
<servlet-name>springMvc</servlet-name>
<url-pattern>/</url-pattern> <!-- 拦截所有请求 -->
</servlet-mapping>
<!--3.配置字符串编码过滤器 (避免中文乱码) -->
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
SpringBoot 核心配置 application.yml
java
#服务器配置
server:
port: 8080 #端口号 项目启动端口
# Spring 核心配置
spring:
# 数据库连接配置
datasource:
url: jdbc:mysql://localhost:3307/boot_demo?useUnicode=true&characterEncoding=utf-8&useSSL=false
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver # 对应的 Mysql 驱动
# JPA配置(操作数据库)
jpa:
hibernate:
ddl-auto: update # 自动创建/更新表结构 (开发环境用,生产环境改 none)
show-sql: true # 控制台打印 sql 语句
properties:
hibernate:
format_sql: true # 格式化 sql
Spring 核心配置文件 3个,SpringBoot 核心配置文件 1 个,是SpringBoot框架不需要管理这些关联吗?当然不是。有句话说"你觉得轻松,因为有人替你负重前行",在这里就是"SpringBoot框架做负重了"。
SpringBoot基于"约定大于配置"的思想配置了默认值、做了套装。自动检测机制使引入Spring-boot-starter-XX后,jar包内有核心配置类,会自动解析配置,注册bean到Spring容器等。
举个栗子:在Spring框架中,需要使用Spring-context.xml配置扫描Bean的范围:
XML
<!-- 1.扫描 service、Repository 等 bean(排除 Controller,由 Spring mvc 扫描) -->
<context:component-scan base-package="com.example.uniq">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
SpringBoot 没有了Spring-context.xml,是如何扫描 Bean 的呢?Bean扫描自动化:默认扫描主启动类所在包(@SpringBootApplication 包含 @ComponentScan),无需手动指定扫描路径;
对于需要自定义的部分,比如配置数据源,SpringBoot 又是怎样封装的呢?
引入spring-boot-starter-data-jpa/jdbc后,jar 中的DataSourceAutoConfiguration自动解析application.xml中的spring.datasource配置,创建数据源Bean。
所有自动配置的触发,都源于@SpringBootApplication中的@EnableAutoConfiguration注解,每个自动配置类通过条件注解实现按需配置等。但这部分不仅仅是工程层面,还涉及到原理,这篇文章我们先了解到是 SpringBoot 框架做了自动检测、默认配置等,原理可能需要结合源码,为了文章结构清晰,易读性,原理我们放到后面的章节。
3、内置 web 服务器
我们发现 SpringBoot框架的 web 工程中,跟 web相关的好像只添加了spring-boot-starter-web 依赖,在 application.yml 配置了server.port:8080。既没手动引入javax.servlet-api,也没配置 Tomcat,直接在 SpringBootUniqApplication 的main 方法中就能启动服务。这样如此简单,一方面得益于前面说的SpringBoot 框架的自动配置,另一方面是 SpringBoot 内置 web 服务器。引入spring-boot-starter-web 后,TomcatAutoConfiguration 自动启动嵌入式 Tomcat(默认 8080 端口),无需部署 War 包到外部 Tomcat。
五、总结
在本章中,我们从工程的角度,横向对比了Spring 框架和 SpringBoot 框架搭建 web 工程需要引入的依赖和配置,直观感受到了使用 SpringBoot 框架上手的高效和轻松。同时也知道,有些配置虽然我们没有手动添加,但不是没有用了,而是由SpringBoot 框架做了自动检测和默认配置。
到这里,读者可能有疑问:spring-boot-starter-XX 高度封装,一键打包,是否会把我不需要的类添加我的工程?自己做饭不吃香菜可以不加,预制菜料包一大包混在一起,我还能不要香菜吗?或者加点别的吗?内置 web 服务器默认端口是 8080,但我有服务已经占用 8080 端口了,可以不用换成别的吗?挠头。。
后面我们一起学习来揭晓答案吧,bye~~