这篇文章接着上篇文章继续。
先上一张步骤图:
上两篇文章我们已经分析到第十步了,这篇文章我们继续往下分析。
具体方法如下:
先判断了Mode,若为OFF则就不打印banner了,从这里我们知道这个banner的打印与否是可以设置的,可以使用spring.main.banner-mode=off配置。接下来创建了一个banner:
从这类中我们看到了很多熟悉的信息,有banner的默认位置和文件名称、类型,默认的Banner类型为SpringBootBanner。我们看到SpringApplication中默认的Mode为console:
所以会运行下面一行代码:
可以看到是使用的System.out输出的,下面看具体的方法:
先看第一行getBanner:
这个方法的逻辑也很简单,就是判断是否有ImageBanner,是否有TextBanner,若都没有,就看看有没有fallbackBanner,若还没有就使用默认的SpringBootBanner,从上面代码看下来发现fallbackBanner是没有赋值的,所以最后使用的是默认的banner。
再看第二行printBanner,既然上面使用的是默认的SpringBootBanner,所以我们看看它的printBanner方法:
在这个类中看到了非常熟悉的东西,每次启动程序的时候都会看到在控制台打印的东西,这里的代码就是打印一些信息到控制台,没什么特别的。我们继续往下看。
看看具体的方法:
我们已经知道webApplcationType是SERVLET,所以走的是第一个case,
BeanUtils的这个方法就是使用传入的类的默认无参构造函数来创建类的实例。
从这个类的注释我们知道这个类接受 @Configuration、@Component、@Inject注解的类作为输入,然后将这些类和classpath下的类注册。我们看到这个类还继承了ServletWebServerApplicationContext类,我们看看这个类有没有无参构造函数。
我们发现这个类也有父类
这个父类中调用了GenericApplicationContext的无参构造函数,
这个类的典型的应用就是初次对所有bean进行注册。它还有一个静态代码块加载了一个Provider类,在构造函数中调用了父类的构造函数。
这个父类的构造函数中除了调用上一级父类的构造函数外还增加了一些要忽略的接口,
再上一级的构造函数就什么也没做了。
这个无参构造上级溯源先到这里,接下来具体看看AnnotationConfigServletWebServerApplicationContext的构造函数做了什么:
先看看这个AnnotatedBeanDefinitionReader是什么:
它是一个适配器,用于注册带注解的bean类。我们看他的构造函数:
这里new了一个ConditionEvaluator,这个又是啥呢?看名字是一个条件计算器,
看注释是用来计算注解的。构造函数中new了一个 ConditionContextImpl:
获取beanFactory:
我们这里这个类是ApplicationContext所以走的是第二个分支,获取到的是GenericApplicationContext类中,在构造函数中创建的DefaultListableBeanFactory。
到这里我们就知道这个ConditionEvaluator中的context属性就是对注册器、bean工厂、环境、资源加载器、类加载器的封装。
所以我们就看到AnnotatedBeanDefinitionReader类中的conditionEvaluator实际上就是对这些工具的封装,但是这里我们看到实际上在AnnotatedBeanDefinitionReader类中这些工具都能够获取到,为什么还要单独的再弄个ConditionEvaluator类呢?我们接着看:
在给定的注册器中注册所有相关的注解处理器。
我们看这个参数是将正在分析的AnnotationConfigServletWebServerApplicationContext作为注册器传入了,我们继续看注册过程:
这一段代码有点多,我们将代码编号:
<1>处的代码就是获取前面构造函数中创建的DefaultListableBeanFactory。
<2>处的代码是添加一个比较器:
为依赖的lists和数组设置的比较器。
<3>处的代码为这个BeanFactory设置一个自定义自动装配候选解析器,我们来看看这个ContextAnnotationAutowireCandidateResolver:
这个类没有显式的无参构造函数,通过注释我们知道这里使用了一个策略模式,我们看它的父类:
通过注释我们知道它是对 @Qualifier和@Value的解析器。构造函数中添加了一个Qualifier的类到qualifierTypes的Set集合中。再看这个类的父类:
发现它是一个基础的解析类,没有显式的构造函数,再往上找它的父类:
这个类是最基础的当没有找到支持的注解时的调用,只是简单地检查bean的定义。
它实现了AutowireCandidateResolver:
继续看<3>处的代码:
对于这个方法中第一处if处的判断,我们看看之前我们分析的传入的参数,ContextAnnotationAutowireCandidateResolver类的父类的父类实现了BeanFactoryAware接口,所以这里会进入if代码块,接下来对于SecurityManager的判断我们debug一下:
发现走的是else分支,所以看来security是null了。这里的else分支中设置beanFactory方法实际上是调用的GenericTypeAwareAutowireCandidateResolver类的setBeanFactory方法,将DefaultListenableBeanFactory设置给解析器。方法中的最后一行代码将传入的ContextAnnotationAutowireCandidateResolver替换了原来默认的SimpleAutowireCandidateResolver。
我们继续看注册的过程:
这个方法中接下来的一大段都是往<4>处的Set中添加BeanDefinitionHolder。
我们看看if中的containsBeanDefinition方法:
这个beanDefinitionMap是什么时候设置的值呢?我们debug一下看看:
发现这个map中是空的,所以<4>处以下的if代码块应该都会走,我们来看看这个RootBeanDefinition
从上面这几段代码来看这个RootBeanDefinition就是记录了一下beanclass。
setRole我发现其实是设置了一个int类型的数值 2。接着看下面的registerBeanDefinition方法:
这个方法就是实际的注册过程了,很详细的 判断,具体的过程下面再分析吧。我们先将主流程继续。
看具体方法:
调用了另一个构造函数:
根据传入的参数,第二个参数是true,根据注释我们知道使用 了默认的 过滤器,包括对于 @Component、@Respository、@Service、@Controller的过滤器。这个构造函数中又调用了一个三个参数的构造函数:
多了个环境参数,然后又调用了一个四个参数的构造函数:
多了个resourceLoader参数,该构造函数中注册了默认的过滤器,设置了环境和资源加载器。我们卡看看这个注册的默认过滤器都有什么:
代码中第一行就添加了一个Component注解类型的filter,那在增加这个过滤器之前原来有没有其他的过滤器已经加载了呢?我们debug一下:
我们看到原来是没有过滤器的,在增加了Component注解类型的过滤器后,又增加了两个javax中的注解 @ManagedBean和@Named。
到这里我们就将run方法中的第12步创建上下文看完了。至于这一段分析的结论如何,我们最后一起看,接下来的分析将在下一篇文章中继续。