目录
4.1.1.@SpringBootConfiguration(声明是一个SpringBoot配置类)
4.1.2.@EnableAutoConfiguration(启用自动配置)
1.了解SpringBoot
1.1.什么是SpringBoot
SpringBoot是Spring项目中的一个子工程,与我们所熟知的Spring-framework同属于Spring的产品。
官方介绍:
Takes an opinionated view of building production-ready Spring applications. Spring Boot favors convention over configuration and is designed to get you up and running as quickly as possible.
翻译:用一些固定的方式来构建生产级别的spring应用。Spring Boot 推崇约定大于配置的方式以便于你能够尽可能快速的启动并运行程序。
其实人们把SpringBoot称为搭建程序的脚手架。其最主要作用就是帮我们快速的构建庞大的spring项目,并且尽可能的减少一切xml配置,做到开箱即用,迅速上手,让我们关注业务而非配置。SpringBoot创建java应用,并使用java -jar启动它,就能得到一个生产级别的web工程。
1.2.SpringBoot的特点
Springboot 主要目标:
· 为所有Spring开发者提供一个非常快速的,广泛接受的入门体验。
· 开箱即用(启动器starter,其实就是Springboot提供的一个jar包),但通过自己设置参数(.properties),即可快速摆脱这种方式。
· 提供了一些大型项目中常见的非功能性特征,如内嵌服务器、安全、指标、健康监测、外部化配置等。
· 绝对没有代码生成,也无需XML配置。
更多细节,可以到官网查看
2.快速入门
2.1.创建工程
直接用ideal进行创建或者到官网进行创建,然后下载压缩包导入ideal即可。
2.2.依赖讲解
SpringBoot提供了一个名为spring-boot-starter-parent的工程,里面已经对各种常用依赖(并非全部)的版本进行了管理,我们的项目需要以这个项目为父工程,这样我们就不用操心依赖的版本问题了,需要什么依赖,直接引入坐标即可。
2.2.1.父工程依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.6.RELEASE</version>
</parent>
2.2.2.Web启动器依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
需要注意的是,我们并没有在这里指定版本信息。因为SpringBoot的父工程已经对版本进行了管理了。
这个时候项目中多出大量的依赖:
这些都是SpringBoot根据spring-boot-starter-web这个依赖自动引入的,而且所有版本都已经管理好,不会出现冲突。
2.2.3.管理jdk版本
默认情况下,maven工程的jdk版本是1.5,而我们开发使用的是1.8,因此这里我们需要修改jdk版本,只需要简单的添加以下属性即可:
<properties>
<java.version>1.8</java.version>
</properties>
3.Java配置
3.1.回顾历史
事实上,在spring3.0开始,Spring官方就已经开始推荐使用java配置来代替传统的xml配置了。
Spring1.0时代
在此时因为jdk1.5刚刚出来,注解开发并未盛行,因此一切Spring配置都是xml格式。
Spring2.0时代
Spring引入了注解开发,但是因为并不完善,因此并未完全替代xml,此时是把xml与注解进行结合。
Spring3.0时代及以后
3.0以后Spring的注解已经非常完善了,因此Spring推荐大家使用完全的Java配置来代替以前的xml,不过在国内没有推广盛行。然后SpringBoot来临,人们才慢慢认识到Java配置的优雅。
3.2.尝试Java配置
java配置主要靠java类和一些注解,比较常用的注解有:
@Configuration:声明一个类作为配置类,代替xml文件
@Bean:声明在方法上,将方法的返回值加入Bean容器,代替<bean>标签
@Value:属性注入
@PropertySource:指定外部属性文件
如:创建一个jdbc.properties文件,编写jdbc属性:
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/demo
jdbc.username=root
jdbc.password=123
编写代码:
@Configuration
@PropertySource("classpath:jdbc.properties")
public class JdbcConfig {
@Value("${jdbc.url}")
String url;
@Value("${jdbc.driverClassName}")
String driverClassName;
@Value("${jdbc.username}")
String username;
@Value("${jdbc.password}")
String password;
@Bean
public DataSource dataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl(url);
dataSource.setDriverClassName(driverClassName);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
}
解读:
· @Configuration:声明我们JdbcConfig是一个配置类
· @PropertySource:指定属性文件的路径是:classpath:jdbc.properties
· 通过@Value 为属性注入值
· 通过@Bean将dataSource()方法声明为一个注册Bean的方法,Spring会自动调用该方法,将方法的返回值加入Spring容器中。
然后我们就可以在任意位置通过@Autowired注入DataSource了!
测试代码:
@RestController
public class HelloController {
@Autowired
private DataSource dataSource;
@GetMapping("hello")
public String hello() {
return "hello, spring boot!" + dataSource;
}
}
3.3.SpringBoot的属性注入
在上面的例子中,我们实验了Java配置方式,不过属性注入使用的是@Value注解。这种方式虽然可行,但是不够强大,因为它只能注入基本类型值。在SpringBoot中,提供了一种新的属性注入方式,支持各种Java基本数据类型及复杂类型的注入。
1)新建一个类,用来进行属性注入:
@ConfigurationProperties(prefix = "jdbc")
public class JdbcProperties{
private String url;
private String driverClassName;
private String username;
private String password;
// ... 略
// getters 和 setters
}
· 在类上通过@ConfigurationProperties注解声明当前类为属性读取类
· prefix="jdbc"读取属性文件中,前缀为jdbc的值。
· 在类上定义各个属性,名称必须与属性文件中 jdbc. 后面部分一致。
· 需要主要的是,这里我们并没有指定属性文件的地址,所以我们需要把 jdbc.properties 名称改为 application.properties ,这是SpringBoot默认读取的属性文件。
2)在JdbcConfig中使用这个属性
@Configuration
@EnableConfigurationProperties(JdbcProperties.class)
public class JdbcConfig {
@Bean
public DataSource dataSource(JdbcProperties jdbc) {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl(jdbc.getUrl());
dataSource.setDriverClassName(jdbc.getDriverClassName());
dataSource.setUsername(jdbc.getUsername());
dataSource.setPassword(jdbc.getPassword());
return dataSource;
}
}
· 通过@EnableConfigurationProperties(JdbcProperties.class)来声明要使用JdbcProperties这个类的对象
· 可以通过以下方式注入JdbcProperties:
1)@Autowired注入
@Autowired
private JdbcProperties prop;
2)构造函数注入
private JdbcProperties prop;
public JdbcConfig(Jdbcproperties prop){
this.prop = prop;
}
3)声明有@Bean的方式注入
@Bean
public Datasource dataSource(JdbcProperties prop){
// ...
}
大家会觉得这种方式似乎更麻烦了,事实上这种方式有更强大的功能,也是SpringBoot推荐的注入方式。两者对比关系:
翻译:@Value注释是一个核心容器特性,它不提供与类型安全配置属性相同的特性。下表总结了@ConfigurationProperties和@Value支持的特性
如果您为自己的组件定义了一组配置键,我们建议您在带有@ConfigurationProperties注释的POJO中对它们进行分组。您还应该知道,由于@Value不支持松散绑定 ,所以如果您需要通过使用环境变量来提供值,那么它不是一个好的选择。
优势:
Relaxed binding(松散绑定 ):
1.不严格要求属性文件中的属性名与成员变量名一直。支持驼峰、中划线,下划线等等转换,甚至支持对象引导。比如:user.friend.name:代表的是user对象中的friend属性中的name属性,显然friend也是对象。@Value注解就难以完成这样的注入方式。
2.meta-data support(元数据支持):帮助IDE生成属性提示(写开源框架会用到)。
3.4.更优雅的注入
事实上,如果一段属性只有一个bean需要使用,我们无需将其注入到一个类(JdbcProperties)中,而是直接在需要的地方声明即可:
@Configuration
public class JdbcConfig {
@Bean
// 声明要注入的属性前缀,SpringBoot会自动把相关属性通过set方法注入到DataSource中
@ConfigurationProperties(prefix = "jdbc")
public DataSource dataSource() {
DruidDataSource dataSource = new DruidDataSource();
return dataSource;
}
}
我们直接把@ConfiggurationProperties(prefix = "jdbc")声明在需要使用的@Bean的方法上,然后SpringBoot就会自动嗲用这个Bean(此处为DataSource)的set方法,然后完成注入。使用的前提是:该类必须有对应属性的set方法。
4.自动配置原理
使用SpringBoot之后,一个整合了SpringMVC的WEB工程开发,变的无比简单,那些繁杂的配置都消失不见了,这是如何做到的?
一切魔力的开始,都是从main函数来的,如下图:
两个特别的地方:
· 注解:@SpringBootApplication
· run方法:SpringApplication.run()
4.1.了解@SpringBootApplication
这里重点的注解有3个:
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
其他的为元注解(运用在注解上的注解称为“元注解”)
4.1.1.@SpringBootConfiguration(声明是一个SpringBoot配置类)
我们继续点击查看源码:
这段我们可以看出,在这个注解上面,又有一个@Configuration注解。通过上面的注释阅读我们知道:这个注解的作用就是声明当前类是一个配置类,然后Spring会自动扫描添加了@Configuration的类,并且读取其中的配置信息。而@SpringBootConfiguration是来声明当前类是SpringBoot应用的配置类,项目中只能有一个。所以一般我们无需自己添加。
4.1.2.@EnableAutoConfiguration(启用自动配置)
官网解释:
The second class-level annotation is `@EnableAutoConfiguration`. This annotation
tells Spring Boot to “guess” how you want to configure Spring, based on the jar
dependencies that you have added. Since `spring-boot-starter-web` added Tomcat
and Spring MVC, the auto-configuration assumes that you are developing a web
application and sets up Spring accordingly.
简单翻译:
第二级的注解@EnableAutoConfiguration,告诉SpringBoot基于你所添加的依赖,去“猜测”你想要如何配置Spring。比如我们引入了 spring-boot-starter-web,而这个启动器中帮我们添加了tomcat、SpringMVC 的依赖。此时自动配置就知道你要开发一个web应用,所以就帮你完成了web及springMVC的默认配置了!
总结:Springboot内部对大量的第三方库或Spring内部库进行了默认配置,这些配置是否生效,取决于我们是否引入了对应库所需的依赖,如果有那么默认配置就会生效。所以,我们使用 SpringBoot 构建一个项目,只需要引入所需框架的依赖,配置就可以交给 SpringBoot 处理了,除非你不希望使用 SpringBoot 的默认配置,它也可以提供自定义配置的入口。
4.1.3.@ComponentScan(配置组件扫描)
观察源码:
翻译:配置组件扫描的指令,提供了类似于<context:compaonent-scan>标签的作用,通过basePackageClasses或者basePackages属性来指定要扫描的包。如果没有指定这些属性,那么将从声明这个注解的类所在的包开始,扫描包及子包。
而我们的@SpringBootApplication注解声明的类就是main函数所在的启动类,因此扫描的包是该类所在包及其子包。因此,一般启动类会放在一个比较前的包目录中。
4.2.默认配置原理
4.2.1.默认配置类
我们知道@EnableAutoConfiguration会开启SpringBoot的自动配置,并且根据你引入的依赖来生效对应的默认配置。那么问题来了:
· 这些默认配置是在哪里定义的呢?
· 为何依赖引入就会触发配置呢?
其实在我们的项目中,已经引入了一个依赖:spring-boot-antoconfigure,其中定义了大量自动配置类:
非常多,几乎涵盖了现在主流的开元框架,例如:redis、jms、amqp、jdbc、jackson、mongodb、jpa、solr、elasticsearch等等。如下,打开WebMvcAutoConfiguration:
我们可以看到这个类上的4个注解:
@Configuration:声明这个类是一个配置类
@ConditionalOnWebApplication(type = Type.SERVLET)
ConditionalOn,翻译就是在某个条件下,此处就是满足项目的类是Type.SERVLET类型,也就是一个普通web工程。
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
这里的条件是OnClass,也就是满足以下类存在:Servlet、DispatcherServlet、WebMvcConfigurer,其中Servlet只要引入了tomcat依赖自然会有,后两个需要引入SpringMVC才会有。这里就是判断你是否引入了相关依赖,引入依赖后该条件成立,当前类的配置才会生效!
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
这个条件与上面不同,OnMissingBean,是说环境中没有指定的Bean这个才生效。其实这就是自定义配置的入口,也就是说,如果我们自己配置了一个WebMVCConfigurationSupport的类,那么这个默认配置就会失效!
这个类定义了:视图解析器、处理器适配器(HandlerAdapter)等等。
4.2.2.默认配置属性
另外,这些默认配置的属性来自哪里呢?
我们看到,这里通过@EnableAutoConfiguration注解引入了两个属性:WebMvcProperties和ResourceProperties。这不正是SpringBoot的属性注入玩法嘛。
查看这两个属性类:
找到了内部资源视图解析器的prefix和suffix属性。
ResourceProperties中主要定义了静态资源(.js,.html,.css等)的路径:
此类也说明了为什么静态资源默认要放在那些文件夹下,这里classpath对应的是:
注意:如果我们要覆盖这些默认属性,只需要在application.properties中定义与其前缀prefix和字段名一致的属性即可。
4.3.SpringBoot中Main方法介绍
4.3.1.main方法中的run方法
如下图:每个springboot启动类中都有一个main方法,是每个SpringBoot的程序入口,也是启动项目的入口,如下图:
4.3.2.源码介绍
如上图所示,run方法返回一个SpringApplication的实例,点击该方法,跳转如下源码:
采用构造器的方式创建实例,点击“this”可进入调用的另一个构造器方法。
方法讲解:
1.ResourceLoader:要使用的资源加载器,
primarySources:是要加载类的来源,即SpringBoot启动类里面的main方法中,run中加入的类,如下图
2.将类来源存储到本类中,通过 WebApplicationType.deduceFromClasspath(); 进行来源类的判断,判断该类为什么类型,并将该来源类的类型存储到本类中,如下图(详情请自行查看源码):
NONE:该应用程序不应作为Web应用程序运行,也不应启动嵌入式Web服务器。
SERVLET:该应用程序应作为基于Servlet的Web应用程序运行,并应启动一个嵌入式Servlet Web服务器。
REACTIVE:该应用程序应作为响应式Web应用程序运行,并应启动一个嵌入式响应式Web服务器。
3.获取spring的工厂实例:
1)获取类加载器
2)Spring工厂利用类加载器获取到加载类的名称,并进存储。加载类主要在这个地方(重要)。如下图:
翻译:使用给定的类加载器从{@value #FACTORIES_RESOURCE_LOCATION}加载给定类型的工厂实现的标准类名。
classpath文件存放地:
翻译:寻找工厂的位置,可以存在于多个JAR文件中。即:不光能在classpath下的配置文件中找,还可以在jar包中寻找。如下图:
注:自动配置能生效就是因为这儿
如上图,ConditionalOn:有条件的;这儿就与@SpringbootApplication注解中的@EnableAutoConfiguration的类解释(请查看上面@EnableAutoConfiguration注解解释)相匹配,形成自动配置,如下图:
3)将参数传入 createSpringFactoriesInstances,利用反射的方式获取到类的构造方法,进行对象的创建。如下图:
4.4.总结
SpringBoot为我们提供了默认配置,而默认生效的条件一般有两个:1.自己引入了相关依赖2.自己没有配置
1)启动器
所以,我们如果不想配置,只需要引入依赖即可,而依赖版本我们也不用操心,因为只要引入了SpringBoot提供的stater(启动器),就会自动管理依赖及版本了。 因此,玩SpringBoot的第一件事情,就是找启动器,SpringBoot提供了大量的默认启动器。
2)全局配置
另外,SpringBoot的默认配置,都会读取默认属性,而这些属性可以通过自定义application.properties
文件来进行覆盖。这样虽然使用的还是默认配置,但是配置中的值改成了我们自定义的。 因此,玩SpringBoot的第二件事情,就是通过application.properties
来覆盖默认属性值,形成自定义配置。我们需要知道SpringBoot的默认属性key,非常多。