前言 Spring Boot核心功能
独立运行jar 包形式 Spring 项目 内嵌 Servlet 容器 提供 Starter 简化 Maven 配置 自动配置 Spring Bean 准生产的应用监控 无代码生成和 XML 配置 Spring Boot 的核心注解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 @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 { @AliasFor( annotation = EnableAutoConfiguration.class ) Class<?>[] exclude() default {}; @AliasFor( annotation = EnableAutoConfiguration.class ) String[] excludeName() default {}; @AliasFor( annotation = ComponentScan.class, attribute = "basePackages" ) String[] scanBasePackages() default {}; @AliasFor( annotation = ComponentScan.class, attribute = "basePackageClasses" ) Class<?>[] scanBasePackageClasses() default {}; @AliasFor( annotation = ComponentScan.class, attribute = "nameGenerator" ) Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class; @AliasFor( annotation = Configuration.class ) boolean proxyBeanMethods() default true; }
Spring Boot 自动配置
Spring Boot 在启动时扫描项目所依赖的 jar 包,寻找包含spring.factories
文件的 jar 包。 根据 spring.factories
配置加载 AutoConfigure 类。 根据 @Conditional等条件注解 的条件,进行自动配置并将 Bean 注入 Spring IoC 中。 Spring Boot 和集成Spring MVC
引入 spring-boot-starter-web
的依赖。 实现 WebMvcConfigurer 接口,可添加自定义的 Spring MVC 配置。 使用 Spring MVC 时,我们一般会做如下几件事情:
实现自己项目需要的拦截器,并在 WebMvcConfigurer 实现类中配置。 配置 @ControllerAdvice
+ @ExceptionHandler
注解,实现全局异常处理。 配置 @ControllerAdvice
,实现 ResponseBodyAdvice 接口,实现全局统一返回。 Spring Boot 和 Spring Security 的配置
引入 spring-boot-starter-security
的依赖。 继承 WebSecurityConfigurerAdapter ,添加自定义 的安全配置。 环境
1 git clone https://github.com/spring-projects/spring-boot
架构 核心模块
spring-boot 模块 spring-boot-autoconfigure 模块 Jar 启动实现 ① META-INF
目录:通过 MANIFEST.MF
文件提供 jar
包的元数据 ,声明了 jar
的启动类。 ② org
目录:为 Spring Boot 提供的 spring-boot-loader
项目,它是 java -jar
启动 Spring Boot 项目的秘密所在 ③ BOOT-INF/lib
目录:我们 Spring Boot 项目中引入的依赖 的 jar
包们。spring-boot-loader
项目很大的一个作用,就是解决 jar
包里嵌套 jar
的情况 ,如何加载到其中的类。 ④ BOOT-INF/classes
目录:我们在 Spring Boot 项目中 Java 类所编译的 .class
、配置文件等等。 SpringApplication 1 2 3 4 5 6 7 8 9 10 11 12 public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) { return run(new Class[]{primarySource}, args); } //// 创建 SpringApplication 对象,并执行运行。 public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { return (new SpringApplication(primarySources)).run(args); } public static void main(String[] args) throws Exception { run(new Class[0], args); }
首先,创建一个 SpringApplication 对象 。 然后,调用 SpringApplication#run(Class<?> primarySource, String... args)
方法,运行 Spring 应用 。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 // SpringApplication.java public ConfigurableApplicationContext run(String... args) { // <1> 创建 StopWatch 对象,并启动。StopWatch 主要用于简单统计 run 启动过程的时长。 StopWatch stopWatch = new StopWatch(); stopWatch.start(); // ConfigurableApplicationContext context = null; Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); // <2> 配置 headless 属性 configureHeadlessProperty(); // 获得 SpringApplicationRunListener 的数组,并启动监听 SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(); try { // <3> 创建 ApplicationArguments 对象 ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); // <4> 加载属性配置。执行完成后,所有的 environment 的属性都会加载进来,包括 application.properties 和外部的属性配置。 ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); configureIgnoreBeanInfo(environment); // <5> 打印 Spring Banner Banner printedBanner = printBanner(environment); // <6> 创建 Spring 容器。 context = createApplicationContext(); // <7> 异常报告器 exceptionReporters = getSpringFactoriesInstances( SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context); // <8> 主要是调用所有初始化类的 initialize 方法 prepareContext(context, environment, listeners, applicationArguments, printedBanner); // <9> 初始化 Spring 容器。 refreshContext(context); // <10> 执行 Spring 容器的初始化的后置逻辑。默认实现为空。 afterRefresh(context, applicationArguments); // <11> 停止 StopWatch 统计时长 stopWatch.stop(); // <12> 打印 Spring Boot 启动的时长日志。 if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch); } // <13> 通知 SpringApplicationRunListener 的数组,Spring 容器启动完成。 listeners.started(context); // <14> 调用 ApplicationRunner 或者 CommandLineRunner 的运行方法。 callRunners(context, applicationArguments); } catch (Throwable ex) { // <14.1> 如果发生异常,则进行处理,并抛出 IllegalStateException 异常 handleRunFailure(context, ex, exceptionReporters, listeners); throw new IllegalStateException(ex); } // <15> 通知 SpringApplicationRunListener 的数组,Spring 容器运行中。 try { listeners.running(context); } catch (Throwable ex) { // <15.1> 如果发生异常,则进行处理,并抛出 IllegalStateException 异常 handleRunFailure(context, ex, exceptionReporters, null); throw new IllegalStateException(ex); } return context; }
#createApplicationContext()
方法,创建 Spring 容器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context." + "annotation.AnnotationConfigApplicationContext"; public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot." + "web.servlet.context.AnnotationConfigServletWebServerApplicationContext"; public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework." + "boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext"; protected ConfigurableApplicationContext createApplicationContext() { Class<?> contextClass = this.applicationContextClass; if (contextClass == null) { try { switch (this.webApplicationType) { case SERVLET: contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS); break; case REACTIVE: contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS); break; default: contextClass = Class.forName(DEFAULT_CONTEXT_CLASS); } } catch (ClassNotFoundException ex) { throw new IllegalStateException( "Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex); } } return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass); }
#prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner)
方法,准备 ApplicationContext 对象,主要是初始化它的一些属性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 // SpringApplication.java private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { // <1> 设置 context 的 environment 属性 context.setEnvironment(environment); // <2> 设置 context 的一些属性 postProcessApplicationContext(context); // <3> 初始化 ApplicationContextInitializer applyInitializers(context); // <4> 通知 SpringApplicationRunListener 的数组,Spring 容器准备完成。 listeners.contextPrepared(context); // <5> 打印日志 if (this.logStartupInfo) { logStartupInfo(context.getParent() == null); logStartupProfileInfo(context); } // Add boot specific singleton beans // <6> 设置 beanFactory 的属性 ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); beanFactory.registerSingleton("springApplicationArguments", applicationArguments); if (printedBanner != null) { beanFactory.registerSingleton("springBootBanner", printedBanner); } if (beanFactory instanceof DefaultListableBeanFactory) { ((DefaultListableBeanFactory) beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); } // Load the sources // <7> 加载 BeanDefinition 们 Set<Object> sources = getAllSources(); Assert.notEmpty(sources, "Sources must not be empty"); load(context, sources.toArray(new Object[0])); // <8> 通知 SpringApplicationRunListener 的数组,Spring 容器加载完成。 listeners.contextLoaded(context); }
自动配置 自动装配原理
@EnableAutoConfiguration 开启自动配置
这个注解也是一个派生注解,其中的关键功能由@Import提供,其导入的AutoConfigurationImportSelector的selectImports()方法通过SpringFactoriesLoader.loadFactoryNames()扫描所有具有META-INF/spring.factories的jar包。
1 2 3 4 5 6 7 8 9 10 # Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\ org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\ org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\ org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\ org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\ org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\ org.springframework.boot.autoconfigure.cloud.CloudServiceConnectorsAutoConfiguration,\ org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
每一个XxxxAutoConfiguration自动配置类都是在某些条件之下才会生效的,这些条件的限制在Spring Boot中以注解的形式体现,常见的条件注解 有如下几项:
1 2 3 4 5 6 7 8 9 @ConditionalOnBean:当容器里有指定的bean的条件下。 @ConditionalOnMissingBean:当容器里不存在指定bean的条件下。 @ConditionalOnClass:当类路径下有指定类的条件下。 @ConditionalOnMissingClass:当类路径下不存在指定类的条件下。 @ConditionalOnProperty:指定的属性是否有指定的值,比如@ConditionalOnProperties(prefix=”xxx.xxx”, value=”enable”, matchIfMissing=true),代表当xxx.xxx为enable时条件的布尔值为true,如果没有设置的情况下也为true。
在全局配置的属性如:server.port等,通过@ConfigurationProperties注解,绑定到对应的XxxxProperties配置实体类上封装为一个bean,然后再通过@EnableConfigurationProperties注解导入到Spring容器中。
Spring Boot启动的时候会通过@EnableAutoConfiguration注解找到META-INF/spring.factories配置文件中的所有自动配置类,并对其进行加载,而这些自动配置类都是以AutoConfiguration结尾来命名的,它实际上就是一个JavaConfig形式的Spring容器配置类,它能通过以Properties结尾命名的类中取得在全局配置文件中配置的属性如:server.port,而XxxxProperties类是通过@ConfigurationProperties注解与全局配置文件中对应的属性进行绑定的。