首页 > 编程语言 > 详细

spring boot 源码探索(一)

时间:2020-04-21 17:34:07      阅读:47      评论:0      收藏:0      [点我收藏+]

SpringBoot启动入口

@SpringBootApplication
public class Application {

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

 这段代码就是Spring boot 应用的启动入口,通过静态main启动应用程序。短短几行代码却已经是一个完成的web应用程序了!

通过调用SpringApplication的静态run方法加载程序内容

SpringApplication run方法

    public static ConfigurableApplicationContext run(Object source, String... args) {
        return run(new Object[] { source }, args);
    }
    public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
     // new 对象
return new SpringApplication(sources).run(args); }

run 方法是多态方法,通过调用自身的run方法  new SpringApplication 对象并执行该对象的run方法。

创建 SpringApplication 对象

    public SpringApplication(Object... sources) {
        initialize(sources);
    }
    public SpringApplication(ResourceLoader resourceLoader, Object... sources) {
        this.resourceLoader = resourceLoader;
        initialize(sources);
    }
private void initialize(Object[] sources) {
        if (sources != null && sources.length > 0) {
            this.sources.addAll(Arrays.asList(sources));
        }
this.webEnvironment = deduceWebEnvironment();
setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.
class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.
class));
this.mainApplicationClass = deduceMainApplicationClass(); }

 SpringApplication 构造函数调用initialize 方法初始化sources、webEnvironment、initializers、listeners、mainApplicationClass;

sources 很简单, 就是main参数中的args参数。initialize 方法通过集合转换讲args 存放到set 集合sources 中。

webEnvironment :webEnvironment 是一个Boolean 变量,代表当前程序是否是web 程序。赋值过程:

private boolean deduceWebEnvironment() {
        for (String className : WEB_ENVIRONMENT_CLASSES) {
            if (!ClassUtils.isPresent(className, null)) {
                return false;
            }
        }
        return true;
}
private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet", "org.springframework.web.context.ConfigurableWebApplicationContext" };

WEB_ENVIRONMENT_CLASSES是一个数组、存放着两个类"javax.servlet.Servlet", "org.springframework.web.context.ConfigurableWebApplicationContext"的名称.
decuceWebEnvironment 方法通过遍历WEB_ENVIRONMENT_CLASSES 数组,判定当前classpath下是否存在包含的类,存在返回true值,代表web应用程序。isPresent()方法通过
forName(className,classLoader)进行类加载,加载成功返回一个true值,反之,返回false。所以webEnvironment的值为true;

setinitializers:初始化initializers变量,该变量是一个ApplicationContentInitializer类型的集合。


private
List<ApplicationContextInitializer<?>> initializers;

// 设置
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));

通过getSpringFactoriesInstances(ApplicationContextInitializer.class)) 方法得到一个ApplicationContextInializer 类型集合,然后添加到initializers集合中去。getSpringFactoriesInstances实现:

private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type) {
        return getSpringFactoriesInstances(type, new Class<?>[] {});
}

private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type,
            Class<?>[] parameterTypes, Object... args) {
     
     // 获取当前线程类加载器 ClassLoader classLoader
= Thread.currentThread().getContextClassLoader();
// 获取到初始化Spring Factories的名字 Set<String> names = new LinkedHashSet<String>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));

     //根据名字创建对应对象  List
<T> instances = createSpringFactoriesInstances(type, parameterTypes,classLoader, args, names);

     //排序 AnnotationAwareOrderComparator.sort(instances);
return instances; }

 首选获取当前线程,根据传入的class type和类加载器,调用SpringFactoriesLoader.loadFactoryNames方法,获取到配置文件中的定义的Application Context Initializers类名称

loadFactoryNames方法,通过classLoader.getResources或getSystemResources方法,从资源文件spring.factories获取到一个一个enumeration 类型的URL。通过遍历URL,获取到property资源,然后获取定义的Intializers名称,并经过转换加入了集合中返回


public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
        String factoryClassName = factoryClass.getName();
        try {
            Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
                    ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
            List<String> result = new ArrayList<String>();
            while (urls.hasMoreElements()) {
                URL url = urls.nextElement();
                Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
                String factoryClassNames = properties.getProperty(factoryClassName);
                result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
            }
            return result;
        }
        catch (IOException ex) {
            throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() +
                    "] factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex);
        }
    }
spring.factories 文件中定义的4个Initializer、分别是:
# 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.context.embedded.ServerPortInfoApplicationContextInitializer
ConfigurationWarningsApplicationContextInitializer的作用是用来报告Spring容器的一些常见的错误配置
ContextIdApplicationContextInitializer的作用是设置Spring应用上下文的ID,会参照环境属性。
DelegatingApplicationContextInitializer的作用是使用环境属性context.initializer.classes指定的初始化器(initializers)进行初始化工作,如果没有指定则什么都不做。
ServerPortInfoApplicationContextInitializer的作用是将内置servlet容器实际使用的监听端口写入到Environment环境属性中。这样属性local.server.port就可以直接通过@Value注入到测试中,或者通过环境属性Environment获取。

 获取到names名字后,调用createSpringFactoriesInstances(type, parameterTypes,classLoader, args, names)方法,根据名字,将4个Initalizer分别创建并加入到集合中返回;创建方法:

private <T> List<T> createSpringFactoriesInstances(Class<T> type,
            Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args,
            Set<String> names) {
        List<T> instances = new ArrayList<T>(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;
    }

首选遍历names、通过ClassUtils.forName方法获取到类对象、然后通过instantiateClass()方法创建实例,然后加入到集合中返回!然后返回到setInitializers、将创建好的ApplicationContextInitializer加入到SpringApplication定义好的initializers中了

public void setInitializers(
            Collection<? extends ApplicationContextInitializer<?>> initializers) {
        this.initializers = new ArrayList<ApplicationContextInitializer<?>>();
        this.initializers.addAll(initializers);
}

 设置listeners

private List<ApplicationListener<?>> listeners; //springApplication 定义的监听list变量
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); //设置监听
//实现方法:通过getSpringFactoriesInstrances(ApplicationListener.class)获取到listeners,并加入到集合中listeners
public void setListeners(Collection<? extends ApplicationListener<?>> listeners) {
this.listeners = new ArrayList<ApplicationListener<?>>();
        this.listeners.addAll(listeners);
}

getSpringFactoriesInstrances方法跟前面的设置setInitializers所使用的方法是一样的,只不过传入的参数不一样,传入监听器AplicationListener.class。所以创建的结果是一个存放listener的集合。spring.factorries中定义的Listener如下:

每个Listener分别处理不同的功能,暂时不说明具体处理内容。
# 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.liquibase.LiquibaseServiceLocatorApplicationListener,\
org.springframework.boot.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.logging.LoggingApplicationListener

mainApplicationClass设置

private void initialize(Object[] sources) {
    ...
    // 为成员变量mainApplicationClass赋值
    this.mainApplicationClass = deduceMainApplicationClass();
    
}

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

mainApplicationClass 通过获取运行当前调用栈,然后找到main方法,然后通过forName获取类对象,最后复制给mainApplicationClass。到这里,SpringApplication 对象初始化过程完成了。获取到了SpringApplication 对象,接着调用run方法。run方法的内容更多,接着下一篇再探索吧。。。。

=================================================================================================================================

spring boot 源码探索(一)

原文:https://www.cnblogs.com/xiaotongye/p/12743015.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!