SpringBoot学习总结
文章目录
概要
- 记录下SpringBoot使用哪些注解简化了Spring的一些繁琐的配置
- 使用这些注解展示一下自己写好的一个自定义的start
- 查看一些SpringBoot的源码,来看看SpringBoot最初是如何启动,如何完成一些自动配置并将一些实例加入到IOC容器中的。
- 查看一些SpringBoot自己写的starter,也就是那些被成为起步依赖的东西。
一、SpringBoot向IOC容器注入实例
一开始学习Spring的时候,就感觉一大堆xml文件十分麻烦,每次都要复制粘贴很多重复的dtd引用之类的信息,另外,xml文件可读性不强,而SpringBoot,它首先就帮我们解决了这个问题。
- 用一个示例展示SpringBoot向ioc容器中注入实例的过程(该例子在下文会有详细介绍,此处只是粗略地介绍下几个注解)
上图中加上了@Configuration注解的类相当于Spring中的一个xml文件,它向Spring容器注入了一个PoetrySercvice和一个DataSource的实体类,其中的@Bean注解就相当于Spring中xml文件的bean标签。
那么,如何向实体类中注入属性值呢?
@Data
@ConfigurationProperties(prefix = "jdbc")
public class JdbcProperties {
private String driverClassName;
private String url;
private String username;
private String password;
}
首先要获取属性值,上面代码中这个带@ConfigurationProperties注解的类是一个JavaBean,会向SpringBoot默认的配置文件(文件名固定为application,可以是yml、properties类型的文件。)中读取以jdbc为前缀的信息。
下图是application.yml文件中的部分内容。显然,该配置文件要生效的前提,是前缀后的key要与JavaBean中的属性名一致。
jdbc:
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/poetry?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
username: root
password: yudong
如上图所示,通过@EnableConfigurationProperties这个注解,就可以使用上面提到的JavaBean(JdbcProperties类)对DataSource实体类进行属性值的注入了(由此可知,@ConfigurationProperties注解是会将JavaBean实体类注入到ioc容器中的)。下面是注入过程的一些代码
另外需要注意的是@EnableConfigurationProperties注解和@ConfigurationProperties注解是配套使用的,两者如果不对应,在ide中是会报错的。
@Bean
public DataSource dataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(jdbcProperties.getDriverClassName());
dataSource.setUrl(jdbcProperties.getUrl());
dataSource.setUsername(jdbcProperties.getUsername());
dataSource.setPassword(jdbcProperties.getPassword());
return dataSource;
}
- 注入过程小结
- 首先有一个SpringBoot的默认配置文件,里面写了一些需要注入的内容
- 然后通过@ConfigurationProperties注解的JavaBean读取配置文件中的内容
- 接着@EnableConfigurationProperties注解的类使用上一步中的JavaBean通过@Bean注解的方法向Spring的ioc容器中注入实体类。
- 不过如果要注入的属性值较少且有对应的构造方法,也可以像下面这样直接省去第二步骤,用更简洁的方式完成属性的注入
@Bean
// 声明要注入的属性前缀,Spring Boot会自动把相关属性通过set方法注入到DataSource中
@ConfigurationProperties(prefix = "jdbc")
public DataSource dataSource() {
return new DruidDataSource();
}
下面的内容是这个过程使用过的注解的一个总结
- @Configuration:声明一个配置类,代替了Spring中的xml文件
- @Bean:向Spring容器中注入实体,代替了Spring中的bean标签
- @ConfigurationProperties(prefix=“xxx”):从SpringBoot中默认的配置文件中读取配置信息,该注解比@Vule更强大,不仅可以注入基本类型的数据,也能注入类成员变量
- @EnableConfigurationProperties({xxx.class,xxx.class(可有多个配置类)}): 和@ConfigurationProperties注解一起使用,使被@ConfigurationProperties注解的类可以生效并被加入到IOC容器中。且该注解一般与@Configuration一起使用。
补充:SpringBoot的默认配置文件也可以有多个,配置方式此处不再赘述。
二、自定义的starter
在这一部分内容中,将通过一个自定义的starter,来展示如何利用SpringBoot写一个可被重复利用的模块,以图减少代码的冗余。通过自己写这样一个模块,也可以加深我们对SpringBoot各个注解的理解。-
项目内容概述:这个starter,简单地实现了从数据库取出数据并装入一个Bean中然后加入IOC容器。
-
具体来说是数据库有两张表,一张存有唐诗数据,一张存有宋词数据,starter根据配置信息从数据库取出所需信息,装入对应的Song类或Tang类实例对象,然后将实例对象加入IOC容器供其他模块使用。
-
数据库名为:poetry,其中有两张表,表名分别为:song,tang
-
表结构(两个表结构是一样的)如下图所示:
-
使用mybatis-plus从数据库取数据,过程十分简单(具体可参考一些mybatis-plus使用的文章博客等资料),同理可创建唐诗的相关类。
先创建接受数据库信息的实体类
@Data
public class Song {
//id
private Long id;
//作者名
private String author;
//题目
private String title;
//内容
private String content;
}
- 创建对应的接口
public interface SongMapper extends BaseMapper<Song> {
}
- 创建Service,这个Service进行了一些简单的逻辑处理,其中只有一个得到内容的方法,它根据传来的朝代参数,确定从哪个表中取数据,然后根据其他参数,确定要取出的唐诗或宋词的内容。代码如下所示:
@AllArgsConstructor
public class PoetryService {
private String author;
private String title;
private String dynasty;
@Autowired
private TangMapper tangMapper;
@Autowired
private SongMapper songMapper;
public String getContent(){
if (dynasty.equals("Tang") ){
QueryWrapper<Tang> tangQueryWrapper= new QueryWrapper();
tangQueryWrapper.eq("author",author);
tangQueryWrapper.eq("title",title);
Tang tang = tangMapper.selectOne(tangQueryWrapper);
return tang.getContent();
}
else if (dynasty.equals("Song"))
{
QueryWrapper<Song> songQueryWrapper = new QueryWrapper<Song>();
songQueryWrapper.eq("author",author);
songQueryWrapper.eq("title",title);
Song song = songMapper.selectOne(songQueryWrapper);
return song.getContent();
}
else return "朝代有误";
}
}
- 上面的步骤中没有用任何注解,只是实现了从数据库中取得数据的一些必要类并写了一个功能,而下面的内容将会实现将数据源和Service实体类注入IOC容器中。
- 只写上面的内容,是没办法从数据库拿数据的,因为没有数据源,压根也没有跟数据库建立连接,所以需要先向IOC容器里注入一个数据源。注入数据库的内容在上面已经展示过了,这里不再重复。
- 为了完整地展示项目,下面记录一下 向IOC容器注入Service的过程(与注入数据源过程类似,可跳过11-13的内容)
- 首先是创建一个从配置文件接受配置的JavaBean
@Data
@ConfigurationProperties(prefix = "myconfig")
public class MyProperties {
private String author;
private String title;
private String dynasty;
}
- 对应的配置信息可以如下图一样设置,然后Service就能拿出水调歌头的内容
- 然后在配置类的注解中加入该类即可。
可能有人会问,这个模块中并没有启动类,那如何利用Bean注解向IOC容器加入实例对象呢?
答:这个模块在安装推送到远程仓库后,是会作为依赖被其他springboot项目引用的,当其他项目启动时,这个模块里的内容才有可能生效。显然,该模块向Bean注入的属性值,也是从其他项目的application文件中读取的。
那么其他项目只要引用该模块就能注入实例吗,这是有条件的。带@ConditionalOnxx的注解表明只有满足一定条件时该配置才能启动,“@ConditionalOnxx”中的“xx”可以是“Class”,“Properties”等,表示当某些类存在或者符合某些配置时,带有该注解的配置类可以启动。
下图中的例子,表示在配置文件中,当前缀mystarter中属性名为isopen的属性值为true时该配置类生效,而matchIfMissing表示如果配置类中没有这样的前缀和这样的属性值时,该配置类是默认生效的。
- 只有上面提及的内容,还无法让依赖starter的项目在启动时就能将需要的JavaBean注入到IOC容器中。毕竟如果只是现在这样写代码,那如果想找到这么一个配置类,就得扫描所有依赖里的所有包中的所有类,这显然是低效。所以,要将需要被使用的配置类放在一个容易被找到的地方,而这个地方,就是下图的spring.factories
- 打开这个文件,在里面写入配置类的全限定名,就能在SpringBoot项目启动时扫描到这个类并且执行需要的IOC注入工作。springfactories文件里打开后如下图所示
补充:该spring.factories文件需要在该starter模块中,所以SpringBoot在扫描的时候才能以该项目的classpath以及限定名找到这个具体的MyConfig类所在的位置。
- 具体的SpringBoot扫描过程将在下面的内容中有个概括性的讲解。
- 下图是在引用了该starter模块的一个测试控制类,此时就可以直接从IOC容器中取出poetryService实例,并享受它提供的服务。
- 最后是测试类运行的结果
三、自动配置原理
SpringBoot真正强大的地方并不是上面提到的化Spring之繁为注解之简,而是它集合了各种Spring平台的应用和第三方组件的内容。引入一些起步依赖后,通过使用注解和yml文件,SpringBoot允许我们使用简洁优雅的设置就能让程序run起来,远离了版本依赖错误和让人眼花缭乱的复杂配置问题。
这一节我将尽量跟踪一些源码,来探究一下SpringBoot项目在启动时是如何注入其他组件内容的,即自动配置的原理
首先来看看SpringBoot项目在运行的时候的入口:
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
关于SpringBoot的注解
- 进入@SpringBootApplication注解,能发现其中需要关注的注解有以下几个:
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM,
classes = AutoConfigurationExcludeFilter.class) })
-
进入@SpringBootConfiguration注解,如下图,可以看到它继承了Configuration的注解,说明带有@SpringBootConfiguartion注解的类,相当于一个xml文件,可以在里面使用@bean注解注入实例
-
进入@EnableAutoConfiguration注解,可以发现,这个注解,就是在刚才自定义starter中,在spring.factories文件中写的一个类(如下图所示)。所以这个注解的作用,就是使得(以下面的config.MyConfig为例)config.MyConfig这个配置类能够在SpringBoot项目启动时自动进行配置。
3. 进入@ComponentScan注解,可以看到这个注解是可以不断扫描组件的,扫描的位置,是位于@SpringBootApplication注解类所在位置及其以下包里的内容。
总结一下,这三个注解,@SpringBootConfiguration表明Application类是唯一的SpringBoot配置类,@EnableAutoConfiguration注解使Application类可以加入其他配置类,@ComponentScan注解使Application类可以扫描配置该SpringBoot里的组件。
SpringBoot的一些方法
SpringBoot项目启动时,先初始化了一个SpringApplication类,进入其构造函数可以看到其初始化时发生的一些关键内容如下:
- 从方法名字可以看出,初始化时使用了很多SpringFactoriesInstances,这些工厂是可以用来向IOC中注入实例,下面我们进入getSpringFactoriesInstances方法查看SpringBoot如何得到这些工厂类。
- 通过一个上下文初始化类(ApplicationContextInitializer)的class作为参数,getSpringFactoriesInstances可以找到一个存有类限定名的Set集合,使用传进来的ApplicationContextInitializer.class进入该loadFactoryNames方法:
- 查看这个FACTORIES_RESOURCE_LOCATION静态final属性有:
可以发现,其地址是: META-INF/spring.factories ,我们知道,ClassLoader默认是从classpath下读取文件,因此,SpringBoot会在初始化的时候,加载所有classpath:META-INF/spring.factories文件,包括jar包当中的。 - @EnableAutoConfiguration注解可以自动启动配置类,其中的可自动配置类被写在了spring.factories文件中。在Application启动时,会从spring.factories文件中得到可自动配置的类并加入需要的bean实例。
- SpringBoot加入了一些常用的可能会被用到的组件,并将他们配置类写在了一个包里,配置类的限定名写在该包的spring.factories中:
从spring.factories可以看出,其中还有其他自动注入的内容。
四、起步依赖
- 起步依赖这个部分紧接着上一节的内容。在上一节的spring.factories文件中,可以看到里面有许多配置类,然而这些配置类并不是都会生效的,因为如前面说过的,配置类想要生效,需要有一定的条件,当需要需要的的类没有被注入时,配置类是无法生效的。
下图举了一个例子,由于我没有注入该配置类生效时需要的类,所以该配置类也不会生效。
- 想要对应的配置类可以生效,需要注入对应的一些starter。
- 进入在pom文件里加入的启动依赖,可以发现每个starter加入的依赖,都有具体版本号,这里就是springboot为程序员进行的版本控制。
- 找到RestTemplate类,可以发现它所在的包,就是在starter中被导入的子依赖。