SpringBoot 启动类 源码解析 (一 .new SpringApplication 解析)

SpringBoot 启动类 源码解析 (二 . run() 方法之 prepareEnvironment createApplicationContext 解析)


 // * 方法为重要的方法 不加 * 的可以不看 意义不大

@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
public class CmasApplication
{
    public static void main(String[] args)
    {
        // 点击run方法进入
        SpringApplication.run(CmasApplication.class, args);
    }
}

public class SpringApplication {

    // run 方法
    public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
        return run(new Class<?>[] { primarySource }, args);
    }

    //  通过 run 方法进入 1. new了 一个 SpringApplication 对象  2.调用 run 方法
    public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
        // 点击 进入  new SpringApplication 的方法 
        return new SpringApplication(primarySources).run(args);
    }

    
    // new SpringApplication 的方法
    public SpringApplication(Class<?>... primarySources) {
        // 进入 this 方法
        this(null, primarySources);
    }

    // this 方法
    public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
        // resourceLoader 资源加载器
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        // primarySources 就是当前启动类 CmasApplication
        this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
        // 该方法表示 当前的web应用类型是否是web项目,debugger返回为 servlet 类型   点击进入 deduceFromClasspath 
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
        // * 设置初始化器 从方法名推断 会 set 一个 初始化器的集合 进入 getSpringFactoriesInstances 方法
        setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
        // * 设置监听器 会 set 一个 监听器的集合
        setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
        // 推断主启动类  也就是我们的 CmasApplication
        this.mainApplicationClass = deduceMainApplicationClass();
    }

    // 获取工厂实例
    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) {
        // 获取类加载器 (不重要)从类加载器可以知道加载到的所有class文件
        ClassLoader classLoader = getClassLoader();
        // 根据类型 加载工厂名称放到集合里(也就是全路径的类名称)参数类型 type = ApplicationContextInitializer.class 可以推断是获取上下文相关的初始化器
        // debugger 可以看到 返回8个初始化器(org.springframework.boot.devtools.restart.RestartScopeInitializer...) 
        Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
        // * 通过名称创建 对应的实例 放到集合里 最终返回8个实例对象
        List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
        // 对实例进行排序,有Order 按照order 排序 没有就按照原本的顺序进行排序  我 debugger 后,结果不是这样,很尴尬 这个排序没懂
        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;
    }

    // 实例化方法
    public static <T> T instantiateClass(Constructor<T> ctor, Object... args) throws BeanInstantiationException {
        Assert.notNull(ctor, "Constructor must not be null");
        try {
            ReflectionUtils.makeAccessible(ctor);
            if (KotlinDetector.isKotlinReflectPresent() && KotlinDetector.isKotlinType(ctor.getDeclaringClass())) {
                return KotlinDelegate.instantiateClass(ctor, args);
            }
            else {
                Class<?>[] parameterTypes = ctor.getParameterTypes();
                Assert.isTrue(args.length <= parameterTypes.length, "Can't specify more arguments than constructor parameters");
                Object[] argsWithDefaultValues = new Object[args.length];
                for (int i = 0 ; i < args.length; i++) {
                    if (args[i] == null) {
                        Class<?> parameterType = parameterTypes[i];
                        argsWithDefaultValues[i] = (parameterType.isPrimitive() ? DEFAULT_TYPE_VALUES.get(parameterType) : null);
                    }
                    else {
                        argsWithDefaultValues[i] = args[i];
                    }
                }
                // 通过构造器 返回实例对象
                return ctor.newInstance(argsWithDefaultValues);
            }
        }
        catch (InstantiationException ex) {
            throw new BeanInstantiationException(ctor, "Is it an abstract class?", ex);
        }
        catch (IllegalAccessException ex) {
            throw new BeanInstantiationException(ctor, "Is the constructor accessible?", ex);
        }
        catch (IllegalArgumentException ex) {
            throw new BeanInstantiationException(ctor, "Illegal arguments for constructor", ex);
        }
        catch (InvocationTargetException ex) {
            throw new BeanInstantiationException(ctor, "Constructor threw exception", ex.getTargetException());
        }
    }

    private Class<?> deduceMainApplicationClass() {
        try {
            StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
            for (StackTraceElement stackTraceElement : stackTrace) {
                // 从堆栈信息查看该文件是否半酣 main 方法
                if ("main".equals(stackTraceElement.getMethodName())) {
                    return Class.forName(stackTraceElement.getClassName());
                }
            }
        }
        catch (ClassNotFoundException ex) {
            // Swallow and continue
        }
        return null;
    }


}

 public final class SpringFactoriesLoader {

    public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";


    // 根据类型获取工厂名 也就是全路径的类名 factoryType = ApplicationContextInitializer.class
    public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
        String factoryTypeName = factoryType.getName();
        // 进入方法  META-INF/spring.factories 路径下的所有文件 并通过 getOrDefault 方法进行过滤 (containsKey(key) 文件中的key是否包含 factoryTypeName)
        return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
    }

    private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
        MultiValueMap<String, String> result = cache.get(classLoader);
        if (result != null) {
            return result;
        }

        try {
            // classLoader 里面可以获取到已经加载的所有class 类文件 从类文件根据文件名获取 所有 META-INF/spring.factories 路径下的所有文件
            Enumeration<URL> urls = (classLoader != null ?
                    classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
                    ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
            result = new LinkedMultiValueMap<>();
            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();
                    for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
                        result.add(factoryTypeName, factoryImplementationName.trim());
                    }
                }
            }
            cache.put(classLoader, result);
            return result;
        }
        catch (IOException ex) {
            throw new IllegalArgumentException("Unable to load factories from location [" +
                    FACTORIES_RESOURCE_LOCATION + "]", ex);
        }
    }
}


public enum WebApplicationType {

    /**
     * 该应用程序不应作为 Web 应用程序运行,也不应启动嵌入式 Web 服务器
     */
    NONE,

    /**
     * 该应用程序应该作为基于 servlet 的 Web 应用程序运行,并且应该启动一个嵌入式 servlet Web 服务器
     */
    SERVLET,

    /**
     * 该应用程序应作为响应式 Web 应用程序运行,并应启动 * 嵌入式响应式 Web 服务器
     */
    REACTIVE;

    //  web 项目包含的 类 
    private static final String[] SERVLET_INDICATOR_CLASSES = { "javax.servlet.Servlet",
            "org.springframework.web.context.ConfigurableWebApplicationContext" };

    // 进入 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;
        }
        // SERVLET_INDICATOR_CLASSES 是个数组 判断 ClassUtils 是否包含 该数组元素 不包含咋返回 NONE
        for (String className : SERVLET_INDICATOR_CLASSES) {
            if (!ClassUtils.isPresent(className, null)) {
                return WebApplicationType.NONE;
            }
        }
        // 包含一个 则返回 SERVLET
        return WebApplicationType.SERVLET;
    }
}


总结一下:
1.创建自身实例对象
2.推断是否为web项目
3.设置所有初始化器 设置监听器 (中途 在调用 loadSpringFactories时读取了类路径下所有META-INF/spring.factories下内容,并全部缓存到了SpringFactoriesLoader的cache缓存中)
4.推断主启动类

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
当在Spring Boot启动中使用`context.getBean()`方法时,通常会出现`NullPointerException`或者`NoSuchBeanDefinitionException`等异常。这是因为Spring Boot启动中的上下文`ApplicationContext`还没有初始化完成,所以在调用`getBean()`方法时会报错。 为了解决这个问题,可以使用`@Autowired`注解或者构造函数注入的方式来获取需要的bean,这样可以确保在启动中获取bean时,上下文已经初始化完成。例如: ```java @SpringBootApplication public class MyApp { @Autowired private MyBean myBean; public static void main(String[] args) { SpringApplication.run(MyApp.class, args); } // ... } ``` 或者: ```java @SpringBootApplication public class MyApp { private final MyBean myBean; public MyApp(MyBean myBean) { this.myBean = myBean; } public static void main(String[] args) { SpringApplication.run(MyApp.class, args); } // ... } ``` 另外,如果你非要在启动中使用`context.getBean()`方法,也可以通过`ApplicationRunner`或者`CommandLineRunner`接口来延迟执行获取bean的操作,如下所示: ```java @SpringBootApplication public class MyApp implements ApplicationRunner { private ApplicationContext context; public static void main(String[] args) { SpringApplication.run(MyApp.class, args); } @Override public void run(ApplicationArguments args) throws Exception { MyBean myBean = context.getBean(MyBean.class); // use myBean } @Autowired public void setContext(ApplicationContext context) { this.context = context; } } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值