文章目录
- 为什么pom.xml文件中导入依赖并不需要版本?
- SpringBoot是如何实现自动配置的?
- 结论:
- 它是如何启动运行的?
为什么pom.xml文件中导入依赖并不需要版本?
- SpringBoot归根结底是一个Maven项目,我们一般从pom.xml文件探究起;在以前无论是Javaweb还是SSM我们在pom.xml文件中导入依赖都需要实现其版本, 但是在SpringBoot项目中没有版本, 但是他有了一个父依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.11.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
- 点进去,发现还有一个父依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.2.11.RELEASE</version>
<relativePath>../../spring-boot-dependencies</relativePath>
</parent>
- 我们再点进行就会发现秘密
- 这里才是真正管理SpringBoot应用里面所有依赖版本的地方,SpringBoot的版本控制中心;
- 在SpringBoot导入依赖默认是不需要写版本, 它会默认匹配自带的版本号;但是如果导入的包没有在依赖中管理着就需要手动配置版本了;
- 在pom.xml中还有一个最重要的web启动器
<dependencies>
<!--核心依赖, 内涵tomcat和dispatchServlet和xml配置都是他帮我们做的-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
- springboot-boot-starter-xxx:就是spring-boot的场景启动器
- spring-boot-starter-web:当我们创建SpringBoot项目选择了web依赖的时候, 系统就帮我们导入了web启动器正常运行所依赖的组件;
- SpringBoot将所有的功能场景都抽取出来,做成一个个的starter (启动器),只需要在项目中引入这些starter即可,所有相关的依赖都会导入进来 , 我们要用什么功能就导入什么样的场景启动器即可
SpringBoot是如何实现自动配置的?
- 我们接下来看主启动类
//通过这个注解, 标注这个类是一个SpringBoot的应用
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
//启动SpringBoot
SpringApplication.run(DemoApplication.class, args);
}
}
- 他主要是通过一个注解 @SpringBootApplication 来表示这是主启动类, 还表明这是个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}
)}
)
- @ComponentScan
这个注解在Spring中很重要 ,它对应XML配置中的元素。作用:自动扫描并加载符合条件的组件或者bean , 将这个bean定义加载到IOC容器中 - @SpringBootConfiguration
作用:SpringBoot的配置类 ,标注在某个类上 , 表示这是一个SpringBoot的配置类;
我们继续进去这个注解查看
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
@AliasFor(
annotation = Configuration.class
)
boolean proxyBeanMethods() default true;
}
- 进去之后我们发现这里有一个 @Configuration,说明这是一个配置类 ,配置类就是对应Spring的xml 配置文件;也就是这也只是一个SpringBoot的配置类, 我们再进去
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
@AliasFor(
annotation = Component.class
)
String value() default "";
boolean proxyBeanMethods() default true;
}
- 里面的 @Component 这就说明,启动类本身也是Spring中的一个组件而已,负责启动应用的时候去加载一些配置文件!
- 我们回到 SpringBootApplication 注解中继续看。
- 有一个 @EnableAutoConfiguration 我们顾名思义这应该是实现自动装配的配置注解, @EnableAutoConfiguration告诉SpringBoot开启自动配置功能,这样自动配置才能生效, 我们点进去看看
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
- 发现有一个@AutoConfigurationPackage :自动配置包, 我们再点进去
@Import({Registrar.class})
public @interface AutoConfigurationPackage {
}
- @import :Spring底层注解@import , 作用就是让Spring识别给容器中导入一个组件
- Registrar.class 作用:将主启动类的所在包及包下面所有子包里面的所有组件扫描到Spring容器
- 这个分析完了,退到上一步,继续看
- @Import({AutoConfigurationImportSelector.class}) :给容器导入组件 AutoConfigurationImportSelector :自动配置导入选择器,那么它会导入哪些组件的选择器呢?我们点击去这个类看源码:
- 进入这个类后我们更具方法名字很容易判断出这个类是在干什么, 若果想要知道更多可以继续点进行看, 但是我们会发现一个很重要的方法 getAutoConfigurationEntry 这个方法是获得自动配置的入口, 里面有一个方法 getCandidateConfigurations 获取候选配置
- 我们进入 getCandidateConfigurations 获取候选配置这个方法
- 我们再进去SpringFactoriesLoader 加载器中
- 我们发现很多加载的地方都用到了一个字符串路径spring.factories, 好 我们搜索这个路径,就会发发现终极秘密
- 我们根据源头打开spring.factories , 看到了很多自动配置的文件;这就是自动配置根源所在!
- 我们打开我们使用的web的配置 WebMvcAutoConfiguration
@Configuration(
proxyBeanMethods = false
)
@ConditionalOnWebApplication(
type = Type.SERVLET
)
@ConditionalOnClass({Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class})
@ConditionalOnMissingBean({WebMvcConfigurationSupport.class})
@AutoConfigureOrder(-2147483638)
@AutoConfigureAfter({DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class, ValidationAutoConfiguration.class})
public class WebMvcAutoConfiguration {
public static final String DEFAULT_PREFIX = "";
public static final String DEFAULT_SUFFIX = "";
private static final String[] SERVLET_LOCATIONS = new String[]{"/"};
public WebMvcAutoConfiguration() {
}
@Bean
@ConditionalOnMissingBean({HiddenHttpMethodFilter.class})
@ConditionalOnProperty(
prefix = "spring.mvc.hiddenmethod.filter",
name = {"enabled"},
matchIfMissing = false
)
- 可以看到最开始使用了@Configuration表明这是一个JavaConfig配置类,而且个个组件都使用了@Bean注入到了Spring中
- 这时我们就会发现貌似是这样的, 自动配置真正实现是从classpath中搜寻所有的META-INF/spring.factories配置文件 ,并将其中对应的 org.springframework.boot.autoconfigure. 包下的配置项,通过反射实例化 为对应标注了 @Configuration的JavaConfig形式的配置类 , 然后将这些都汇总成为一个实例并以Bean的形式加载到IOC容器中。
- 那我们点开我们没有导入启动器的配置类又会如何
- 这个配置文件是不会生效的, 因为在其内部的注解需要的类是找不到, 那我们也可以这样想, 这个spring.factories文件中的所有配置类不会都实现, 只有导入了对应的启动器才会生效.
结论:
SpringBoot所有的自动配置 都是在启动的时候扫描并加载,META-INF/spring.factories中所有的内容, 但并不是所有的自动配置类都生效, 其内部会有一个判断条件, 判断条件是 是否导入了这个配置对应的start启动器, 如果导入了有了启动器才会进行自动配置
- SpringBoot在启动的时候, 从类路径下/META-INF/spring-factories获取指定的值
- 将这些自动配置的类导入容器,自动配置类就会生效, 帮我们进行自动配置
- 以前我们配置的那一堆东西, 现在都是springBoot帮我们做了
- 整合整个javaEE的所有解决方案和自动配置都在spring.autoconfigure-2.2.11.RELEASE.jar这个包下
- 他会把所有需要导入的组件一类名的方式返回 ,这些组件就会被添加到容器中
- 容器中又会有很多的XXXAutoConfiguation的文件(@Bean), 就是这些类给容器导入了某个场景需要的组件, 并自动配置 @Configuation
- 有了自动配置就省去了我们自动配置的麻烦事
它是如何启动运行的?
- SpringApplication.run分析
- SpringBoot的启动主要是通过实例化SpringApplication来启动的,启动过程主要做了以下几件事情
- 使用构造函数加载初初始化, 在这里他会判断是不是web项目, 如果是web项目就不会自动中断程序, 如果不是那等程序main结束就会结束这个程序
- 获取监听器,发布应用开始启动事件
- 初始化输入参数、配置环境,
- 输出banner、创建上下文、预处理上下文、刷新上下文、再刷新上下文、发布应用已经启动事件、发布应用启动完成事件。
- 在SpringBoot中启动tomcat的工作在刷新上下这一步。而tomcat的启动主要是实例化两个组件:Connector、Container,一个tomcat实例就是一个Server,一个Server包含多个Service,也就是多个应用程序,每个Service包含多个Connector和一个Container,而一个Container下又包含多个子容器。
- tomcat本身就是一个web容器, 他使用socket编程实现处理请求和返回响应的数据封装, 所以在springboot中就使用tomcat源码实现这个逻辑
- 而其中我们常用的有两个:TomcatServletWebServerFactory和JettyServletWebServerFactory。
- 开发阶段对我们来说使用内置的tomcat是非常够用了,当然也可以使用jetty。
- getWebServer这个方法创建了Tomcat对象, 返回的就是一个TomcatWebServer。
- tomcat结构图
- tomcat最顶层容器是Server,代表着整个服务器,一个Server包含多个Service。从上图可以看除Service主要包括多个Connector和一个Container。Connector用来处理连接相关的事情,并提供Socket到Request和Response相关转化。Container用于封装和管理Servlet,以及处理具体的Request请求。
- 综上所述,一个tomcat只包含一个Server,一个Server可以包含多个Service,一个Service只有一个Container,但有多个Connector,这样一个服务可以处理多个连接。
- 多个Connector和一个Container就形成了一个Service,有了Service就可以对外提供服务了,但是Service要提供服务又必须提供一个宿主环境,那就非Server莫属了,所以整个tomcat的声明周期都由Server控制。