美文网首页
Spring boot 启动原理以及加载Bean(一)

Spring boot 启动原理以及加载Bean(一)

作者: 多喝热水丶 | 来源:发表于2020-01-08 16:29 被阅读0次
    • 本文将探索学习Spring Boot2.0.x 启动原理以及加载Bean的源码
    // 代码入口 
    // 调用SpringApplication.run 方法
    public static void main(String[] args) throws IOException { 
       SpringApplication.run(SpringCloudBaseApplication.class, args);
    }
    
    • 经过几轮调用最终会进入SpringApplication构造函数
    public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
            this.resourceLoader = resourceLoader;
            // 判断加载主类是否为空
            Assert.notNull(primarySources, "PrimarySources must not be null");
            // 加载主类,这里使用Set 可知可以有多个主类实例
            this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
            // 1.判断是否是web环境(1.WebFlux 2.servlet 3.None)
            this.webApplicationType = WebApplicationType.deduceFromClasspath();
            // 初始化ApplicationContextInitializer的所有实现类
            // ApplicationContextInitializer 会在
            // ConfigurableApplicationContext(容器) refresh() 之前进行一些初始化
            setInitializers((Collection) getSpringFactoriesInstances(
                    ApplicationContextInitializer.class));
            // 初始化ApplicationListener的监听器 run会触发这些监听器
            setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
            //  推断引用主类,通过判断方法名称是否是Main方法判断主类
            this.mainApplicationClass = deduceMainApplicationClass();
    }
     //  推断引用主类,通过判断方法名称是否是Main方法判断主类
    private Class<?> deduceMainApplicationClass() {
            try {
                StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
                for (StackTraceElement stackTraceElement : stackTrace) {
                    if ("main".equals(stackTraceElement.getMethodName())) {
                        return Class.forName(stackTraceElement.getClassName());
                    }
                }
            }
            catch (ClassNotFoundException ex) {
                // Swallow and continue
            }
            return null;
        }
    
    // 判断是否是web环境(1.WebFlux 2.servlet 3.None)
    static WebApplicationType deduceFromClasspath() {
            //判断当前ClassLoader 中是否存在webFlux类,并且不存在WebServlet
            if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null)
                    && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
                    && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
                return WebApplicationType.REACTIVE;
            }
            // 判断是否是web项目,如果不是则返回None(简单的java程序)
            for (String className : SERVLET_INDICATOR_CLASSES) {
                if (!ClassUtils.isPresent(className, null)) {
                    return WebApplicationType.NONE;
                }
            }
            // 如果以上条件不成立则返回是Servlet
            return WebApplicationType.SERVLET;
        }
    
      // 监听器以及初始化类都调用getSpringFactoriesInstances
      // 这个方法用于加载所有的spring.factories 文件中的类
    private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
                Class<?>[] parameterTypes, Object... args) {
             // 获取当前上下文的类加载器
            ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
            // Use names and ensure unique to protect against duplicates
            // 使用名称并确保唯一以防止重复
            Set<String> names = new LinkedHashSet<>(
                    SpringFactoriesLoader.loadFactoryNames(type, classLoader));
            // 创建加载过的Object实例 
            List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
                    classLoader, args, names);
            // 根据@Order(value) 注解所指定的值进行排序
            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<>(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;
        }
    
    • 关于加载Spring.factories 的代码我们独立贴出来分析
    • SpringFactoriesLoader.loadFactoryNames (加载文件并且以ConcurrentReferenceHashMap 的形式储存起来)
    • spring.factories
    public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
        // 用传入的Class type 作为 Key ,当所有的类加载成功后使用key进行筛选相应的类
        String factoryClassName = factoryClass.getName();
        // loadSpringFactories 加载spring.factories 文件的核心方法 getOrDefault 根据 key 获取相应的value
        return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
    }
    // 缓存所有加载spring.factories的文件 (由于多次调用 loadSpringFactories) 2.0.x 这一块新增缓存功能
    private static final Map<ClassLoader, MultiValueMap<String, String>> cache = new ConcurrentReferenceHashMap<>();
    // 加载所有的spring.factories 并添加到缓存map中
    private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
            MultiValueMap<String, String> result = cache.get(classLoader);
            // 如果当前map中有数据则直接返回
            if (result != null) {
                return result;
            }
    
            try {
                // 所以的URL都将储存在这里
                Enumeration<URL> urls = (classLoader != null ?
                        classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
                        ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
                result = new LinkedMultiValueMap<>();
                // 循环所有URL元素
                while (urls.hasMoreElements()) {
                    URL url = urls.nextElement();
                    // 根据url 获取相应的资源
                    UrlResource resource = new UrlResource(url);
                    // 加载文件转换成Properties 对象 (接口路径为key,value 是所有的实现类逗号隔开)
                    Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                    for (Map.Entry<?, ?> entry : properties.entrySet()) {
                    //  将所有的value,隔开的 解析为List对象 并且放入 result map对象中
                        List<String> factoryClassNames = Arrays.asList(
                                StringUtils.commaDelimitedListToStringArray((String) entry.getValue()));
                        result.addAll((String) entry.getKey(), factoryClassNames);
                    }
                }
                // 同时也加入cache对象中以便下次使用
                cache.put(classLoader, result);
                return result;
            }
            catch (IOException ex) {
                throw new IllegalArgumentException("Unable to load factories from location [" +
                        FACTORIES_RESOURCE_LOCATION + "]", ex);
            }
        }
    
    • 接下来开始正式分析 Run 方法的核心内容
    public ConfigurableApplicationContext run(String... args) {
             // 计时器,用于最后打印Spring boot 最终的运行时间
            StopWatch stopWatch = new StopWatch();
            stopWatch.start();
            // 初始化容器
            ConfigurableApplicationContext context = null;
            // 错误信息报告集合
            Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
            // 配置属性 设置 java.awt.headless 系统属性为true
            /*如果名字为java.awt.headless的系统属性被设置true,那么headless工具包就会被使用。应用程序可以执行如下操作:
            (1)创建轻量级组件。
            (2)收集关于可用的字体、字体指标和字体设置的信息。
            (3)设置颜色来渲染准备图片。
            (4)创造和获取图像,为渲染准备图片。
            (5)使用java.awt.PrintJob,java.awt.print.*,和javax.print.*类里德打印。 */
            configureHeadlessProperty();
             // 获取运行时监听器 SpringApplicationRunListener  
            SpringApplicationRunListeners listeners = getRunListeners(args);
            // 启动监听器
            listeners.starting();
            try {
                // 设置参数
                ApplicationArguments applicationArguments = new DefaultApplicationArguments(
                        args);
                // 环境准备加载配置文件 另起文章->
                ConfigurableEnvironment environment = prepareEnvironment(listeners,
                        applicationArguments);
                //  设置 spring.beaninfo.ignore 为true 
                configureIgnoreBeanInfo(environment);
                // 打印Spring Boot Banner
                Banner printedBanner = printBanner(environment);
                // 创建上下文对象了 -> 另起文章
                context = createApplicationContext();
               //  获取并打印Spring boot 在运行中的异常信息 ->另起文章
                exceptionReporters = getSpringFactoriesInstances(
                        SpringBootExceptionReporter.class,
                        new Class[] { ConfigurableApplicationContext.class }, context);
                // 环境上下文准备 ->核心点 另起文章
                prepareContext(context, environment, listeners, applicationArguments,
                        printedBanner);
                // 刷新容器扫描加载bean -> 另起文章
                refreshContext(context);
                // 后置处理
                afterRefresh(context, applicationArguments);
                // run方法 运行完毕 停止计时器
                stopWatch.stop();
                if (this.logStartupInfo) {
                    // 打印最终启动时间日志
                    new StartupInfoLogger(this.mainApplicationClass)
                            .logStarted(getApplicationLog(), stopWatch);
                }
                listeners.started(context);
                callRunners(context, applicationArguments);
            }
            catch (Throwable ex) {
                handleRunFailure(context, ex, exceptionReporters, listeners);
                throw new IllegalStateException(ex);
            }
    
            try {
                listeners.running(context);
            }
            catch (Throwable ex) {
                handleRunFailure(context, ex, exceptionReporters, null);
                throw new IllegalStateException(ex);
            }
            return context;
        }
    
    • 由于内容较多所以文章将分为多篇讲解

    相关文章

      网友评论

          本文标题:Spring boot 启动原理以及加载Bean(一)

          本文链接:https://www.haomeiwen.com/subject/fqqsactx.html