zoukankan      html  css  js  c++  java
  • 微服务 SpringBoot 2.0(三):启动剖析之@SpringBootApplication

    我原以为就一个注解,背后竟然还有3个 —— Java面试必修

    引言

    前面两章我们先后认识了SpringBoot和它的极简配置,为新手入门的学习降低了门槛,会基本的使用后,接下来我们将进一步认识SpringBoot,它为何能做到服务秒开,就来跟随我一起分析SpringBoot运行启动的原理吧。

    启动原理分2章讲解,本章讲解@SpringBootApplication注解部分,若需了解SpringApplication.run方法部分请点击此处

    运行启动

    工具

    • SpringBoot版本:2.0.4
    • 开发工具:IDEA 2018
    • Maven:3.3 9
    • JDK:1.8

    首先我们看一段启动代码

    @SpringBootApplication
    public class DemoApplication {
        public static void main(String[] args) {
            SpringApplication.run(DemoApplication.class, args);
        }
    }

    这不就是个启动类嘛?从字面理解我都知道Spring启动的入口,有啥好看的。可别小瞧了这几行代码

    开始推理

    从上面代码来看,@SpringBootApplication 和 SpringApplication.run长得很相似,比较诡异,所以我们从这两个开始分析,首先先看注解

    @SpringBootApplication
    @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 {
    ...
    }

    不要被那么多元注解声明所吓到,仔细查看可以看出其中有3个重要的注解

    • @SpringBootConfiguration
    • @EnableAutoConfiguration
    • @ComponentScan

    所以我觉得,这3个才是真正的幕后主使,@SpringBootApplication只是他们找来挡枪口的,他们合体应该等价于@SpringBootApplication,如下代码正常运行

    @SpringBootConfiguration
    @EnableAutoConfiguration
    @ComponentScan
    public class DemoApplication {
        public static void main(String[] args) {
            SpringApplication.run(DemoApplication.class, args);
        }
    }

    哈哈,经过查询资料发现,Spring Boot 1.2版之前,真的是由这3个注解出面,之后呢,才隐居幕后,由小喽喽在前面挡枪


    @SpringBootConfiguration

    首先我们来分析这个,点开看到源码之后我慌了,这特么什么都没有,唯一的疑点在@Configuration。
    好吧,还挺会伪装,我们就分析@Configuration这个注解吧

    1. 用之前spring的思维,我推断这个应该是解决当前Class的XML配置问题,凡是经过该注解修饰的,均被实例化到Spring应用程序上下文中,由spring统一管理生命周期,我说IoC大家应该熟悉了吧。
    2. 举个例子
      传统的XML配置对象的写法
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"
           default-lazy-init="true">
        <!--bean定义-->
      <bean id="userService" class="..UserServiceImpl">
        ...
      </bean>
    </beans>

    使用@Configuration之后的写法

    @Configuration
    public class SpringConfiguration{
        @Bean
        public UserService userService(){
            return new UserServiceImpl();
        }
    }

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

    1. 表达依赖注入关系层面
    <bean id="userService" class="..UserServiceImpl">
        <propery name ="dependencyService" ref="dependencyService" />
    </bean>
    
    <bean id="dependencyService" class="DependencyServiceImpl"></bean>
    @Configuration
    public class SpringConfiguration{
        @Bean
        public UserService userService(){
            return new UserServiceImpl(dependencyService());
        }
        
        @Bean
        public DependencyService dependencyService(){
            return new DependencyServiceImpl();
        }
    }

    如果一个bean的定义依赖其他bean,则直接调用对应的JavaConfig类中依赖bean的创建方法即可,如上方的dependencyService()


    @ComponentScan

    这个注解在Spring中很重要,它对应XML配置中的元素,@ComponentScan的功能其实就是自动扫描并加载符合条件的组件(比如@Component@Repository等)或者bean定义,最终将这些bean定义加载到IoC容器中。

    我们可以通过basePackages等属性来细粒度的定制@ComponentScan自动扫描的范围,如果不指定,则默认Spring框架实现会从声明@ComponentScan所在类的package进行扫描。

    以前xml中是这么定义的,如下

     <context:component-scan base-package="com.platform.fox.html.**.controller"/>
     <context:component-scan base-package="com.platform.fox.html.**.repository"/>
     <context:component-scan base-package="com.platform.fox.html.**.service"/>
     <context:component-scan base-package="com.platform.fox.html.**.wshandler"/>

    注:所以SpringBoot的启动类最好是放在root package下,因为默认不指定basePackages


    @EnableAutoConfiguration

    Enable,通常来说我们认为它一定是在开启或者支持什么功能,比如:@EnableScheduling@EnableCaching,所以他们要做的事情应该都是相似的,根据源码判断,简单概括一下就是,借助@Import的支持,收集和注册特定场景相关的bean定义。

    @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 {};
    }

    我理解就是要开船了,EnableAutoConfigurationImportSelector根据名单把水手,舵手、安检员都统一叫过来各就各位。帮助SpringBoot应用将所有符合条件的@Configuration配置都加载到当前SpringBoot创建并使用的IoC容器。就像一管理员一样

    借助于Spring框架原有的一个工具类:SpringFactoriesLoader的支持,@EnableAutoConfiguration可以智能的自动配置功效才得以大功告成!

    EnableAutoConfigurationImportSelector中自动配置的关键方法如下

    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
            //获取符合条件的带有Configuration的注解示例类
            List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.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
    所以真正的工作者是SpringFactoriesLoaderSpringFactoriesLoader属于Spring框架私有的一种扩展方案,其主要功能就是从指定的配置文件META-INF/spring.factories加载配置

    public abstract class SpringFactoriesLoader {
        //...
        public static <T> List<T> loadFactories(Class<T> factoryClass, @Nullable ClassLoader classLoader) {
            ...
        }
    
    
        public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
            ....
        }
    }

    配合@EnableAutoConfiguration使用的话,它更多是提供一种配置查找的功能支持,即根据@EnableAutoConfiguration的完整类名org.springframework.boot.autoconfigure.EnableAutoConfiguration作为查找的Key,获取对应的一组@Configuration类

     
    spring.factories

    上图就是从SpringBoot的autoconfigure依赖包中的META-INF/spring.factories配置文件中摘录的一段内容,可以很好地说明问题。我从中随机挑取一个类查看源代码

     
    WebMvcAutoConfiguration

    所以,其底层真正实现是从classpath中搜寻所有的META-INF/spring.factories配置文件,并将其中org.springframework.boot.autoconfigure.EnableutoConfiguration对应的配置项通过反射实例化为对应的标注了@Configuration的JavaConfig形式的IoC容器配置类,然后汇总为一个并加载到IoC容器。

    @SpringBootApplication注解说完了,接下来我们来分析SpringApplication.run方法


    作者有话说:喜欢的话就请关注Java面试必修 ,请自备水,更多干、干、干货等着你

  • 相关阅读:
    C语言 realloc为什么要有返回值,realloc返回值具体解释/(解决随意长度字符串输入问题)。
    opencv中的vs框架中的Blob Tracking Tests的中文注释。
    Java实现 蓝桥杯VIP 算法提高 棋盘多项式
    Java实现 蓝桥杯VIP 算法提高 棋盘多项式
    Java实现 蓝桥杯VIP 算法提高 棋盘多项式
    Java实现 蓝桥杯VIP 算法提高 棋盘多项式
    Java实现 蓝桥杯VIP 算法提高 分苹果
    Java实现 蓝桥杯VIP 算法提高 分苹果
    Java实现 蓝桥杯VIP 算法提高 分苹果
    Java实现 蓝桥杯VIP 算法提高 分苹果
  • 原文地址:https://www.cnblogs.com/itmsbx/p/9696598.html
Copyright © 2011-2022 走看看