zoukankan      html  css  js  c++  java
  • SpringBoot源码解析

    SpringBoot源码解析

    我们启动SpringBoot的代码:

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

    我们主要从3大点着手解析:

    • @SpringBootApplication注解
    • 构建SpringApplication对象
    • SpringApplication的run方法

    @SpringBootApplication解析

    我们来看看SpringBootApplication的注解的代码:

    @Target(ElementType.TYPE)    //注解的适用范围,Type表示注解可以描述在类、接口、注解或枚举中
    @Retention(RetentionPolicy.RUNTIME) ///表示注解的生命周期,Runtime运行时
    @Documented ////表示注解可以记录在javadoc中
    @Inherited   //表示可以被子类继承该注解
    
    @SpringBootConfiguration //// 标明该类为配置类
    @EnableAutoConfiguration  // 启动自动配置功能
    @ComponentScan(excludeFilters = {   // 包扫描器 <context:component-scan base-package="com.xxx.xxx"/>
    		@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
    		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
    public @interface SpringBootApplication 
    

    排除掉上面的4个元注解外,还剩下3个注解,我们一一分析。

    1. @SpringBootConfiguration

    这个里面什么都没有,就只是在这个注解上标明了@Configuration,代表是一个配置类。
    2. @EnableAutoConfiguration

    这个注解主要是启动自动配置功能,我们点进去详细看看。

    @AutoConfigurationPackage		//自动配置包 : 会把@springbootApplication注解标注的类所在包名拿到,并且对该包及其子包进行扫描,将组件添加到容器中
    @Import(AutoConfigurationImportSelector.class)  //可以帮助springboot应用将所有符合条件的@Configuration配置都加载到当前SpringBoot创建并使用的IoC容器(ApplicationContext)中
    public @interface EnableAutoConfiguration {
    

    2.1 我们先看@AutoConfigurationPackage注解

    @Import(AutoConfigurationPackages.Registrar.class)  //  默认将主配置类(@SpringBootApplication)所在的包及其子包里面的所有组件扫描到Spring容器中
    public @interface AutoConfigurationPackage {
    
    }
    

    可以看出主要是引入了Registrar这个类,继续跟踪Registrar这个类

    static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
    
    		// 获取的是项目主程序启动类所在的目录
    		//metadata:注解标注的元数据信息
    		@Override
    		public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
    			//默认将会扫描@SpringBootApplication标注的主配置类所在的包及其子包下所有组件
    			register(registry, new PackageImport(metadata).getPackageName());
    		}
    
    		@Override
    		public Set<Object> determineImports(AnnotationMetadata metadata) {
    			return Collections.singleton(new PackageImport(metadata));
    		}
    
    	}
    

    主要目的获取SpringBoot主程序启动类的包名并注册

    2.2 继续看@Import(AutoConfigurationImportSelector.class)

    @Override
    	public String[] selectImports(AnnotationMetadata annotationMetadata) {
    		//判断 enableautoconfiguration注解有没有开启,默认开启(是否进行自动装配)
    		if (!isEnabled(annotationMetadata)) {
    			return NO_IMPORTS;
    		}
    		//1. 加载配置文件META-INF/spring-autoconfigure-metadata.properties,从中获取所有支持自动配置类的条件
    		//作用:SpringBoot使用一个Annotation的处理器来收集一些自动装配的条件,那么这些条件可以在META-INF/spring-autoconfigure-metadata.properties进行配置。
    		// SpringBoot会将收集好的@Configuration进行一次过滤进而剔除不满足条件的配置类
    		// 自动配置的类全名.条件=值
    		AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
    		AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);
    		return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
    	}
    

    主要是获取并筛选出自动配置类并返回。

    1. @ComponentScan

    这个注解就是包扫描的作用,类似Spring里面的:

    <context:component-scan base-package="com.xxx.xxx"/>
    

    构建SpringApplication对象

    public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    
    		this.sources = new LinkedHashSet();
    		this.bannerMode = Mode.CONSOLE;
    		this.logStartupInfo = true;
    		this.addCommandLineProperties = true;
    		this.addConversionService = true;
    		this.headless = true;
    		this.registerShutdownHook = true;
    		this.additionalProfiles = new HashSet();
    		this.isCustomEnvironment = false;
    		this.resourceLoader = resourceLoader;
    		Assert.notNull(primarySources, "PrimarySources must not be null");
    
    		//项目启动类 SpringbootDemoApplication.class设置为属性存储起来
    		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    
    		//设置应用类型是SERVLET应用(Spring 5之前的传统MVC应用)还是REACTIVE应用(Spring 5开始出现的WebFlux交互式应用)
    		this.webApplicationType = WebApplicationType.deduceFromClasspath();
    
    		// 设置初始化器(Initializer),最后会调用这些初始化器
    		//所谓的初始化器就是org.springframework.context.ApplicationContextInitializer的实现类,在Spring上下文被刷新之前进行初始化的操作
    		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    
    		// 设置监听器(Listener)
    		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    
    		// 初始化 mainApplicationClass 属性:用于推断并设置项目main()方法启动的主程序启动类
    		this.mainApplicationClass = deduceMainApplicationClass();
    	}
    

    run方法

    public ConfigurableApplicationContext run(String... args) {
    	    // 创建 StopWatch 对象,并启动。StopWatch 主要用于简单统计 run 启动过程的时长。
    		StopWatch stopWatch = new StopWatch();
    		stopWatch.start();
    		// 初始化应用上下文和异常报告集合
    		ConfigurableApplicationContext context = null;
    		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    		// 配置 headless 属性
    		configureHeadlessProperty();
    
    
    		//   (1)获取并启动监听器
    		SpringApplicationRunListeners listeners = getRunListeners(args);
    		listeners.starting();
    		try {
    		    // 创建  ApplicationArguments 对象 初始化默认应用参数类
    			// args是启动Spring应用的命令行参数,该参数可以在Spring应用中被访问。如:--server.port=9000
    			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
    
    			//(2)项目运行环境Environment的预配置
    			// 创建并配置当前SpringBoot应用将要使用的Environment
    			// 并遍历调用所有的SpringApplicationRunListener的environmentPrepared()方法
    			ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
    
    			configureIgnoreBeanInfo(environment);
    			// 准备Banner打印器 - 就是启动Spring Boot的时候打印在console上的ASCII艺术字体
    			Banner printedBanner = printBanner(environment);
    
    			// (3)创建Spring容器
    			context = createApplicationContext();
    			// 获得异常报告器 SpringBootExceptionReporter 数组
    			//这一步的逻辑和实例化初始化器和监听器的一样,
    			// 都是通过调用 getSpringFactoriesInstances 方法来获取配置的异常类名称并实例化所有的异常处理类。
    			exceptionReporters = getSpringFactoriesInstances(
    					SpringBootExceptionReporter.class,
    					new Class[] { ConfigurableApplicationContext.class }, context);
    
    
    			// (4)Spring容器前置处理
    			//这一步主要是在容器刷新之前的准备动作。包含一个非常关键的操作:将启动类注入容器,为后续开启自动化配置奠定基础。
    			prepareContext(context, environment, listeners, applicationArguments,
    					printedBanner);
    
    			// (5):刷新容器
    			refreshContext(context);
    
    			// (6):Spring容器后置处理
    			//扩展接口,设计模式中的模板方法,默认为空实现。
    			// 如果有自定义需求,可以重写该方法。比如打印一些启动结束log,或者一些其它后置处理
    			afterRefresh(context, applicationArguments);
    			// 停止 StopWatch 统计时长
    			stopWatch.stop();
    			// 打印 Spring Boot 启动的时长日志。
    			if (this.logStartupInfo) {
    				new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
    			}
    			// (7)发出结束执行的事件通知
    			listeners.started(context);
    
    			// (8):执行Runners
    			//用于调用项目中自定义的执行器XxxRunner类,使得在项目启动完成后立即执行一些特定程序
    			//Runner 运行器用于在服务启动时进行一些业务初始化操作,这些操作只在服务启动后执行一次。
    			//Spring Boot提供了ApplicationRunner和CommandLineRunner两种服务接口
    			callRunners(context, applicationArguments);
    		} catch (Throwable ex) {
    		    // 如果发生异常,则进行处理,并抛出 IllegalStateException 异常
    			handleRunFailure(context, ex, exceptionReporters, listeners);
    			throw new IllegalStateException(ex);
    		}
    
            //   (9)发布应用上下文就绪事件
    		//表示在前面一切初始化启动都没有问题的情况下,使用运行监听器SpringApplicationRunListener持续运行配置好的应用上下文ApplicationContext,
    		// 这样整个Spring Boot项目就正式启动完成了。
    		try {
    			listeners.running(context);
    		} catch (Throwable ex) {
                // 如果发生异常,则进行处理,并抛出 IllegalStateException 异常
                handleRunFailure(context, ex, exceptionReporters, null);
    			throw new IllegalStateException(ex);
    		}
    		 //返回容器
    		return context;
    	}
    
    书山有路勤为径,学海无涯苦作舟
  • 相关阅读:
    sp2010 升级sp2013 用户无法打开网站
    powerviot install in sharepoint 2013
    can not connect cube in performancce dashboard
    westrac server security configure user info
    添加报表服务在多服务器场
    sharepoint 2013 office web app 2013 文档在线浏览 IE11 浏览器不兼容解决方法
    delete job definition
    目前付款申请单内网打开慢的问题
    item style edit in sharepoint 2013
    Could not load file or assembly '$SharePoint.Project.AssemblyFullName$'
  • 原文地址:https://www.cnblogs.com/javammc/p/15659153.html
Copyright © 2011-2022 走看看