• 微服务 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面试必修 ,请自备水,更多干、干、干货等着你

  • 相关阅读:
    [Swift]数学库函数math.h | math.h -- mathematical library function
    [Swift]LeetCode492. 构造矩形 | Construct the Rectangle
    FansMail:邮件发送标准API与技术实现(Java)
    FansMail:邮件发送标准API与技术实现(Java)
    大话世界格局:春秋五霸与战国七雄
    大话世界格局:春秋五霸与战国七雄
    大家好,我是FansUnion,雷文
    大家好,我是FansUnion,雷文
    2013年总结(2)-财务收入与支出
    2013年总结(2)-财务收入与支出
  • 原文地址:https://www.cnblogs.com/itmsbx/p/9696598.html
Copyright © 2020-2023  润新知