zoukankan      html  css  js  c++  java
  • 5、Spring Boot 2.x 启动原理解析

    1.5 Spring Boot 启动原理解析

    前言

    前面几章我们见识了SpringBoot为我们做的自动配置,确实方便快捷,但是对于新手来说,如果不大懂SpringBoot内部启动原理,以后难免会吃亏。所以这次就跟你们一起一步步揭开SpringBoot的神秘面纱,让它不在神秘。

    1.5.1 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 {
    ……
    }
    
    

    虽然定义使用了多个Annotation进行了原信息标注,但实际上重要的只有三个Annotation:

    • @Configuration(@SpringBootConfiguration点开查看发现里面还是应用了@Configuration)
    • @EnableAutoConfiguration
    • @ComponentScan
    1.5.1.1 @Configuration注解

    这里的@Configuration对我们来说不陌生,它就是JavaConfig形式的Spring Ioc容器的配置类使用的那个@Configuration,SpringBoot社区推荐使用基于JavaConfig的配置形式,所以,这里的启动类标注了@Configuration之后,本身其实也是一个IoC容器的配置类。

    举几个简单例子回顾下,XML跟config配置方式的区别:

    1.5.1.1.1 表达形式层面
    • 基于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定义-->
    </beans>
    
    • JavaConfig的配置方式是这样
    @Configuration
    public class MockConfiguration{
        //bean定义
    }
    

    任何一个标注了@Configuration的Java类定义都是一个JavaConfig配置类。

    1.5.1.1.2 注册bean定义层面
    • ,基于XML的配置形式是这样:
    <bean id="mockService" class="..MockServiceImpl">
        ...
    </bean>
    
    • 而基于JavaConfig的配置形式是这样的:
    @Configuration
    public class MockConfiguration{
        @Bean
        public MockService mockService(){
            return new MockServiceImpl();
        }
    }
    

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

    1.5.1.1.3 表达依赖注入关系层面
    • 为了表达bean与bean之间的依赖关系,在XML形式中一般是这样:
    <bean id="mockService" class="..MockServiceImpl">
        <propery name ="dependencyService" ref="dependencyService" />
    </bean>
    
    <bean id="dependencyService" class="DependencyServiceImpl"></bean>
    
    • 而基于JavaConfig的配置形式是这样的:
    @Configuration
    public class MockConfiguration{
        @Bean
        public MockService mockService(){
            return new MockServiceImpl(dependencyService());
        }
        
        @Bean
        public DependencyService dependencyService(){
            return new DependencyServiceImpl();
        }
    }
    

    如果一个bean的定义依赖其他bean,则直接调用对应的JavaConfig类中依赖bean的创建方法就可以了。

    1.5.1.2 @ComponentScan 注解

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

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

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

    1.5.1.3 @EnableAutoConfiguration 注解

    详见 1.4 Spring Boot 自动配置原理

    1.5.2 SpringApplication执行流程

    1.5.2.1 SpringApplication.run()源码分析
    public ConfigurableApplicationContext run(String... args) {
            // 时间监控
    		StopWatch stopWatch = new StopWatch();
    		stopWatch.start();
    		ConfigurableApplicationContext context = null;
    		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    		// java.awt.headless是J2SE的一种模式用于在缺少显示屏、键盘或者鼠标时的系统配置,很多监控工具如jconsole 需要将该值设置为true,系统变量默认为true
    		configureHeadlessProperty();
    		// 获取spring.factories中的监听器变量,args为指定的参数数组,默认为当前类SpringApplication
    		//第一步:获取并启动监听器
    		SpringApplicationRunListeners listeners = getRunListeners(args);
    		listeners.starting();
    		try {
    			ApplicationArguments applicationArguments = new DefaultApplicationArguments(
    					args);
    			//第二步:构造容器环境
    			ConfigurableEnvironment environment = prepareEnvironment(listeners,
    					applicationArguments);
    			//  设置需要忽略的bean
    			configureIgnoreBeanInfo(environment);
    			//  打印banner
    			Banner printedBanner = printBanner(environment);
    			//第三步:创建容器
    			context = createApplicationContext();
    			//第四步:实例化SpringBootExceptionReporter.class,用来支持报告关于启动的错误
    			exceptionReporters = getSpringFactoriesInstances(
    					SpringBootExceptionReporter.class,
    					new Class[] { ConfigurableApplicationContext.class }, context);
    			//第五步:准备容器
    			prepareContext(context, environment, listeners, applicationArguments,
    					printedBanner);
    			//第六步:刷新容器
    			refreshContext(context);
    		    //第七步:刷新容器后的扩展接口
    			afterRefresh(context, applicationArguments);
    			stopWatch.stop();
    			if (this.logStartupInfo) {
    				new StartupInfoLogger(this.mainApplicationClass)
    						.logStarted(getApplicationLog(), stopWatch);
    			}
    			listeners.started(context);
    			callRunners(context, applicationArguments);
    		}
    		catch (Throwable ex) {
    			handleRunFailure(context, ex, exceptionReporters, listeners);
    			throw new IllegalStateException(ex);
    		}
    
    		try {
    			listeners.running(context);
    		}
    		catch (Throwable ex) {
    			handleRunFailure(context, ex, exceptionReporters, null);
    			throw new IllegalStateException(ex);
    		}
    		return context;
    	}
    

    SpringApplication的run方法的实现是我们本次旅程的主要线路,该方法的主要流程大体可以归纳如下:

    • 第一步:获取并启动监听器
    • 第二步:构造容器环境
    • 第三步:创建容器
    • 第四步:实例化SpringBootExceptionReporter.class,用来支持报告关于启动的错误
    • 第五步:准备容器
    • 第六步:刷新容器
    • 第七步:刷新容器后的扩展接口
  • 相关阅读:
    1058 A+B in Hogwarts (20)
    1036. Boys vs Girls (25)
    1035 Password (20)
    1027 Colors in Mars (20)
    1009. Product of Polynomials (25)
    1006. Sign In and Sign Out
    1005 Spell It Right (20)
    1046 Shortest Distance (20)
    ViewPager页面滑动,滑动到最后一页,再往后滑动则执行一个事件
    IIS7.0上传文件限制的解决方法
  • 原文地址:https://www.cnblogs.com/Grand-Jon/p/9986263.html
Copyright © 2011-2022 走看看