spring-boot之条件注解底层探秘

泪湿孤枕 提交于 2020-05-01 02:11:53

上一篇中我们聊到条件注解的实现。在最后有一个问题,“使用了条件注解的类为什么在条件不成立的时候,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);
	}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!