SpringBoot原理讲解
一.问题的引入
首先我们来看一个最简单的例子。
我们先创建一个springboot的工程,如何创建一个springboot工程就不说了,不会请自行解决。然后写一个controller类,通过请求路径,返回helloworld在浏览器页面上显示。
上面两张图就是程序的一个整体的结构和运行的结果,那么问题来了,springboot程序中没有任何配置,不像spring框架,写一大堆配置信息在xml文件中,那么程序是怎么将我们这里的controller类扫描到spring容器中的呢?
二.原理讲解。
首先第一点,我们来看一下springboot的启动类,
package com.example.demo;
import org.springframework.boot.springapplication;
import org.springframework.boot.autoconfigure.springbootapplication;
@springbootapplication
public class demoapplication {
public static void main(string[] args) {
//启动springboot应用
springapplication.run(demoapplication.class, args);
}
}
其中,我们看到一个注解@springbootapplication,springboot应用标注在某个类上说明这个类是springboot的主配置类,springboot就应该运行这个类的main方法来启动springboot应用;
然后我们点进去看看这个注解到底包含了什么,
@target(elementtype.type)
@retention(retentionpolicy.runtime)
@documented
@inherited
@springbootconfiguration
@enableautoconfiguration
@componentscan(excludefilters = { @filter(type = filtertype.custom, classes = typeexcludefilter.class),
@filter(type = filtertype.custom, classes = autoconfigurationexcludefilter.class) })
public @interface springbootapplication {
其中我们需要重点关注的就是@springbootconfiguration、@enableautoconfiguration
下面我们逐个来讲解:
@springbootconfiguration这个注解顾名思义,标注在某个类上,这个类就是springboot的配置类,我们再点进去看看。
@target(elementtype.type)
@retention(retentionpolicy.runtime)
@documented
@configuration
原来这个注解是底层是spring中的注解@configuration,而@configuration注解底层就是一个@component,代表一个容器。
springboot的精髓是在@enableautoconfiguration这个注解上,根据名字,我们可以知道这是一个自动配置类,也就是能够实现一些自动配置的功能,那么具体是配置了什么东西呢?
我们再点进去看看
@target(elementtype.type)
@retention(retentionpolicy.runtime)
@documented
@inherited
@autoconfigurationpackage
@import(autoconfigurationimportselector.class)
我们看见了和这个注解有关联的重要的两个注解@autoconfigurationpackage,@import(autoconfigurationimportselector.class),也是分别给大家讲解一下。
1.@autoconfigurationpackage
@autoconfigurationpackage:这是一个自动配置的包,我们来看下在这个程序中是配置了哪个包,点进来
@import(autoconfigurationpackages.registrar.class)
再点到register,我们看到在大概123行的位置,有一段代码。
@override
public void registerbeandefinitions(annotationmetadata metadata, beandefinitionregistry registry) {
register(registry, new packageimport(metadata).getpackagename());
}
我们看到这个方法器中一个参数metadata,代表注解的元信息,然后我们通过这个注解的元信息来获这个包名,我们在这打个断点,debug运行,可以看见一些元信息的基本内容,最主要我们要知道是导入了哪个包,我们选中new packageimport(metadata).getpackagename(),右键计算一下
发现导入的包就是我们项目的根目录,假如我们新建一个包叫com.controller,在里面写一个controller类,大家可以运行的试一下是否能成功呢。
2.@import(autoconfigurationimportselector.class)
@import(autoconfigurationimportselector.class):这个注解代表的是一个自动配置的选择器,那么要导入哪些组件的选择器呢?
我们点进去,看到非常重要的一段。
protected autoconfigurationentry getautoconfigurationentry(autoconfigurationmetadata autoconfigurationmetadata,
annotationmetadata annotationmetadata) {
if (!isenabled(annotationmetadata)) {
return empty_entry;
}
annotationattributes attributes = getattributes(annotationmetadata);
list<string> configurations = getcandidateconfigurations(annotationmetadata, attributes);
configurations = removeduplicates(configurations);
set<string> exclusions = getexclusions(annotationmetadata, attributes);
checkexcludedclasses(configurations, exclusions);
configurations.removeall(exclusions);
configurations = filter(configurations, autoconfigurationmetadata);
fireautoconfigurationimportevents(configurations, exclusions);
return new autoconfigurationentry(configurations, exclusions);
}
这里我们获得到了一个configurations 的list集合,我们打个断点看看这个集合中到底给我们装了些什么东西。
我们发现这个configurations 里面装配了124个自动配置类,原来我们没有配置的一些信息,都通过springboot的自动配置类给我配置好了。
那么我还想知道这些类springboot程序给我们放到哪了呢,我们看到getcandidateconfigurations(annotationmetadata, attributes);
点进去,我们看到:
list<string> configurations = springfactoriesloader.loadfactorynames(getspringfactoriesloaderfactoryclass()
那么再点进去我们看看到底加载的工厂名称是啥,
try {
enumeration<url> urls = (classloader != null ?
classloader.getresources(factories_resource_location) :
classloader.getsystemresources(factories_resource_location));
result = new linkedmultivaluemap<>();
while (urls.hasmoreelements()) {
url url = urls.nextelement();
urlresource resource = new urlresource(url);
properties properties = propertiesloaderutils.loadproperties(resource);
for (map.entry<?, ?> entry : properties.entryset()) {
string factorytypename = ((string) entry.getkey()).trim();
for (string factoryimplementationname : stringutils.commadelimitedlisttostringarray((string) entry.getvalue())) {
result.add(factorytypename, factoryimplementationname.trim());
}
}
}
我们看到其中类加载器给我们获取了资源,我们点进去,
点了这么多次,终于找到我们想要的答案了!!!
public static final string factories_resource_location = "meta-inf/spring.factories";
原来这些自动配置类都在类路径下的"meta-inf/spring.factories"
还有下面的autoconfigure包中的内容,我们也看看
这里面我们所有所用到的配置类全部由springboot给我们配置了,所以我们知道springboot表面上是零配置的,其实底层都给我们封装好了,也是方便我们程序员进行开发。
那么有些人肯定又有疑惑,springboot怎么知道给我们自动配置哪些类呢?
欲知后事如何,请关注一下作者,纯手打码字不易,也是希望和大家多多交流,一起学习,谢谢!