SpringBoot启动解析

Spring Boot、Spring MVC 和 Spring 有什么区别?

分开描述各自的特征:
Spring 框架就像一个家族,有众多衍生产品例如 boot、security、jpa等等。但他们的基础都是Spring 的ioc和 aop,ioc 提供了依赖注入的容器, aop解决了面向横切面的编程,然后在此两者的基础上实现了其他延伸产品的高级功能。

Spring MVC提供了一种轻度耦合的方式来开发web应用。它是Spring的一个模块,是一个web框架。通过Dispatcher Servlet, ModelAndView 和 View Resolver,开发web应用变得很容易。解决的问题领域是网站应用程序或者服务开发——URL路由、Session、模板引擎、静态Web资源等等。

Spring Boot实现了自动配置,降低了项目搭建的复杂度。它主要是为了解决使用Spring框架需要进行大量的配置太麻烦的问题,所以它并不是用来替代Spring的解决方案,而是和Spring框架紧密结合用于提升Spring开发者体验的工具。同时它集成了大量常用的第三方库配置(例如Jackson, JDBC, Mongo, Redis, Mail等等),Spring Boot应用中这些第三方库几乎可以零配置的开箱即用(out-of-the-box)。

所以,用最简练的语言概括就是:
Spring 是一个“引擎”;
Spring MVC 是基于Spring的一个 MVC 框架;
Spring Boot 是基于Spring4的条件注册的一套快速开发整合包。

SpringBoot到底是怎么做到自动配置的?

从代码里看项目SpringBoot的项目启动类只有一个注解@SpringBootApplication和一个run方法。


  1. @SpringBootApplication

  2. public class Application {

  3. public static void main(String[] args) {

  4. SpringApplication.run(Application.class, args);

  5. }

  6. }

  7. 复制代码

直接看@SpringBootApplication的代码:


  1. @Target({ElementType.TYPE})

  2. @Retention(RetentionPolicy.RUNTIME)

  3. @Documented

  4. @Inherited

  5. @SpringBootConfiguration

  6. @EnableAutoConfiguration

  7. @ComponentScan(

  8. excludeFilters = {@Filter(

  9. type = FilterType.CUSTOM,

  10. classes = {TypeExcludeFilter.class}

  11. ), @Filter(

  12. type = FilterType.CUSTOM,

  13. classes = {AutoConfigurationExcludeFilter.class}

  14. )}

  15. )

  16. public @interface SpringBootApplication {

  17. @AliasFor(

  18. annotation = EnableAutoConfiguration.class

  19. )

  20. Class<?>[] exclude() default {};
  21.  

  22. @AliasFor(

  23. annotation = EnableAutoConfiguration.class

  24. )

  25. String[] excludeName() default {};
  26.  

  27. @AliasFor(

  28. annotation = ComponentScan.class,

  29. attribute = "basePackages"

  30. )

  31. String[] scanBasePackages() default {};
  32.  

  33. @AliasFor(

  34. annotation = ComponentScan.class,

  35. attribute = "basePackageClasses"

  36. )

  37. Class<?>[] scanBasePackageClasses() default {};

  38. }

  39. 复制代码

@SpringBootApplication:包含了@SpringBootConfiguration(打开是@Configuration),@EnableAutoConfiguration,@ComponentScan注解。

@Configuration

JavaConfig形式的Spring Ioc容器的配置类使用的那个@Configuration,SpringBoot社区推荐使用基于JavaConfig的配置形式,所以,这里的启动类标注了@Configuration之后,本身其实也是一个IoC容器的配置类。
对比一下传统XML方式和config配置方式的区别:

  • XML声明和定义配置方式:

  1. <?xml version="1.0" encoding="UTF-8"?>

  2. <beans xmlns="http://www.springframework.org/schema/beans"

  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"

  4. xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"

  5. xsi:schemaLocation="http://www.springframework.org/schema/beans

  6. http://www.springframework.org/schema/beans/spring-beans-3.0.xsd

  7. http://www.springframework.org/schema/aop

  8. http://www.springframework.org/schema/aop/spring-aop-3.0.xsd

  9. http://www.springframework.org/schema/context

  10. http://www.springframework.org/schema/context/spring-context-3.0.xsd

  11. http://www.springframework.org/schema/tx

  12. http://www.springframework.org/schema/tx/spring-tx-3.0.xsd

  13. ">

  14. <bean id="app" class="com..." />

  15. 复制代码
  • 用一个过滤器举例,JavaConfig的配置方式是这样:

  1. @Configuration

  2. public class DruidConfiguration {

  3. @Bean

  4. public FilterRegistrationBean statFilter(){

  5. //创建过滤器

  6. FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new WebStatFilter());

  7. //设置过滤器过滤路径

  8. filterRegistrationBean.addUrlPatterns("/*");

  9. //忽略过滤的形式

  10. filterRegistrationBean.addInitParameter("exclusions","*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");

  11. return filterRegistrationBean;

  12. }

  13. }
  14.  

  15. 复制代码

任何一个标注了@Configuration的Java类定义都是一个JavaConfig配置类。
任何一个标注了@Bean的方法,其返回值将作为一个bean定义注册到Spring的IoC容器,方法名将默认成该bean定义的id。

@ComponentScan

@ComponentScan对应XML配置中的元素,@ComponentScan的功能其实就是自动扫描并加载符合条件的组件(比如@Component和@Repository等)或者bean定义,最终将这些bean定义加载到IoC容器中。
我们可以通过basePackages等属性来细粒度的定制@ComponentScan自动扫描的范围,如果不指定,则默认Spring框架实现会从声明@ComponentScan所在类的package进行扫描。
注:所以SpringBoot的启动类最好是放在root package下,因为默认不指定basePackages。在这里顺便给大家推荐一个架构交流群:617434785,里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化这些成为架构师必备的知识体系。还能领取免费的学习资源。相信对于已经工作和遇到技术瓶颈的码友,在这个群里会有你需要的内容。

@EnableAutoConfiguration

核心内容)看英文意思就是自动配置,概括一下就是,借助@Import的帮助,将所有符合自动配置条件的bean定义加载到IoC容器。


  1. @Target({ElementType.TYPE})

  2. @Retention(RetentionPolicy.RUNTIME)

  3. @Documented

  4. @Inherited

  5. @AutoConfigurationPackage

  6. @Import({AutoConfigurationImportSelector.class})

  7. public @interface EnableAutoConfiguration {

  8. String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
  9.  

  10. Class<?>[] exclude() default {};
  11.  

  12. String[] excludeName() default {};

  13. }

  14. 复制代码

里面最关键的是@Import(EnableAutoConfigurationImportSelector.class),借助EnableAutoConfigurationImportSelector,@EnableAutoConfiguration可以帮助SpringBoot应用将所有符合条件的@Configuration配置都加载到当前SpringBoot创建并使用的IoC容器。该配置模块的主要使用到了SpringFactoriesLoader。

SpringFactoriesLoader详解

SpringFactoriesLoader为Spring工厂加载器,该对象提供了loadFactoryNames方法,入参为factoryClass和classLoader即需要传入工厂类名称和对应的类加载器,方法会根据指定的classLoader,加载该类加器搜索路径下的指定文件,即spring.factories文件,传入的工厂类为接口,而文件中对应的类则是接口的实现类,或最终作为实现类。


  1. public abstract class SpringFactoriesLoader {

  2. //...

  3. public static <T> List<T> loadFactories(Class<T> factoryClass, ClassLoader classLoader) {

  4. ...

  5. }

  6. public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {

  7. ....

  8. }

  9. }

  10. 复制代码

所以文件中一般为如下图这种一对多的类名集合,获取到这些实现类的类名后,loadFactoryNames方法返回类名集合,方法调用方得到这些集合后,再通过反射获取这些类的类对象、构造方法,最终生成实例。

下图有助于我们形象理解自动配置流程(盗个图)

AutoConfigurationImportSelector

继续上面讲的AutoConfigurationImportSelector.class。该类主要关注selectImports方法


  1. public String[] selectImports(AnnotationMetadata annotationMetadata) {

  2. if (!this.isEnabled(annotationMetadata)) {

  3. return NO_IMPORTS;

  4. } else {

  5. AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);

  6. AnnotationAttributes attributes = this.getAttributes(annotationMetadata);

  7. List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);

  8. configurations = this.removeDuplicates(configurations);

  9. Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);

  10. this.checkExcludedClasses(configurations, exclusions);

  11. configurations.removeAll(exclusions);

  12. configurations = this.filter(configurations, autoConfigurationMetadata);

  13. this.fireAutoConfigurationImportEvents(configurations, exclusions);

  14. return StringUtils.toStringArray(configurations);

  15. }

  16. }

  17. 复制代码

该方法在springboot启动流程——bean实例化前被执行,返回要实例化的类信息列表。如果获取到类信息,spring可以通过类加载器将类加载到jvm中,现在我们已经通过spring-boot的starter依赖方式依赖了我们需要的组件,那么这些组件的类信息在select方法中就可以被获取到。


  1. protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {

  2. List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());

  3. Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");

  4. return configurations;

  5. }

  6. 复制代码

方法中的getCandidateConfigurations方法,其返回一个自动配置类的类名列表,方法调用了loadFactoryNames方法,查看该方法


  1. public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {

  2. String factoryClassName = factoryClass.getName();

  3. return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());

  4. }

  5. 复制代码

自动配置器会跟根据传入的factoryClass.getName()到项目系统路径下所有的spring.factories文件中找到相应的key,从而加载里面的类。我们就选取这个mybatis-spring-boot-autoconfigure下的spring.factories文件


  1. # Auto Configure

  2. org.springframework.boot.autoconfigure.EnableAutoConfiguration=\

  3. org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration

  4. 复制代码

进入org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration中,又是一堆注解


  1. @org.springframework.context.annotation.Configuration

  2. @ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class})

  3. @ConditionalOnBean({DataSource.class})

  4. @EnableConfigurationProperties({MybatisProperties.class})

  5. @AutoConfigureAfter({DataSourceAutoConfiguration.class})

  6. public class MybatisAutoConfiguration

  7. {

  8. private static final Logger logger = LoggerFactory.getLogger(MybatisAutoConfiguration.class);

  9. private final MybatisProperties properties;

  10. private final Interceptor[] interceptors;

  11. private final ResourceLoader resourceLoader;

  12. private final DatabaseIdProvider databaseIdProvider;

  13. private final List<ConfigurationCustomizer> configurationCustomizers;

  14. 复制代码
  • @Spring的Configuration是一个通过注解标注的springBean,
  • @ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class})这个注解的意思是:当存在SqlSessionFactory.class, SqlSessionFactoryBean.class这两个类时才解析MybatisAutoConfiguration配置类,否则不解析这一个配置类。我们需要mybatis为我们返回会话对象,就必须有会话工厂相关类
  • @CondtionalOnBean(DataSource.class):只有处理已经被声明为bean的dataSource
  • @ConditionalOnMissingBean(MapperFactoryBean.class)这个注解的意思是如果容器中不存在name指定的bean则创建bean注入,否则不执行

以上配置可以保证sqlSessionFactory、sqlSessionTemplate、dataSource等mybatis所需的组件均可被自动配置,@Configuration注解已经提供了Spring的上下文环境,所以以上组件的配置方式与Spring启动时通过mybatis.xml文件进行配置起到一个效果。
只要一个基于SpringBoot项目的类路径下存在SqlSessionFactory.class, SqlSessionFactoryBean.class,并且容器中已经注册了dataSourceBean,就可以触发自动化配置,意思说我们只要在maven的项目中加入了mybatis所需要的若干依赖,就可以触发自动配置,但引入mybatis原生依赖的话,每集成一个功能都要去修改其自动化配置类,那就得不到开箱即用的效果了。所以Spring-boot为我们提供了统一的starter可以直接配置好相关的类,触发自动配置所需的依赖(mybatis)如下:


  1. <dependency>

  2. <groupId>org.mybatis.spring.boot</groupId>

  3. <artifactId>mybatis-spring-boot-starter</artifactId>

  4. </dependency>

  5. 复制代码

因为maven依赖的传递性,我们只要依赖starter就可以依赖到所有需要自动配置的类,实现开箱即用的功能。也体现出Springboot简化了Spring框架带来的大量XML配置以及复杂的依赖管理,让开发人员可以更加关注业务逻辑的开发。

再贴个盗的图SpringBoot的启动结构图

猜你喜欢

转载自blog.csdn.net/hellozhxy/article/details/81591024