Spring Boot 原理的分析(超详细!!!)

2023-05-16

1 Spring Boot

Spring Boot 没有特定的业务,将其他框架进行整合, 去掉配置

开箱即用

Spring Boot 跟 Spring MVC 的整合

Spring Boot 跟 Thymeleaf 的整合

Spring Boot 跟 MyBatis 的整合

Spring Boot 跟 MyBatis Plus 的整合

Spring Boot 跟 Swagger 的整合

2 Spring Boot 自动装配 原理

入口 启动类 Application

核心注解 @SpringBootApplication,由 3 个注解组成

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-17rQcIWY-1621772154364)(D:\图片\springBoot.png)]

2.1@SpringBootConfiguration

它本质上就是一个 @Configuration 注解, @Configuration 非常重要,因为它是 Spring Boot 框架 中使用最多的一个注解。

2.1.1 @Configuration

标注了一个配置类,一个类如果添加了 @Configuration 注解,那么这个类就是一个配置类

什么是配置类? 

取代 XML 的

Spring 中配置一个 bean

用xml方式

1、User

public class User{
	private Integer id;
	private String name;
}

2、spring.xml

<bean id="user" class="User">
	<property name="id" value="1"/>
	<property name="name" value="张三"/>
</bean>

<bean id="accout" class="Accout">
</bean>

使用 @Configuration 注解的方式完成。

注意事项

  • 配置类一定要被启动类扫描到,配置扫包的结构

  • 配置类本身也会被注入到 IOC 容器中。

2.1.2 @ConfigurationProperties

可以直接读取 YML 中的数据,封装到 bean 中

package com.wdzl.entity;

import lombok.Data;
import
org.springframework.boot.context.properties.ConfigurationProperties;

@Data
@ConfigurationProperties(prefix = "people")
public class People {
	private Integer id;
	private String name;
	private String tel;
}
people:
  id: 11
  name: Tom
  tel: 13378900987
package com.wdzl.configuration;
import com.wdzl.entity.People;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableConfigurationProperties(People.class)
public class PeopleConfiguration {
}

使用 @ConfigurationProperties 有什么用呢?

Spring Boot 整合 MyBatis 自动装配 MyBatis 需要的各种组件,DataSource 那么 Spring Boot 是怎么自动装配的呢?

SSM : XML 自己配 bean

@Configuration 取代了 XML, @ConfigurationProperties 的功能是从 YML 文件中读取信息,进而自动创建对象。

2.2@EnableAutoConfiguration

Spring Boot 框架中需要自动装载 bean

所有的 bean 可以分为两类:

  • 框架自带的 bean,比如 DataSource、 SqlSessionFactory

    • 开发者自定义的 bean,通过 @ComponentScan 注解 完成
  • 开发者自定义的,比如 controller、service、 repository

    • 框架自带的 bean,通过 @EnableAutoConfiguration 来完成,本质上就@Import(Class) 注解
    • 它的 Selector 又指定让 Spring Boot 去读取 META-INF/spring.factories 文件,这个文件中就将框架需要的 bean 全部配合,等着 Spring Boot 来读取,进而加载。

@Import 注解的作用也是给 IOC 中注入对象,需要结合 传入它内部的类来处理的

类中返回需要注入的 bean 的信息,再由 @Import 完成 注入

自定义 Selector

  • 创建实体类
package com.wdzl.entity;
import lombok.Data;

@Data
public class Account {
	private String title;
}
  • 创建 Selector
package com.wdzl.selector;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;

public class MySelector implements ImportSelector {
	@Override
	public String[]	selectImports(AnnotationMetadataannotationMetadata) {
        return new String[]
        {"com.wdzl.entity.Account"};
    }
}
  • 创建 @Import 所在的类
package com.wdzl.configuration;
import com.wdzl.selector.MySelector;
import org.springframework.context.annotation.Import;

@Import(MySelector.class)
public class ImportConfiguration {
}

通过 @Import 注入 MySelector.class 中返回的 bean 信 息,Account

ImportConfiguration 怎么生效?只需要让 Spring Boot 读到它即可,怎么让 Spring Boot 读到它?

需要在工程中创建 META-INF/spring.factories 文件,将 目标类配置进来即可

org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.wdzl.configuration.ImportConfiguration

2.3@ComponentScan

自动扫描并加载符合条件的组件,通过设置 basePackage 参数来指定要扫描的根目录,该目录下的所有子类都会被扫描。

给哪个类添加该注解,那么该类所在的包就是默认的扫描路径

package com.wdzl;
import
com.wdzl.configuration.MyConfiguration;
import com.wdzl.controller.UserController;
import com.wdzl.entity.People;
import com.wdzl.entity.User;
import com.wdzl.repository.UserRepository;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigurationExcludeFilter;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.TypeExcludeFilter;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;
//@SpringBootApplication
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(basePackages ={"com.wdzl.controller","com.wdzl.repository
"})
public class Springboot002Application {
    public static void main(String[] args) {
        ApplicationContext applicationContext = springApplication.run(Springboot002Application.class, args);
        System.out.println(applicationContext.getBean(UserRepository.class));
        System.out.println(applicationContext.getBean(UserController.class));
    }
}

3 Spring Boot整合第三方框架的实现

所有的Spring Boot 和第三方框架的整合都是通过Starter来完成

框架的整合操作全部封装到Starter

MyBatis Starter

Starter 相当于 Spring Boot 提供的一种规范,你只要实现这种规范,那么你的代码就会融入 Spring Boot中。

手写一个 Spring Boot Starter

自己写一个工程,可以导入Spring Boot进行使用

3.1创建自己的工程

将 MSS 引入到其他的 Spring Boot 中,可以读取 Spring Boot 的 YML 配置。

1、创建Spring Boot工程,pom.xml

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

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

2、创建属性读取类 ServiceProperties

package com.wdzl.properties;

import org.springframework.boot.context.properties.ConfigurationProperties;


@ConfigurationProperties("zhao")
public class ServiceProperties {


    private String prefix;
    private String suffix;


    public String getPrefix() {
        return prefix;
    }

    public void setPrefix(String prefix) {
        this.prefix = prefix;
    }

    public String getSuffix() {
        return suffix;
    }

    public void setSuffix(String suffix) {
        this.suffix = suffix;
    }

    @Override
    public String toString() {
        return "ServiceProperties{" +
                "prefix='" + prefix + '\'' +
                ", suffix='" + suffix + '\'' +
                '}';
    }
}

3、创建 Service

package com.wdzl.service;

public class Service {
    private String prefix;
    private String suffix;

    public Service(String prefix, String suffix) {
        this.prefix = prefix;
        this.suffix = suffix;
    }

    public String doService(String value){
        return this.prefix+value+this.suffix;
    }
}

4、创建自动配置类 AutoConfigure

package com.wdzl.configure;

import com.wdzl.properties.ServiceProperties;
import com.wdzl.service.Service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@ConditionalOnClass(Service.class)//必须要service类
@EnableConfigurationProperties(ServiceProperties.class)
public class AutoConfigure {
    @Autowired
    private ServiceProperties serviceProperties;


    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnProperty(
            prefix = "zhao",
            value = "enable",
            havingValue = "true"
    )
    public Service service(){
        return new Service(this.serviceProperties.getPrefix(),this.serviceProperties.getSuffix());
    }

}

5、在 resources 路径下创建 META-INF/spring.fatories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.wdzl.configure.AutoConfigure

6、将项目打成 jar 包,不要用 Maven 插件

7、将 jar 安装到本地仓库

mvn install:install-file -DgroupId=com.wdzl.spring.boot -DartifactId=demo_starter -Dversion=1.1.0 -Dpackaging=jar -Dfile=D:\代码\demo\out\artifacts\demo_jar\demo.jar

3.2 导入现有的 Spring Boot 工程

1、pom.xml

		<dependency>
            <groupId>com.wdzl.spring.boot</groupId>
            <artifactId>demo_starter</artifactId>
            <version>1.1.0</version>
        </dependency>

2、application.yml

zhao:
  prefix: 赵大帅哥
  suffix: 腰缠万贯
  enable: true

3、Controller

package com.wdzl.controller;


import com.wdzl.entity.Account;
import com.wdzl.service.Service;
import com.wdzl.service.impl.AccountServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
 * <p>
 *  前端控制器
 * </p>
 *
 * @author 赵大帅哥
 * @since 2021-05-16
 */
@RestController
@RequestMapping("//account")
public class AccountController {


    @Autowired
    private Service service;

  

    @GetMapping("/test/{value}")
    public String list(@PathVariable("value") String value){

        return this.service.doService(value);
    }

}

4 Spring Boot整合持久层框架

ORM

MyBatis MyBatis Plus

JdbcTemplate Spring自带的一个JDBC封装框架,轻量级框架

Spring Data JPA底层就是Hibernate相当于Spring框架对Hibernate进行了封装,变得更加简单

Spring Data JPA

1、创建 Spring Boot 工程,引入 lombok、web、mysql Driver、Spring Data JPA

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

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

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
         </dependency>

		<dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

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


2、创建实体类

package com.wdzl.entity;

import lombok.Data;

import javax.persistence.*;

@Data
@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    @Column
    private Integer age;
    @Column
    private String name;
    @Column
    private String password;
}

3、创建UserRepository接口

package com.wdzl.dao;

import com.wdzl.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface UserDao extends JpaRepository<User,Integer> {
}

4、创建 UserController

package com.wdzl.controller;

import com.wdzl.dao.UserDao;
import com.wdzl.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
public class UserController {

    @Autowired
    private UserDao userDao;

    @GetMapping("/list")
    public List<User> findAll(){
        return this.userDao.findAll();
    }

    @GetMapping("/findById/{id}")
    public User findById(@PathVariable("id") Integer id){
        return this.userDao.findById(id).get();
    }

    @PostMapping("/save")
    public User save( User user){
        return this.userDao.save(user);
    }

    @PutMapping("/update")
    public User update(User user){
        return this.userDao.save(user);
    }

    @DeleteMapping("/delete/{id}")
    public void delete(@PathVariable("id")Integer id){
        this.userDao.deleteById(id);
    }
}

5、application.yml

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: 13992794421
  jpa:
    show-sql: true
    properties:
      hibernate:
        format_sql: true
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Spring Boot 原理的分析(超详细!!!) 的相关文章

  • 搭建redis集群的时候碰到的错误

    1 出现from usr lib ruby site ruby 1 8 rubygems custom require rb 31 in 96 require from redis trib rb 2 错误 在使用ruby进行搭建redis
  • 疫情分析项目

    疫情期间各类政府媒体及社交网站 xff0c 均发布了相关疫情每日统计数据 xff0c 下面基于数据仓库工具Hive请你统计分析相关疫情数据 数据字段为 xff1a 日期 省份 城市 新增确诊 新增出院 新增死亡 消息来源 来源1 xff0c
  • selenium之css元素定位方法

    一 单一属性定位 xff08 id用 xff0c class用 xff09 1 xff1a type selector driver find element by css selector 39 input 39 2 xff1a id 定
  • 应用宝YSDK的快速接入(单机版只包含了游客模式没有加入其他的功能)

    可复制的代码 xff1a mainfest lt activity android name 61 34 com tencent tauth AuthActivity 34 android noHistory 61 34 true 34 a
  • Linux中error while loading shared libraries错误解决办法

    转载自http www cnblogs com codingmengmeng p 7456539 html 默认情况下 xff0c 编译器只会使用 lib和 usr lib这两个目录下的库文件 xff0c 通常通过源码包进行安装时 xff0
  • Java:常用类

    文章目录 一 Object类1 概述1 hashcode xff08 xff09 2 toString xff08 xff09 3 clone xff08 xff09 4 getClass xff08 xff09 5 notify xff0
  • JavaWeb介绍

    文章目录 1 基本概念1 1介绍1 2 Web应用程序1 静态web2 动态web 2 Web服务器2 1 技术介绍1 ASP2 PHP3 JSP Servlet 2 2 服务器介绍1 IIS2 Tomcat 3 Tomcat3 1 安装3
  • 邮件发送原理及实现

    文章目录 一 邮件发送原理1 1 接收发送过程1 2 邮件服务器1 3 邮件传输协议 二 Java邮件发送2 1 准备环境2 2 介绍2 2 1 授权码 2 3 简单邮件2 3 1 引入2 3 2 步骤一 xff1a 准备参数2 3 3 步
  • 11、MyBatis的逆向工程

    文章目录 11 MyBatis的逆向工程11 1 创建逆向工程的步骤1 添加依赖和插件2 创建mybatis config xml的核心配置文件3 创建逆向工程的配置文件4 执行MBG插件的generate目标5 效果6 窜库问题 11 2
  • 20、单元测试

    文章目录 1 JUnit5 的变化2 JUnit5常用注解3 断言 xff08 assertions xff09 1 简单断言2 数组断言3 组合断言4 异常断言5 超时断言6 快速失败 4 前置条件 xff08 assumptions x
  • 使用模拟器发送短信出现错误的解决方法

    在安卓应用开发揭秘第四章讲解使用模拟器发送短信并使用Toast显示短信息的时候 xff0c 本人遇到过如下错误 xff0c 现有一点个人的浅显理解 xff1a 错误 xff1a Couldn 39 t open fd for content
  • 22、原理解析

    文章目录 1 Profile功能1 application profile功能2 64 Profile条件装配功能3 profile分组 2 外部化配置1 外部配置源2 配置文件查找位置3 配置文件加载顺序 xff1a 4 指定环境优先 x
  • 5、网络配置

    文章目录 5 网络配置5 1 VMware三种模式5 1 1 桥连模式5 1 2 NAT模式5 1 3 仅主机模式 5 2 查看网络IP和网关5 2 1 查看虚拟网络编辑器5 2 2 修改虚拟网卡 Ip5 2 3 查看网关5 2 4 查看
  • 1、认识IntelliJ IDEA

    文章目录 1 认识IntelliJ IDEA1 1 JetBrains公司介绍1 2 IntelliJ IDEA介绍1 3 IDEA的主要优势 xff08 对比Eclipse xff09 1 3 1 功能强大1 3 2 符合人体工程学 1
  • 21、指标监控

    文章目录 1 SpringBoot Actuator1 简介2 1 x与2 x的不同3 如何使用4 可视化 2 Actuator Endpoint1 最常使用的端点2 Health Endpoint3 Metrics Endpoint4 管
  • Android->Activity四种启动模式详解和onNewIntent调用时机

    Activity四种启动模式详解 xff1a 1 standard 默认启动模式 xff0c 每次激活Activity时都会创建Activity xff0c 并放入任务栈中 xff0c 永远不会调用onNewIntent 2 singleT
  • MyEclipse调试小技巧

    前言 现在我们很多人都是使用 MyEclipse 来进行开发 xff0c 最近一段时间我也在使用 MyEclipse xff0c 结合我自己使用过程中的经验以及搜罗的一些小技巧 xff0c 在这里跟大家分享一下 xff0c 帮助我们大家一起
  • Log4j漏洞补救 Log4j2 + SLF4j 升级到最新版本

    一 背景 因Log4j的2 X版本和1 x版本接连爆出漏洞 xff0c 使Log4j不得不升级到最新版本了 xff0c 本博客整合了网上的文章结合自己的实际项目记录本次升级过程 二 搭建步骤 2 1 xff1a 去除直接和间接依赖的log4
  • 统计这句话中每个字母出现的次数 并 打印次数最多和最少的两个字母

    package com zhiyou entity import java util HashMap import java util Map public class ZYtongjicishu public static lt K V
  • 什么是范数(Norm),其具有哪些性质

    文章目录 直观的感受一下范数范数的定义直观的感受下范数的边界图像范数的性质参考资料 直观的感受一下范数 先直观的感受一下二维空间的范数 xff0c 假设在二维空间的向量为 v 61 x y

随机推荐

  • Hugging Face快速入门(重点讲解模型(Transformers)和数据集部分(Datasets))

    文章目录 本文内容HuggingFace简介Hugging Face模型讲解Transforms简介Transformers安装使用Transformers进行推理查找Hugging Face模型使用Hugging Face模型迁移学习 H
  • Eslint 规则说明

    1 34 no alert 34 0 禁止使用alert confirm prompt 2 34 no array constructor 34 2 禁止使用数组构造器 3 34 no bitwise 34 0 禁止使用按位运算符 4 34
  • eclipse无线循环输出时,怎样关闭

    eclipse控制台无限循环输出的时候找到 xff0c 找到控制台右边有一个红色方块按钮 xff0c 点击即可停止运行 如下图 有问题欢迎私聊或者发送邮箱 xff08 964427082 64 qq com xff09 一起讨论
  • goland中报错: Unresolved reference 错误解决

    前言 今天早上项目导入的包标红了 xff0c 而且也包了unresolved reference的错误 xff0c 但是程序却可以正常运行 xff0c 在网上找了多种方法 xff0c 最后可以了 xff0c 但是并不知道是哪一个起了作用 x
  • 关于打游戏ping值不稳定问题的解决经历(疑难篇)

    首先 xff0c 大概几天之前 xff0c 笔者发现自己的电脑在打游戏 xff08 lol xff09 的时候ping值忽高忽低 xff0c 就是突然从20跳到10000 43 xff0c 没有丝毫夸张 xff0c 就是这样吓人 xff0c
  • react、angularjs、vue原理应用场景总结

    React 如图 xff1a React的虚拟DOM的生成是可以在任何支持Javascript的环境生成的 xff0c 所以可以在NodeJS或Iojs环境生成 虚拟DOM可以直接转成String 然后插入到html文件中输出给浏览器便可
  • c++中对象和类的概念以及联系

    1 概念 xff1a 类是对一组性质相同的事物的程序描述 如果类在定义中不指定是private或者public的 xff0c 则系统默认为private的 使用struct声明的类 xff0c 如果对其成员不作private或者public
  • c++学习总结(一些零碎的小知识点)

    1 C 语言中 和 gt 区别 结构体变量用 运算符来访问结构体的成员 指向结构体的指针用 gt 来访问其指向的结构体的成员 gt 指向指针变量的运算符 举例 xff1a p gt m 表示指针 p 指向结构体变量中的成员 m xff1b
  • html学习之

    1 lt xff01 DOCTYPE gt 声明帮助浏览器正确的显示网页 xff0c 不是HTML标签 xff0c 它为浏览器提供了一项声明 xff0c 即HTML是用什么版本编写的 lt DOCTYE html gt lt html gt
  • Ubuntu下ssh服务器文件操作命令

    用java写了一个监视 web服务器的程序 需要部署到Ubuntu服务器版本的系统中 xff0c 遇到的第一个问题就是怎么把这个程序copy到服务器上去 xff33 xff33 xff28 服务器 什么是 xff33 xff33 xff28
  • 小狼毫配置

    小狼毫配置 安装下载 设置 安装完成后 xff0c 右键单击任务栏的小狼毫图标 xff0c 点击 输入法设定 xff0c 勾选输入法 xff08 推荐 朙月拼音 简化字 xff09 xff0c 点击 中 xff0c 选择皮肤后即可使用小狼毫
  • 第27章 联合网关 - Identity Server 4 中文文档(v1.0.0)

    通用架构是所谓的联合网关 在此方法中 xff0c IdentityServer充当一个或多个外部身份提供商的网关 该架构具有以下优点 您的应用程序只需要了解一个令牌服务 xff08 网关 xff09 xff0c 并且屏蔽了有关连接到外部提供
  • Asp.net core3.1 框架中 采用Serilog实现log日志记录

    本文以MVC框架为例 xff0c 实现log记录 在默认情况下 xff0c asp net core有自带的可实现将日志输出到控制台 xff0c 注意 xff0c 此时需要 xff0c 运行时 xff0c 要运行自托管模式才能调出控制台 如
  • 关于Lwip如何实现单网卡多IP设置

    记录 xff1a 关于Lwip如何实现单网卡多IP设置 https wenku baidu com view fb49542683d049649b6658fe html https blog csdn net sinat 20006769
  • 学习java的第一步

    1 走进Java 1 Java的三个平台版本 1 JavaSE J2SE JavaSE是其他两个平台版本的基础 2 JavaME J2ME 针对于移动端开发的版本 3 JavaEE J2EE 针对于web应用的开发版本 跨平台性 因为Jav
  • 面向对象的三大特点

    封装继承多态 1 封装 概念 xff1a 隐藏对象的属性和实现细节 xff0c 对外提供公共的访问方式 原则 xff1a 不需要用户访问的内容隐藏起来 优点 xff1a 1 安全性高 2 独立性高 3 复用性高 span class tok
  • 面向对象版图书管理系统

    span class token keyword package span org span class token punctuation span wdit span class token punctuation span unit0
  • 抽象类

    1 抽象类 概述 xff1a 用来描述抽象概念的类 xff0c 叫做抽象类 抽象类中的方法不一定有具体的实现 span class token keyword package span org span class token punctu
  • 对于MyBatis框架的学习

    1 MyBatis MyBatis 它是Apache的一个开源项目 iBatis 2010年这个项目由apache software foundation 迁 移到了google code xff0c 并且改名为MyBatis 2013年1
  • Spring Boot 原理的分析(超详细!!!)

    1 Spring Boot Spring Boot 没有特定的业务 xff0c 将其他框架进行整合 xff0c 去掉配置 开箱即用 Spring Boot 跟 Spring MVC 的整合 Spring Boot 跟 Thymeleaf 的