SpringBoot初探

SpringBoot初探

一、SpringBoot介绍

回顾什么是Spring

Spring是一个开源框架,2003 年兴起的一个轻量级的Java 开发框架,作者:Rod Johnson 。

Spring是为了解决企业级应用开发的复杂性而创建的,简化开发。

Spring框架是如何简化开发的

为了降低Java开发的复杂性,Spring采用了以下4种关键策略:

  • 基于POJO的轻量级和最小侵入性编程,所有东西都是bean;

  • 通过IOC,依赖注入(DI)和面向接口实现松耦合;

  • 基于切面(AOP)和惯例进行声明式编程;

  • 通过切面和模版减少样式代码,RedisTemplate,xxxTemplate;

什么是SpringBoot

学过javaweb的同学就知道,开发一个web应用,从最初开始接触Servlet结合Tomcat, 跑出一个Hello Wolrld程序,是要经历特别多的步骤;后来就用了框架Struts,再后来是SpringMVC,到了现在的SpringBoot,过一两年又会有其他web框架出现;你们有经历过框架不断的演进,然后自己开发项目所有的技术也在不断的变化、改造吗?建议都可以去经历一遍;

言归正传,什么是SpringBoot呢,就是一个javaweb的开发框架,和SpringMVC类似,对比其他javaweb框架的好处,官方说是简化开发,约定大于配置, you can “just run”,能迅速的开发web应用,几行代码开发一个http接口。

所有的技术框架的发展似乎都遵循了一条主线规律:从一个复杂应用场景 衍生 一种规范框架,人们只需要进行各种配置而不需要自己去实现它,这时候强大的配置功能成了优点;发展到一定程度之后,人们根据实际生产应用情况,选取其中实用功能和设计精华,重构出一些轻量级的框架;之后为了提高开发效率,嫌弃原先的各类配置过于麻烦,于是开始提倡“约定大于配置”,进而衍生出一些一站式的解决方案。

是的这就是Java企业级应用->J2EE->spring->springboot的过程。

随着 Spring 不断的发展,涉及的领域越来越多,项目整合开发需要配合各种各样的文件,慢慢变得不那么易用简单,违背了最初的理念,甚至人称配置地狱。Spring Boot 正是在这样的一个背景下被抽象出来的开发框架,目的为了让大家更容易的使用 Spring 、更容易的集成各种常用的中间件、开源软件;

Spring Boot 基于 Spring 开发,Spirng Boot 本身并不提供 Spring 框架的核心特性以及扩展功能,只是用于快速、敏捷地开发新一代基于 Spring 框架的应用程序。也就是说,它并不是用来替代 Spring 的解决方案,而是和 Spring 框架紧密结合用于提升 Spring 开发者体验的工具。Spring Boot 以约定大于配置的核心思想,默认帮我们进行了很多设置,多数 Spring Boot 应用只需要很少的 Spring 配置。同时它集成了大量常用的第三方库配置(例如 Redis、MongoDB、JPA、RabbitMQ、Quartz 等等),Spring Boot 应用中这些第三方库几乎可以零配置的开箱即用。

简单来说就是SpringBoot其实不是什么新的框架,它默认配置了很多框架的使用方式,就像maven整合了所有的jar包,spring boot整合了所有的框架 。

Spring Boot 出生名门,从一开始就站在一个比较高的起点,又经过这几年的发展,生态足够完善,Spring Boot 已经当之无愧成为 Java 领域最热门的技术。

Spring Boot的主要优点:

  • 为所有Spring开发者更快的入门
  • 开箱即用,提供各种默认配置来简化项目配置
  • 内嵌式容器简化Web项目
  • 没有冗余代码生成和XML配置的要求

SpringBoot应用的创建

准备工作

我们将学习如何快速的创建一个Spring Boot应用,并且实现一个简单的Http请求处理。通过这个例子对Spring Boot有一个初步的了解,并体验其结构简单、开发快速的特性。

我的环境准备:

  • java version “1.8.0_181”
  • Maven-3.6.3
  • SpringBoot 2.x 最新版

开发工具:

  • IDEA

创建Springboot项目的两种方式

项目创建方式一

Spring官方提供了非常方便的工具让我们快速构建应用

Spring Initializr:https://start.spring.io/

通过使用Spring Initializr 的 Web页面创建项目

1、打开 https://start.spring.io/

2、填写项目信息

3、点击”Generate Project“按钮生成项目;下载此项目

4、解压项目包,并用IDEA以Maven项目导入,一路下一步即可,直到项目导入完毕。

5、如果是第一次使用,可能速度会比较慢,包比较多、需要耐心等待一切就绪。

项目创建方式二:

通过IDEA直接创建一个新的项目

1、创建一个新项目(new project)

2、选择spring initalizr(可以看到默认就是去官网的快速构建工具那里实现)

3、填写项目信息

4、选择初始化的组件(初学勾选 Web 即可)

5、填写项目路径

6、等待项目构建成功

项目结构分析:

通过上面步骤完成了基础项目的创建。就会自动生成以下文件。

1、程序的主启动类(SpringbootXXXApplication)

2、一个 application.properties 配置文件

3、一个 测试类

4、一个 pom.xml

pom.xml文件分析

打开pom.xml,看看Spring Boot项目的依赖:


<!-- 父依赖 -->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.5.RELEASE</version>
    <relativePath/>
</parent>

<dependencies>
    <!-- web场景启动器 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- springboot单元测试 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
        <!-- 剔除依赖 -->
        <exclusions>
            <exclusion>
                <groupId>org.junit.vintage</groupId>
                <artifactId>junit-vintage-engine</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
</dependencies>

<build>
    <plugins>
        <!-- 打包插件 -->
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

Hello World的实现

编写一个HTTP接口

1、在主程序的同级目录下,新建一个controller包,一定要在同级目录下,否则识别不到

2、在包中新建一个HelloController类

@RestController  //返回一个JSON字符串
public class HelloController {

    @RequestMapping("/hello")
    public String hello() {
        return "Hello World";
    }
    
}

3、编写完毕后,从主程序启动项目,浏览器发起请求,看页面返回;控制台输出了 Tomcat 访问的端口号!

4、在网页中输入localhost:端口号 即可!

二、SpringBoot运行原理初探

核心:自动装配

pom.xml文件

  • spirng-boot-dependencies:核心依赖在父工程中
  • 我们在写一些springboot依赖的时候,不需要指定版本,因为有这些依赖的版本库(跟随Springboot更新)

启动器概念spring-boot-starter

	<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.4.4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <!-- 其中点进去还有一个父依赖
 	这里才是真正管理SpringBoot应用里面所有依赖版本的地方,SpringBoot的版本控制中心;
    以后我们导入依赖默认是不需要写版本;但是如果导入的包没有在依赖中管理着就需要手动配置版本了;
	-->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-dependencies</artifactId>
        <version>2.4.4</version>
	</parent>
	<dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>
  • 启动器:说白了就是SpringBoot的启动场景;
    • 比如spring-boot-starter-web,他就会帮我们自动导入web环境的所有依赖
    • SpringBoot会将所有的功能场景,都变成一个一个的启动器(相当于封装在一个容器里,该容器为spingboot服务)
    • 我们需要使用什么功能,就只需要找到对应的启动器就可以了starter

SpringApplication

// @SpringBootApplication 标记这个类是一个springboot的应用
// 标注在某个类上说明这个类是SpringBoot的主配置类,SpringBoot就应该运行这个类的main方法来启动SpringBoot应用;
@SpringBootApplication
public class Springboot01Application {

    public static void main(String[] args) {
		//将SpringBoot应用启动
        SpringApplication.run(Springboot01Application.class, args);
    }

}

这个类主要做了以下四件事情:

1、推断应用的类型是普通的项目还是Web项目

2、查找并加载所有可用初始化器 , 设置到initializers属性中

3、找出所有的应用程序监听器,设置到listeners属性中

4、推断并设置main方法的定义类,找到运行的主类

  • 注解

    • @SpringBootConfiguration:SpringBoot配置
      	@Configuration:Spring配置类
      		@Component:启动类本身也是Spring中的一个组件
      @EnableAutoConfiguration:开启自动配置功能
      	@AutoConfigurationPackage:自动配置包
      		@Import(AutoConfigurationPackages.Registrar.class):自动配置`包注册`Spring底层注解@import , 给容器中导入一个组件
      			Registrar.class 作用:将主启动类的所在包及包下面所有子包里面的所有组件扫描到Spring容器 ;
      @Import(AutoConfigurationImportSelector.class):自动配置导入到容器中
      		AutoConfigurationImportSelector :自动配置导入选择器,那么它会导入哪些组件的选择器呢?我们点击去这个类看源码:
      @ComponentScan:自动扫描并加载符合条件的组件或者bean , 将这个bean定义加载到IOC容器中
      

spring.factories

spring.factories文件是自动配置根源所在!

# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener

# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer

# Auto Configuration Import Listeners
org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\
org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener

# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
org.springframework.boot.autoconfigure.condition.OnClassCondition,\
org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\

我们在上面的自动配置类随便找一个打开看看,比如 :WebMvcAutoConfiguration

@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
		ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {

	/**
	 * The default Spring MVC view prefix.
	 */
	public static final String DEFAULT_PREFIX = "";

	/**
	 * The default Spring MVC view suffix.
	 */
	public static final String DEFAULT_SUFFIX = "";

	private static final String SERVLET_LOCATION = "/";

	@Bean
	@ConditionalOnMissingBean(HiddenHttpMethodFilter.class)
	@ConditionalOnProperty(prefix = "spring.mvc.hiddenmethod.filter", name = "enabled", matchIfMissing = false)
	public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {
		return new OrderedHiddenHttpMethodFilter();
	}

可以看到这些一个个的都是JavaConfig配置类,而且都注入了一些Bean,可以找一些自己认识的类,看着熟悉一下!

所以,自动配置真正实现是从classpath中搜寻所有的META-INF/spring.factories配置文件 ,并将其中对应的 org.springframework.boot.autoconfigure. 包下的配置项,通过反射实例化为对应标注了 @Configuration的JavaConfig形式的IOC容器配置类 , 然后将这些都汇总成为一个实例并加载到IOC容器中。

结论:

  1. SpringBoot在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值
  2. 将这些值作为自动配置类导入容器 , 自动配置类就生效 , 帮我们进行自动配置工作;
  3. 整个J2EE的整体解决方案和自动配置都在springboot-autoconfigure的jar包中;
  4. 它会给容器中导入非常多的自动配置类 (xxxAutoConfiguration), 就是给容器中导入这个场景需要的所有组件 , 并配置好这些组件 ;
  5. 有了自动配置类 , 免去了我们手动编写配置注入功能组件等的工作;

三、SpringBoot配置注入

配置文件

**配置文件的作用 :**修改SpringBoot自动配置的默认值,因为SpringBoot在底层都给我们自动配置好了;

比如我们可以在配置文件中修改Tomcat 默认启动的端口号!测试一下!

server:
	port: 8081

properties方式(application.properties)

语法:key=value

yaml/yml方式 (application.yml)

YAML是 “YAML Ain’t a Markup Language” (YAML不是一种标记语言)的递归缩写。在开发的这种语言时,YAML 的意思其实是:“Yet Another Markup Language”(仍是一种标记语言)

这种语言以数据作为中心,而不是以标记语言为重点!

方式:key:空格value

说明:语法要求严格!

1、空格不能省略

2、以缩进来控制层级关系,只要是左边对齐的一列数据都是同一个层级的。

3、属性和值的大小写都是十分敏感的。

注意:

  • “ ” 双引号,不会转义字符串里面的特殊字符 , 特殊字符会作为本身想表示的意思;

    比如 :name: “zz \n zz” 输出 :zz 换行 zz

  • ‘’ 单引号,会转义特殊字符 , 特殊字符最终会变成和普通字符一样输出

    比如 :name: ‘zz \n zz’ 输出 :zz \n zz

支持方式

① key-value

name: zxg

②对象、Map(键值对)

student: {	name: zxg,age: 21}   

student:
	name: zxg
	age: 21

③数组注入

pets: [cat,dog,big]

pets:
	- cat
	- dog
	- pig

属性注值

yaml文件更强大的地方在于,他可以给我们的实体类直接注入匹配值!

yaml注入配置文件

1、在springboot项目中的resources目录下新建一个文件 application.yml

2、编写一个实体类 Dog;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.stereotype.Component;

@Component  //注册bean到容器中
//有参无参构造、get、set方法、toString()方法
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Dog {
    private String name;
    private Integer age;
}

3、思考,我们原来是如何给bean注入属性值的!@Value,给狗狗类测试一下:


@Component //注册bean
//有参无参构造、get、set方法、toString()方法
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Dog {
    @Value("zxg")
    private String name;
    @Value("21")
    private Integer age;
}

4、在SpringBoot的测试类下注入狗狗输出一下;

@SpringBootTest
public class SpringbootPojoApplicationTests {
    @Autowired
    Dog dog;

    @Test
    void dogTest(){
        System.out.println(dog);
    }
}

结果成功输出,@Value注入成功,这是我们原来的办法。

5、我们在编写一个复杂一点的实体类:Person 类


@Component //注册bean到容器中
//有参无参构造、get、set方法、toString()方法
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person {
    private String name;
    private Integer age;
    private Boolean happy;
    private Date birth;
    private Map<String,Object> maps;
    private List<Object> lists;
    private Dog dog;
}

6、我们来使用yaml配置的方式进行注入,大家写的时候注意区别和优势,我们编写一个yaml配置!

person:
  name: zxg
  age: 21
  happy: false
  birth: 2000/01/01
  maps: {k1: v1,k2: v2}
  lists:
   - code
   - girl
   - music
  dog:
    name: 虎子
    age: 3

7、我们刚才已经把person这个对象的所有值都写好了,我们现在来注入到我们的类中!

在对应的实体类上加入@ConfigurationProperties(prefix = "person"),使用该注解时则这个类为Spring容器中的组件,通常使用@Component注册Bean

/*
@ConfigurationProperties作用:
将配置文件中配置的每一个属性的值,映射到这个组件中;
告诉SpringBoot将本类中的所有属性和配置文件中相关的配置进行绑定
参数 prefix = “person” : 将配置文件中的person下面的所有属性一一对应
*/
@Component //注册bean到容器中
//有参无参构造、get、set方法、toString()方法
@Data
@AllArgsConstructor
@NoArgsConstructor
@ConfigurationProperties(prefix = "person")
public class Person {
    private String name;
    private Integer age;
    private Boolean happy;
    private Date birth;
    private Map<String,Object> maps;
    private List<Object> lists;
    private Dog dog;
}

8、IDEA 提示,springboot配置注解处理器没有找到,需要加入一个配置依赖!

<!-- 导入配置文件处理器,配置文件进行绑定就会有提示,需要重启 -->
    <dependency>  
        <groupId>org.springframework.boot</groupId>  
        <artifactId>spring-boot-configuration-processor</artifactId>  
        <optional>true</optional>
    </dependency>

9、确认以上配置都OK之后,我们去测试类中测试一下:

@SpringBootTest
public class SpringbootPojoApplicationTests {
    @Autowired
    Person person;

    @Test
    void personTest(){
        System.out.println(person);
    }
}

结果:所有值全部注入成功!

yaml配置注入到实体类完全OK!

@ConfigurationProperties(yml方式)与@Value比较

1、@ConfigurationProperties只需要写一次即可 , @Value则需要每个字段都添加

2、松散绑定:这个什么意思呢? 比如我的yml中写的last-name,这个和lastName是一样的, - 后面跟着的字母默认是大写的。这就是松散绑定。可以测试一下

3、JSR303数据校验:这个就是我们可以在字段是增加一层过滤器验证 , 可以保证数据的合法性

4、复杂类型封装:yml中可以封装对象 , 使用value就不支持

结论:

配置yml和配置properties都可以获取到值 , 强烈推荐 yml;

如果我们在某个业务中,只需要获取配置文件中的某个值,可以使用一下 @value;

如果说,我们专门编写了一个JavaBean来和配置文件进行一一映射,就直接@configurationProperties,不要犹豫!

四、JSR303数据校验及多环境切换

JSR303数据校验

Springboot中可以用@validated来校验数据,如果数据异常则会统一抛出异常,方便异常中心统一处理。我们这里来写个注解让我们的name只能支持Email格式;


@Component //注册bean
@ConfigurationProperties(prefix = "person")
@Validated  //数据校验
public class Person {

    @Email(message="邮箱格式错误") //name必须是邮箱格式
    private String name;
}

运行结果 :default message [不是一个合法的电子邮件地址];

使用数据校验,可以在服务器端保证数据的正确性;

常见参数


@NotNull(message="名字不能为空")
private String userName;
@Max(value=120,message="年龄最大不能查过120")
private int age;
@Email(message="邮箱格式错误")
private String email;

空检查
@Null       验证对象是否为null
@NotNull    验证对象是否不为null, 无法查检长度为0的字符串
@NotBlank   检查约束字符串是不是Null还有被Trim的长度是否大于0,只对字符串,且会去掉前后空格.
@NotEmpty   检查约束元素是否为NULL或者是EMPTY.
    
Booelan检查
@AssertTrue     验证 Boolean 对象是否为 true  
@AssertFalse    验证 Boolean 对象是否为 false  
    
长度检查
@Size(min=, max=) 验证对象(Array,Collection,Map,String)长度是否在给定的范围之内  
@Length(min=, max=) string is between min and max included.

日期检查
@Past       验证 Date 和 Calendar 对象是否在当前时间之前  
@Future     验证 Date 和 Calendar 对象是否在当前时间之后  
@Pattern    验证 String 对象是否符合正则表达式的规则

.......等等
除此以外,我们还可以自定义一些数据校验规则

多环境切换

profile是Spring对不同环境提供不同配置功能的支持,可以通过激活不同的环境版本,实现快速切换环境;

properties的多配置文件

我们在主配置文件编写的时候,文件名可以是 application-{profile}.properties/yml , 用来指定多个环境版本;

例如:

application-test.properties 代表测试环境配置

application-dev.properties 代表开发环境配置

但是Springboot并不会直接启动这些配置文件,它默认使用application.properties主配置文件

我们需要通过一个配置来选择需要激活的环境:


#比如在配置文件中指定使用dev环境,我们可以通过设置不同的端口号进行测试;
#我们启动SpringBoot,就可以看到已经切换到dev下的配置了;
spring.profiles.active=dev

yaml的多文档块

和properties配置文件中一样,但是使用yml去实现不需要创建多个配置文件,更加方便了 !

#启动对应端口号开启对应配置环境
server:
 port: 8081
#选择要激活那个环境块
spring:
 profiles:
   active: prod

---
server:
 port: 8083
spring:
 profiles: dev #配置环境的名称


---

server:
 port: 8084
spring:
 profiles: prod  #配置环境的名称

注意:如果yml和properties同时都配置了端口,并且没有激活其他环境 , 默认会使用properties配置文件的!

五、自动装配原理

我们的配置文件到底能写些什么配置,在SpringBoot中有非常大量的配置,我们无法一一记住,但相关配置都是有分类的

分析自动配置原理

我们以**HttpEncodingAutoConfiguration(Http编码自动配置)**为例解释自动配置原理;

//表示这是一个配置类,和以前编写的配置文件一样,也可以给容器中添加组件;
@Configuration 

//启动指定类的ConfigurationProperties功能;
  //进入这个HttpProperties查看,将配置文件中对应的值和HttpProperties绑定起来;
  //并把HttpProperties加入到ioc容器中
@EnableConfigurationProperties({HttpProperties.class}) 

//Spring底层@Conditional注解
  //根据不同的条件判断,如果满足指定的条件,整个配置类里面的配置就会生效;
  //这里的意思就是判断当前应用是否是web应用,如果是,当前配置类生效
@ConditionalOnWebApplication(
    type = Type.SERVLET
)

//判断当前项目有没有这个类CharacterEncodingFilter;SpringMVC中进行乱码解决的过滤器;
@ConditionalOnClass({CharacterEncodingFilter.class})

//判断配置文件中是否存在某个配置:spring.http.encoding.enabled;
  //如果不存在,判断也是成立的
  //即使我们配置文件中不配置pring.http.encoding.enabled=true,也是默认生效的;
@ConditionalOnProperty(
    prefix = "server.servlet.encoding",
    value = {"enabled"},
    matchIfMissing = true
)

public class HttpEncodingAutoConfiguration {
    //他已经和SpringBoot的配置文件映射了
    private final Encoding properties;
    //只有一个有参构造器的情况下,参数的值就会从容器中拿
    public HttpEncodingAutoConfiguration(HttpProperties properties) {
        this.properties = properties.getEncoding();
    }
    
    //给容器中添加一个组件,这个组件的某些值需要从properties中获取
    @Bean
    @ConditionalOnMissingBean //判断容器没有这个组件?
    public CharacterEncodingFilter characterEncodingFilter() {
        CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
        filter.setEncoding(this.properties.getCharset().name());
        filter.setForceRequestEncoding(this.properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpProperties.Encoding.Type.REQUEST));
        filter.setForceResponseEncoding(this.properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpProperties.Encoding.Type.RESPONSE));
        return filter;
    }
    //.........
}

总结 :根据当前不同的条件判断,决定这个配置类是否生效!

  • 一但这个配置类生效;这个配置类就会给容器中添加各种组件;问:怎么生效,加入@ConfigurationProperties(prefix = "prefixname")
  • 这些组件的属性是从对应的properties类中获取的,这些类里面的每一个属性又是和配置文件绑定的;
  • 所有在配置文件中能配置的属性都是在xxxxProperties类中封装着;
  • 配置文件能配置什么就可以参照某个功能对应的这个属性类
//从配置文件中获取指定的值和bean的属性进行绑定
@ConfigurationProperties(prefix = "server.servlet") 
public class HttpProperties {
    // .....
}

我们再去配置文件里面试试前缀,看提示!
在这里插入图片描述

这就是自动装配的原理!

自动装配

1、SpringBoot启动会加载大量的自动配置类

2、我们看我们需要的功能有没有在SpringBoot默认写好的自动配置类当中;

3、我们再来看这个自动配置类中到底配置了哪些组件;(只要我们要用的组件存在在其中,我们就不需要再手动配置了)

4、给容器中自动配置类添加组件的时候,会从properties类中获取某些属性。我们只需要在配置文件中指定这些属性的值即可;

xxxxAutoConfigurartion:自动配置类;给容器中添加组件

xxxxProperties:封装配置文件中相关属性;

我们怎么知道哪些自动配置类生效了?

我们可以通过启用 debug=true属性;来让控制台打印自动配置报告,这样我们就可以很方便的知道哪些自动配置类生效;

#开启springboot的调试类
debug=true

Positive matches:(自动配置类启用的:正匹配)

Negative matches:(没有启动,没有匹配成功的自动配置类:负匹配)

Unconditional classes: (没有条件的类)

六、静态资源处理

第一种静态资源映射规则

首先,我们搭建一个普通的SpringBoot项目,回顾一下HelloWorld程序!

写请求非常简单,那我们要引入我们前端资源,我们项目中有许多的静态资源,比如css,js等文件,这个SpringBoot怎么处理呢?

如果我们是一个web应用,我们的main下会有一个webapp,我们以前都是将所有的页面导在这里面的,对吧!但是我们现在的pom呢,打包方式是为jar的方式,那么这种方式SpringBoot能不能来给我们写页面呢?当然是可以的,但是SpringBoot对于静态资源放置的位置,是有规定的!

我们先来聊聊这个静态资源映射规则:

SpringBoot中,SpringMVC的web配置都在 WebMvcAutoConfiguration 这个配置类里面;

我们可以去看看 WebMvcAutoConfigurationAdapter 中有很多配置方法;

有一个方法:addResourceHandlers 添加资源处理

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    if (!this.resourceProperties.isAddMappings()) {
        // 已禁用默认资源处理
        logger.debug("Default resource handling disabled");
        return;
    }
    // 缓存控制
    Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
    CacheControl cacheControl = this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl();
    // webjars 配置
    if (!registry.hasMappingForPattern("/webjars/**")) {
        customizeResourceHandlerRegistration(registry.addResourceHandler("/webjars/**")
                                             .addResourceLocations("classpath:/META-INF/resources/webjars/")
                                             .setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
    }
    // 静态资源配置
    String staticPathPattern = this.mvcProperties.getStaticPathPattern();
    if (!registry.hasMappingForPattern(staticPathPattern)) {
        customizeResourceHandlerRegistration(registry.addResourceHandler(staticPathPattern)
                                             .addResourceLocations(getResourceLocations(this.resourceProperties.getStaticLocations()))
                                             .setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
    }
}

读一下源代码:比如所有的 /webjars/** , 都需要去 classpath:/META-INF/resources/webjars/ 找对应的资源;

什么webjars呢?

Webjars本质就是以jar包的方式引入我们的静态资源 , 我们以前要导入一个静态资源文件,直接导入即可。

使用SpringBoot需要使用Webjars,我们可以去搜索一下:

网站:https://www.webjars.org

要使用jQuery,我们只要要引入jQuery对应版本的pom依赖即可!

<dependency>
    <groupId>org.webjars</groupId>
    <artifactId>jquery</artifactId>
    <version>3.4.1</version>
</dependency>

导入完毕,可去Maven库中的org.webjars目录结构查看,并访问Jquery.js文件!

**访问:**只要是静态资源,SpringBoot就会去对应的路径寻找资源,我们这里访问:http://localhost:8080/webjars/jquery/3.4.1/jquery.js

第二种静态资源映射规则

那我们项目中要是使用自己的静态资源该怎么导入呢?我们看下一行代码;

我们去找staticPathPattern发现第二种映射规则 :/** , 访问当前的项目任意资源,它会去找 resourceProperties 这个类,我们可以点进去看一下分析:

// 进入方法
public String[] getStaticLocations() {
    return this.staticLocations;
}
// 找到对应的值
private String[] staticLocations = CLASSPATH_RESOURCE_LOCATIONS;
// 找到路径
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = { 
    "classpath:/META-INF/resources/",
  "classpath:/resources/", 
    "classpath:/static/", 
    "classpath:/public/" 
};

ResourceProperties 可以设置和我们静态资源有关的参数;这里面指向了它会去寻找资源的文件夹,即上面数组的内容。

所以得出结论,以下四个目录存放的静态资源可以被我们识别:

"classpath:/META-INF/resources/"
"classpath:/resources/"
"classpath:/static/"
"classpath:/public/"

我们可以在resources根目录下新建对应的文件夹,都可以存放我们的静态文件;

比如我们访问 http://localhost:8080/1.js , 他就会去这些文件夹中寻找对应的静态资源文件;

自定义静态资源路径配置

我们也可以自己通过配置文件来指定一下,哪些文件夹是需要我们放静态资源文件的,在application.properties中配置;

spring.resources.static-locations=classpath:/coding/,classpath:/zxg/

注:一旦自己定义了静态文件夹的路径,原来的自动配置就都会失效了!

七、Thymeleaf模板引擎

什么是模板引擎?

前端交给我们的页面,是html页面。如果是我们以前开发,我们需要把他们转成jsp页面,jsp好处就是当我们查出一些数据转发到JSP页面以后,我们可以用jsp轻松实现数据的显示,及交互等。

jsp支持非常强大的功能,包括能写Java代码,但是呢,我们现在的这种情况,SpringBoot这个项目首先是以jar的方式,不是war,我们用的还是嵌入式的Tomcat,所以呢,他现在默认是不支持jsp的

那不支持jsp,如果我们直接用纯静态页面的方式,那给我们开发会带来非常大的麻烦,那怎么办呢?

随后,SpringBoot使用模板引擎来处理传入到页面的数据

模板引擎,我们其实大家听到很多,其实jsp就是一个模板引擎,还有用的比较多的freemarker,包括SpringBoot给我们推荐的Thymeleaf,模板引擎的作用就是我们来写一个页面模板,比如有些值呢,是动态的,我们写一些表达式。而这些值,从哪来呢,就是我们在后台封装一些数据。然后把这个模板和这个数据交给我们模板引擎,模板引擎按照我们这个数据帮你把这表达式解析、填充到我们指定的位置,然后把这个数据最终生成一个我们想要的内容给我们写出去,这就是我们这个模板引擎,不管是jsp还是其他模板引擎,都是这个思想。不同模板引擎之间,他们的语法可能有点不一样。

注:Spring-boot支持FreeMarker、Thymeleaf、jsp、veocity 。但是对freemarker和thymeleaf的支持最好,不推荐使用jsp

引入Thymeleaf

对于SpringBoot应用来说,引入资源,就等于加入相应的starter。

Thymeleaf 官网:https://www.thymeleaf.org/

对应的pom依赖:

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

Thymeleaf分析

查看Thymeleaf的自动配置类ThymeleafProperties

@ConfigurationProperties(
    prefix = "spring.thymeleaf"
)
public class ThymeleafProperties {
    private static final Charset DEFAULT_ENCODING;
    public static final String DEFAULT_PREFIX = "classpath:/templates/";
    public static final String DEFAULT_SUFFIX = ".html";
    private boolean checkTemplate = true;
    private boolean checkTemplateLocation = true;
    private String prefix = "classpath:/templates/";
    private String suffix = ".html";
    private String mode = "HTML";
    private Charset encoding;
}

我们可以在其中看到默认的前缀和后缀!

我们只需要把我们的html页面放在类路径下的templates下,thymeleaf就可以帮我们自动渲染了。

使用thymeleaf什么都不需要配置,只需要将他放在指定的文件夹下即可!

Thymeleaf语法

th属性

html有的属性,Thymeleaf基本都有,而常用的属性大概有七八个。其中th属性执行的优先级从1~8,数字越低优先级越高。

  • th:text :设置当前元素的文本内容,相同功能的还有th:utext,两者的区别在于前者不会转义html标签,后者会。

  • th:value:设置当前元素的value值,类似修改指定属性的还有th:src,th:href。

  • th:each:遍历循环元素,和th:text或th:value一起使用。注意该属性修饰的标签位置,详细往后看。

  • th:if:条件判断,类似的还有th:unless,th:switch,th:case。

  • th:insert:代码块引入,类似的还有th:replace,th:include,三者的区别较大,若使用不恰当会破坏html结构,常用于公共代码块提取的场景。

  • th:fragment:定义代码块,方便被th:insert引用。

  • th:object:声明变量,一般和*{}一起配合使用,达到偷懒的效果。

  • th:attr:修改任意属性,实际开发中用的较少,因为有丰富的其他th属性帮忙,类似的还有th:attrappend,th:attrprepend。

使用Thymeleaf属性需要注意点以下几点:

  • 若要使用Thymeleaf语法,首先要声明名称空间: xmlns:th="www.thymeleaf.org"

  • 设置文本内容 th:text,设置input的值 th:value,循环输出 th:each,条件判断 th:if,插入代码块 th:insert,定义代码块 th:fragment,声明变量 th:object

  • th:each 的用法需要格外注意,打个比方:如果你要循环一个div中的p标签,则th:each属性必须放在p标签上。若你将th:each属性放在div上,则循环的是将整个div。

  • 变量表达式中提供了很多的内置方法,该内置方法是用#开头,请不要与#{}消息表达式弄混。

案例
  • 1、在controller中绑定数据:
@Controller
public class ThController {
    @RequestMapping("/success")
    public String success(Model model){
        //1.绑定一个字符串
        model.addAttribute("msg","this is a <b>String</b>");
        model.addAttribute("msgUtext","this is a <b>String</b>");

        //2.绑定一个pojo对象--先去创建一个Emp
        Emp emp=new Emp("张三",20);
        model.addAttribute("emp",emp);

        //3.绑定一个list
        List list=new ArrayList();
        list.add(emp);
        list.add(new Emp("李四",20));
        list.add(new Emp("王五",20));
        model.addAttribute("emps",list);

        //4.绑定一个map
        Map<String,Object> map=new HashMap<>();
        map.put("Boss",new Emp("boss",30));
        model.addAttribute("map",map);

        //前缀:classpath:/templates/ success  后缀.html
        return "success";
    }
}
  • 2、在html中使用thmeleaf语法获取数据

    先声明命名空间

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>this is success.html</h1>
</body>
</html>
th:text和th:value
<!--th:text 设置当前元素的文本内容,常用,优先级不高-->
<p th:text="${msg}"></p>
<p th:utext="${msgUtext}"></p>

<!--th:value 设置当前元素的value值,常用,优先级仅比th:text高-->
姓名:<input type="text" th:value="${emp.name}" />
年龄:<input type="text" th:value="${emp.age}" />

th:each

<!--th:each 遍历列表,常用,优先级很高,仅此于代码块的插入,遍历被修饰的元素-->
<table border="1px" th:width="200px">
    <thead>
        <tr>
            <th>编号</th>
            <th>姓名</th>
            <th>年龄</th>
            <th>操作</th>
        </tr>
    </thead>
    <tbody>
        <tr th:each="e:${emps}">
            <td>编号</td>
            <td th:text="${e.name}"></td>
            <td th:text="${e.age}"></td>
            <td>删除/修改</td>
        </tr>
    </tbody>
    <tbody>
    <tr th:each="e,eState:${emps}">
        <td th:text="${eState.index+1}"></td>
        <td th:text="${e.name}"></td>
        <td th:text="${e.age}"></td>
        <td>删除/修改</td>
    </tr>
    </tbody>
</table>
<!-- 
说明:
th:each="e,eState : ${emps}"
其中e为循环的每一项,eState是下标属性(可省略),eState属性包括:
index:列表状态的序号,从0开始;
count:列表状态的序号,从1开始;
size:列表状态,列表数据条数;
current:列表状态,当前数据对象
even:列表状态,是否为奇数,boolean类型
odd:列表状态,是否为偶数,boolean类型
first:列表状态,是否为第一条,boolean类型
last:列表状态,是否为最后一条,boolean类型
-->

th:if

<!--th:if 条件判断,类似的有th:switch,th:case,优先级仅次于th:each,-->
<p th:text="${map.Boss.name}" th:if="${map.Boss.age gt 20}"></p>

<!--
其中关系运算的缩写:
gt:great than(大于)
ge:great equal(大于等于)
eq:equal(等于)
lt:less than(小于)
le:less equal(小于等于)
ne:not equal(不等于)
-->
标准表达式语法

消息表达式 #{…}

消息表达式一般用于消息提示的场景。例如:国际化,结构:th:placeholder="#{login.username}"。

<input type="text" class="form-control" name="username" th:placeholder="#{login.username}" required="" autofocus="">

链式表达式 @{…}

  • 无参:@{/xxx}
  • 有参:@{/xxx(k1=v1,k2=v2)} 对应url结构:xxx?k1=v1&k2=v2
  • 引入本地资源:@{/项目本地的资源路径}
  • 引入外部资源:@{/webjars/资源在jar包中的路径}
<!--表单提交路径 @{/xxx}-->
<form class="form-signin" th:action="@{/login}" method="post">

<!--超链接跳转路径附带参数 @{/xxx(k1=v1,k2=v2)}-->
<a class="btn btn-sm" th:href="@{/index.html(lang='zh_CN')}">中文</a>
<a class="btn btn-sm" th:href="@{/index.html(lang='en_US')}">English</a>

<!--引入外部资源 @{/webjars/资源在jar包中的路径}-->
<link th:href="@{/webjars/bootstrap/4.0.0/css/bootstrap.css}" rel="stylesheet">

<!--引入本地资源 @{/项目本地的资源路径}-->
<link th:href="@{/css/signin.css}" rel="stylesheet">

变量表达式${…}

变量表达式有丰富的内置方法、内置对象,使其更强大,更方便。

  • 可以获取对象的属性和方法

  • 可以使用ctx,vars,locale,request,response,session,servletContext内置对象

  • 可以使用dates,numbers,strings,objects,arrays,lists,sets,maps等内置方法

    例如

// java 代码将用户名放在session中
session.setAttribute("userinfo",username);
// Thymeleaf通过内置对象直接获取
th:text="${session.userinfo}"

内置方法

  • strings:字符串格式化方法,常用的Java方法它都有。比如:equals,equalsIgnoreCase,length,trim,toUpperCase,toLowerCase,indexOf,substring,replace,startsWith,endsWith,contains,containsIgnoreCase等

  • numbers:数值格式化方法,常用的方法有:formatDecimal等

  • bools:布尔方法,常用的方法有:isTrue,isFalse等

  • arrays:数组方法,常用的方法有:toArray,length,isEmpty,contains,containsAll等

  • lists,sets:集合方法,常用的方法有:toList,size,isEmpty,contains,containsAll,sort等

  • maps:对象方法,常用的方法有:size,isEmpty,containsKey,containsValue等

  • dates:日期方法,常用的方法有:format,year,month,hour,createNow等

// java 代码将itdragonStr放在model中
model.addAttribute("itdragonStr","this is a DEMO");
<!-- 在html中使用内置方法 -->
<h3>#strings</h3>
<div th:if="${not #strings.isEmpty(itdragonStr)}" >
    <p>Old Str : <span th:text="${itdragonStr}"/></p>
    <p>toUpperCase : <span th:text="${#strings.toUpperCase(itdragonStr)}"/></p>
    <p>toLowerCase : <span th:text="${#strings.toLowerCase(itdragonStr)}"/></p>
    <p>equals : <span th:text="${#strings.equals(itdragonStr, 'itdragonblog')}"/></p>
    <p>equalsIgnoreCase : <span th:text="${#strings.equalsIgnoreCase(itdragonStr, 'itdragonblog')}"/></p>
    <p>indexOf : <span th:text="${#strings.indexOf(itdragonStr, 'r')}"/></p>
    <p>substring : <span th:text="${#strings.substring(itdragonStr, 2, 8)}"/></p>
    <p>replace : <span th:text="${#strings.replace(itdragonStr, 'it', 'IT')}"/></p>
    <p>startsWith : <span th:text="${#strings.startsWith(itdragonStr, 'it')}"/></p>
    <p>contains : <span th:text="${#strings.contains(itdragonStr, 'IT')}"/></p>
</div>

八、项目案例:基于 SpringBoot + MyBatis + MySQL +Thymeleaf整合

学习资源,请克隆Git仓库资源:https://gitee.com/embrace-each-other/springboot-mybatis.git

注:这是本人学习框架的项目,仅供学习参考,望多多支持,Star以及纠错更新!

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值