springboot 从main启动开始到完成征途记之一前半生

对于java程序员来说,除了java本身外,spring应该是最需要具备的技能了。spring家族的给我带来的惊喜,全家桶带来的“芳香四溢”,让我们受用无穷。

说明

由于springboot 从main启动开始到启动完成涉及太多内容,所以将此征途记分为:前半生和后半生,所谓后半生是将SpringApplication.run()中的refreshContext(context)单拿出来,即ApplicationContext.refresh()作为后半生。

目标

本文主要介绍spring boot在启动过程中所做的事情。如下图中spring的图标是哪个类打出来的,日志信息又都是哪个类中的;如果我们想换掉这个spring图标又改如何操作呢等等,读后你会自有答案。本文力争将spring boot启动过程说全说细

环境

  • java version 1.8.0_151
  • spring boot 1.5.8
  • intellij 2017.3.2

最简单的springboot应用

App类及pom.xml

/**
 * spring boot 应用启动入口
 */
@SpringBootApplication
public class App {
    public static void main(String[] args) throws Exception {
        System.out.println("app"+new Date());
        // spring boot启动入口,将完成加载jar文件,加载配置文件,从中找到class或class name,加载class and/or class name等
        SpringApplication.run(App.class, args);
    }
}

------------------------------------------------------
pom.xml
<parent>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-parent</artifactId>
   <version>1.5.8.RELEASE</version>
</parent>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
复制代码

如上图代码,就可以运行spring boot 应用了。我们的讲解也将从这里开始,利用debug一起探索的旅程。

ready? --> Go!

征途开始

main方法中SpringApplication.run(App.class, args)是关键,spring boot 启动、加载、实例化等一系列的操作都从这里出发. SpringApplication类的注释说明

默认SpringApplication.run执行如下步骤去启动你的应用

1. 创建一个合适的applicationContext实例
2. 注册一个 CommandLinePropertySource 去暴露命令行参数作为spring参数。也就是 - jar 后面的参数
3. 刷新刚才创建的applicationContext,加载所有的单实例bean
4. 触发所有CommandLineRunner bean
复制代码

进入SpringApplication.run(App.class, args)方法,这个方法做了两大事情:SpringApplication实例化和调用run方法。如下

public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
    return new SpringApplication(sources).run(args);
}
复制代码

SpringApplication实例化

在构造函数中调用一个很重要的方法initialize(source),参数source为main方法中的App.class。initialize方法做了四件事判断是否为web应用、获取initializers、获取listeners、获取启动应用main方法所在的类

/**
 * 创建一个SpringApplication实例.这个实例在调用run方法前可以被自定义. The application context将从指定的sources加载beans.
 */
public SpringApplication(Object... sources) { 
    initialize(sources);
}
// 从指定资源(META-INF/spring.factories)加载指定的类型:ApplicationContextInitializer子类集合和ApplicationListener子类集合
private void initialize(Object[] sources) {
    this.sources.addAll(Arrays.asList(sources));
   
    // 推断是不是web环境,javax.servlet.Servlet","org.springframework.web.context.ConfigurableWebApplicationContext 两个类有且都存在才是web环境
    this.webEnvironment = deduceWebEnvironment();
    
    //获取META-INF/spring.factories中ApplicationContextInitializer的子类
    setInitializers((Collection) getSpringFactoriesInstances(
    		ApplicationContextInitializer.class));
    
    //获取META-INF/spring.factories中ApplicationListener的子类
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    
    // 获取启动应用的main方法所在的类
    this.mainApplicationClass = deduceMainApplicationClass();
}
复制代码

initialize方法会加载各个jar下的META-INF/spring.factories,从中获取ApplicationContextInitializer子类集合与ApplicationListener子类集合。获取的子类集合的作用下面会讲到。加载的方式见getSpringFactoriesInstances方法,如下

private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type,Class<?>[] parameterTypes, Object... args) {
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();

    // 通过要加载的类型和加载器浏览jar包们,从jar包们找到META-INF/spring.factories,从spring.factories中获取其子类名称集合
    Set<String> names = new LinkedHashSet<String>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));

    // 通过反射实例化加载从文件中获取的类们
    List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
    AnnotationAwareOrderComparator.sort(instances);
    return instances;
}

// 使用类名通过反射获取对象实例
private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args, Set<String> names) {
    List<T> instances = new ArrayList<T>(names.size());
    for (String name : names) {
    	try {
    	    // 通过类名称获取类class
    	    Class<?> instanceClass = ClassUtils.forName(name, classLoader);
    	    Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
    	    // 通过类class获取其构造类,结合反射获取类实例
    	    T instance = (T) BeanUtils.instantiateClass(constructor, args);
    	    instances.add(instance);
    	}
    	catch (Throwable ex) {throw new IllegalArgumentException()}
    }
    return instances;
}

// 通过要加载的类型和加载器浏览jar包们,从jar包们找到META-INF/spring.factories,从spring.factories中获取其子类名称集合
public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
    String factoryClassName = factoryClass.getName();
    try {
        //获取指定的资源(String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories")的url。此处的url即为jar的路径,如jar:file:/Users/yaoliang/.m2/repository/org/springframework/boot/spring-boot/1.5.8.RELEASE/spring-boot-1.5.8.RELEASE.jar!/META-INF/spring.factories
        Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
        		ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
        List<String> result = new ArrayList<String>();
        while (urls.hasMoreElements()) {
            URL url = urls.nextElement();
            Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url)); 
            // 获取ApplicationContextInitializer子类名称集合
            String factoryClassNames = properties.getProperty(factoryClassName);
            result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
        }
        return result;
    }
    catch (IOException ex) {throw new IllegalArgumentException()}
}
复制代码

springboot实例化过程中的不同阶段都会用这些ApplicationContextInitializer/ApplicationListener子类集合。看下springboot默认加载的ApplicationContextInitializer/ApplicationListener子类集合都有哪些,如下图:

SpringApplication.run(args)方法

SpringApplication(sources).run(args)方法做了几乎前半生所有的事。首先它要获取SpringApplicationRunListener事件发布监听器集合,获取方式同ApplicationListener子类集合(加载META-INF/spring.factories获取), 然后创建一个ApplicationContext对象。随后给它的多个属性赋值。如environment、profile、打印spring图标、log打印级别等属性赋值。这些赋值操作是通过类发布事件监听器(EventPublishingRunListener)广播事件给真正的监听器来完成的。

public ConfigurableApplicationContext run(String... args) {
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    ConfigurableApplicationContext context = null;
    FailureAnalyzers analyzers = null;
    configureHeadlessProperty();
    SpringApplicationRunListeners listeners = getRunListeners(args);// (1)
    listeners.starting(); // (2)
    try {
    	ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
    	ConfigurableEnvironment environment = prepareEnvironment(listeners,
    			applicationArguments);// (3)
    	Banner printedBanner = printBanner(environment);// (4)
    	context = createApplicationContext();// (5)
    	analyzers = new FailureAnalyzers(context);
    	prepareContext(context, environment, listeners, applicationArguments,
    			printedBanner);// (6)
    	refreshContext(context);// (7)
    	afterRefresh(context, applicationArguments);// (8)
    	listeners.finished(context, null);// (9)
    	stopWatch.stop();
    	return context;
    }catch (Throwable ex) {
    	handleRunFailure(context, listeners, analyzers, ex);
    	throw new IllegalStateException(ex);
    }
}
复制代码

run方法(1)处是获取事件发布监听器集合。run方法的功能实现很大部分都是通过事件发布监听器实现的,所以先看下事件发布监听器即SpringApplicationRunListener的结构

/**
 * 这是一个用于监听SpringApplication类的main方法的监听器。每个run方法对应一个监听器对象
 */
public interface SpringApplicationRunListener {
    // SpringApplication.run()方法首次开始时立刻调用此方法。这个可以用于早期的实例化
    void starting();
    
    // 一旦environment准备好的时候,但是在ApplicationContext创建之前,调用此方法
    void environmentPrepared(ConfigurableEnvironment environment);
    
    // 一旦ApplicationContext创建和准备好的时候,但是在资源加载之前,调用此方法
    void contextPrepared(ConfigurableApplicationContext context);
    
    // 一旦the application context加载完但是它刷新之前,调用此方法
    void contextLoaded(ConfigurableApplicationContext context);
    
    // 在run方法结束的时候立刻调用此方法
    void finished(ConfigurableApplicationContext context, Throwable exception);
}
复制代码

SpringApplicationRunListener及其子类EventPublishingRunListener是特殊的监听器,叫事件发布监听器,它会广播事件给所有真正已注册的监听器。事件发布监听器持有SimpleApplicationEventMulticaster广播类属性和SpringApplication,监听流程为事件发布监听器把事件给广播类,广播类再通过retrieveApplicationListeners方法获取真正处理事件的监听器。从而实现监听器可以监听到事件的功能。我们以starting()为例,说明监听流程的代码实现

// SpringApplicationRunListener类
public void starting() {
    this.initialMulticaster.multicastEvent(new ApplicationStartedEvent(this.application, this.args));
}

// SimpleApplicationEventMulticaster类
public void multicastEvent(ApplicationEvent event) {
    multicastEvent(event, resolveDefaultEventType(event));
}
public void multicastEvent(final ApplicationEvent event, ResolvableType eventType) {
    // 遍历所有ApplicationLister发送事件
    for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
        // 把事件给具体的监听类
        invokeListener(listener, event);
    }
}
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
    doInvokeListener(listener, event);
}
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
    listener.onApplicationEvent(event);
}

starting()调用链: starting-->multicastEvent-->for循环invokeListener-->doInvokeListener-->listener.onApplicationEvent(event).
复制代码

SpringApplicationRunListener其他事件方法逻辑同starting()方法,environmentPrepared()、contextPrepared()、contextLoaded()、finished()。只是广播事件不一样。

下面解析SpringApplication.run()方法并结合SpringApplicationRunListener事件方法来说明每个事件方法发布的事件及处理这个事件的监听器们都做了什么事

SpringApplication.run()方法(2)处

此处调用的正是SpringApplicationRunListener.starting()事件方法,只有LoggingApplicationListener监听器处理这个广播事件,它会加载有关log的jar,同时实例化logContext

SpringApplication.run()方法(3)处

此处创建一个ConfigurableEnvironment对象,后面会使用这个对象并给它的属性赋值。Environment家族层级图

private ConfigurableEnvironment prepareEnvironment(
    SpringApplicationRunListeners listeners,ApplicationArguments applicationArguments) {
    // 获取或创建一个对象。我们是web环境,所以创建StandardServletEnvironment对象
    ConfigurableEnvironment environment = getOrCreateEnvironment();
    // 配置Environment类的PropertySources和Profiles属性
    configureEnvironment(environment, applicationArguments.getSourceArgs());
    // 调用事件发布监听器的environmentPrepared方法广播事件给监听器们
    listeners.environmentPrepared(environment);
    return environment;
}
复制代码

如上代码所示,做了三件事,如代码中注释。其中,广播事件给监听器们需要重点分析。这个事件方法广播的事件为:“environment准备好了”,监听器们收到这个事件,开始执行各自的功能。这个事件类为ApplicationEnvironmentPreparedEvent,ConfigFileApplicationListener和LoggingApplicationListener对这个事件有重要的处理。

  1. LoggingApplicationListener进行日志log相关的配置,设置LogContext对象的属性(如level)、指定日志配置文件的路径(通过这个logging.config属性指定)、指定log日志文件的目录(LOG_PATH)、指定log日志文件的名字(LOG_FILE)、指定日志的样式(FILE_LOG_PATTERN)等
/**
 * 通过Environment和classpath实例化有自己偏好的logging system
 */
protected void initialize(ConfigurableEnvironment environment,ClassLoader classLoader) {
    new LoggingSystemProperties(environment).apply();
    LogFile logFile = LogFile.get(environment);
    logFile.applyToSystemProperties();
    initializeEarlyLoggingLevel(environment);
    initializeSystem(environment, this.loggingSystem, logFile);
    initializeFinalLoggingLevels(environment, this.loggingSystem);
    registerShutdownHookIfNecessary(environment, this.loggingSystem);
}

private void initializeEarlyLoggingLevel(ConfigurableEnvironment environment) {
    if (this.parseArgs && this.springBootLogging == null) {
    	if (isSet(environment, "debug")) {
    		this.springBootLogging = LogLevel.DEBUG;
    	}
    	if (isSet(environment, "trace")) {
    		this.springBootLogging = LogLevel.TRACE;
    	}
    }
}
复制代码
  1. ConfigFileApplicationListener在这个事件中做了一个重要的操作:加载配置文件
这个重要的操作就是加载应用的配置文件(classpath:/, classpath:/config/, file:./, file:./config/)application.(yml/yaml/properties/xml),(classpath:/, classpath:/config/, file:./, file:./config/application-{profile}.(yml/yaml/properties/xml)
,然后通过PropertySourceLoader加载文件并解析这些配置文件(key/value形式),并set到this.environment.propertySources中。

平时工作中我们在配置文件指定的spring.profiles.active(默认为default),指定的spring.config.location配置文件的路径(默认为classpath:/,classpath:/config/,file:./,file:./config/),指定的spring.config.name配置文件的名字(默认为application)都是在这时set到this.environment.propertySources中的

如下代码是寻找、加载和解析配置文件的过程。
// 调用栈:事件被监听到,执行监听器的逻辑
ConfigFileApplicationListener.onApplicationEvent(ApplicationEvent event)
-this.onApplicationEnvironmentPreparedEvent(event)
--this.postProcessEnvironment(ConfigurableEnvironment,SpringApplication)
---this.addPropertySources(ConfigurableEnvironment,ResourceLoader)
----new Loader(environment, resourceLoader).load()

load方法分为两大步:确定要加载的配置文件有哪些;加载解析这些配置文件,解析的结果放入profiles,propertiesLoader.propertySources
步骤一:确定要加载的配置文件有哪些。springboot默认寻找classpath:/,classpath:/config/, file:./, file:./config/这些位置的application-{profile}.(yml/yaml/properties/xml文件。把应用中实际声明的配置文件和默认路径,默认名称想匹配,匹配到的等待加载解析了
// 确定要加载的配置文件有哪些
public void load() {
    this.propertiesLoader = new PropertySourcesLoader();
    this.profiles.addAll(getUnprocessedActiveProfiles(initialActiveProfiles))
    while (!this.profiles.isEmpty()) {
    	Profile profile = this.profiles.poll();
    	for (String location : getSearchLocations()) {
    	    ... 略各种情况的判断 ...
    		if (!location.endsWith("/")) {
    		    // 加载解析此路径此名称的配置文件
    			load(location, null, profile);
    		}
    		else {
    			for (String name : getSearchNames()) {
    				load(location, name, profile);
    			}
    		}
    	}
     }
}
private void load(String location, String name, Profile profile) {
    for (String ext : this.propertiesLoader.getAllFileExtensions()) {
        loadIntoGroup(group, location + name + "-" + profile + "." + ext,profile);
    }
}

// 加载解析配置文件并set到Environment.propertySources
private PropertySource<?> loadIntoGroup(String identifier, String location,
		Profile profile) {
    return doLoadIntoGroup(identifier, location, profile);
}
private PropertySource<?> doLoadIntoGroup(String identifier, String location, Profile profile){
    Resource resource = this.resourceLoader.getResource(location);
    // 加载的配置文件内容存入propertySources
    PropertySource<?> propertySource = this.propertiesLoader.load(resource, group, name,(profile == null ? null : profile.getName()));
    // propertySources赋值给Environment,供后面使用
    handleProfileProperties(propertySource);
    return propertySource;
    ... 略msg信息 ...
}
复制代码
SpringApplication.run()方法(4)处

看方法的名称就知道,此处根据Environment打印spring banner。banner是啥呢,看效果图。感觉很熟悉吧

SpringApplicationBannerPrinter类完成打印操作,它根据banner.location,默认banner.location值为banner.txt,同时使用ResourceLoader加载banner.location的。这是打印文本;同时还可以配置banner.image.location打印图片,支持的图片格式为:"gif", "jpg", "png"。

// SpringApplication类。打印Banner到日志文件and/or终端
private Banner printBanner(ConfigurableEnvironment environment) {
    // 可以关闭打印Banner功能
    if (this.bannerMode == Banner.Mode.OFF) { return null;}
    ResourceLoader resourceLoader = this.resourceLoader != null ? this.resourceLoader: new DefaultResourceLoader(getClassLoader());
    // 创建SpringApplicationBannerPrinter,负责打印应用的banner
    SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(
    		resourceLoader, this.banner);
    if (this.bannerMode == Mode.LOG) {
        // 执行打印到日志
    	return bannerPrinter.print(environment, this.mainApplicationClass, logger);
    }
    // 执行打印到终端
    return bannerPrinter.print(environment, this.mainApplicationClass, System.out);
}
// SpringApplicationBannerPrinter类
public Banner print(Environment environment, Class<?> sourceClass, PrintStream out) {
    // 获取banner 可能有图片banner、文本banner、默认banner
    Banner banner = getBanner(environment, this.fallbackBanner);
    banner.printBanner(environment, sourceClass, out);
    return new PrintedBanner(banner, sourceClass);
}
// 默认的banner,对照代码和打印的信息,一种焕然的感觉升起
class SpringBootBanner implements Banner {
    private static final String[] BANNER = { "",
        "  .   ____          _            __ _ _",
        " /\\\\ / ___'_ __ _ _(_)_ __  __ _ \\ \\ \\ \\",
        "( ( )\\___ | '_ | '_| | '_ \\/ _` | \\ \\ \\ \\",
        " \\\\/  ___)| |_)| | | | | || (_| |  ) ) ) )",
        "  '  |____| .__|_| |_|_| |_\\__, | / / / /",
        " =========|_|==============|___/=/_/_/_/" };
        
    private static final String SPRING_BOOT = " :: Spring Boot :: ";
    
    public void printBanner(Environment environment, Class<?> sourceClass,PrintStream printStream) {
        // 组织打印信息
        for (String line : BANNER) {
            printStream.println(line);
        }
        String version = SpringBootVersion.getVersion();
        version = (version == null ? "" : " (v" + version + ")");
        String padding = "";
        while (padding.length() < STRAP_LINE_SIZE- (version.length() + SPRING_BOOT.length())) {
            padding += " ";
        }
        // 打印信息到终端或日志文件
        printStream.println(AnsiOutput.toString(AnsiColor.GREEN, SPRING_BOOT,AnsiColor.DEFAULT, padding, AnsiStyle.FAINT, version));
        printStream.println();
    }

}
复制代码
SpringApplication.run()方法(5)处

从方法名称知道,创建ApplicationContext对象。其实创建的是其子类:AnnotationConfigEmbeddedWebApplicationContext.

// 通过反射将类名实例为对象,反射过程中执行该类及父类们的构造方法
protected ConfigurableApplicationContext createApplicationContext() {
    // DEFAULT_WEB_CONTEXT_CLASS = "org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext"
    Class<?> contextClass = Class.forName(DEFAULT_WEB_CONTEXT_CLASS);
    return (ConfigurableApplicationContext)BeanUtils.instantiate(contextClass);
}
// 下面是调用AnnotationConfigEmbeddedWebApplicationContext类及父类们的构造方法。通过上面可以了解WebApplicationContext层级关系
public AnnotationConfigEmbeddedWebApplicationContext() {
    this.reader = new AnnotatedBeanDefinitionReader(this);		
    this.scanner = new ClassPathBeanDefinitionScanner(this);	
}        
// 创建DefaultListableBeanFactory对象和赋值GenericApplicationContext.DefaultListableBeanFactory属性,DefaultListableBeanFactory非常重要(BeanFactory的默认实例对象)。	    
public GenericApplicationContext() {		
    this.beanFactory = new DefaultListableBeanFactory();
}         
创建PathMatchingResourcePatternResolver并赋值给AbstractApplicationContext.ResourcePatternResolver属性	    
public AbstractApplicationContext() {		
    this.resourcePatternResolver = new PathMatchingResourcePatternResolver(this);
}	    
protected ResourcePatternResolver getResourcePatternResolver() {
    return new ServletContextResourcePatternResolver(this);
}

复制代码

通过AnnotationConfigEmbeddedWebApplicationContext构造方法,我们可以看到,这个构造方法创建并赋值两个重要的属性:

1. ClassPathBeanDefinitionScanner
2. AnnotatedBeanDefinitionReader
复制代码

ClassPathBeanDefinitionScanner用于扫描classpath目录下带注解的beanclass;AnnotatedBeanDefinitionReader解析并读取BeanDefinition。同时AnnotatedBeanDefinitionReader有两个重要的属性BeanNameGenerator、ScopeMetadataResolver。BeanNameGenerator默认实现类是AnnotationBeanNameGenerator,AnnotationBeanNameGenerator用于生成带有@Component、@Controller、@Service、@Repository注解的beanDefinition; ScopeMetadataResolver默认实现类是AnnotationScopeMetadataResolver,用于处理带有@Scope注解的beanDefinition。

AnnotatedBeanDefinitionReader构造方法会使用AnnotationConfigUtils类来注册BeanPostProcessor、BeanFactoryPostProcessor、AutowireCandidateResolver的各自子类,如下代码

public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry,Environment environment) {
    this.registry = registry;	
    // 注册相关的BeanPostProcessor、BeanFactoryPostProcessor、AutowireCandidateResolver到ApplicationContext
    AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}
/**
 * 在给定的注册表中注册所有相关的注释后处理器(post processors)
 * @return a Set of BeanDefinitionHolders, containing all bean definitions
 * that have actually been registered by this call
 */
public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(BeanDefinitionRegistry registry, Object source) {
    DefaultListableBeanFactory beanFactory =unwrapDefaultListableBeanFactory(registry);
    beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
    beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
    
    Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<BeanDefinitionHolder>(4);
    
    // 将以下几个类及对应的RootBeanDefinition放入DefaultListableBeanFactory.beanDefinitionMap中
    RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
    def.setSource(source);
    beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
    
    RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
    def.setSource(source);
    beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
    
    RootBeanDefinition def = new RootBeanDefinition(RequiredAnnotationBeanPostProcessor.class);
    def.setSource(source);
    beanDefs.add(registerPostProcessor(registry, def, REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
    
    RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);
    def.setSource(source);
    beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
    
    RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class);
    def.setSource(source);
    beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME));
    
    RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class);
    def.setSource(source);
    beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME));
   
    return beanDefs;
}

private static BeanDefinitionHolder registerPostProcessor(BeanDefinitionRegistry registry, RootBeanDefinition definition, String beanName) {
    definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    registry.registerBeanDefinition(beanName, definition);
    return new BeanDefinitionHolder(definition, beanName);
}
复制代码

放入DefaultListableBeanFactory.beanDefinitionMap的RootBeanDefinition如下

BeanFactoryPostProcessor子类:    
  ConfigurationClassPostProcessor: 用于处理注解了@Configuration那些类(process @Configuration classes)  
BeanPostProcessor子类:    
  AutowiredAnnotationBeanPostProcessor:用于处理注解了@Autowired、@Value、@Inject那些类(process @Autowired、@Value、@Inject classes)    
  RequiredAnnotationBeanPostProcessor: 用于处理注解了@Required那些类(process @Required classes)     
  CommonAnnotationBeanPostProcessor:用于处理注解了@PostConstruct、@PreDestroy那些类 (process @PostConstruct、@PreDestroy classes)  
EventListenerMethodProcessor:将注解了@EventListener方法注册为单独的ApplicationListener实例 
复制代码

事实上,只要是容器所负责的类都会放入这个属性,后面实例化过程中的注册的bean也会陆续放入DefaultListableBeanFactory.beanDefinitionMap中,即我们常说的Beans被spring容器所管理

SpringApplication.run()方法(6)处

配置context。具体为context关联environment、context.beanFactory.beanDefinitionMap赋值、应用ApplicationContextInitializer子类集合、后置处理ApplicationContext、发布事件监听器发布contextPrepared(context)和listeners.contextLoaded(context)事件方法、加载beans到the application context

private void prepareContext(ConfigurableApplicationContext context,ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,ApplicationArguments applicationArguments, Banner printedBanner) {
    // environment赋值给ApplicationContext的属性
    context.setEnvironment(environment);//(a1)
    postProcessApplicationContext(context);//(a2)
    // SpringApplicationRunListener.starting()方法获取的ApplicationContextInitializer的子类开始使用,见图三
    applyInitializers(context); //(a3)
    listeners.contextPrepared(context);//(a4)
    if (this.logStartupInfo) {
    	logStartupInfo(context.getParent() == null);//(a5)
    	logStartupProfileInfo(context);//(a6)
    }
    
    // Add boot specific singleton beans
    context.getBeanFactory().registerSingleton("springApplicationArguments",applicationArguments);//(a7)
    
    context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);//(a8)
    
    // Load the sources
    Set<Object> sources = getSources();
    load(context, sources.toArray(new Object[sources.size()]));//(a9)
    listeners.contextLoaded(context);//(a10)
}
复制代码

如上代码,几乎每句都很重要,下面具体(a*)的作用

- (a1)处:更新context.environment属性值,同时为ClassPathBeanDefinitionScanner.environment和AnnotatedBeanDefinitionReader.environment赋值
- (a2)处:postProcessApplicationContext此时没有实际的作用,但是SpringApplication子类可重写它
- (a3)处:调用各个ApplicationContextInitializer子类的initilize()方法,待详说
- (a4)处:ApplicationContext对象创建和部分属性赋值后,事件发布监听器开始执行contextPrepared事件方法。这个事件方法是空实现
- (a5)处:打印应用startup的日志,包括应用名称、id、版本、classes路径等。如“[INFO ] [main]   23:29:48 com.yy.App => Starting App on bogon with PID 5550 (/Users/yaoliang/skyler/project/mytest/java_example/target/classes started by yaoliang in /Users/yaoliang/skyler/project/mytest)”
- (a6)处:打印应用profile信息日志,如“[INFO ] [main]   23:32:02 com.yy.App => No active profile set, falling back to default profiles: default”或“The following profiles are active: test,dev”
- (a7)(a8)处:将应用启动参数(springApplicationArguments)、PrintedBanner这两个singleton beans对象放入ApplicationContext.beanFactory.beanDefinitionMap和singletonObjects属性中
- (a9)处:加载source("com.yy.App")到beanFactory.beanDefinitionMap.
其实load方法的通用逻辑为将classpath下的classes和指定sources("com.yy.App")下classes的beans放入BeanDefinitionRegistry的BeanDefinitions中.具体代码如下,两步:创建BeanDefinitionLoader,使用它加载source资源
protected void load(ApplicationContext context, Object[] sources) {
    BeanDefinitionLoader loader = createBeanDefinitionLoader(
    		getBeanDefinitionRegistry(context), sources);
    loader.load();
}
步1.首先会创建一个BeanDefinitionLoader对象
BeanDefinitionLoader(BeanDefinitionRegistry registry, Object... sources){
	this.sources = sources;
	this.annotatedReader = new AnnotatedBeanDefinitionReader(registry);
	this.xmlReader = new XmlBeanDefinitionReader(registry);
	if (isGroovyPresent()) {
	    this.groovyReader = new GroovyBeanDefinitionReader(registry);
	}
	this.scanner = new ClassPathBeanDefinitionScanner(registry);
	this.scanner.addExcludeFilter(new ClassExcludeFilter(sources));
}
从代码可以看到,BeanDefinitionLoader可以加载Xml、javaConfig、ClassPath形式的bean
步2.loader.load().开始加载beans load(Object source)可根据source类型加载不同类型资源从而得到beans.
private int load(Object source) {
    if (source instanceof Class<?>) {
    	return load((Class<?>) source);
    }
    if (source instanceof Resource) {
    	return load((Resource) source);
    }
    if (source instanceof Package) {
    	return load((Package) source);
    }
    if (source instanceof CharSequence) {
    	return load((CharSequence) source);
    }
}
当前应用的source为com.yy.App类,所以使用AnnotatedBeanDefinitionReader对象加载com.yy.App类目录下的beans
private int load(Class<?> source) {
    if (isComponent(source)) {
    	this.annotatedReader.register(source);
    	return 1;
    }
    return 0;
}
public void register(Class<?>... annotatedClasses) {
    for (Class<?> annotatedClass : annotatedClasses) {
    	registerBean(annotatedClass);
    }
}
public void registerBean(Class<?> annotatedClass, String name, Class<? extends Annotation>... qualifiers) {
    // 解析annotatedClass把他和他的metadata注解构造到AnnotatedGenericBeanDefinition中
    AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);
    // 没懂他的用处 TODO
    ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
    abd.setScope(scopeMetadata.getScopeName());
    // 生成beanName,生成逻辑值得你自己debug走下
    String beanName = this.beanNameGenerator.generateBeanName(abd, this.registry));
    // 检查abd是否有@Lazy,@Primary,@DependsOn,@Role,@Description,有就set进Description
    AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);

    // 创建持有abd,beanName的BeanDefinitionHolder
    BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
    // 没懂 TODO
    definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
    // 把BeanDefinition开始读取并注册到beanFactory.beanDefinitionMap中
    BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
}
- (a10)处:
代码走到此处,意味着application context has been loaded but before it has been refreshed,所以调用事件发布监听器的contextLoaded(context)方法,这个方法在发布事件的之前,把SpringApplication.listeners的值赋值到ApplicationContext.applicationListeners中。
下面发布事件功能,contextLoaded发布ApplicationPreparedEvent事件,监听到这个事件的ApplicationListener子类监听器会做什么呢
  1.ConfigFileApplicationListener
把new PropertySourceOrderingPostProcessor放入ApplicationContext.beanFactoryPostProcessors属性中
  2.LoggingApplicationListener
把springBootLoggingSystem子类对象放入DefaultSingletonBeanRegistry.singletonObjects、registeredSingletons和DefaultListableBeanFactroy.manualSingletonNames中
复制代码
SpringApplication.run()方法(7)处

refreshContext(context),看方法名称就知道,刷新ApplicationContext。重点来了,怎么刷新,具体都刷新什么。看下方法代码

public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
    	// Prepare this context for refreshing.
    	prepareRefresh();
    
    	// Tell the subclass to refresh the internal bean factory.
    	ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
    
    	// Prepare the bean factory for use in this context.
    	prepareBeanFactory(beanFactory);
    
    	try {
            // Allows post-processing of the bean factory in context subclasses.
            postProcessBeanFactory(beanFactory);
    
    	    // Invoke factory processors registered as beans in the context.
    	    invokeBeanFactoryPostProcessors(beanFactory);
    
    	    // Register bean processors that intercept bean creation.
    	    registerBeanPostProcessors(beanFactory);
    
    	    // Initialize message source for this context.
    	    initMessageSource();
    
    	    // Initialize event multicaster for this context.
    	    initApplicationEventMulticaster();
    
    	    // Initialize other special beans in specific context subclasses.
    	    onRefresh();
    
    	    // Check for listener beans and register them.
    	    registerListeners();
    
    	    // Instantiate all remaining (non-lazy-init) singletons.
    	    finishBeanFactoryInitialization(beanFactory);
    
    	    // Last step: publish corresponding event.
    	    finishRefresh();
    	}catch (BeansException ex) {
    		destroyBeans();
    
    		// Reset 'active' flag.
    		cancelRefresh(ex);

    		// Propagate exception to caller.
    		throw ex;
    	}finally {
    		resetCommonCaches();
    	}
    }
}
复制代码

这个方法会做很东西,刷新ApplicationContext、调用BeanFactory 的postProcessors(后置处理器)、注册BeanPostProcessors到BeanFactory.beanPostProcessors等等,这就是从main启动开始到完成征途记之一后半生》 的内容了

SpringApplication.run()方法(8)处

ApplicationContext refresh之后调用此处方法,此方法功能为调用ApplicationRunner.run和CommandLineRunner.run方法,这里我们可以自定义两个类的子类实现我们自己的逻辑

private void callRunners(ApplicationContext context, ApplicationArguments args) {
    List<Object> runners = new ArrayList<Object>();
    runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
    runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
    AnnotationAwareOrderComparator.sort(runners);
    for (Object runner : new LinkedHashSet<Object>(runners)) {
    	if (runner instanceof ApplicationRunner) {
            callRunner((ApplicationRunner) runner, args);
    	}
    	if (runner instanceof CommandLineRunner) {
            callRunner((CommandLineRunner) runner, args);
    	}
    }
}
复制代码
SpringApplication.run()方法(9)处

此处为调用事件发布监听器的finished(ConfigurableApplicationContext, Throwable)方法。没有异常的话,会发布ApplicationReadyEvent事件。此时发布事件不同以往广播形式,此次用ApplicationContext.publishEvent(event)监听此事件的监听器监听到此事件后执行自己的逻辑

  • 注意:ApplicationContext.publishEvent(event)内部使用的还是广播事件方式:getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType)

结束

转载于:https://juejin.im/post/5ba9b2386fb9a05d1013e476

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值