博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
SpringBoot源码学习(一)
阅读量:4210 次
发布时间:2019-05-26

本文共 14138 字,大约阅读时间需要 47 分钟。

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

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

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

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

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的初始化方法。那么在初始化方法中又做了哪些事情?

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
< span="">, List> loadSpringFactories(@Nullable ClassLoader classLoader) { MultiValueMap
< 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类的核心区。

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是做什么的?

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();

public void starting() {    Iterator var1 = this.listeners.iterator();    while(var1.hasNext()) {        SpringApplicationRunListener listener = (SpringApplicationRunListener)var1.next();        listener.starting();    }}

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

@Data@AllArgsConstructorpublic 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中加入该监听器的地址

org.springframework.boot.SpringApplicationRunListener=\com.scaffold.simple.admin.TestApplicationListener

启动的效果

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

我们看一下prepareEnvironment方法

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的容器。

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

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

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方法

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中了。

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注册到了容器中了。

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

转载地址:http://tqkmi.baihongyu.com/

你可能感兴趣的文章
yii2 db 操作
查看>>
mongodb group 有条件的过滤组合个数。
查看>>
关于mongodb的 数组分组 array group
查看>>
MongoDB新的数据统计框架介绍
查看>>
mongodb 增加全文检索索引
查看>>
symfony
查看>>
mysql数据库主从同步的问题解决方法
查看>>
LoadRunner如何在脚本运行时修改log设置选项?
查看>>
QC数据库表结构
查看>>
自动化测试工具的3个关键部分
查看>>
测试工具厂商的编程语言什么时候“退休”?
查看>>
资源监控工具 - Hyperic HQ
查看>>
LoadRunner中Concurrent与Simultaneous的区别
查看>>
SiteScope - Agentless监控
查看>>
使用QTP的.NET插件扩展技术测试ComponentOne的ToolBar控件
查看>>
用上帝之眼进行自动化测试
查看>>
为LoadRunner写一个lr_save_float函数
查看>>
PrefTest工作室全新力作-《性能测试与调优实战》课程视频即将上线
查看>>
质量度量分析与测试技术 培训大纲
查看>>
欢迎加入【亿能测试快讯】邮件列表!
查看>>