如今的项目都是springboot的了,相比以前的ssm或者ssh项目,springboot采用约定大于配置模式项目搭建特别简单,并且springboot项目的开发效率也提高了好几倍。只需在SpringApplication运行run方法,就能够运行springboot项目,可是不少人就只了解到这里了,彻底不了解springboot背后是怎么作到的。因此就跟随着我根据源码来一块儿探索这背后的原理吧。web
/** Spring Boot应用启动类* Created by fqh on 17/6/24.*/@SpringBootApplication@ComponentScan("com.wish.platform")public class Application{/*** 无 applicationContext.xml 和 web.xml, 靠下述方式进行配置:* 1. 扫描当前package下的class设置自动注入的Bean* 2. 也支持用@Bean标注的类配置Bean* 3. 根据classpath中的三方包Class及集中的application.properties条件配置三方包,如线程池* 4. 也支持用@Configuration标注的类配置三方包.*/public static void main(String[] args) {SpringApplication.run(Application.class, args);}}
上面是一个比较标准的springboot启动类,比较扎眼的的地方其实就两个spring
注解 @SpringBootApplicationspringboot
main中的SpringApplication.run(Application.class, args);
咱们挨个进行分析,一层层的揭开这神秘的面纱
首先@SpringBootApplication,点击源码查看app
@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 {...略...}
一扎眼看好多注解,可是实际上重要的就几个,(PS: @Inherited是注解子类能够继承 @Documented只是用于生成文档注视)框架
@SpringBootConfigurationless
@EnableAutoConfigurationssh
@ComponentScan
因此从这里咱们能够判断出,一个@SpringBootApplication等于上述三个注解。ide
@SpringBootConfiguration解析
这个点击进去发现,其实这个就是个@Configuration工具
@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@Configurationpublic @interface SpringBootConfiguration {}
Configuration应该不陌生了把。spring为了减小xml配置,从版本3.0开始提供了@Configuration配合@Bean用于在JavaConfig类上进行标注,用于代替xml的配置。例如:
Configuration
public class DaoConfig{
Bean
public UserDao userDao(){ return new UserDaoImpl();}
Bean
public BookDao bookDao(){return new BookDaoImpl();}
}
若是一个bean的定义依赖其余bean,则直接调用对应的JavaConfig类中依赖bean的建立方法就能够了。测试
@ComponentScan解析
做用就是在指定的目录下(basePackages属性控制)自动扫描复合加载条件的bean(包括@Compoent,@Controller,@Repository),并最终把这些bean加入到spring的IOC容器里.
@EnableAutoConfiguration解析
源码:
@SuppressWarnings("deprecation")@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited@AutoConfigurationPackage@Import(EnableAutoConfigurationImportSelector.class)public @interface EnableAutoConfiguration {String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";...其余略..}
重点来了,发现EnableAutoConfiguration是一个复合Annotation,关键的组合注解以下:
@AutoConfigurationPackage :标注自动配置包
@Import(EnableAutoConfigurationImportSelector.class) :最重要的
借助于EnableAutoConfigurationImportSelector,能够帮助springboot程序将全部复合条件的@Configuration配置都加载到 由当前springboot建立使用的IOC容器中,保证全部实例都
能够在一个ApplicationContext里
EnableAutoConfigurationImportSelector类里有这么几个方法都用到了SpringFactoriesLoader这个spring框架的工具类
protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters() {return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class,this.beanClassLoader);}/*** Return the auto-configuration class names that should be considered. By default* this method will load candidates using {@link SpringFactoriesLoader} with* {@link #getSpringFactoriesLoaderFactoryClass()}.* @param metadata the source metadata* @param attributes the {@link #getAttributes(AnnotationMetadata) annotation* attributes}* @return a list of candidate configurations*/protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,AnnotationAttributes attributes) {List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());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.");return configurations;}
借助于SpringFactoriesLoader的支持,@EnableAutoConfiguration 才能够智能的实现自动配置!
这里再简单介绍一下SpringFactoriesLoader,这是spring的本身的扩展方案,主要经过META-INF/spring.factories加载配置
搜索之后能够看到不少spring相关的jar里都有这个文件,
随便打开一个
发现key是一个接口类,value是具体的一个或者一组实现类。
总结:这样@EnableutoConfiguration配合SpringFactoriesLoader,就是从classpath里搜索全部的META-INF/spring.factories配置文件,并将配置文件的配置项key接口类 经过反射 实例化全部value的实现类。而后这些实例化的类都汇总加载到当前springboot 上下文的IOC容器中
追踪源码,能够看到SpringApplication的静态run方法实际上返回的是ApplicationContext,具体中间实现过程能够看一下代码,我加了一些注视,方便你们理解
public ConfigurableApplicationContext run(String... args) {StopWatch stopWatch = new StopWatch();//提供一组方法和属性,可用于准确地测量运行时间。stopWatch.start();//计时开始ConfigurableApplicationContext context = null;FailureAnalyzers analyzers = null;this.configureHeadlessProperty();SpringApplicationRunListeners listeners = this.getRunListeners(args);//获取springboot运行时监听listeners.starting();//监听开始try {ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);//获取运行时环境(就是你设置的profile,开发环境,测试环境这些)Banner printedBanner = this.printBanner(environment);//springboot的bannner信息,佛祖保佑context = this.createApplicationContext();//建立上下文new FailureAnalyzers(context);this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);//根据当前context,profile,参数,banner,来预处理上下文(说白了就是把这些参数都对应赋值到上下文上)。this.refreshContext(context);//根据上下文参数来判断建立什么类型的上下文,根据条件决定是否添加ShutDownHook钩子。this.afterRefresh(context, applicationArguments);//最核心的一步,把全部经过EnableAutoConfiguration获取的实例添加到一个IOC容器里,而且都添加到当前的上下文ApplicationContext里listeners.finished(context, (Throwable)null);//最后一道,结束监听stopWatch.stop();//能够计算出启动时间if (this.logStartupInfo) {(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);}return context;} catch (Throwable var9) {this.handleRunFailure(context, listeners, (FailureAnalyzers)analyzers, var9);throw new IllegalStateException(var9);}}
到此,SpringBoot的核心组件完成了基本的解析,综合来看,大部分都是Spring框架背后的一些概念和实践方式,SpringBoot只是在这些概念和实践上对特定的场景事先进行了固化和升华,而也偏偏是这些固化让咱们开发基于Sping框架的应用更加方便高效。PS:但愿我这个花了三四个小时才写完的文章,能给你带来深刻的启发!