美文网首页
SpringBoot中starter解析

SpringBoot中starter解析

作者: 你弄啥来 | 来源:发表于2019-07-20 23:52 被阅读0次

1 SpringApplication构造函数的参数

其中run方法只是SpringApplication的一个静态方法,SpringApplication也是有成员run方法启动的。需要的参数是就是当前方法所在的class数组。

/**
SpringApplication中构造函数中需要参数有 类加载器resourceLoader,和primarySource 主要资源。所以如果primarySources 如果是多个的话,
是不是会把多个primarySources 所在包的子子孙孙都能扫描到呢?事实证明是可以的。
*
/
    public SpringApplication(Class<?>... primarySources) {
        this(null, primarySources);
    }

启动类是有两个,这样就可以实现将两个不同启动类包下的子子孙孙类都能在初始化到spring容器中


image.png

2 SpringApplication构造函数解析

    public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
        setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));//1 
        setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));//2
        this.mainApplicationClass = deduceMainApplicationClass();
    }

2.1 设置 实现了 ApplicationContextInitializer 和 ApplicationListener实例。设置的方式是从spring.factories实例获得的,这是Springboot的一个特性也是因为这个特性使得springboot可以轻松实现很多自动化的配置。
实现:在resource下创建META-INF文件在此文件夹下创建spring.factories。SpringApplication在实例化的时候就会自动扫描所有包下spring.factories的文件了(因为是配置文件所以可以通过classloader去获得)。
获得方法:

     MultiValueMap<String, String> result = new LinkedMultiValueMap<>();
        try{
            Enumeration<URL> urls = classLoader.getResources("META-INF/chen.properties");
            while (urls.hasMoreElements()) {
                URL url = urls.nextElement();
                UrlResource resource = new UrlResource(url);
                Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                for (Map.Entry<?, ?> entry : properties.entrySet()) {
                    String factoryClassName = ((String) entry.getKey()).trim();
                    for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
                        result.add(factoryClassName, factoryName.trim());
                    }
                }
            }
        }catch (Exception e){

        }
image.png

其中文件格式要是key问要实现的接口,value是指对应的具体值(这个我们在spring-boot-autoconfigure包中找到很多相配置):

org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener

3 SpringApplication中run方法解析:

    public ConfigurableApplicationContext run(String... args) {  // 3.1
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
        configureHeadlessProperty();
        SpringApplicationRunListeners listeners = getRunListeners(args); //3.2
        listeners.starting();
        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);//3.3
            configureIgnoreBeanInfo(environment);
            Banner printedBanner = printBanner(environment);
            context = createApplicationContext();//3.4
            exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                    new Class[] { ConfigurableApplicationContext.class }, context);
            prepareContext(context, environment, listeners, applicationArguments, printedBanner);
            refreshContext(context);
            afterRefresh(context, applicationArguments);
            stopWatch.stop();
            if (this.logStartupInfo) {
                new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
            }
            listeners.started(context);
            callRunners(context, applicationArguments);//3.5
        }
        catch (Throwable ex) {
            handleRunFailure(context, ex, exceptionReporters, listeners);
            throw new IllegalStateException(ex);
        }

        try {
            listeners.running(context);
        }
        catch (Throwable ex) {
            handleRunFailure(context, ex, exceptionReporters, null);
            throw new IllegalStateException(ex);
        }
        return context;
    }

3.1 返回值

该方法的返回值是 ConfigurableApplicationContext ,该类也是一个Spring容器。

3.2 获取所有的监听器(观察者模式对run方法进行监听)

该处是在先实例化所有的 SpringApplicationRunListener 封装成 SpringApplicationRunListeners,使用观察者模式(
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新),比如接下来的listeners.starting()、prepareEnvironment()、prepareContext()、 listeners.started()。
自定义也可以实现Listener,自定义Listener类 并实现 SpringApplicationRunListener 配置到 spring.factories 中 key 为 SpringApplicationRunListener 全路径,value为自定义listener的全路径。

public class ChenApplicationListener implements SpringApplicationRunListener {

    private final SpringApplication application;

    private final String[] args;

    public ChenApplicationListener(SpringApplication application, String[] args) {
        this.application = application;
        this.args = args;
    }

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

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

    @Override
    public void contextPrepared(ConfigurableApplicationContext context) {
        System.out.println("ChenApplication contextPrepared"+context);
    }

    @Override
    public void contextLoaded(ConfigurableApplicationContext context) {
        System.out.println("ChenApplication contextLoaded"+context);
    }

    @Override
    public void started(ConfigurableApplicationContext context) {
        System.out.println("ChenApplication started"+context);
    }

    @Override
    public void running(ConfigurableApplicationContext context) {
        System.out.println("ChenApplication running"+context);
    }

    @Override
    public void failed(ConfigurableApplicationContext context, Throwable exception) {
        System.out.println("ChenApplication failed"+context);
    }
}

3.3 准备环境

此阶段主要是用来准备springboot运行起来需要的一些基本环境,需要的参数:所有的观察者和命令行参数封装好的对象。比如有些时候,需要制定配置文件的名称或位置是可以在这边配置。


image.png

3.3.1 环境准备完成

发送广播消息环境已经准备完成。所有的监听器都会收到该消息。在这里就可以看到我们自己定义的消息监听器也会被通知到。在Spring配置的 EventPublishingRunListener 中封装了其他的消息监听器。这边需要看一下一个叫ConfigFileApplicationListener,他是用来加载配置文件的。


image.png

这边就回到环境中找是否有 spring.application.name的命令;

        private Set<String> getSearchNames() {
            if (this.environment.containsProperty(CONFIG_NAME_PROPERTY)) {
                String property = this.environment.getProperty(CONFIG_NAME_PROPERTY);
                return asResolvedSet(property, null);
            }
            return asResolvedSet(ConfigFileApplicationListener.this.names, DEFAULT_NAMES);
        }

3.4 创建applicationContext

根据之前确定的枚举webApplicationType在此来确定创建ApplicationContext.(AnnotationConfigServletWebServerApplicationContext)构造完成之后需要refresh ApplicationContext();
在此就会去扫描指定的包并完成Spring容器的IOC.就是在这边的时候它需要启动内置的tomcat.要找到启动的入口需要先看一下 AnnotationConfigServletWebServerApplicationContext 的父类是ServletWebServerApplicationContext,在这个类中就有启动Tomcat的入口。

image.png

AbstractApplicationContext 在finishRefresh(),之后就会来启动WebServer 及内置的tomcat

@Override
    protected void finishRefresh() {
        super.finishRefresh();
        WebServer webServer = startWebServer();
        if (webServer != null) {
            publishEvent(new ServletWebServerInitializedEvent(webServer, this));
        }
    }

3.5 Springboot项目启动完成之后

在Springboot项目启动完成之后,如果你还想搞点啥动作时可以实现 ApplicationRunner 接口,实现其中的run 方法,就可以。启动的原因是在SpringApplication调用run方法之后会有一个叫做callRunners()的方法来执行实现Runner的方法。

3.6 SpringBoot的自动配置

SpringBoot的之所以很受欢迎的另一个原因在于他能够实现自动化的配置,比如jdbc的配置,我们只需要的在配置文件里面完成相关的配置,SpringBoot就会自动帮我们把DataSourceBean配置好,那是相当的方便。实现的方式也很简单。
1 新建项目之后引入相关jar包:

   <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-configuration-processor</artifactId>
      <version>2.1.6.RELEASE</version>
    </dependency>

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-autoconfigure</artifactId>
      <version>2.1.6.RELEASE</version>
    </dependency>

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter</artifactId>
      <version>2.1.6.RELEASE</version>

2 spring.factories配置文件中增加自己写的自动配置的类,key 为EnableAutoConfiguration

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.boot.chen.SelfCommonConfiguration

3 (可以忽略)如果想让你的自动配置bean读取到将来引入到Springboot中配置文件的数据,再创建一个读取配置文件的bean

package com.boot.chen;

import org.springframework.boot.context.properties.ConfigurationProperties;

import java.util.List;
import java.util.Map;

@ConfigurationProperties(prefix = "chen")
public class ChenProperties {

    private String name;

    private Integer age;

    private String[] wifes;

    private Map<String,String> attributes;

    private Map<String, List<String>> courses;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String[] getWifes() {
        return wifes;
    }

    public void setWifes(String[] wifes) {
        this.wifes = wifes;
    }

    public Map<String, String> getAttributes() {
        return attributes;
    }

    public void setAttributes(Map<String, String> attributes) {
        this.attributes = attributes;
    }

    public Map<String, List<String>> getCourses() {
        return courses;
    }

    public void setCourses(Map<String, List<String>> courses) {
        this.courses = courses;
    }
}

4 在需要自动配置的类中完成相关bean 的配置.

package com.boot.chen;

import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;

//@Configuration
@EnableConfigurationProperties(ChenProperties.class)
public class SelfCommonConfiguration {
    @Bean
    public ServiceChen createService(ChenProperties chenProperties) {
        ServiceChen serviceChen = new ServiceChen();
        System.out.println(chenProperties.getAge());
        System.out.println(chenProperties.getName());
        return serviceChen;
    }

}

5 打包之后引入到相关的Springboot项目中就可以啦。

相关文章

网友评论

      本文标题:SpringBoot中starter解析

      本文链接:https://www.haomeiwen.com/subject/hrfplctx.html