• SpringBoot运行源码剖析(一)


    1、首先问自己一个问题,为啥要读SpringBoot源码呢?

    像SpringBoot,你难道不好奇他的自动化配置、IOC和AOP的实现原理吗?

    带着上面的问题,我们来Debug看看!

    2、环境准备

    Ⅰ、maven依赖关系(不同的SpringBoot版本、不同的依赖关系,运行结果会不一样,因为SpringBoot会自动配置)

    <dependencies>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.1.4</version>
    </dependency>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
    <dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-boot-starter</artifactId>
    <version>3.0.0</version>
    </dependency>
    <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
    </dependency>
    <dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
    </dependency>
    <dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.7.2</version>
    </dependency>
    <dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.4.0</version>
    </dependency>
    <dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-generator</artifactId>
    <version>3.4.0</version>
    </dependency>
    <dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
    <version>2.10.0</version>
    </dependency>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
    </dependency>
    </dependencies>

    Ⅱ、Debug前的准备

    • clean
    • compile
    • install

    3、开始调试

    就像我们学习C语言时的HelloWorld一样,SpringBoot也有一个开场白:

    package com.study.redis;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    /**
     * @author lemon
     */
    @SpringBootApplication
    public class RedisApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(RedisApplication.class, args);
        }
    }

    然后我们Ctrl+鼠标左键点进SpringApplication的run方法可以看到下图:

    首先是调用形参为单个class的run方法,然后调用形参为class数组形式的run方法。

    另外需要注意的是上面的那个Decompiled .class file,bytecode version: 52.0(Java 8)    Download Sources Choose Sources...

    为什么要注意这个呢,因为现在是对.class文件Debug,而不是Java文件,稍后Debug就会出现错误:

    Source code does not match the byte code

    我们选择Download Sources,如若有必要可以再Choose Sources,选择刚刚下载的Sources,然后就会看到带注释的方法了:

    /**
         * Static helper that can be used to run a {@link SpringApplication} from the
         * specified source using default settings.
         * @param primarySource the primary source to load
         * @param args the application arguments (usually passed from a Java main method)
         * @return the running {@link ApplicationContext}
         */
        public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
            return run(new Class<?>[] { primarySource }, args);
        }
    
        /**
         * Static helper that can be used to run a {@link SpringApplication} from the
         * specified sources using default settings and user supplied arguments.
         * @param primarySources the primary sources to load
         * @param args the application arguments (usually passed from a Java main method)
         * @return the running {@link ApplicationContext}
         */
        public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
            return new SpringApplication(primarySources).run(args);
        }

    这里最终实例化了一个SpringApplication对象,并调用它的run方法,我们先看看初始化函数:

    /**
         * Create a new {@link SpringApplication} instance. The application context will load
         * beans from the specified primary sources (see {@link SpringApplication class-level}
         * documentation for details. The instance can be customized before calling
         * {@link #run(String...)}.
         * @param primarySources the primary bean sources
         * @see #run(Class, String[])
         * @see #SpringApplication(ResourceLoader, Class...)
         * @see #setSources(Set)
         */
        public SpringApplication(Class<?>... primarySources) {
            this(null, primarySources);
        }
    
        /**
         * Create a new {@link SpringApplication} instance. The application context will load
         * beans from the specified primary sources (see {@link SpringApplication class-level}
         * documentation for details. The instance can be customized before calling
         * {@link #run(String...)}.
         * @param resourceLoader the resource loader to use
         * @param primarySources the primary bean sources
         * @see #run(Class, String[])
         * @see #setSources(Set)
         */
        @SuppressWarnings({ "unchecked", "rawtypes" })
        public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
            this.resourceLoader = resourceLoader;
            Assert.notNull(primarySources, "PrimarySources must not be null");
            this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
            this.webApplicationType = WebApplicationType.deduceFromClasspath();
            this.bootstrapRegistryInitializers = getBootstrapRegistryInitializersFromSpringFactories();
            setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
            setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
            this.mainApplicationClass = deduceMainApplicationClass();
        }

    初始化函数就先看到这儿,等要用的时候我们再回来看,接下来我们来看run方法:

    /**
         * Run the Spring application, creating and refreshing a new
         * {@link ApplicationContext}.
         * @param args the application arguments (usually passed from a Java main method)
         * @return a running {@link ApplicationContext}
         */
        public ConfigurableApplicationContext run(String... args) {
            StopWatch stopWatch = new StopWatch();
            stopWatch.start();
            DefaultBootstrapContext bootstrapContext = createBootstrapContext();
            ConfigurableApplicationContext context = null;
            configureHeadlessProperty();
            SpringApplicationRunListeners listeners = getRunListeners(args);
            listeners.starting(bootstrapContext, this.mainApplicationClass);
            try {
                ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
                ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
                configureIgnoreBeanInfo(environment);
                Banner printedBanner = printBanner(environment);
                context = createApplicationContext();
                context.setApplicationStartup(this.applicationStartup);
                prepareContext(bootstrapContext, 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, listeners);
                throw new IllegalStateException(ex);
            }
    
            try {
                listeners.running(context);
            }
            catch (Throwable ex) {
                handleRunFailure(context, ex, null);
                throw new IllegalStateException(ex);
            }
            return context;
        }

    首先实例化了一个StopWatch对象:

    /**
         * Construct a new {@code StopWatch}.
         * <p>Does not start any task.
         */
        public StopWatch() {
            this("");
        }
    
        /**
         * Construct a new {@code StopWatch} with the given ID.
         * <p>The ID is handy when we have output from multiple stop watches and need
         * to distinguish between them.
         * <p>Does not start any task.
         * @param id identifier for this stop watch
         */
        public StopWatch(String id) {
            this.id = id;
        }

    然后调用了start方法,我们看看它的start方法干了啥:

    /**
         * Start an unnamed task.
         * <p>The results are undefined if {@link #stop()} or timing methods are
         * called without invoking this method first.
         * @see #start(String)
         * @see #stop()
         */
        public void start() throws IllegalStateException {
            start("");
        }
    
        /**
         * Start a named task.
         * <p>The results are undefined if {@link #stop()} or timing methods are
         * called without invoking this method first.
         * @param taskName the name of the task to start
         * @see #start()
         * @see #stop()
         */
        public void start(String taskName) throws IllegalStateException {
            if (this.currentTaskName != null) {
                throw new IllegalStateException("Can't start StopWatch: it's already running");
            }
            this.currentTaskName = taskName;
            this.startTimeNanos = System.nanoTime();
        }

    感觉啥也没干哈哈(菜鸟级理解)

    接下来就是createBootstrapContext方法:

    private DefaultBootstrapContext createBootstrapContext() {
            DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
            this.bootstrapRegistryInitializers.forEach((initializer) -> initializer.initialize(bootstrapContext));
            return bootstrapContext;
        }

    首先实例化了一个DefaultBootstrapContext,调用的是默认的构造函数,无代码展示。

    然后将bootstrapRegistryInitializer里面的每一个实例都初始化,那我们回来之前SpringApplication的构造函数:

    this.bootstrapRegistryInitializers = getBootstrapRegistryInitializersFromSpringFactories();
    @SuppressWarnings("deprecation")
        private List<BootstrapRegistryInitializer> getBootstrapRegistryInitializersFromSpringFactories() {
            ArrayList<BootstrapRegistryInitializer> initializers = new ArrayList<>();
            getSpringFactoriesInstances(Bootstrapper.class).stream()
                    .map((bootstrapper) -> ((BootstrapRegistryInitializer) bootstrapper::initialize))
                    .forEach(initializers::add);
            initializers.addAll(getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
            return initializers;
        }

    看到这个@SuppressWarnings("deprecation")注解,作者估计是不想用这个了吧。

    可以看到initializers里面的元素都是从getSpringFactoriesInstances方法中来的:

    private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
            return getSpringFactoriesInstances(type, new Class<?>[] {});
        }
    private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
            ClassLoader classLoader = getClassLoader();
            // Use names and ensure unique to protect against duplicates
            Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
            List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
            AnnotationAwareOrderComparator.sort(instances);
            return instances;
        }

    首先它实例化了一个类加载器,其实也就是下面这个:

    Thread.currentThread().getContextClassLoader()

    然后通过SpringFactoriesLoade的LoadFactoryNames方法实例实例化了一个Set<String>集合:

    /**
         * Load the fully qualified class names of factory implementations of the
         * given type from {@value #FACTORIES_RESOURCE_LOCATION}, using the given
         * class loader.
         * <p>As of Spring Framework 5.3, if a particular implementation class name
         * is discovered more than once for the given factory type, duplicates will
         * be ignored.
         * @param factoryType the interface or abstract class representing the factory
         * @param classLoader the ClassLoader to use for loading resources; can be
         * {@code null} to use the default
         * @throws IllegalArgumentException if an error occurs while loading factory names
         * @see #loadFactories
         */
        public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
            ClassLoader classLoaderToUse = classLoader;
            if (classLoaderToUse == null) {
                classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
            }
            String factoryTypeName = factoryType.getName();
            return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
        }

    最终还是调用的loadSpringFactories方法:

    private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
            Map<String, List<String>> result = cache.get(classLoader);
            if (result != null) {
                return result;
            }
    
            result = new HashMap<>();
            try {
                Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
                while (urls.hasMoreElements()) {
                    URL url = urls.nextElement();
                    UrlResource resource = new UrlResource(url);
                    Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                    for (Map.Entry<?, ?> entry : properties.entrySet()) {
                        String factoryTypeName = ((String) entry.getKey()).trim();
                        String[] factoryImplementationNames =
                                StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
                        for (String factoryImplementationName : factoryImplementationNames) {
                            result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
                                    .add(factoryImplementationName.trim());
                        }
                    }
                }
    
                // Replace all lists with unmodifiable lists containing unique elements
                result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
                        .collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
                cache.put(classLoader, result);
            }
            catch (IOException ex) {
                throw new IllegalArgumentException("Unable to load factories from location [" +
                        FACTORIES_RESOURCE_LOCATION + "]", ex);
            }
            return result;
        }

    我们先来看看cache是啥:

    static final Map<ClassLoader, Map<String, List<String>>> cache = new ConcurrentReferenceHashMap<>();

    很明细,里面是空的,接下来我们看看下面这句:

    Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
    public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

    通过Debug,我们得到如下结果:

    算了,这个urls里面东西太多了,反正就是一些包相关信息,我们直接看result吧,反正信息都是从类加载器取出来的:

    现在我们回到LoadFactoryNames方法,由于返回的result中没有对应Bootstrapper.class类型的键,所以返回一个空列表。

    那我们再看看是否有对应的BootstrapRegistryInitializer.class类型的键,同样也没有。

    所以初始化构造的bootstrapRegistryInitializer是一个空列表。

    但是initializer和listeners有值:

    接下来我们回到createBootstrapContext方法,由于bootstrapRegistryInitializer是一个空列表,

    所以该方法相当于直接返回一个默认的DefaultBootstrapContext,没有做其他操作。

    现在再回到run方法中的configureHeadlessProperty方法:

    private void configureHeadlessProperty() {
            System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS,
                    System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
        }

    这一步就简单的设置一下系统所属性,接下来我们看看getRunListeners方法:

    private SpringApplicationRunListeners getRunListeners(String[] args) {
            Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
            return new SpringApplicationRunListeners(logger,
                    getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args),
                    this.applicationStartup);
        }

    我们先看看构造函数:

    SpringApplicationRunListeners(Log log, Collection<? extends SpringApplicationRunListener> listeners,
                ApplicationStartup applicationStartup) {
            this.log = log;
            this.listeners = new ArrayList<>(listeners);
            this.applicationStartup = applicationStartup;
        }

    简单的赋值,没啥好说的,我们回到getSpringFactoriesInstances方法:

    可以看到返回了一个listeners(EventPublishingRunlistener),这个我们稍后会用到。

    接下来看看createSpringFactoriesInstances方法实例化对象:

    @SuppressWarnings("unchecked")
        private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes,
                ClassLoader classLoader, Object[] args, Set<String> names) {
            List<T> instances = new ArrayList<>(names.size());
            for (String name : names) {
                try {
                    Class<?> instanceClass = ClassUtils.forName(name, classLoader);
                    Assert.isAssignable(type, instanceClass);
                    Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
                    T instance = (T) BeanUtils.instantiateClass(constructor, args);
                    instances.add(instance);
                }
                catch (Throwable ex) {
                    throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);
                }
            }
            return instances;
        }

    可以看到是通过类路径和类加载器来实例化对象的,我们再回到run方法:(该listeners中包含一个EventPublishListener对象)

    listeners.starting(bootstrapContext, this.mainApplicationClass);
    void starting(ConfigurableBootstrapContext bootstrapContext, Class<?> mainApplicationClass) {
            doWithListeners("spring.boot.application.starting", (listener) -> listener.starting(bootstrapContext),
                    (step) -> {
                        if (mainApplicationClass != null) {
                            step.tag("mainApplicationClass", mainApplicationClass.getName());
                        }
                    });
        }
    private void doWithListeners(String stepName, Consumer<SpringApplicationRunListener> listenerAction,
                Consumer<StartupStep> stepAction) {
            StartupStep step = this.applicationStartup.start(stepName);
            this.listeners.forEach(listenerAction);
            if (stepAction != null) {
                stepAction.accept(step);
            }
            step.end();
        }

    通过调用listeners的start方法,最终调用该实例的的listeners的starting方法,我们再看看step干了啥:

    private static final DefaultStartupStep DEFAULT_STARTUP_STEP = new DefaultStartupStep();
    
        @Override
        public DefaultStartupStep start(String name) {
            return DEFAULT_STARTUP_STEP;
        }

    虽然方法名叫start,但是只是实例化一个DefaultStartupStep对象,接下来再看listener的starting方法(也就是EventPublishListener):

    @Override
        public void starting(ConfigurableBootstrapContext bootstrapContext) {
            this.initialMulticaster
                    .multicastEvent(new ApplicationStartingEvent(bootstrapContext, this.application, this.args));
        }

    我们再看看EventPublishListener的构造函数,他的属性是怎么赋值的:

    public EventPublishingRunListener(SpringApplication application, String[] args) {
            this.application = application;
            this.args = args;
            this.initialMulticaster = new SimpleApplicationEventMulticaster();
            for (ApplicationListener<?> listener : application.getListeners()) {
                this.initialMulticaster.addApplicationListener(listener);
            }
        }

    可以看到initialMulticaster是SimpleApplicationEventMulticaster的实例,再看看ApplicationStartingEvent构造函数:

    /**
         * Create a new {@link ApplicationStartingEvent} instance.
         * @param bootstrapContext the bootstrap context
         * @param application the current application
         * @param args the arguments the application is running with
         */
        public ApplicationStartingEvent(ConfigurableBootstrapContext bootstrapContext, SpringApplication application,
                String[] args) {
            super(application, args);
            this.bootstrapContext = bootstrapContext;
        }

    反正就是构造一个实例就完了,那我们再回到initialMulticaster的multicasEvent方法:

    @Override
        public void multicastEvent(ApplicationEvent event) {
            multicastEvent(event, resolveDefaultEventType(event));
        }
    @Override
        public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
            ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
            Executor executor = getTaskExecutor();
            for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
                if (executor != null) {
                    executor.execute(() -> invokeListener(listener, event));
                }
                else {
                    invokeListener(listener, event);
                }
            }
        }

    event就是ApplicationStartingEvent的一个实例,由于SimpleApplicationEventMulticaster的构造函数如下:

    /**
         * Create a new SimpleApplicationEventMulticaster.
         */
        public SimpleApplicationEventMulticaster() {
        }

    所以Executor是空的,接下来我们看看getApplicationListeners方法:

        /**
         * Return a Collection of ApplicationListeners matching the given
         * event type. Non-matching listeners get excluded early.
         * @param event the event to be propagated. Allows for excluding
         * non-matching listeners early, based on cached matching information.
         * @param eventType the event type
         * @return a Collection of ApplicationListeners
         * @see org.springframework.context.ApplicationListener
         */
        protected Collection<ApplicationListener<?>> getApplicationListeners(
                ApplicationEvent event, ResolvableType eventType) {
    
            Object source = event.getSource();
            Class<?> sourceType = (source != null ? source.getClass() : null);
            ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);
    
            // Potential new retriever to populate
            CachedListenerRetriever newRetriever = null;
    
            // Quick check for existing entry on ConcurrentHashMap
            CachedListenerRetriever existingRetriever = this.retrieverCache.get(cacheKey);
            if (existingRetriever == null) {
                // Caching a new ListenerRetriever if possible
                if (this.beanClassLoader == null ||
                        (ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) &&
                                (sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) {
                    newRetriever = new CachedListenerRetriever();
                    existingRetriever = this.retrieverCache.putIfAbsent(cacheKey, newRetriever);
                    if (existingRetriever != null) {
                        newRetriever = null;  // no need to populate it in retrieveApplicationListeners
                    }
                }
            }
    
            if (existingRetriever != null) {
                Collection<ApplicationListener<?>> result = existingRetriever.getApplicationListeners();
                if (result != null) {
                    return result;
                }
                // If result is null, the existing retriever is not fully populated yet by another thread.
                // Proceed like caching wasn't possible for this current local attempt.
            }
    
            return retrieveApplicationListeners(eventType, sourceType, newRetriever);
        }

    我们来看看对应的event、eventType是什么:

    source就是之前构造的SpringApplication实例:

    接下来根据eventType和sourceType创建了一个cacheKey:

    public ListenerCacheKey(ResolvableType eventType, @Nullable Class<?> sourceType) {
                Assert.notNull(eventType, "Event type must not be null");
                this.eventType = eventType;
                this.sourceType = sourceType;
            }

    就是简单的赋值,没啥好说的,然后根据该key获取一个值:

    CachedListenerRetriever existingRetriever = this.retrieverCache.get(cacheKey);
    final Map<ListenerCacheKey, CachedListenerRetriever> retrieverCache = new ConcurrentHashMap<>(64);

    由于retrieverCache是一个空的ConcurrentHashMap(),为啥要用并发Map,我们先不管。

    由于existingRetriever是空的,并且beanClassLoader也是空的,所以我们直接看retrieveApplicationListeners方法:

    /**
         * Actually retrieve the application listeners for the given event and source type.
         * @param eventType the event type
         * @param sourceType the event source type
         * @param retriever the ListenerRetriever, if supposed to populate one (for caching purposes)
         * @return the pre-filtered list of application listeners for the given event and source type
         */
        private Collection<ApplicationListener<?>> retrieveApplicationListeners(
                ResolvableType eventType, @Nullable Class<?> sourceType, @Nullable CachedListenerRetriever retriever) {
    
            List<ApplicationListener<?>> allListeners = new ArrayList<>();
            Set<ApplicationListener<?>> filteredListeners = (retriever != null ? new LinkedHashSet<>() : null);
            Set<String> filteredListenerBeans = (retriever != null ? new LinkedHashSet<>() : null);
    
            Set<ApplicationListener<?>> listeners;
            Set<String> listenerBeans;
            synchronized (this.defaultRetriever) {
                listeners = new LinkedHashSet<>(this.defaultRetriever.applicationListeners);
                listenerBeans = new LinkedHashSet<>(this.defaultRetriever.applicationListenerBeans);
            }
    
            // Add programmatically registered listeners, including ones coming
            // from ApplicationListenerDetector (singleton beans and inner beans).
            for (ApplicationListener<?> listener : listeners) {
                if (supportsEvent(listener, eventType, sourceType)) {
                    if (retriever != null) {
                        filteredListeners.add(listener);
                    }
                    allListeners.add(listener);
                }
            }
    
            // Add listeners by bean name, potentially overlapping with programmatically
            // registered listeners above - but here potentially with additional metadata.
            if (!listenerBeans.isEmpty()) {
                ConfigurableBeanFactory beanFactory = getBeanFactory();
                for (String listenerBeanName : listenerBeans) {
                    try {
                        if (supportsEvent(beanFactory, listenerBeanName, eventType)) {
                            ApplicationListener<?> listener =
                                    beanFactory.getBean(listenerBeanName, ApplicationListener.class);
                            if (!allListeners.contains(listener) && supportsEvent(listener, eventType, sourceType)) {
                                if (retriever != null) {
                                    if (beanFactory.isSingleton(listenerBeanName)) {
                                        filteredListeners.add(listener);
                                    }
                                    else {
                                        filteredListenerBeans.add(listenerBeanName);
                                    }
                                }
                                allListeners.add(listener);
                            }
                        }
                        else {
                            // Remove non-matching listeners that originally came from
                            // ApplicationListenerDetector, possibly ruled out by additional
                            // BeanDefinition metadata (e.g. factory method generics) above.
                            Object listener = beanFactory.getSingleton(listenerBeanName);
                            if (retriever != null) {
                                filteredListeners.remove(listener);
                            }
                            allListeners.remove(listener);
                        }
                    }
                    catch (NoSuchBeanDefinitionException ex) {
                        // Singleton listener instance (without backing bean definition) disappeared -
                        // probably in the middle of the destruction phase
                    }
                }
            }
    
            AnnotationAwareOrderComparator.sort(allListeners);
            if (retriever != null) {
                if (filteredListenerBeans.isEmpty()) {
                    retriever.applicationListeners = new LinkedHashSet<>(allListeners);
                    retriever.applicationListenerBeans = filteredListenerBeans;
                }
                else {
                    retriever.applicationListeners = filteredListeners;
                    retriever.applicationListenerBeans = filteredListenerBeans;
                }
            }
            return allListeners;
        }

    我们直接来到下面这段代码:

    synchronized (this.defaultRetriever) {
                listeners = new LinkedHashSet<>(this.defaultRetriever.applicationListeners);
                listenerBeans = new LinkedHashSet<>(this.defaultRetriever.applicationListenerBeans);
            }

    发现这个defaultRetriever.applicationListeners直接有值了

    它是从哪儿来的呢?

    private final DefaultListenerRetriever defaultRetriever = new DefaultListenerRetriever();
    /**
         * Helper class that encapsulates a general set of target listeners.
         */
        private class DefaultListenerRetriever {
    
            public final Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet<>();
    
            public final Set<String> applicationListenerBeans = new LinkedHashSet<>();
    
            public Collection<ApplicationListener<?>> getApplicationListeners() {
                List<ApplicationListener<?>> allListeners = new ArrayList<>(
                        this.applicationListeners.size() + this.applicationListenerBeans.size());
                allListeners.addAll(this.applicationListeners);
                if (!this.applicationListenerBeans.isEmpty()) {
                    BeanFactory beanFactory = getBeanFactory();
                    for (String listenerBeanName : this.applicationListenerBeans) {
                        try {
                            ApplicationListener<?> listener =
                                    beanFactory.getBean(listenerBeanName, ApplicationListener.class);
                            if (!allListeners.contains(listener)) {
                                allListeners.add(listener);
                            }
                        }
                        catch (NoSuchBeanDefinitionException ex) {
                            // Singleton listener instance (without backing bean definition) disappeared -
                            // probably in the middle of the destruction phase
                        }
                    }
                }
                AnnotationAwareOrderComparator.sort(allListeners);
                return allListeners;
            }
        }

    这里我们并未找到该值的来源,那我们找一下哪些地方用到了这个defaultRetriever:

    @Override
        public void addApplicationListener(ApplicationListener<?> listener) {
            synchronized (this.defaultRetriever) {
                // Explicitly remove target for a proxy, if registered already,
                // in order to avoid double invocations of the same listener.
                Object singletonTarget = AopProxyUtils.getSingletonTarget(listener);
                if (singletonTarget instanceof ApplicationListener) {
                    this.defaultRetriever.applicationListeners.remove(singletonTarget);
                }
                this.defaultRetriever.applicationListeners.add(listener);
                this.retrieverCache.clear();
            }
        }

    这里向里面添加listener的,我们再看看有那个地方调用了这个方法:

    public EventPublishingRunListener(SpringApplication application, String[] args) {
            this.application = application;
            this.args = args;
            this.initialMulticaster = new SimpleApplicationEventMulticaster();
            for (ApplicationListener<?> listener : application.getListeners()) {
                this.initialMulticaster.addApplicationListener(listener);
            }
        }

    发现原来是在最开始初始化EventPublishRunListener的时候添加的。

    接下来就是判断是否支持事件,添加到对应的listeners中去,另外由于listenersBean是空的,所以没啥好说的。

    我们直接看返回结果:

    分析这么多,我们终于可以回到以下代码了:

    @Override
        public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
            ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
            Executor executor = getTaskExecutor();
            for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
                if (executor != null) {
                    executor.execute(() -> invokeListener(listener, event));
                }
                else {
                    invokeListener(listener, event);
                }
            }
        }
    /**
         * Invoke the given listener with the given event.
         * @param listener the ApplicationListener to invoke
         * @param event the current event to propagate
         * @since 4.1
         */
        protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
            ErrorHandler errorHandler = getErrorHandler();
            if (errorHandler != null) {
                try {
                    doInvokeListener(listener, event);
                }
                catch (Throwable err) {
                    errorHandler.handleError(err);
                }
            }
            else {
                doInvokeListener(listener, event);
            }
        }
    @SuppressWarnings({"rawtypes", "unchecked"})
        private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
            try {
                listener.onApplicationEvent(event);
            }
            catch (ClassCastException ex) {
                String msg = ex.getMessage();
                if (msg == null || matchesClassCastMessage(msg, event.getClass()) ||
                        (event instanceof PayloadApplicationEvent &&
                                matchesClassCastMessage(msg, ((PayloadApplicationEvent) event).getPayload().getClass()))) {
                    // Possibly a lambda-defined listener which we could not resolve the generic event type for
                    // -> let's suppress the exception.
                    Log loggerToUse = this.lazyLogger;
                    if (loggerToUse == null) {
                        loggerToUse = LogFactory.getLog(getClass());
                        this.lazyLogger = loggerToUse;
                    }
                    if (loggerToUse.isTraceEnabled()) {
                        loggerToUse.trace("Non-matching event type for listener: " + listener, ex);
                    }
                }
                else {
                    throw ex;
                }
            }
        }

    最终调用的是onApplicationEvent方法,不同的listener该方法不一样:

    以上就是为了让listeners跑起来,除了第一个初始化了日志系统,其他的执行都是false,啥也没干哈哈。

    可能我想要的东西在下面,接下来我们看看run方法中try语句块的代码:

    try {
                ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
                ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
                configureIgnoreBeanInfo(environment);
                Banner printedBanner = printBanner(environment);
                context = createApplicationContext();
                context.setApplicationStartup(this.applicationStartup);
                prepareContext(bootstrapContext, 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, listeners);
                throw new IllegalStateException(ex);
            }
    
            try {
                listeners.running(context);
            }
            catch (Throwable ex) {
                handleRunFailure(context, ex, null);
                throw new IllegalStateException(ex);
            }
            return context;

    我们先来看下DefaultApplicationArguments构造函数:

    public DefaultApplicationArguments(String... args) {
            Assert.notNull(args, "Args must not be null");
            this.source = new Source(args);
            this.args = args;
        }

    很简单的赋值函数,接下来我们看看prepareEnvironment方法:

    private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
                DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
            // Create and configure the environment
            ConfigurableEnvironment environment = getOrCreateEnvironment();
            configureEnvironment(environment, applicationArguments.getSourceArgs());
            ConfigurationPropertySources.attach(environment);
            listeners.environmentPrepared(bootstrapContext, environment);
            DefaultPropertiesPropertySource.moveToEnd(environment);
            bindToSpringApplication(environment);
            if (!this.isCustomEnvironment) {
                environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
                        deduceEnvironmentClass());
            }
            ConfigurationPropertySources.attach(environment);
            return environment;
        }

    我们看看environment是什么:

    private ConfigurableEnvironment getOrCreateEnvironment() {
            if (this.environment != null) {
                return this.environment;
            }
            switch (this.webApplicationType) {
            case SERVLET:
                return new StandardServletEnvironment();
            case REACTIVE:
                return new StandardReactiveWebEnvironment();
            default:
                return new StandardEnvironment();
            }
        }

    首先我们先回到SpringApplication的构造函数:

    /**
         * Create a new {@link SpringApplication} instance. The application context will load
         * beans from the specified primary sources (see {@link SpringApplication class-level}
         * documentation for details. The instance can be customized before calling
         * {@link #run(String...)}.
         * @param resourceLoader the resource loader to use
         * @param primarySources the primary bean sources
         * @see #run(Class, String[])
         * @see #setSources(Set)
         */
        @SuppressWarnings({ "unchecked", "rawtypes" })
        public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
            this.resourceLoader = resourceLoader;
            Assert.notNull(primarySources, "PrimarySources must not be null");
            this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
            this.webApplicationType = WebApplicationType.deduceFromClasspath();
            this.bootstrapRegistryInitializers = getBootstrapRegistryInitializersFromSpringFactories();
            setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
            setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
            this.mainApplicationClass = deduceMainApplicationClass();
        }

    并没有初始化environment,也没有默认值,从之前Debud的结果也可以看出来,我们看看deduceFromClasspath方法:

    static WebApplicationType deduceFromClasspath() {
            if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
                    && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
                return WebApplicationType.REACTIVE;
            }
            for (String className : SERVLET_INDICATOR_CLASSES) {
                if (!ClassUtils.isPresent(className, null)) {
                    return WebApplicationType.NONE;
                }
            }
            return WebApplicationType.SERVLET;
        }

    反正这个也是通过ClassUtils来判断环境是啥,最后返回的是WebApplicationType.SERVLET。

    接下来看看configureEnvironment方法:

    /**
         * Template method delegating to
         * {@link #configurePropertySources(ConfigurableEnvironment, String[])} and
         * {@link #configureProfiles(ConfigurableEnvironment, String[])} in that order.
         * Override this method for complete control over Environment customization, or one of
         * the above for fine-grained control over property sources or profiles, respectively.
         * @param environment this application's environment
         * @param args arguments passed to the {@code run} method
         * @see #configureProfiles(ConfigurableEnvironment, String[])
         * @see #configurePropertySources(ConfigurableEnvironment, String[])
         */
        protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
            if (this.addConversionService) {
                ConversionService conversionService = ApplicationConversionService.getSharedInstance();
                environment.setConversionService((ConfigurableConversionService) conversionService);
            }
            configurePropertySources(environment, args);
            configureProfiles(environment, args);
        }

    由于addConversionService默认是true,我们接下来看看getSharedInstance方法:

    /**
         * Return a shared default application {@code ConversionService} instance, lazily
         * building it once needed.
         * <p>
         * Note: This method actually returns an {@link ApplicationConversionService}
         * instance. However, the {@code ConversionService} signature has been preserved for
         * binary compatibility.
         * @return the shared {@code ApplicationConversionService} instance (never
         * {@code null})
         */
        public static ConversionService getSharedInstance() {
            ApplicationConversionService sharedInstance = ApplicationConversionService.sharedInstance;
            if (sharedInstance == null) {
                synchronized (ApplicationConversionService.class) {
                    sharedInstance = ApplicationConversionService.sharedInstance;
                    if (sharedInstance == null) {
                        sharedInstance = new ApplicationConversionService();
                        ApplicationConversionService.sharedInstance = sharedInstance;
                    }
                }
            }
            return sharedInstance;
        }
    private static volatile ApplicationConversionService sharedInstance;

    既然该方法用到了锁,我们就当他是线程安全的方法来分析:(对sharedInstance读和写只在这个方法里)

    为什么这里在锁后要再读一次呢,因为最先获取锁的线程初始化过了,所以不用再初始化。

    public ApplicationConversionService() {
            this(null);
        }
    public ApplicationConversionService(StringValueResolver embeddedValueResolver) {
            if (embeddedValueResolver != null) {
                setEmbeddedValueResolver(embeddedValueResolver);
            }
            configure(this);
        }
    /**
         * Configure the given {@link FormatterRegistry} with formatters and converters
         * appropriate for most Spring Boot applications.
         * @param registry the registry of converters to add to (must also be castable to
         * ConversionService, e.g. being a {@link ConfigurableConversionService})
         * @throws ClassCastException if the given FormatterRegistry could not be cast to a
         * ConversionService
         */
        public static void configure(FormatterRegistry registry) {
            DefaultConversionService.addDefaultConverters(registry);
            DefaultFormattingConversionService.addDefaultFormatters(registry);
            addApplicationFormatters(registry);
            addApplicationConverters(registry);
        }

    反正就是初始化一个格式转换器,具体干嘛用的也不知道,看下一行代码吧:

    /**
         * Add, remove or re-order any {@link PropertySource}s in this application's
         * environment.
         * @param environment this application's environment
         * @param args arguments passed to the {@code run} method
         * @see #configureEnvironment(ConfigurableEnvironment, String[])
         */
        protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) {
            MutablePropertySources sources = environment.getPropertySources();
            if (!CollectionUtils.isEmpty(this.defaultProperties)) {
                DefaultPropertiesPropertySource.addOrMerge(this.defaultProperties, sources);
            }
            if (this.addCommandLineProperties && args.length > 0) {
                String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;
                if (sources.contains(name)) {
                    PropertySource<?> source = sources.get(name);
                    CompositePropertySource composite = new CompositePropertySource(name);
                    composite.addPropertySource(
                            new SimpleCommandLinePropertySource("springApplicationCommandLineArgs", args));
                    composite.addPropertySource(source);
                    sources.replace(name, composite);
                }
                else {
                    sources.addFirst(new SimpleCommandLinePropertySource(args));
                }
            }
        }

    这个函数由于都是false,相当于什么都没执行,configureProfiles方法是个空函数,我们回到attach方法:

    /**
         * Attach a {@link ConfigurationPropertySource} support to the specified
         * {@link Environment}. Adapts each {@link PropertySource} managed by the environment
         * to a {@link ConfigurationPropertySource} and allows classic
         * {@link PropertySourcesPropertyResolver} calls to resolve using
         * {@link ConfigurationPropertyName configuration property names}.
         * <p>
         * The attached resolver will dynamically track any additions or removals from the
         * underlying {@link Environment} property sources.
         * @param environment the source environment (must be an instance of
         * {@link ConfigurableEnvironment})
         * @see #get(Environment)
         */
        public static void attach(Environment environment) {
            Assert.isInstanceOf(ConfigurableEnvironment.class, environment);
            MutablePropertySources sources = ((ConfigurableEnvironment) environment).getPropertySources();
            PropertySource<?> attached = sources.get(ATTACHED_PROPERTY_SOURCE_NAME);
            if (attached != null && attached.getSource() != sources) {
                sources.remove(ATTACHED_PROPERTY_SOURCE_NAME);
                attached = null;
            }
            if (attached == null) {
                sources.addFirst(new ConfigurationPropertySourcesPropertySource(ATTACHED_PROPERTY_SOURCE_NAME,
                        new SpringConfigurationPropertySources(sources)));
            }
        }

    oh,shit English,what's mean?大致意思好像是适配环境的,跳过:

    void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) {
            doWithListeners("spring.boot.application.environment-prepared",
                    (listener) -> listener.environmentPrepared(bootstrapContext, environment));
        }
    private void doWithListeners(String stepName, Consumer<SpringApplicationRunListener> listenerAction) {
            doWithListeners(stepName, listenerAction, null);
        }
    private void doWithListeners(String stepName, Consumer<SpringApplicationRunListener> listenerAction,
                Consumer<StartupStep> stepAction) {
            StartupStep step = this.applicationStartup.start(stepName);
            this.listeners.forEach(listenerAction);
            if (stepAction != null) {
                stepAction.accept(step);
            }
            step.end();
        }

    仿佛又回到了原来的方法,只是稍有变动:

    @Override
        public void environmentPrepared(ConfigurableBootstrapContext bootstrapContext,
                ConfigurableEnvironment environment) {
            this.initialMulticaster.multicastEvent(
                    new ApplicationEnvironmentPreparedEvent(bootstrapContext, this.application, this.args, environment));
        }
    @Override
        public void multicastEvent(ApplicationEvent event) {
            multicastEvent(event, resolveDefaultEventType(event));
        }
    
        @Override
        public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
            ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
            Executor executor = getTaskExecutor();
            for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
                if (executor != null) {
                    executor.execute(() -> invokeListener(listener, event));
                }
                else {
                    invokeListener(listener, event);
                }
            }
        }

    不过最终都是调用的multicastEvent方法,我们直接看getApplicationListeners的返回结果吧:

    这次返回了六个listener,比上次多两个,而且有重复的,最后还是调用这个方法:

    @SuppressWarnings({"rawtypes", "unchecked"})
        private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
            try {
                listener.onApplicationEvent(event);
            }
            catch (ClassCastException ex) {
                String msg = ex.getMessage();
                if (msg == null || matchesClassCastMessage(msg, event.getClass()) ||
                        (event instanceof PayloadApplicationEvent &&
                                matchesClassCastMessage(msg, ((PayloadApplicationEvent) event).getPayload().getClass()))) {
                    // Possibly a lambda-defined listener which we could not resolve the generic event type for
                    // -> let's suppress the exception.
                    Log loggerToUse = this.lazyLogger;
                    if (loggerToUse == null) {
                        loggerToUse = LogFactory.getLog(getClass());
                        this.lazyLogger = loggerToUse;
                    }
                    if (loggerToUse.isTraceEnabled()) {
                        loggerToUse.trace("Non-matching event type for listener: " + listener, ex);
                    }
                }
                else {
                    throw ex;
                }
            }
        }

    environment这一块我们就不看了吧,现在直接看banner:

    private Banner printBanner(ConfigurableEnvironment environment) {
            if (this.bannerMode == Banner.Mode.OFF) {
                return null;
            }
            ResourceLoader resourceLoader = (this.resourceLoader != null) ? this.resourceLoader
                    : new DefaultResourceLoader(null);
            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);
        }

    由于默认bannerMode为Banner.Mode.CONSOLE,并且resourceLoader为空,resourceLoaser为DefaultResourceLoader。

    Banner print(Environment environment, Class<?> sourceClass, PrintStream out) {
            Banner banner = getBanner(environment);
            banner.printBanner(environment, sourceClass, out);
            return new PrintedBanner(banner, sourceClass);
        }
    private Banner getBanner(Environment environment) {
            Banners banners = new Banners();
            banners.addIfNotNull(getImageBanner(environment));
            banners.addIfNotNull(getTextBanner(environment));
            if (banners.hasAtLeastOneBanner()) {
                return banners;
            }
            if (this.fallbackBanner != null) {
                return this.fallbackBanner;
            }
            return DEFAULT_BANNER;
        }

    如果是图片的话:

    private Banner getImageBanner(Environment environment) {
            String location = environment.getProperty(BANNER_IMAGE_LOCATION_PROPERTY);
            if (StringUtils.hasLength(location)) {
                Resource resource = this.resourceLoader.getResource(location);
                return resource.exists() ? new ImageBanner(resource) : null;
            }
            for (String ext : IMAGE_EXTENSION) {
                Resource resource = this.resourceLoader.getResource("banner." + ext);
                if (resource.exists()) {
                    return new ImageBanner(resource);
                }
            }
            return null;
        }
    static final String BANNER_IMAGE_LOCATION_PROPERTY = "spring.banner.image.location";

    可以通过配置文件路径或者在resource目录下新建一个名为banner,后缀只能为gif、jpg、png的图片。

    如果是文本的话:

    private Banner getTextBanner(Environment environment) {
            String location = environment.getProperty(BANNER_LOCATION_PROPERTY, DEFAULT_BANNER_LOCATION);
            Resource resource = this.resourceLoader.getResource(location);
            try {
                if (resource.exists() && !resource.getURL().toExternalForm().contains("liquibase-core")) {
                    return new ResourceBanner(resource);
                }
            }
            catch (IOException ex) {
                // Ignore
            }
            return null;
        }
    static final String BANNER_LOCATION_PROPERTY = "spring.banner.location";
    static final String DEFAULT_BANNER_LOCATION = "banner.txt";

    也可以通过配置文件路径或者在resource目录下新建一个banner.txt文件。

    如果都没有配置并且fallBackBanner为空的话,就返回默认的Banner:

    private static final Banner DEFAULT_BANNER = new SpringBootBanner();

    我们看看默认的banner的printBanner方法:

    @Override
        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 + ")" : "";
            StringBuilder padding = new StringBuilder();
            while (padding.length() < STRAP_LINE_SIZE - (version.length() + SPRING_BOOT.length())) {
                padding.append(" ");
            }
    
            printStream.println(AnsiOutput.toString(AnsiColor.GREEN, SPRING_BOOT, AnsiColor.DEFAULT, padding.toString(),
                    AnsiStyle.FAINT, version));
            printStream.println();
        }
    private static final String[] BANNER = { "", "  .   ____          _            __ _ _",
                " /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \", "( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \",
                " \\/  ___)| |_)| | | | | || (_| |  ) ) ) )", "  '  |____| .__|_| |_|_| |_\__, | / / / /",
                " =========|_|==============|___/=/_/_/_/" };
    
        private static final String SPRING_BOOT = " :: Spring Boot :: ";

    Banner介绍完了,我们接下来看看createApplicationContext方法:

    /**
         * Strategy method used to create the {@link ApplicationContext}. By default this
         * method will respect any explicitly set application context class or factory before
         * falling back to a suitable default.
         * @return the application context (not yet refreshed)
         * @see #setApplicationContextClass(Class)
         * @see #setApplicationContextFactory(ApplicationContextFactory)
         */
        protected ConfigurableApplicationContext createApplicationContext() {
            return this.applicationContextFactory.create(this.webApplicationType);
        }

    最终跳到这个接口方法中:

    /**
         * A default {@link ApplicationContextFactory} implementation that will create an
         * appropriate context for the {@link WebApplicationType}.
         */
        ApplicationContextFactory DEFAULT = (webApplicationType) -> {
            try {
                switch (webApplicationType) {
                case SERVLET:
                    return new AnnotationConfigServletWebServerApplicationContext();
                case REACTIVE:
                    return new AnnotationConfigReactiveWebServerApplicationContext();
                default:
                    return new AnnotationConfigApplicationContext();
                }
            }
            catch (Exception ex) {
                throw new IllegalStateException("Unable create a default ApplicationContext instance, "
                        + "you may need a custom ApplicationContextFactory", ex);
            }
        };

    为什么会跳到这个接口方法中呢,因为这个applicationContextFactory指向的就是上面的方法,这个方法名应该可以随便取吧!

    private ApplicationContextFactory applicationContextFactory = ApplicationContextFactory.DEFAULT;

    因为webApplicationType是SERVLET,所以返回WebServerApplicationContext上下文。

    接下来我们看prepareContext方法:

    private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
                ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
                ApplicationArguments applicationArguments, Banner printedBanner) {
            context.setEnvironment(environment);
            postProcessApplicationContext(context);
            applyInitializers(context);
            listeners.contextPrepared(context);
            bootstrapContext.close(context);
            if (this.logStartupInfo) {
                logStartupInfo(context.getParent() == null);
                logStartupProfileInfo(context);
            }
            // Add boot specific singleton beans
            ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
            beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
            if (printedBanner != null) {
                beanFactory.registerSingleton("springBootBanner", printedBanner);
            }
            if (beanFactory instanceof DefaultListableBeanFactory) {
                ((DefaultListableBeanFactory) beanFactory)
                        .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
            }
            if (this.lazyInitialization) {
                context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
            }
            // Load the sources
            Set<Object> sources = getAllSources();
            Assert.notEmpty(sources, "Sources must not be empty");
            load(context, sources.toArray(new Object[0]));
            listeners.contextLoaded(context);
        }

     我们直接看到postProcessApplicationContext方法:

    /**
         * Apply any relevant post processing the {@link ApplicationContext}. Subclasses can
         * apply additional processing as required.
         * @param context the application context
         */
        protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
            if (this.beanNameGenerator != null) {
                context.getBeanFactory().registerSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,
                        this.beanNameGenerator);
            }
            if (this.resourceLoader != null) {
                if (context instanceof GenericApplicationContext) {
                    ((GenericApplicationContext) context).setResourceLoader(this.resourceLoader);
                }
                if (context instanceof DefaultResourceLoader) {
                    ((DefaultResourceLoader) context).setClassLoader(this.resourceLoader.getClassLoader());
                }
            }
            if (this.addConversionService) {
                context.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance());
            }
        }

    由于beanNameGenerator和resourceLoader都为空,但是addConversionService不为空,所以就设置了一个格式转换器。

    然后就是调用applyInitializers方法:

    /**
         * Apply any {@link ApplicationContextInitializer}s to the context before it is
         * refreshed.
         * @param context the configured ApplicationContext (not refreshed yet)
         * @see ConfigurableApplicationContext#refresh()
         */
        @SuppressWarnings({ "rawtypes", "unchecked" })
        protected void applyInitializers(ConfigurableApplicationContext context) {
            for (ApplicationContextInitializer initializer : getInitializers()) {
                Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
                        ApplicationContextInitializer.class);
                Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
                initializer.initialize(context);
            }
        }
    /**
         * Returns read-only ordered Set of the {@link ApplicationContextInitializer}s that
         * will be applied to the Spring {@link ApplicationContext}.
         * @return the initializers
         */
        public Set<ApplicationContextInitializer<?>> getInitializers() {
            return asUnmodifiableOrderedSet(this.initializers);
        }

    就是调用SpringApplication的initializers的初始化方法,就不介绍了(大家可以自己看看),然后就是contextPrepared方法:

    void contextPrepared(ConfigurableApplicationContext context) {
            doWithListeners("spring.boot.application.context-prepared", (listener) -> listener.contextPrepared(context));
        }

    还是一样的套路:

    private void doWithListeners(String stepName, Consumer<SpringApplicationRunListener> listenerAction) {
            doWithListeners(stepName, listenerAction, null);
        }
    
        private void doWithListeners(String stepName, Consumer<SpringApplicationRunListener> listenerAction,
                Consumer<StartupStep> stepAction) {
            StartupStep step = this.applicationStartup.start(stepName);
            this.listeners.forEach(listenerAction);
            if (stepAction != null) {
                stepAction.accept(step);
            }
            step.end();
        }

    这里也不介绍了,然后就是close方法:

    /**
         * Method to be called when {@link BootstrapContext} is closed and the
         * {@link ApplicationContext} is prepared.
         * @param applicationContext the prepared context
         */
        public void close(ConfigurableApplicationContext applicationContext) {
            this.events.multicastEvent(new BootstrapContextClosedEvent(this, applicationContext));
        }
    @Override
        public void multicastEvent(ApplicationEvent event) {
            multicastEvent(event, resolveDefaultEventType(event));
        }

    还是一样的套路,不介绍了,我们看后面的代码:

    if (this.logStartupInfo) {
                logStartupInfo(context.getParent() == null);
                logStartupProfileInfo(context);
            }

    这个是和日志相关的,我们不看了,接下来看看beanFactory相关的:

    /**
         * Return the single internal BeanFactory held by this context
         * (as ConfigurableListableBeanFactory).
         */
        @Override
        public final ConfigurableListableBeanFactory getBeanFactory() {
            return this.beanFactory;
        }
    /**
         * Create a new GenericApplicationContext.
         * @see #registerBeanDefinition
         * @see #refresh
         */
        public GenericApplicationContext() {
            this.beanFactory = new DefaultListableBeanFactory();
        }

    反正就是实例化了一个DefaultListableBeanFactory对象,然后通过该实例添加了两个bean。

    由于lazyInitialization为false,所以不会执行addBeanFactoryPostProcessor方法,我们直接看load方法:

    /**
         * Load beans into the application context.
         * @param context the context to load beans into
         * @param sources the sources to load
         */
        protected void load(ApplicationContext context, Object[] sources) {
            if (logger.isDebugEnabled()) {
                logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
            }
            BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
            if (this.beanNameGenerator != null) {
                loader.setBeanNameGenerator(this.beanNameGenerator);
            }
            if (this.resourceLoader != null) {
                loader.setResourceLoader(this.resourceLoader);
            }
            if (this.environment != null) {
                loader.setEnvironment(this.environment);
            }
            loader.load();
        }

    该方法会加载bean到应用上下文,我们看一下getBeanDefinitionRegistry方法:

    /**
         * Get the bean definition registry.
         * @param context the application context
         * @return the BeanDefinitionRegistry if it can be determined
         */
        private BeanDefinitionRegistry getBeanDefinitionRegistry(ApplicationContext context) {
            if (context instanceof BeanDefinitionRegistry) {
                return (BeanDefinitionRegistry) context;
            }
            if (context instanceof AbstractApplicationContext) {
                return (BeanDefinitionRegistry) ((AbstractApplicationContext) context).getBeanFactory();
            }
            throw new IllegalStateException("Could not locate BeanDefinitionRegistry");
        }

    它会直接返回第一个,我们再看看createBeanDefinitionLoader方法:

    /**
         * Factory method used to create the {@link BeanDefinitionLoader}.
         * @param registry the bean definition registry
         * @param sources the sources to load
         * @return the {@link BeanDefinitionLoader} that will be used to load beans
         */
        protected BeanDefinitionLoader createBeanDefinitionLoader(BeanDefinitionRegistry registry, Object[] sources) {
            return new BeanDefinitionLoader(registry, sources);
        }
    /**
         * Create a new {@link BeanDefinitionLoader} that will load beans into the specified
         * {@link BeanDefinitionRegistry}.
         * @param registry the bean definition registry that will contain the loaded beans
         * @param sources the bean sources
         */
        BeanDefinitionLoader(BeanDefinitionRegistry registry, Object... sources) {
            Assert.notNull(registry, "Registry must not be null");
            Assert.notEmpty(sources, "Sources must not be empty");
            this.sources = sources;
            this.annotatedReader = new AnnotatedBeanDefinitionReader(registry);
            this.xmlReader = (XML_ENABLED ? new XmlBeanDefinitionReader(registry) : null);
            this.groovyReader = (isGroovyPresent() ? new GroovyBeanDefinitionReader(registry) : null);
            this.scanner = new ClassPathBeanDefinitionScanner(registry);
            this.scanner.addExcludeFilter(new ClassExcludeFilter(sources));
        }

    接下来我们直接看load方法,由于beanNameGenerator、resourceLoader和environment都为空:

    /**
         * Load the sources into the reader.
         */
        void load() {
            for (Object source : this.sources) {
                load(source);
            }
        }
    private void load(Object source) {
            Assert.notNull(source, "Source must not be null");
            if (source instanceof Class<?>) {
                load((Class<?>) source);
                return;
            }
            if (source instanceof Resource) {
                load((Resource) source);
                return;
            }
            if (source instanceof Package) {
                load((Package) source);
                return;
            }
            if (source instanceof CharSequence) {
                load((CharSequence) source);
                return;
            }
            throw new IllegalArgumentException("Invalid source type " + source.getClass());
        }

    怎么说呢,这个sources只有一个,那就是我的项目主类,很明显这个不是我们想要的问题的答案,接着往下看:

    又到了listeners.contextLoaded方法了,为啥说又呢,因为这个还是一样的套路,不说了。

    我们接下来回到run方法中的refreshContext方法,据说该方法中包含我们想要的答案(调试了这么久,终于到高潮了啊)

    private void refreshContext(ConfigurableApplicationContext context) {
            if (this.registerShutdownHook) {
                try {
                    context.registerShutdownHook();
                }
                catch (AccessControlException ex) {
                    // Not allowed in some environments.
                }
            }
            refresh((ApplicationContext) context);
        }

     我们看看registerShutdownHook方法:

    /**
         * Register a shutdown hook {@linkplain Thread#getName() named}
         * {@code SpringContextShutdownHook} with the JVM runtime, closing this
         * context on JVM shutdown unless it has already been closed at that time.
         * <p>Delegates to {@code doClose()} for the actual closing procedure.
         * @see Runtime#addShutdownHook
         * @see ConfigurableApplicationContext#SHUTDOWN_HOOK_THREAD_NAME
         * @see #close()
         * @see #doClose()
         */
        @Override
        public void registerShutdownHook() {
            if (this.shutdownHook == null) {
                // No shutdown hook registered yet.
                this.shutdownHook = new Thread(SHUTDOWN_HOOK_THREAD_NAME) {
                    @Override
                    public void run() {
                        synchronized (startupShutdownMonitor) {
                            doClose();
                        }
                    }
                };
                Runtime.getRuntime().addShutdownHook(this.shutdownHook);
            }
        }

    反正就是和上面注释说的一样,注册一个关闭上下文的线程,我们直接看refresh方法:

    /**
         * Refresh the underlying {@link ApplicationContext}.
         * @param applicationContext the application context to refresh
         */
        protected void refresh(ConfigurableApplicationContext applicationContext) {
            applicationContext.refresh();
        }
    @Override
        public final void refresh() throws BeansException, IllegalStateException {
            try {
                super.refresh();
            }
            catch (RuntimeException ex) {
                WebServer webServer = this.webServer;
                if (webServer != null) {
                    webServer.stop();
                }
                throw ex;
            }
        }

    好了重点来了,就是下面这一段代码:

    @Override
        public void refresh() throws BeansException, IllegalStateException {
            synchronized (this.startupShutdownMonitor) {
                StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
    
                // 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);
    
                    StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
                    // Invoke factory processors registered as beans in the context.
                    invokeBeanFactoryPostProcessors(beanFactory);
    
                    // Register bean processors that intercept bean creation.
                    registerBeanPostProcessors(beanFactory);
                    beanPostProcess.end();
    
                    // 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) {
                    if (logger.isWarnEnabled()) {
                        logger.warn("Exception encountered during context initialization - " +
                                "cancelling refresh attempt: " + ex);
                    }
    
                    // Destroy already created singletons to avoid dangling resources.
                    destroyBeans();
    
                    // Reset 'active' flag.
                    cancelRefresh(ex);
    
                    // Propagate exception to caller.
                    throw ex;
                }
    
                finally {
                    // Reset common introspection caches in Spring's core, since we
                    // might not ever need metadata for singleton beans anymore...
                    resetCommonCaches();
                    contextRefresh.end();
                }
            }
        }

    啊,好多呀,不想写了,我们把refresh方法后面的几个方法介绍完吧,refresh留到下一篇介绍。

    首先就是afterRefresh,这个方法是空函数:

    /**
         * Called after the context has been refreshed.
         * @param context the application context
         * @param args the application arguments
         */
        protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) {
        }

    没啥好说的,接下来就是stopWatch的stop方法:

    /**
         * Stop the current task.
         * <p>The results are undefined if timing methods are called without invoking
         * at least one pair of {@code start()} / {@code stop()} methods.
         * @see #start()
         * @see #start(String)
         */
        public void stop() throws IllegalStateException {
            if (this.currentTaskName == null) {
                throw new IllegalStateException("Can't stop StopWatch: it's not running");
            }
            long lastTime = System.nanoTime() - this.startTimeNanos;
            this.totalTimeNanos += lastTime;
            this.lastTaskInfo = new TaskInfo(this.currentTaskName, lastTime);
            if (this.keepTaskList) {
                this.taskList.add(this.lastTaskInfo);
            }
            ++this.taskCount;
            this.currentTaskName = null;
        }

    嗯,就是记录了一下运行时间啥的等等,没啥好讲的。

    然后就是日志也没啥好说的,这个listeners见到好多次了都是一个套路,我们跳过他,看看callRunners:

    private void callRunners(ApplicationContext context, ApplicationArguments args) {
            List<Object> runners = new ArrayList<>();
            runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
            runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
            AnnotationAwareOrderComparator.sort(runners);
            for (Object runner : new LinkedHashSet<>(runners)) {
                if (runner instanceof ApplicationRunner) {
                    callRunner((ApplicationRunner) runner, args);
                }
                if (runner instanceof CommandLineRunner) {
                    callRunner((CommandLineRunner) runner, args);
                }
            }
        }

    由于runners是一个空数组,我们就不看callRunner了,这个函数就也相当于啥也没干。

    至此,整个run方法除了refresh方法就全Debug完了,这么看下去好像也没有什么收获,看不到他的设计思想。

  • 相关阅读:
    Masscan入门手册
    Deepin安装Python3
    docker 配置 kafka+zookeeper,golang操作kafka
    VMware安装Centos7超详细过程(图文)
    国家代码查询
    thinkphp5 关于跨域的一些坑,附上解决办法(比较全面了)
    网络协议分析与抓包 TCP/IP UDP等
    一维数组分组成二维数组
    Fiddler在抓取https数据包时如何解决Tunnel to 443的问题?
    十条nmap常用的扫描命令
  • 原文地址:https://www.cnblogs.com/M-Anonymous/p/15341644.html
Copyright © 2020-2023  润新知