Springboot源码跟读1--SpringApplicati

2020-04-15  本文已影响0人  安中古天乐

使用Springboot框架进行应用开发,方便得一P,但是不能仅满足于会用,而要了解其背后的运行机制,这样才能在框架出现报错时,不慌不乱,谈笑间消灭掉Bug。

而源代码是框架最好的导师,不废话,开讲!

要启动Springboot应用,需要配置启动类,如下:

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

可以看到,关键在于SpringApplication的run方法:

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

public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
    return (new SpringApplication(primarySources)).run(args);
}

本质上是将启动类的Class包装成Class[]作为primarySources,然后传给SpringApplication的构造函数创建其实例,最后再调用SpringApplication实例的run方法完成应用的启动。

由于Springboot Application启动涉及的东西太多,我们分多篇来讲。第1篇先讲一讲SpringApplication的创建过程。

点进去SpringApplication的构造函数:

public SpringApplication(Class... primarySources) {
    this((ResourceLoader)null, primarySources);
}

public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) {
    this.sources = new LinkedHashSet();
    this.bannerMode = Mode.CONSOLE;
    this.logStartupInfo = true;
    this.addCommandLineProperties = true;
    this.headless = true;
    this.registerShutdownHook = true;
    this.additionalProfiles = new HashSet();
    this.resourceLoader = resourceLoader;
    Assert.notNull(primarySources, "PrimarySources must not be null");
    this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
    this.webApplicationType = this.deduceWebApplicationType();
    this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
    this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
    this.mainApplicationClass = this.deduceMainApplicationClass();
}

需要关注的有deduceWebApplicationType、setInitializers、setListeners、mainApplicationClass这4个方法。

deduceWebApplicationType

该方法主要用于推断应用类型。

private WebApplicationType deduceWebApplicationType() {
    if (ClassUtils.isPresent("org.springframework.web.reactive.DispatcherHandler", (ClassLoader)null) && !ClassUtils.isPresent("org.springframework.web.servlet.DispatcherServlet", (ClassLoader)null)) {
        return WebApplicationType.REACTIVE;
    } else {
        String[] var1 = WEB_ENVIRONMENT_CLASSES;
        int var2 = var1.length;

        for(int var3 = 0; var3 < var2; ++var3) {
            String className = var1[var3];
            if (!ClassUtils.isPresent(className, (ClassLoader)null)) {
                return WebApplicationType.NONE;
            }
        }

        return WebApplicationType.SERVLET;
    }
}

deduceMainApplicationClass

该方法主要用于推断启动类Class。

private Class<?> deduceMainApplicationClass() {
    try {
        StackTraceElement[] stackTrace = (new RuntimeException()).getStackTrace();
        StackTraceElement[] var2 = stackTrace;
        int var3 = stackTrace.length;

        for(int var4 = 0; var4 < var3; ++var4) {
            StackTraceElement stackTraceElement = var2[var4];
            if ("main".equals(stackTraceElement.getMethodName())) {
                return Class.forName(stackTraceElement.getClassName());
            }
        }
    } catch (ClassNotFoundException var6) {
        ;
    }

    return null;
}

基本原理是创建1个RuntimeException实例,然后获取其堆栈信息,遍历各栈帧的方法名,若该栈帧的方法名为"main",则返回该栈帧所在的Class。

setInitializers和setListeners

2种方法分别用于初始化所有ApplicationContextInitializer和ApplicationListener的实例,底层均调用的getSpringFactoriesInstances方法,下面具体看看getSpringFactoriesInstances的实现:

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
    return this.getSpringFactoriesInstances(type, new Class[0]);
}

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
    // 获取Class加载器
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
    // 获取该Type所有继承类的Class Name
    Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
    // 根据Class Name通过反射创建各继承类的实例
    List<T> instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
    // 根据@Order注解排序后返回
    AnnotationAwareOrderComparator.sort(instances);
    return instances;
}

先看一下loadFactoryNames方法:

public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
    String factoryClassName = factoryClass.getName();
    return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}

private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
    MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
    if (result != null) {
        return result;
    } else {
        try {
            Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
            LinkedMultiValueMap result = new LinkedMultiValueMap();

            while(urls.hasMoreElements()) {
                URL url = (URL)urls.nextElement();
                UrlResource resource = new UrlResource(url);
                Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                Iterator var6 = properties.entrySet().iterator();

                while(var6.hasNext()) {
                    Entry<?, ?> entry = (Entry)var6.next();
                    List<String> factoryClassNames = Arrays.asList(StringUtils.commaDelimitedListToStringArray((String)entry.getValue()));
                    result.addAll((String)entry.getKey(), factoryClassNames);
                }
            }

            cache.put(classLoader, result);
            return result;
        } catch (IOException var9) {
            throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var9);
        }
    }
}

loadFactoryNames方法先从缓存中获取,若缓存中不存在,则扫描ClassPath中所有的"META-INF/spring.factories"加载,更新到缓存,然后返回结果。

拿到各继承类的className信息后,就可以通过createSpringFactoriesInstances方法来构建实例。

private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args, Set<String> names) {
    List<T> instances = new ArrayList(names.size());
    Iterator var7 = names.iterator();

    while(var7.hasNext()) {
        String name = (String)var7.next();

        try {
            // 通过name加载Class
            Class<?> instanceClass = ClassUtils.forName(name, classLoader);
            Assert.isAssignable(type, instanceClass);
            // 获取Class的构造器
            Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
            // 反射创建该Class的实例
            T instance = BeanUtils.instantiateClass(constructor, args);
            instances.add(instance);
        } catch (Throwable var12) {
            throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, var12);
        }
    }

    return instances;
}

Tip: 这里有2个工具类:ClassUtils和BeanUtils,有兴趣的可以看一下。

实践一把,构建一个空Springboot的Web项目,然后在SpringApplication的run方法开头打个断点,Debug运行:

1.jpg 2.jpg 3.jpg

可以看到,SpringApplication已创建,各项属性也已初始化。

OK,SpringApplication创建的源码已跟完,下篇开始run方法的源码跟读。

上一篇下一篇

猜你喜欢

热点阅读