前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SpringBoot源码学习(一)

SpringBoot源码学习(一)

作者头像
写一点笔记
发布2020-09-10 15:43:34
5350
发布2020-09-10 15:43:34
举报
文章被收录于专栏:程序员备忘录程序员备忘录

工作中对于我们java开发者来说最经常使用的框架就是spring了,那么了解spring的基础原理对于我们的能力提升具有很大的好处。首先,作为框架首先它肯定还是从java基础演变而来,也就是说框架的代码都是基于我们日常使用的继承、多态已经各种设计模式的整合而抽取出来的一套规范。我们开发项目要想搭乘spring的快车就需要按规范做事,按规范开发。那么规范的熟悉程度就等价于项目质量的高低。当然框架的基础都是好的idea,好的idea不仅兼容一切还简单。这就和物理界的大一统理论一样。怎么做不重要,重要的是怎么兼容一切,因为做的办法会有无限种。实在无法实现也可以同缩小解的范围。废话不说了。让我们开启spring的流浪之旅。

工作中因为使用的是springboot,因此我们就直接从springboot来分析spring。

代码语言:javascript
复制
public static void main(String[] args) {
SpringApplication.run(SimpleAdminService.class, args)
}
代码语言:javascript
复制

我们看到是springAppcliatIion这个类启动的,我们跟踪一下

代码语言:javascript
复制
代码语言:javascript
复制
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);
}

发现在第二个run方法的时候进行调用了springApplication的初始化方法。那么在初始化方法中又做了哪些事情?

代码语言:javascript
复制
public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) {
    this.sources = new LinkedHashSet();//打印sprnigboot的启动标志的模式
    this.bannerMode = Mode.CONSOLE;//日志的标志
    this.logStartupInfo = true;//启动命令的标志
    this.addCommandLineProperties = true;
    this.addConversionService = true;
    this.headless = true;
    this.registerShutdownHook = true;
    this.additionalProfiles = new HashSet();
    this.isCustomEnvironment = false;
    this.resourceLoader = resourceLoader;
    Assert.notNull(primarySources, "PrimarySources must not be null");
    this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
    //用来判断是那种应用,对后边创建application的类型有作用this.webApplicationType = WebApplicationType.deduceFromClasspath();//加载spring.factories中注册的bean
    this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));//读取spring.factories文件加载applicationListener类然后实例化并返回
    this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
    this.mainApplicationClass = this.deduceMainApplicationClass();
}
发现在合理做了一些配置,主要是针对springApplication这个类的。那么是如何读这个配置文件的?跟踪代码,这块的代码主要是将spring.factories中注册的bean全部放到缓存中。private CollectiongetSpringFactoriesInstances(Classtype) {
    return this.getSpringFactoriesInstances(type, new Class[0]);
}
private SpringApplicationRunListeners getRunListeners(String[] args) {
    Class[] types = new Class[]{SpringApplication.class, String[].class};
    return new SpringApplicationRunListeners(logger,   this.getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}

private CollectiongetSpringFactoriesInstances(Classtype) {
    return this.getSpringFactoriesInstances(type, new Class[0]);
}

private CollectiongetSpringFactoriesInstances(Classtype, Class[] parameterTypes, Object... args) {
    ClassLoader classLoader = this.getClassLoader();
    Setnames =new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
    Listinstances =this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
    AnnotationAwareOrderComparator.sort(instances);
    return instances;
}

private ListcreateSpringFactoriesInstances(Classtype, Class[] parameterTypes, ClassLoader classLoader, Object[] args, Setnames) {
    Listinstances =new ArrayList(names.size());
    Iterator var7 = names.iterator();

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

        try {
            Class instanceClass = ClassUtils.forName(name, classLoader);
            Assert.isAssignable(type, instanceClass);
            Constructor constructor = instanceClass.getDeclaredConstructor(parameterTypes);
            T instance = BeanUtils.instantiateClass(constructor, args);
            instances.add(instance);
        } catch (Throwable var12) {
            throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, var12);
        }
    }

    return instances;
}
private static Map<string< span="">, List> loadSpringFactories(@Nullable ClassLoader classLoader) {
    MultiValueMap<string< span="">, String> result = (MultiValueMap)cache.get(classLoader);
    if (result != null) {
        return result;
    } else {
        try {
            Enumerationurls = 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();
                    String factoryClassName = ((String)entry.getKey()).trim();
                    String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                    int var10 = var9.length;
                    for(int var11 = 0; var11 < var10; ++var11) {
                        String factoryName = var9[var11];
                        result.add(factoryClassName, factoryName.trim());
                    }
                }
            }
            cache.put(classLoader, result);
            return result;
        } catch (IOException var13) {
            throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
        }
    }

继续跟踪代码。进入了springApplication类的核心区。

代码语言:javascript
复制
代码语言:javascript
复制
public ConfigurableApplicationContext run(String... args) {
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();//创建一个上下文,这个是接口。
    ConfigurableApplicationContext context = null;
    CollectionexceptionReporters =new ArrayList();
    this.configureHeadlessProperty();//拿到spring.factories文件,找到获取springApplication的监听类。
    SpringApplicationRunListeners listeners = this.getRunListeners(args);//启动spring.factories中的bean
    listeners.starting();
    Collection exceptionReporters;
    try {//将传入的参数进行封装
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);//将传入的参数封装为configurableEnvironment
        ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
        this.configureIgnoreBeanInfo(environment);//打印启动的环境spring.profiles.active
        Banner printedBanner = this.printBanner(environment);//创建ioc容器
        context = this.createApplicationContext();//处理异常的,也是从spring.factories中读取的。
        exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);//ioc的前置处理
        this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);//核心方法
        this.refreshContext(context);//ioc的后置处理,但是为空
        this.afterRefresh(context, applicationArguments);
        stopWatch.stop();
        if (this.logStartupInfo) {
            (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
        }
       //对监听的器发布已经启动的消息
        listeners.started(context);
        this.callRunners(context, applicationArguments);
    } catch (Throwable var10) {
        this.handleRunFailure(context, var10, exceptionReporters, listeners);
        throw new IllegalStateException(var10);
    }

    try {//向监听器广播项目已经启动的消息
        listeners.running(context);
        return context;
    } catch (Throwable var9) {
        this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
        throw new IllegalStateException(var9);
    }
}

那么getRunListeners是做什么的?

代码语言:javascript
复制
代码语言:javascript
复制
private SpringApplicationRunListeners getRunListeners(String[] args) {
    Class[] types = new Class[]{SpringApplication.class, String[].class};
    return new SpringApplicationRunListeners(logger, this.getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}

我们能发现和之前获取spring.factories的过程一样,但获取的是ApplicationListen的类。那么我们看一下listeners.starting();

代码语言:javascript
复制
代码语言:javascript
复制
public void starting() {
    Iterator var1 = this.listeners.iterator();
    while(var1.hasNext()) {
        SpringApplicationRunListener listener = (SpringApplicationRunListener)var1.next();
        listener.starting();
    }
}

发现通过轮询所有的listener,然后逐个去调用onApplicationEvent方法。那么如何自定义和一个springBootApplicationListen?

代码语言:javascript
复制
代码语言:javascript
复制
@Data
@AllArgsConstructor
public class TestApplicationListener implements SpringApplicationRunListener {

    public TestApplicationListener(SpringApplication application, String[]  args){
        System.out.println("constructor");
    }

    @Override
    public void starting() {
        System.out.println("start");
    }

    @Override
    public void environmentPrepared(ConfigurableEnvironment environment) {
        System.out.println(environment.toString());
    }

    @Override
    public void contextPrepared(ConfigurableApplicationContext context) {
        System.out.println("拿到容器");
    }

    @Override
    public void contextLoaded(ConfigurableApplicationContext context) {

        System.out.println("容器加载完毕");
    }

    @Override
    public void started(ConfigurableApplicationContext context) {

    }

    @Override
    public void running(ConfigurableApplicationContext context) {

        System.out.println("run方法");
    }

    @Override
    public void failed(ConfigurableApplicationContext context, Throwable exception) {

        System.out.println("失败");
    }
}

在META-INF的spring.factories中加入该监听器的地址

代码语言:javascript
复制
代码语言:javascript
复制
org.springframework.boot.SpringApplicationRunListener=\
com.scaffold.simple.admin.TestApplicationListener

启动的效果

通过例子,我们知道spring是轮询的找到注册到spring.factories中的applicationrunlisenner,然后调用starting方法。

我们看一下prepareEnvironment方法

代码语言:javascript
复制
代码语言:javascript
复制
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {
    ConfigurableEnvironment environment = this.getOrCreateEnvironment();
    this.configureEnvironment((ConfigurableEnvironment)environment, applicationArguments.getSourceArgs());//广播配置准备结束的消息
    listeners.environmentPrepared((ConfigurableEnvironment)environment);//将配置绑定到springapplication
    this.bindToSpringApplication((ConfigurableEnvironment)environment);
    if (!this.isCustomEnvironment) {
        environment = (new EnvironmentConverter(this.getClassLoader())).convertEnvironmentIfNecessary((ConfigurableEnvironment)environment, this.deduceEnvironmentClass());
    }

    ConfigurationPropertySources.attach((Environment)environment);
    return (ConfigurableEnvironment)environment;
}

在createApplicationContext创建ioc容器的时候,因为的springApplication构造方法的时候已经决定了webapplicationType,因此这块就直接创建了。咱们一般web都是走的AnnotationConfigReactiveWebServerApplicationContext的容器。

代码语言:javascript
复制
代码语言:javascript
复制
protected ConfigurableApplicationContext createApplicationContext() {
    Class contextClass = this.applicationContextClass;
    if (contextClass == null) {
        try {
            switch(this.webApplicationType) {
            case SERVLET:
                contextClass = Class.forName("org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext");
                break;
            case REACTIVE:
                contextClass = Class.forName("org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext");
                break;
            default:
                contextClass = Class.forName("org.springframework.context.annotation.AnnotationConfigApplicationContext");
            }
        } catch (ClassNotFoundException var3) {
            throw new IllegalStateException("Unable create a default ApplicationContext, please specify an ApplicationContextClass", var3);
        }
    }
    return (ConfigurableApplicationContext)BeanUtils.instantiateClass(contextClass);
}

在前置处理的是怎么处理的呐?

代码语言:javascript
复制
代码语言:javascript
复制
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {//ioc容器中设置envirment
    context.setEnvironment(environment);//给ioc容器设置一些辅助类
    this.postProcessApplicationContext(context);//将spring.factories中读取的实现了ApplicationContextInitializer并初始化到容器中
    this.applyInitializers(context);//对监听器进行广播
    listeners.contextPrepared(context);
    if (this.logStartupInfo) {
        this.logStartupInfo(context.getParent() == null);//打印spring.profiles.active
        this.logStartupProfileInfo(context);
    }
    ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
    beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
    if (printedBanner != null) {
        beanFactory.registerSingleton("springBootBanner", printedBanner);
    }

    if (beanFactory instanceof DefaultListableBeanFactory) {
        ((DefaultListableBeanFactory)beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
    }
//这里的source就是启动类。
    Setsources =this.getAllSources();
    Assert.notEmpty(sources, "Sources must not be empty");
    this.load(context, sources.toArray(new Object[0]));//容器广播
    listeners.contextLoaded(context);
}

通过跟踪this.load方法

代码语言:javascript
复制
protected void load(ApplicationContext context, Object[] sources) {
    if (logger.isDebugEnabled()) {
        logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
    }
//创建beanDefinitionLoader
    BeanDefinitionLoader loader = this.createBeanDefinitionLoader(this.getBeanDefinitionRegistry(context), sources);
    if (this.beanNameGenerator != null) {//设置bean名称的生成器
        loader.setBeanNameGenerator(this.beanNameGenerator);
    }

    if (this.resourceLoader != null) {
        loader.setResourceLoader(this.resourceLoader);
    }

    if (this.environment != null) {
        loader.setEnvironment(this.environment);
    }
    loader.load();
}

加载的最后大概是将启动类注册到beandefinition中了。

代码语言:javascript
复制
代码语言:javascript
复制
private int load(Class source) {
    if (this.isGroovyPresent() && BeanDefinitionLoader.GroovyBeanDefinitionSource.class.isAssignableFrom(source)) {
        BeanDefinitionLoader.GroovyBeanDefinitionSource loader = (BeanDefinitionLoader.GroovyBeanDefinitionSource)BeanUtils.instantiateClass(source, BeanDefinitionLoader.GroovyBeanDefinitionSource.class);
        this.load(loader);
    }

    if (this.isComponent(source)) {
        this.annotatedReader.register(new Class[]{source});
        return 1;
    } else {
        return 0;
    }
}

通过一大篇的load,然后通过静态方法将该bean注册到了容器中了。

声明:以上代码解析为个人观点,不对之处,希望指正。谢谢!

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-09-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序员备忘录 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档