SpringBoot源码理解(一)

SpringBoot源码理解(一),第1张

SpringBoot源码理解(一) 1.梦开始的地方

我们一般SpringBoot的启动类入口为以下代码:

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

第一个参数传@SpringBootApplication注解所在类,大部分情况下应该和main函数同类,第二个参数一般传空数组。

Debug进入run方法,我们可以看到以下代码:

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

所以分为两部分:

  • new一个SpringApplication对象
  • 运行SpringApplication对象的run方法

接下来一步步看。

2.创建SpringApplication对象

Debug进入SpringApplication对象的构造方法如下:

    public SpringApplication(Object... sources) {
        this.bannerMode = Mode.CONSOLE;
        this.logStartupInfo = true;
        this.addCommandLineProperties = true;
        this.headless = true;
        this.registerShutdownHook = true;
        this.additionalProfiles = new HashSet();
        this.initialize(sources);
    }

构造方法里初始化了SpringApplication的一些属性,重点看initialize()方法:

    private void initialize(Object[] sources) {
        if (sources != null && sources.length > 0) {
            this.sources.addAll(Arrays.asList(sources));
        }

        this.webEnvironment = this.deduceWebEnvironment();
        this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
        this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
        this.mainApplicationClass = this.deduceMainApplicationClass();
    }

以上方法的属性我们一个个看

  • sources对象,这边sources参数就是我们一开始传的带@SpringBootApplication注解的类。
  • webEnvironment对象,就是判断javax.servlet.Servlet,org.springframework.web.context.ConfigurableWebApplicationContext两个类是否存在。
  • mainApplicationClass对象,就是根据调用堆栈来获取我们main方法所在的类。
  • 剩下的Initializers和Listeners对象初始化方法一样,同样该方法也是SpringBoot启动过程中一个非常重要的方法。拎出来单独讲讲。
2.1 getSpringFactoriesInstances方法
    private  Collection getSpringFactoriesInstances(Class type, Class[] parameterTypes, Object... args) {
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        Set names = new linkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
        List instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
        AnnotationAwareOrderComparator.sort(instances);
        return instances;
    }

根据上面的函数名随便猜一猜,SpringFactoriesLoader.loadFactoryNames(type, classLoader)一看就是通过classLoader去获取对应的Class集合,createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names)这个一看就是通过上面获取的Class集合去实例化一些东西,顺便提一嘴getContextClassLoader()获取到的ClassLoader为AppClassLoader,这个又和那个双亲委派模型有关系了,后面再提,接着验证下刚才两个函数。

    public static List loadFactoryNames(Class factoryClass, ClassLoader classLoader) {
        String factoryClassName = factoryClass.getName();

        try {
            Enumeration urls = classLoader != null ? classLoader.getResources("meta-INF/spring.factories") : ClassLoader.getSystemResources("meta-INF/spring.factories");
            ArrayList result = new ArrayList();

            while(urls.hasMoreElements()) {
                URL 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 var8) {
            throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() + "] factories from location [" + "meta-INF/spring.factories" + "]", var8);
        }
    }

可以看到loadFactoryNames就是去加载"meta-INF/spring.factories",然后获取factories文件里的配置,StringUtils.commaDelimitedListToStringArray(factoryClassNames)这个函数以前没用过,挺好用的,记一下。看下factories文件的格式,可以看到是key-value(list)组成的配置文件,具体如下:

# 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

# 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

再看下createSpringFactoriesInstances方法

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

可以看到基本思路就是拿到Class然后通过反射来拿构造方法实例化对象。

好了SpringApplication的初始化写完了,接下来就是该run方法了,这个太长,分两篇写好了。

欢迎分享,转载请注明来源:内存溢出

原文地址:https://www.54852.com/zaji/5697041.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2022-12-17
下一篇2022-12-17

发表评论

登录后才能评论

评论列表(0条)

    保存