在上一篇中我们聊到条件注解的实现。在最后有一个问题,“使用了条件注解的类为什么在条件不成立的时候,spring不会注入bean呢”。伴随着这个问题,今天我们来看下spring-boot的启动流程,在该流程中为啥我们的bean会伴随着条件注解的成立与否而被注入。
我们来看下spring-boot的启动关键步骤
1.程序的启动点
//1.入口
SpringApplication.run(SampleTomcatJspApplication.class, args);
//2.跟着入口一直看下去,我们就到了这个地方
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
到这里的话,我们就看到了一个实际上是创建一个SpringApplication对象然后执行run方法。我们先看一下创建对应的时候都是做了什么事情
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));
//获取对应的web应用类型
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//设置ApplicationContextInitializer,该接口也被成为回调函数
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
//设置监听器(参考观察者模式)
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//获取main函数对应的类名称
this.mainApplicationClass = deduceMainApplicationClass();
}
在构造函数中,主要有三个方法,我们要注意一下,第一个是获取web应用的类型,这个决定了我们后面容器的创建。ApplicationContextInitializer的设置,这个是一个扩展点,主要在容器刷新之前,我们可以做一些环境设置。最后一个就是监听器的注册也是一个扩展点
3. SpringApplication的run方法
SpringApplication创建以后,到现在我们能够判断出当前的web应用是那种类型。那么我们继续看SpringApplication对象中的run方法
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
//1.创建容器
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
//2.刷新容器
refreshContext(context);
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
callRunners(context, applicationArguments);
}
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
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);
}
主要根据我们获取到的web应用类型,来创建对应的容器。跟踪代码可以知道web类型这里是SERVLET,所以对应的创建的容器就是AnnotationConfigServletWebServerApplicationContext。下面我们就看下AnnotationConfigServletWebServerApplicationContext这个类的无参构造函数里面到底做了个什么
3.2 实例化AnnotationConfigServletWebServerApplicationContext
public AnnotationConfigServletWebServerApplicationContext() {
// AnnotatedBeanDefinitionReader 初始化BeanDefinition读取类
this.reader = new AnnotatedBeanDefinitionReader(this);
//ClassPathBeanDefinitionScanner 初始BeanDefinition扫描类
this.scanner = new ClassPathBeanDefinitionScanner(this);
}
以前看过spring 源码的同学是不是对这个两个类很熟悉。我们先不关心扫描类,我们来看下AnnotatedBeanDefinitionReader都干了什么。
3.2.1 AnnotatedBeanDefinitionReader创建
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
Assert.notNull(environment, "Environment must not be null");
this.registry = registry;
//看着这个地方是不是和条件注解有点关系了?
this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
//注册注释后置处理器(这个是关键点)
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}
代码看到这里就和我们今天的主题有点关系了。ConditionEvaluator这个类就是用来评估条件注解标注的类。换句通俗的话说就是他的行为结果决定了类是否可以应该被注入。
AnnotationConfigUtils.registerAnnotationConfigProcessors() 这个太重要了,重要到我们不得不去里面瞄一眼里面都是有什么东西
public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
BeanDefinitionRegistry registry, @Nullable Object source) {
DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);
if (beanFactory != null) {
if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) {
beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
}
if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) {
beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
}
}
Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8);
// 看这个是啥,用于处理@Configuration注解相关的,我们的文章的答案就在这个类里面
if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
}
//同理这个就是用来处理@Autowired等
if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
}
// Check for JSR-250 support, and if present add the CommonAnnotationBeanPostProcessor.
if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
}
// Check for JPA support, and if present add the PersistenceAnnotationBeanPostProcessor.
if (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition();
try {
def.setBeanClass(ClassUtils.forName(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME,
AnnotationConfigUtils.class.getClassLoader()));
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Cannot load optional framework class: " + PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, ex);
}
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME));
}
if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME));
}
if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME));
}
return beanDefs;
}
这个方法重要吧,日常的注解能够被使用。这个地方就是处理他们的入口,我们直接来看ConfigurationClassPostProcessor#postProcessBeanFactory(),跟踪这个方法,会进入到processConfigBeanDefinitions(),在processConfigBeanDefinitions()中有段代码
do {
parser.parse(candidates);//这地方就是解析注解的地方,一路跟踪下去
parser.validate();
}
while (!candidates.isEmpty());
跟踪此方法,最后会进入到这个地方
protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {
// 通过 conditionEvaluator来判断是否能够注入类。这个就是我们条件注解能够生效的原因
if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
return;
}
ConfigurationClass existingClass = this.configurationClasses.get(configClass);
if (existingClass != null) {
if (configClass.isImported()) {
if (existingClass.isImported()) {
existingClass.mergeImportedBy(configClass);
}
// Otherwise ignore new imported config class; existing non-imported class overrides it.
return;
}
else {
// Explicit bean definition found, probably replacing an import.
// Let's remove the old one and go with the new one.
this.configurationClasses.remove(configClass);
this.knownSuperclasses.values().removeIf(configClass::equals);
}
}
// Recursively process the configuration class and its superclass hierarchy.
SourceClass sourceClass = asSourceClass(configClass, filter);
do {
sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
}
while (sourceClass != null);
this.configurationClasses.put(configClass, configClass);
}
来源:oschina
链接:https://my.oschina.net/nixi0608/blog/4259978