总结一下自己最近学的springboot笔记
yaml语法
#对象
student:
name: juzi
age: 666
#行内写法
kid: {name: jujuju,age: 888}
#数组
chars:
- naruto
- sasuke
- itachi
names: [zhangsan, lisi, wangwu]
yaml内还支持Spring的el表达式
@ConfigurationProperties
@ConfigurationProperties是SpringBoot特有的,有以下支持:
- 支持yaml和xml
- 支持松散语法,即各种case的对应
- 支持jsr303数据校验,添加@Validation注解
不同于Spring的@PropertySource,其只能支持xml(支持别的需要额外配置)
路径变量
classpath:
在当前.class文件路径下去寻找,默认是从最近的classpath下找。
file:
使用绝对路径。不推荐这样做,这样会使应用配置和具体的绝对路径耦合在一起。
自动配置原理
谈一谈自己的理解,不一定对,只是对现有知识的一个总结。
springboot一来就有一个主启动类,点进注解会发现有一个EnableAutoConfiguration的注解,由名字也大概可得知这是一个开启自动配置的类,因为它会通过Import注释将各种自动配置注入到容器中。
SpringBoot的一大特色就是自动配置。见官网Starters条目下的解释:
Starters are a set of convenient dependency descriptors that you can include in your application. You get a one-stop shop for all the Spring and related technologies that you need without having to hunt through sample code and copy-paste loads of dependency descriptors. For example, if you want to get started using Spring and JPA for database access, include the spring-boot-starter-data-jpa dependency in your project.
接着,点进这个注解可以发现用了Import注解,这是是用了ImportSelector接口的用法,这个接口主要是根据情况返回需要做bean的类,进入这个接口的实现方法selectImports:
这个方法的返回值是需要导入的类的名字的集合,从上面我们可以知道这个集合是由红框圈住的方法getAutoConfigurationEntry获得的,再继续进入这个方法:
在这个方法中我们会发现,里面调用了ImportCandidates.load,在这里对每个元素进行了configurations::add,再返回这个configurations,从而获得到了所有的配置类。
进入ImportCandidates.load方法:
这里
LOCATION
是一个常量,值为META-INF/spring/%s.imports
。
这里最终是使用java.lang.ClassLoader#getResources
方法去查找classpath下所有META-INF/spring/%s.imports
文件。
文件是所有自动配置类,不过当然不会全部用到。随便点进一个类会发现这个类也被Configuration注解修饰了,表明这是一个容器,输送bean。此外,还有一个EnableConfigurationProperties的注解,该注解的值所对应的类都标注了ConfigurationProperties注解,作用是让这些类被注入到容器,不然的话ConfigurationProperties注解并不会生效。点进设置要开启ConfigurationProperties的类可以发现他就是从默认的application.properties文件读取的,以此来达到客制化的效果,可以看到该类下面都有默认值设定。此外考虑到不是所有springboot项目都需要引进所有的依赖,所以在这些标识了自动配置类的文件中可以发现一些控制是否配置的条件的注解。
springboot3.0以后就不采用这种方式了,而是会放在META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports。
我觉得理解以上的关键还是要把Spring的IOC理解好,尤其各种方式,可以看见springboot里都没有用beans.xml的方式来配置了,都是Java代码的方式配置,其中涉及到很多注解,没了解过的话是很难读懂springboot的运行原理的。
查看哪些东西已经自动配置了
在application.properties中配置debug=true,然后启动项目,查看cmd窗口,positive matches下的的就是生效了,negative matches下的就是没生效。
@Configuration和@AutoConfiguration区别
字面上这两这个注解都是关于配置的,即添加相关bean到容器中去,那他们的区别是什么呢?我认为主要有以下几点区别:
1. 启动上
方式上来说,springboot的主启动类由于自动添加了ComponentScan,所以和主启动类在同一包下的带有Configuration注解的类都会被扫描到。而想要AutoConfiguration注解生效,不仅需要给类添加上相关注解,还需要在spring.factories文件中声明(可以是在自己定制的jar下的META-INF文件夹下,也可以是spring-boot-autoconfigure这个jar包下的META-INF文件夹下),以此告知springboot,达到自动装配的效果(就如上面提到了,EnableConfigurationProperties注解是去这下面去找对应的类加载)。
时机上来说,默认先启动Configuration注解的类,再启动AutoConfiguration注解的类,我想这也是因为自动配置类由于都是一些默认配置,可能会受到自己自定义配置类的影响,所以才采用这个顺序。
2. 用途上
其实由上述说明就可窥见二者在定位上是不一样的了。Configuration注解主要是为了程序员自己定义一些bean,而AutoConfiguration注解更多是为了与springboot框架协作,这也是AutoConfiguration这一注解为springboot框架专有,而非在spring框架中定义的原因。springboot主打的是自动配置,有了这个注解,第三方库可以更好的与springboot框架协作。所以AutoConfiguration注解有很多额外功能,比如before、after等等属性用以控制加载顺序,也经常配合@ConditionalOnBean、@ConditionalOnMissingBean等等一起使用。
参考以下两个链接:@Configuration和@AutoConfiguration有什么区别?, 该选择用Configuration还是AutoConfiguration?
3. proxyBeanMethods属性
先来了解proxyBeanMethods。这个属性主要是决定在Configuration注解下的配置类中的Bean注解所标识的方法是否返回单例,即多次调用是否返回同一对象的引用。
这篇文章很好地讲清了proxyBeanMethods属性是干嘛的:
Spring主要是通过Cglib动态代理去实现的每次调用这个方法只返回同一值,所以多了许多约束,比如该方法不能为final,此外还会降低性能,主要见于源码注释:
了解完proxyBeanMethods属性后,下面讲讲两个注解在该属性的区别。Configuration注解该属性默认为true,而AutoConfiguration注解该属性默认为false。有些库里面用了大量Configuration注解,而这是一笔不小的性能开支,为了不破坏向后兼容性,Spring Boot官不能直接将Configuration注解的该属性改为false,所以只好将在AutoConfiguration中设置为true。
AutoConfiguration注解其实就是再注上了一个设置值为false的Configuration注解。
这篇答案很好地阐述了这两个注解在这一点上的差异:
@ComponentScan和@AutoConfigurePackage区别
与上面两个注释相同,这里ComponentScan是spring中的注释,用以告知spring需要扫描哪些包下的组件,而AutoConfigurationPackage是springboot特有的注释,有注释的类其所在包会被扫描到,其主要用途是配合AutoConfigurePackages工具类的get方法,让第三方组件可以获得主启动类包所在位置,对其中的资源文件或者类进行扫描,譬如Mybatis的Mapper注解就是这样被获取到的(见源码org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration.AutoConfiguredMapperScannerRegistrar#registerBeanDefinitions处)。
Starter原理
starter结构图如图所示:
图中我们可见starter模块主要有两个模块构成。直接看图可能比较抽象,这里举例说明。
spring-boot-starter-web是springbootweb开发时常用的starter,由上文可知自动配置的其实是扫描spring-boot-autoconfigure包下META-INF/spring.factories这个文件中对应的自动配置类,所以这个starter肯定和spring-boot-autoconfigure这个包有关,但是我们点进spring-boot-starter-web的依赖会发现它并没有spring-boot-autoconfigure这个包,但是有spring-boot-starter这个包的依赖,点进这个包会发现这个引入绝大部分spring-boot-starter-xxx模块所需的基础模块的依赖,如下图:
红线为这个包的描述,包含了spring-boot-starter的基本功能
由上文可知自动配置类都是要满足一些条件才导入容器中,比如有没有某个类之类的——@ConditionalOnClass,这里其实就涉及到一个问题,假如我们确实没有导入一些类,为什么这段代码不会报错,按理来说没有这个类,那么这个关键字是会爆红的,通过不了编译的,这是因为这些文件都已经被编译成.class文件了,所以不会报错,那么就说明最开始编译时其实是包括这些类的包的,只不过后面又剔除了,所以spring-boot-autoconfigure这个包肯定包含所有那些要满足条件的类的依赖,查看spring-boot-autoconfigure的pom依赖,发现确实,只不过这些依赖都包含了optional标签,这个标签表示依赖不会传递,默认是为false。
传递是指当C依赖B,B选择依赖A时,导入C只会导入B,而不会导入A。
所以最开始编写spring-boot-autoconfigure这个包时是有那些依赖,而由于这些依赖都是optional的,所以当你引入spring-boot-starter-web这个starter,即使有一些非web相关的包你没有导入,但是也依然不会报错。
既然由于可选依赖,会导致导入spring-boot-starter-web这个starter时,不会导入spring-boot-autoconfigure这个包有的依赖,那么spring-boot-starter-web这个starter肯定自己导入了web依赖,查看spring-boot-starter-web的pom文件,发现确实,如下图:
红框中包含了常用的web包
综上所述,你的项目中导入了spring-boot-starter-web这个starter,就会导入spring-boot-autoconfigure,从而实现自动配置,而spring-boot-autoconfigure这个包还包含其他的自动配置所需的依赖,但由于可选依赖的原因,都不会被导入到你的项目文件中去,这样就完美实现了自动配置。
这也符合开始的图,spring-boot-starter-web作为starter module,spring-boot-autoconfigure作为autoconfigure module,前者依赖第三方组件诸如json、tomcat,后者可选依赖这些web依赖,然后前者依赖后者,导入这个starter就可以导入自动配置,就可以开启相关自动配置。当然实际上spring-boot-autoconfigure这个包除了web相关依赖外,还依赖了更多,因为它作为了很多starter的自动配置模组,但是由于可选依赖的关系,仅当你导入对应starter模组才会使得对应自动配置生效!