Java OpenResty Spring Spring Boot MySQL Redis MongoDB PostgreSQL Linux Android Nginx 面试 小程序 Arthas JVM AQS juc Kubernetes Docker 诊断工具


走进 Spring Boot 第二步之 SpringApplicaiton 构造函数

Spring Boot Java 大约 8781 字

Spring Boot 启动类

SpringApplication.run

@SpringBootApplication
public class DemoApplication {

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

SpringApplication

构造函数

resourceLoader参数为空,primarySources参数为DemoApplication的字节码类。

//SpringApplication类
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    this.resourceLoader = resourceLoader;
    Assert.notNull(primarySources, "PrimarySources must not be null");
    // primarySources可变参数为单一的一个DemoApplication
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    //判断DemoApplication的应用类型
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    //从Spring工厂示例中获得Initializers
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    //从Spring工厂示例中获得Listeners
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    //判断应用main方法入口
    this.mainApplicationClass = deduceMainApplicationClass();
}

deduceFromClasspath

从类路径推断应用类型,先判断是否是REACTIVE,若不是,则判断SERVLET_INDICATOR_CLASSES数组中的两个全路径类名能否加载,如果能加载则是SERVLET应用,否则就是NONE

//WebApplicationType类
private static final String[] SERVLET_INDICATOR_CLASSES = { 
            "javax.servlet.Servlet",
            "org.springframework.web.context.ConfigurableWebApplicationContext" 
};
private static final String WEBMVC_INDICATOR_CLASS = "org.springframework.web.servlet.DispatcherServlet";
private static final String WEBFLUX_INDICATOR_CLASS = "org.springframework.web.reactive.DispatcherHandler";
private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer";

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;
}

ClassUtilsisPresent判断能否更具全路径名称加载该类,捕获了ClassNotFoundException异常

//ClassUtils类
public static boolean isPresent(String className, @Nullable ClassLoader classLoader) {
    try {
        forName(className, classLoader);
        return true;
    }
    catch (IllegalAccessError err) {
        throw new IllegalStateException("Readability mismatch in inheritance hierarchy of class [" +
                className + "]: " + err.getMessage(), err);
    }
    catch (Throwable ex) {
        // Typically ClassNotFoundException or NoClassDefFoundError...
        return false;
    }
}

public static Class<?> forName(String name, @Nullable ClassLoader classLoader)
        throws ClassNotFoundException, LinkageError {

    Assert.notNull(name, "Name must not be null");

    // 省略了判断字节码类是否原子类型

    ClassLoader clToUse = classLoader;
    if (clToUse == null) {
        clToUse = getDefaultClassLoader();
    }
    try {
        return Class.forName(name, false, clToUse);
    }
    catch (ClassNotFoundException ex) {
        throw ex;
    }
}

getSpringFactoriesInstances

根据全路径名从Spring工厂中获得实例。

//SpringApplication类
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
    // 获取classLoader,非重点,不展开
    ClassLoader classLoader = getClassLoader();
    // 使用名称确保唯一来防止重复
    // 加载META-INF/spring.factories配置的类
    Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
    // 创建实例
    List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
    //根据注解来排序
    AnnotationAwareOrderComparator.sort(instances);
    return instances;
}

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

public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
    String factoryClassName = factoryClass.getName();
    // 根据key获取对应的value
    // Initializers/Listeners的key正好都是interface基类
    return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}

private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
    //省略了从缓存取

    try {
        // 加载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);
            // 读取配置信息,PropertiesLoaderUtils为Spring工具类用于读取配置文件,此处不展开
            // META-INF/spring.factories配置文件中的反斜杠\会被解析为一行,了解更多Java Properties类可参见先前文章
            Properties properties = PropertiesLoaderUtils.loadProperties(resource);
            for (Map.Entry<?, ?> entry : properties.entrySet()) {
                String factoryClassName = ((String) entry.getKey()).trim();
                // 将逗号分隔的value值解析为String数组
                // 遍历数组放入LinkedMultiValueMap对象中,LinkedMultiValueMap实际就是一个LinkedHashMap
                for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
                    result.add(factoryClassName, factoryName.trim());
                }
            }
        }
        // 配置信息放入缓存
        cache.put(classLoader, result);
        return result;
    }
    catch (IOException ex) {
        throw new IllegalArgumentException("Unable to load factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex);
    }
}

META-INF/spring.factories中的InitializersListeners配置

# Application Context Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer

# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener

createSpringFactoriesInstances

使用反射初始化各个InitializersListeners等。

//SpringApplication
private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args, Set<String> names) {
    List<T> instances = new ArrayList<>(names.size());
    // 遍历,使用反射初始化类,BeanUtils等反射初始化类等此处不展开
    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;
}

deduceMainApplicationClass

主动new出一个运行时异常,根据堆栈信息获取main方法对应的堆栈元素,根据该堆栈元素来获取main方法所在的类名。

//SpringApplication
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;
}

流程总结

  1. 判断应用类型
  2. 获得Initializers实例集合
  3. 获得Listeners实例集合
  4. 获得main方法所在类
阅读 4161 · 发布于 2020-04-04

————        END        ————

Give me a Star, Thanks:)

https://github.com/fendoudebb

扫描下方二维码关注公众号和小程序↓↓↓

扫描二维码关注我
昵称:
随便看看 换一批