关于springboot:SpringBoot成长记5Spring容器的创建

37次阅读

共计 4839 个字符,预计需要花费 13 分钟才能阅读完成。

后面你相熟了 SpringBoot 的扩大点 SpringApplicationRunListeners 的设计,配置文件 ConfigurableEnvironment 的形象封装。其实这些都还不是它最外围的,最最外围的时 Spring 的容器的创立和筹备,主动配置的拆卸,tomcat 的容器的启动。

这一节咱们就来开始钻研 Spring 的容器相干的逻辑,看看它有什么形象的设计和扩大点,又次要做了哪一些事件呢?

public ConfigurableApplicationContext run(String... args) {// 扩大点 SpringApplicationRunListeners listeners.starting();
   // 配置文件的解决和形象封装 ConfigurableEnvironment
   // 容器相干解决
   context = createApplicationContext();
   exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                          new Class[] {ConfigurableApplicationContext.class}, context);
    prepareContext(context, environment, listeners, applicationArguments,printedBanner);
    refreshContext(context);
   // 其余逻辑
}

容器相干的逻辑次要由三个办法组成:createApplicationContext()、prepareContext()、refreshContext()。

这三个办法,每一个都做了很多事件,咱们一个个来剖析下。

Spring 容器的创立时的外围组件

createApplicationContext 容器创立的逻辑,其实非常简单,就是通过反射创立了一个容器的实现类,默认创立 AnnotationConfigServletWebServerApplicationContext。

代码如下:

public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot."
            + "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";

protected ConfigurableApplicationContext createApplicationContext() {
        Class<?> contextClass = this.applicationContextClass;
        if (contextClass == null) {
            try {switch (this.webApplicationType) {
                case SERVLET:
                    contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
                    break;
                case REACTIVE:
                    contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
                    break;
                default:
                    contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
                }
            }
            catch (ClassNotFoundException ex) {
                throw new IllegalStateException("Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex);
            }
        }
        return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
    }

你可能第一次看这个办法,只是这样晓得了反射创立了一个容器的实现类可能就完结了。

你其实能够看下这个类反射创立也是会走构造函数的,所以能够简略剖析下创立时,初始化了那些组件。

你跟着成长记,剖析了很多对象的创立,你会发现,剖析这个类的脉络,或者组件,画一个图,根本都曾经造成一个固定的剖析套路了。

    // 本身构造函数
    public AnnotationConfigServletWebServerApplicationContext() {this.reader = new AnnotatedBeanDefinitionReader(this);
        this.scanner = new ClassPathBeanDefinitionScanner(this);
    }
    // 父类构造函数
    public GenericApplicationContext() {this.beanFactory = new DefaultListableBeanFactory();
    }

次要创立的是 DefaultListableBeanFactory,Reader 和一个 Scanner。

之前遍及过一个 Spring 根本术语,大家还记得么?容器通常称为 ApplicationContext 或者 BeanFactory,context 也简称为容器。ApplicationContext 包装了 BeanFactory,封装更高级的 API 而已,有各种实现类。

所以DefaultListableBeanFactory 这个就是真正的容器对象。

术语遍及BeanDefinition、BeanDefinitionRegistry、InternalBean

这里波及到了新的术语遍及BeanDefinition、BeanDefinitionRegistry

BeanDefinition 示意了其实就是咱们交给对 Spring 容器治理对象,也就是 Bean 的形容和定义,比方这个 Bean 是否是单例、是否有依赖其余 Bean、依赖了那些 Bean、Bean 的名称、别名等等。

BeanDefinitionRegistry 封装了对 BeanDefinition 常见操作的接口,容器默认实现了这个接口,所以个别它也代表了容器。容器 BeanFactory 实现了它,能够通过实现的办法,保护 List<BeanDefinition>。

InternalBean 其实是指 Spring 本人注入的相干 Bean,用来实现 SpringBoot 的外围性能的。

Context 的构造函数除了创立 DefaultListableBeanFactory 容器,而 Reader 和一个 Scanner 他们各自有本人的创立流程。我简略画个图给大家概括下:

上图概括了 Reader 和 Scanner 的组件,其中最最外围的是 外部 Bean 的加载,其余的组件都是为了容器服务的而已。

总而言之,能够看到容器创立时候,Reader 和一个 Scanner 是负责扫描和解析注解的,能够扫描所有相干 Bean 定义的注解,并且本人还会补充外部提前定义的一些 Bean。并且这些 internalBean 都是默认写死的一些 BeanDefination,暗藏在构造函数汇中加载的一步,这一步十分要害,SpringBoot 外围性能的实现点都是靠写 bean 来实现的,前面咱们会看到的。

从 Reader 和 Scanner 开始思考下 Spring 容器的形象设计

晓得这个 AnnotationConfigServletWebServerApplicationContext 容器的外围组件后,咱们能够思考下,这些组件是用来做什么的?之间的关系是啥,次要形象进去是为了做什么呢?

你能够设想下,Spring 容器的目标是为了 IOC,也就是帮咱们保护大量 Bean 对象的创立和治理。那么外围问题就是,这些 Bean 是怎么找到,并且创立到容器中的呢?

这就关系到了很 要害的形象设计。它就是从 Resource->ClassLoader->Reader/Scanner->BeanDefination 的设计了。

Resource

我来具体解释下,Resource 顾名思义,示意的是各种资源。包含了各种 Bean 形容文件,如 java 类中 @Bean @Configuration 的定义,@Service 和 @Controller 的定义,又比方 xml 中 <bean/> 标签的定义,groovy 中 bean 的定义等等。这些够能够统称为资源。

ResourceLoader(ClassLoader)

这些资源都是在 classpath 上面的,只有通过 ClassLoader 咱们能够扫描到和加载到的。

Reader

然而因为这些资源的定义 bean 的格局各种各样,此时就须要一个形象,来对立解析这些资源,所以就有了 Reader 和 Scanner 了,不同的 Reader 解析不同的文件,如 xmlReader,groovyReader 等,注解定义的 Bean 通过 Scanner 来找到,并且解析辨认。

BeanDefination

最终辨认为 Bean 的一个形象定义 BeanDefination。容器会保护相应的汇合,记录所有的 Bean 的定义。

这就是 Spring 容器十分好的一个形象设计,很值得咱们借鉴学习。

整体能够能够概括如下图所示:

创立了容器,容器外部,有一些组件能够帮忙容器辨认到 Bean,并且对立解析为 BeanDefination。心愿你能够领悟到这个,这个对你了解 Spring 容器十分要害的。

至于具体每一个 Reader 或 Scanner 如何通过 ClassLoader 扫描和查找资源的,创立容器这里还没有波及,之后如果能够的话,大家再仔细分析这些组件就能够了。

最初提一点,创立容器后,有一段 exceptionReporters 的获取,这个从名字就能够猜出来,是解决异样统计和汇报的组件,不是很要害,咱们抓大放小,过就能够了。

public ConfigurableApplicationContext run(String... args) {// 扩大点 SpringApplicationRunListeners listeners.starting();
   // 配置文件的解决和形象封装 ConfigurableEnvironment
   // 容器相干解决,外围剖析了它的外围组件脉络和形象设计
   context = createApplicationContext();
   // 解决异样统计和汇报的组件, 不重要,抓大放小,过就能够了。exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                          new Class[] {ConfigurableApplicationContext.class}, context);
    prepareContext(context, environment, listeners, applicationArguments,printedBanner);
    refreshContext(context);
   // 其余逻辑
}

小结

明天咱们次要剖析了容器的创立。外围剖析了它的外围组件脉络和形象设计

1)外围组件 Reader 和 Scanner,还有最要害的 DefaultListableBeanFactory。并且相熟了 BeanDefinition、BeanDefinitionRegistry 的概念。

2)容器要害的形象设计,从 Resource->ClassLoader->Reader/Scanner->BeanDefination。Spring 通过这样的设计来获取到 Bean 对象的定义,最终帮咱们治理对象。

下一节咱们来一起看下有了容器对象,会为容器筹备和设置哪些其余的内容,在这个过程中,容器又有会执行哪一些扩大点。

本文由博客一文多发平台 OpenWrite 公布!

正文完
 0