3.spring源码-@Configuration

当我们给一个类加上@Configuration,spring底层到底做了哪些事情?

文章标题3:主要是先有1.spring容器的基本架构 2.spring bean的生命周期 的知识再看下面文章会更好懂。

1.入口

spring核心框架里面一个bean信息注册相关的重要的类

org.springframework.context.annotation.AnnotatedBeanDefinitionReader

它的作用就是通过他,可以使得BeanDefinitionRegistry的类拥有bean类的编程(注解)式注册的能力。

public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
//...省略部分代码
//这个方法就是,将那些注解@Component、@Configuration等的解析能力授予给BeanDefinitionRegistry 
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
	}

下面就是registerAnnotationConfigProcessors的源码。

public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
			BeanDefinitionRegistry registry, @Nullable Object source) {
		//部分源码
		Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8);

		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));
		}
}

主要就是ConfigurationClassPostProcessor这个类,这个类就让BeanDefinitionRegistry拥有了处理@Configuration的能力。

2.ConfigurationClassPostProcessor分析

1.最关键的方法postProcessBeanDefinitionRegistry,这个方法对BeanDefinitionRegistry 一些事情, 比如说我要这个BeanDefinitionRegistry 拥有对注解了@Configuration的类的注册能力。

postProcessBeanDefinitionRegistry方法的核心代码processConfigBeanDefinitions();
他主要做了:
1.获取每个有加@Configuration 的BeanDefinition
2.设置各种 beanName生成器
3(最核心).解析每个@Configuration class,注解了@Configuration的类到底要干些什么事情
4.将解析完的配置类信息ConfigurationClass,通过 ConfigurationClassBeanDefinitionReader.loadBeanDefinitions (注册中心注册bean定义。)
5.如果之前没有或者上面类定义信息,就将ImportRegistry注册为bean

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
		List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
		String[] candidateNames = registry.getBeanDefinitionNames();
		//1.获取每个类有加@Configuration 的BeanDefinition
		for (String beanName : candidateNames) {
			BeanDefinition beanDef = registry.getBeanDefinition(beanName);
			
			if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
				if (logger.isDebugEnabled()) {
					logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
				}
			}
			else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
				configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
			}
		}

		//没有找到一个加@Configuration 的BeanDefinition 直接返回
		if (configCandidates.isEmpty()) {
			return;
		}
			
		 //2.对@order的进行排序
		configCandidates.sort((bd1, bd2) -> {
			int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
			int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
			return Integer.compare(i1, i2);
		});

		// Detect any custom bean name generation strategy supplied through the enclosing application context
		SingletonBeanRegistry sbr = null;
		if (registry instanceof SingletonBeanRegistry) {
			sbr = (SingletonBeanRegistry) registry;
			if (!this.localBeanNameGeneratorSet) {
				BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(
						AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR);
				if (generator != null) {
					this.componentScanBeanNameGenerator = generator;
					this.importBeanNameGenerator = generator;
				}
			}
		}

		if (this.environment == null) {
			this.environment = new StandardEnvironment();
		}

		// 3.(最核心的)解析每个@Configuration class, 注解了@Configuration的类到底要干些什么事情(下面重点分析)
		ConfigurationClassParser parser = new ConfigurationClassParser(
				this.metadataReaderFactory, this.problemReporter, this.environment,
				this.resourceLoader, this.componentScanBeanNameGenerator, registry);

		Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
		Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
		do {
			//下面这个就是最最最关键的解析代码
			parser.parse(candidates);
			parser.validate();

			Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
			configClasses.removeAll(alreadyParsed);

			// 4.将解析完的配置类信息ConfigurationClass,通过 ConfigurationClassBeanDefinitionReader.loadBeanDefinitions (注册中心注册bean定义。)
			if (this.reader == null) {
				this.reader = new ConfigurationClassBeanDefinitionReader(
						registry, this.sourceExtractor, this.resourceLoader, this.environment,
						this.importBeanNameGenerator, parser.getImportRegistry());
			}
			this.reader.loadBeanDefinitions(configClasses);
			alreadyParsed.addAll(configClasses);

			candidates.clear();
			if (registry.getBeanDefinitionCount() > candidateNames.length) {
				String[] newCandidateNames = registry.getBeanDefinitionNames();
				Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
				Set<String> alreadyParsedClasses = new HashSet<>();
				for (ConfigurationClass configurationClass : alreadyParsed) {
					alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
				}
				for (String candidateName : newCandidateNames) {
					if (!oldCandidateNames.contains(candidateName)) {
						BeanDefinition bd = registry.getBeanDefinition(candidateName);
						if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
								!alreadyParsedClasses.contains(bd.getBeanClassName())) {
							candidates.add(new BeanDefinitionHolder(bd, candidateName));
						}
					}
				}
				candidateNames = newCandidateNames;
			}
		}
		while (!candidates.isEmpty());

		// 5.如果之前没有或者类配类定义信息,将ImportRegistry注册为bean以支持
		if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
			sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
		}
		
		//6.清除一些缓存信息
		if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
			// Clear cache in externally provided MetadataReaderFactory; this is a no-op
			// for a shared cache since it'll be cleared by the ApplicationContext.
			((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
		}
	}

3.加了@Configuration的类,到底要做些什么事情

主要做的事情:(下面列出来比较重要的,更完整的流程下更下面补充说明)
1.处理配置类的@PropertySource 注解 : 其实就是将@PropertySource 做了那些事情,记录下来。
2.处理配置类的@ComponentScans 注解。扫描这个包的类, 如果有配置类的, 将他们当成配置类处理。
3.处理@Import 注解 。
4.处理@ImportResource
5.处理注解了@Bean的方法

6.记录他的超类, 主要是防止重新处理等一些操作。

以上就是主要做的事情,但是源码里面要做的还不止于此,还要考虑下面的问题
1.首先每个配置类里面还可能包含这内部类,所以对每个加了@Component的内部类注册,如果这个内部类也是配置类。那就要执行上面的操作。
2.然后spring考虑到了 这个类的上级的所有超类。所以递归执行上面的需要做的事情,终止条件就是找不到更上级的超类。
3.@Import 注解,循环Import的问题。
比如 AConfiguration @Import 一个 配置类BConfiguration,BConfiguration然后又 通过@Import AConfiguration 会导致 循环Import的问题。 Spring 是通过 ImportStack 一个栈来确定是否有这个循环Import的问题,(处理AConfiguration 之前先判断栈里面是否有处理AConfiguration ,没有就压栈, 有说明循环Import 了)。有的话就抛处CircularImportProblem错误

整体主要的解析流程:
主要代码再 org.springframework.context.annotation.ConfigurationClassParser#processConfigurationClass
第【1】操作.该类一直到最上面的父类,都进行一下步骤操作(递归的过程)
下面的步骤操作代码主要在:(org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass)

1. 对该类里面的内部类(不包括父类的) 如果是配置类的 全都从第【1】个操作。 相当于递归。
2. 处理配置类的@PropertySource 注解 : 其实就是将@PropertySource 做了那些事情,记录下来。

3. 处理配置类的@ComponentScans 注解。扫描这个包的类, 如果有配置类的, 将他们当成配置类处理,从第【1】个操作处理。
4. 处理@Import 注解 。
5. 处理@ImportResource
6. 处理注解了@Bean的方法
7. 记录他的超类, 主要是防止重新处理等一些操作。

4.关键解析代码

下面列一下关键的代码 从 上面介绍的 processConfigBeanDefinitions 的 parser.parse(candidates);
完整路径如下:
org.springframework.context.annotation.ConfigurationClassParser#parse(java.util.Set<org.springframework.beans.factory.config.BeanDefinitionHolder>)

public void parse(Set<BeanDefinitionHolder> configCandidates) {
		for (BeanDefinitionHolder holder : configCandidates) {
			BeanDefinition bd = holder.getBeanDefinition();
			try {
			//主要就是parse方法,。
				if (bd instanceof AnnotatedBeanDefinition) {
				//主要是这个, 下面列出的就是这个代码
					parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
				}
				else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
					parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
				}
				else {
					parse(bd.getBeanClassName(), holder.getBeanName());
				}
			}
			catch (BeanDefinitionStoreException ex) {
				throw ex;
			}
			catch (Throwable ex) {
				throw new BeanDefinitionStoreException(
						"Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
			}
		}

		this.deferredImportSelectorHandler.process();
	}

进去parse();

protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
		processConfigurationClass(new ConfigurationClass(metadata, beanName), DEFAULT_EXCLUSION_FILTER);
	}

进去processConfigurationClass();

protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {
		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);
			}
		}

		//    第【1】操作.该类一直到最上面的父类,都进行一下步骤操作(递归的过程)
		SourceClass sourceClass = asSourceClass(configClass, filter);
		do {
			//进去这个
			sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
		}
		while (sourceClass != null);

		this.configurationClasses.put(configClass, configClass);
	}

进去doProcessConfigurationClass();

protected final SourceClass doProcessConfigurationClass(
			ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
			throws IOException {

		if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
			// a.对该类里面的内部类(不包括父类的) 如果是配置类的 全都从第【1】个操作。 相当于递归。
			processMemberClasses(configClass, sourceClass, filter);
		}

		// 处理配置类的@PropertySource 注解 : 其实就是将@PropertySource 做了那些事情,记录下来。
		for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
				sourceClass.getMetadata(), PropertySources.class,
				org.springframework.context.annotation.PropertySource.class)) {
			if (this.environment instanceof ConfigurableEnvironment) {
				processPropertySource(propertySource);
			}
			else {
				logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
						"]. Reason: Environment must implement ConfigurableEnvironment");
			}
		}

		// 处理配置类的@ComponentScans 注解。扫描这个包的类, 如果有配置类的, 将他们当成配置类处理,从第【1】个操作处理。
		Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
				sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
		if (!componentScans.isEmpty() &&
				!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
			for (AnnotationAttributes componentScan : componentScans) {
				// The config class is annotated with @ComponentScan -> perform the scan immediately
				Set<BeanDefinitionHolder> scannedBeanDefinitions =
						this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
				// Check the set of scanned definitions for any further config classes and parse recursively if needed
				for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
					BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
					if (bdCand == null) {
						bdCand = holder.getBeanDefinition();
					}
					if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
						parse(bdCand.getBeanClassName(), holder.getBeanName());
					}
				}
			}
		}

		// 处理@Import 注解 。
		processImports(configClass, sourceClass, getImports(sourceClass), filter, true);

		//处理@ImportResource
		AnnotationAttributes importResource =
				AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
		if (importResource != null) {
			String[] resources = importResource.getStringArray("locations");
			Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
			for (String resource : resources) {
				String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
				configClass.addImportedResource(resolvedResource, readerClass);
			}
		}

		// 处理注解了@Bean的方法
		Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
		for (MethodMetadata methodMetadata : beanMethods) {
			configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
		}

		// 处理接口的默认方法
		processInterfaces(configClass, sourceClass);

		//g.记录他的超类, 主要是防止重新处理等一些操作。
		if (sourceClass.getMetadata().hasSuperClass()) {
			String superclass = sourceClass.getMetadata().getSuperClassName();
			if (superclass != null && !superclass.startsWith("java") &&
					!this.knownSuperclasses.containsKey(superclass)) {
				this.knownSuperclasses.put(superclass, configClass);
				// Superclass found, return its annotation metadata and recurse
				return sourceClass.getSuperClass();
			}
		}

		// No superclass -> processing is complete
		return null;
	}

其实以上源码所做都是为了准备BeanDefinition, 将BeanDefinition注册到BeanDefinitionRegistry。

有问题欢迎留言讨论~~

DataSourceJmxConfiguration: Did not match: - @ConditionalOnBooleanProperty (spring.jmx.enabled=true) found different value in property &#39;spring.jmx.enabled&#39; (OnPropertyCondition) DataSourcePoolMetadataProvidersConfiguration.CommonsDbcp2PoolDataSourceMetadataProviderConfiguration: Did not match: - @ConditionalOnClass did not find required class &#39;org.apache.commons.dbcp2.BasicDataSource&#39; (OnClassCondition) DataSourcePoolMetadataProvidersConfiguration.OracleUcpPoolDataSourceMetadataProviderConfiguration: Did not match: - @ConditionalOnClass did not find required classes &#39;oracle.ucp.jdbc.PoolDataSource&#39;, &#39;oracle.jdbc.OracleConnection&#39; (OnClassCondition) DataSourcePoolMetadataProvidersConfiguration.TomcatDataSourcePoolMetadataProviderConfiguration: Did not match: - @ConditionalOnClass did not find required class &#39;org.apache.tomcat.jdbc.pool.DataSource&#39; (OnClassCondition) DataSourceTransactionManagerAutoConfiguration.JdbcTransactionManagerConfiguration#transactionManager: Did not match: - @ConditionalOnMissingBean (types: org.springframework.transaction.TransactionManager; SearchStrategy: all) found beans of type &#39;org.springframework.transaction.TransactionManager&#39; transactionManager (OnBeanCondition) DispatcherServletAutoConfiguration.DispatcherServletConfiguration#multipartResolver: Did not match: - @ConditionalOnBean (types: org.springframework.web.multipart.MultipartResolver; SearchStrategy: all) did not find any beans of type org.springframework.web.multipart.MultipartResolver (OnBeanCondition) ElasticsearchClientAutoConfiguration: Did not match: - @ConditionalOnClass did not find required class &#39;co.elastic.clients.elasticsearch.ElasticsearchClient&#39; (OnClassCondition) ElasticsearchDataAutoConfiguration: Did not match: - @ConditionalOnClass did not find required class &#39;org.springframework.data.elasticsearch.client.elc.ElasticsearchTemplate&#39; (OnClassCondition) ElasticsearchRepositoriesAutoConfiguration: Did not match: - @ConditionalOnClass did not find required class &#39;org.springframework.data.elasticsearch.repository.ElasticsearchRepository&#39; (OnClassCondition) ElasticsearchRestClientAutoConfiguration: Did not match: - @ConditionalOnClass did not find required class &#39;org.elasticsearch.client.RestClientBuilder&#39; (OnClassCondition) EmbeddedLdapAutoConfiguration: Did not match: - @ConditionalOnClass did not find required class &#39;com.unboundid.ldap.listener.InMemoryDirectoryServer&#39; (OnClassCondition) EmbeddedWebServerFactoryCustomizerAutoConfiguration: Did not match: - Application is deployed as a WAR file. (OnWarDeploymentCondition) Matched: - @ConditionalOnWebApplication (required) found &#39;session&#39; scope (OnWebApplicationCondition) ErrorWebFluxAutoConfiguration: Did not match: - @ConditionalOnClass did not find required class &#39;org.springframework.web.reactive.config.WebFluxConfigurer&#39; (OnClassCondition) FlywayAutoConfiguration: Did not match: - @ConditionalOnClass did not find required class &#39;org.flywaydb.core.Flyway&#39; (OnClassCondition) FreeMarkerAutoConfiguration: Did not match: - @ConditionalOnClass did not find required class &#39;freemarker.template.Configuration&#39; (OnClassCondition) GraphQlAutoConfiguration: Did not match: - @ConditionalOnClass did not find required class &#39;graphql.GraphQL&#39; (OnClassCondition) GraphQlQueryByExampleAutoConfiguration: Did not match: - @ConditionalOnClass did not find required class &#39;graphql.GraphQL&#39; (OnClassCondition) GraphQlQuerydslAutoConfiguration: Did not match: - @ConditionalOnClass did not find required class &#39;com.querydsl.core.Query&#39; (OnClassCondition) GraphQlRSocketAutoConfiguration: Did not match: - @ConditionalOnClass did not find required class &#39;graphql.GraphQL&#39; (OnClassCondition) GraphQlReactiveQueryByExampleAutoConfiguration: Did not match: - @ConditionalOnClass did not find required class &#39;graphql.GraphQL&#39; (OnClassCondition) GraphQlReactiveQuerydslAutoConfiguration: Did not match: - @ConditionalOnClass did not find required class &#39;com.querydsl.core.Query&#39; (OnClassCondition) GraphQlWebFluxAutoConfiguration: Did not match: - @ConditionalOnClass did not find required class &#39;graphql.GraphQL&#39; (OnClassCondition) GraphQlWebFluxSecurityAutoConfiguration: Did not match: - @ConditionalOnClass did not find required class &#39;graphql.GraphQL&#39; (OnClassCondition) GraphQlWebMvcAutoConfiguration: Did not match: - @ConditionalOnClass did not find required class &#39;graphql.GraphQL&#39; (OnClassCondition) GraphQlWebMvcSecurityAutoConfiguration: Did not match: - @ConditionalOnClass did not find required class &#39;graphql.GraphQL&#39; (OnClassCondition) GroovyTemplateAutoConfiguration: Did not match: - @ConditionalOnClass did not find required class &#39;groovy.text.markup.MarkupTemplateEngine&#39; (OnClassCondition) GsonAutoConfiguration: Did not match: - @ConditionalOnClass did not find required class &#39;com.google.gson.Gson&#39; (OnClassCondition) GsonHttpMessageConvertersConfiguration: Did not match: - @ConditionalOnClass did not find required class &#39;com.google.gson.Gson&#39; (OnClassCondition) H2ConsoleAutoConfiguration: Did not match: - @ConditionalOnClass did not find required class &#39;org.h2.server.web.JakartaWebServlet&#39; (OnClassCondition) HazelcastAutoConfiguration: Did not match: - @ConditionalOnClass did not find required class &#39;com.hazelcast.core.HazelcastInstance&#39; (OnClassCondition) HazelcastCacheConfiguration: Did not match: - @ConditionalOnClass did not find required class &#39;com.hazelcast.core.HazelcastInstance&#39; (OnClassCondition) HazelcastJpaDependencyAutoConfiguration: Did not match: - @ConditionalOnClass did not find required class &#39;com.hazelcast.core.HazelcastInstance&#39; (OnClassCondition) HttpHandlerAutoConfiguration: Did not match: - @ConditionalOnClass did not find required class &#39;org.springframework.web.reactive.DispatcherHandler&#39; (OnClassCondition) HypermediaAutoConfiguration: Did not match: - @ConditionalOnClass did not find required class &#39;org.springframework.hateoas.EntityModel&#39; (OnClassCondition) InfinispanCacheConfiguration: Did not match: - @ConditionalOnClass did not find required class &#39;org.infinispan.spring.embedded.provider.SpringEmbeddedCacheManager&#39; (OnClassCondition) IntegrationAutoConfiguration: Did not match: - @ConditionalOnClass did not find required class &#39;org.springframework.integration.config.EnableIntegration&#39; (OnClassCondition)
最新发布
11-03
<think>我们正在处理Spring Boot启动时关于DataSourceJmxConfiguration和DataSourcePoolMetadataProvidersConfiguration的条件匹配失败日志。这些日志通常出现在应用启动时,表明某些自动配置类因为条件不满足而被跳过。这些消息通常是信息性的(INFO级别),并不一定表示错误(除非你确实需要使用这些功能)。然而,它们可以帮助我们理解为什么某些配置没有生效。 原因分析: 1. 条件注解:Spring Boot使用诸如`@ConditionalOnClass`、`@ConditionalOnProperty`等条件注解来控制配置类的加载。当条件不满足时,配置类不会被加载,此时Spring Boot会记录一条条件评估报告(ConditionEvaluationReport),在启动日志中显示为“did not match”。 2. 常见场景: a. 缺少相关依赖:例如,`DataSourceJmxConfiguration`需要`org.springframework.jmx.export.MBeanExporter`类存在,如果应用中没有引入Spring的JMX支持,则该配置不会加载。 b. 属性配置:某些配置依赖于特定的属性值,例如`spring.jmx.enabled=false`会禁用JMX相关的自动配置。 c. 其他条件:比如`@ConditionalOnBean`要求容器中存在某个Bean,如果不存在,则配置类不会加载。 具体到这两个配置类: - `DataSourceJmxConfiguration`:负责注册数据源的JMX MBean。它的条件包括: - 存在`MBeanExporter`类(即引入了JMX依赖) - 属性`spring.jmx.enabled`不为false - 存在`DataSource`类型的Bean - `DataSourcePoolMetadataProvidersConfiguration`:配置数据源连接池的元数据提供者,用于监控(如Spring Boot Actuator)。它的条件包括: - 存在`DataSourcePoolMetadataProvider`类(通常由连接池的依赖提供,例如HikariCP、Tomcat JDBC等) - 存在`ObjectProvider<DataSourcePoolMetadataProvider>`(即至少有一个提供者) 所以,这些配置类被跳过的原因可能是: - 你没有使用连接池(但这种情况很少,因为Spring Boot默认会配置连接池) - 你使用的连接池没有提供对应的`DataSourcePoolMetadataProvider`实现(例如,如果你使用的是DBCP2,则需要`DataSourcePoolMetadataProvidersConfiguration`中对应的配置类,而Spring Boot自动配置支持HikariCP, Tomcat, Commons DBCP2等) - 你禁用了JMX(通过`spring.jmx.enabled=false`) - 你没有引入JMX的依赖(但通常Spring Boot的starter会包含) 如何排查? 1. 检查依赖:确保你引入了相关的连接池依赖(如HikariCP)以及Spring Boot的Actuator(如果需要监控)。对于JMX,通常`spring-boot-starter-web`会传递引入JMX的支持,但如果你没有使用Web,可能需要单独引入`spring-boot-starter-actuator`。 2. 检查配置:查看你的`application.properties`或`application.yml`,是否设置了`spring.jmx.enabled=false`。另外,检查是否排除了相关的自动配置类(通过`@SpringBootApplication(exclude={...})`)。 3. 查看完整的条件评估报告:Spring Boot在启动时会打印条件评估报告,但默认是INFO级别,且可能因为日志配置而看不到全部。你可以通过以下方式获取详细报告: - 在启动时添加JVM参数:`-Ddebug`(或者在你的配置文件中设置`debug=true`),这样启动时会打印详细的自动配置条件匹配报告,包括匹配的和未匹配的原因。 示例:设置`debug=true`后,你可以在日志中看到类似这样的信息: ``` DataSourceJmxConfiguration: Did not match: - @ConditionalOnClass found required class &#39;org.springframework.jmx.export.MBeanExporter&#39; (OnClassCondition) Matched: - @ConditionalOnBean (types: javax.sql.DataSource; SearchStrategy: all) found bean &#39;dataSource&#39; (OnBeanCondition) ``` 上面的例子说明,因为缺少`MBeanExporter`类,所以`DataSourceJmxConfiguration`没有匹配。 4. 如果你确实不需要这些功能,则可以忽略这些日志。但如果你需要,则根据报告中的提示添加所需的依赖或配置。 解决方案: - 如果你需要JMX支持,请确保没有禁用JMX(即`spring.jmx.enabled`为true,默认为true),并且引入了所需的依赖。通常,Spring Boot的starter-web或starter-actuator会引入JMX,如果没有,可以手动添加: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> ``` - 关于连接池元数据提供者,通常你使用的连接池会自动配置对应的提供者。如果你使用的连接池不在Spring Boot默认支持之列(比如Druid),那么你可能需要自己配置一个`DataSourcePoolMetadataProvider`的Bean。 总结: 这些日志大多数情况下是信息性的,说明Spring Boot根据当前的环境跳过了某些配置。如果这些配置不是你需要的,可以忽略。如果需要,则根据条件评估报告中的提示补充依赖或配置。 相关问题: 1. 如何查看Spring Boot自动配置的条件匹配报告? 2. 如何为自定义连接池配置DataSourcePoolMetadataProvider? 3. Spring Boot中如何启用和配置JMX? 4. 为什么我的Spring Boot应用没有使用预期的连接池? </think>Spring Boot应用启动时出现`DataSourceJmxConfiguration`和`DataSourcePoolMetadataProvidersConfiguration`等配置类条件匹配失败的原因通常与Spring Boot的条件装配机制(`@Conditional`)相关。以下是主要排查方向: ### 一、核心原因分析 1. **依赖缺失** - `DataSourceJmxConfiguration`需要JMX相关依赖(如`spring-boot-starter-actuator`) - `DataSourcePoolMetadataProvidersConfiguration`需要连接池实现依赖(如HikariCP、Tomcat JDBC等) - 验证依赖是否引入: ```xml <!-- 检查pom.xml中是否存在 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>com.zaxxer</groupId> <artifactId>HikariCP</artifactId> <!-- 或其他连接池 --> </dependency> ``` 2. **属性配置冲突** - JMX默认启用检查: ```properties spring.jmx.enabled=true # 默认值,若设为false则禁用 ``` - 数据源自动配置开关: ```properties spring.datasource.type=com.zaxxer.hikari.HikariDataSource # 显式指定连接池 ``` 3. **条件注解不满足** 源码示例(简化): ```java // DataSourceJmxConfiguration的装配条件 @ConditionalOnClass({ MBeanExporter.class, DataSource.class }) @ConditionalOnProperty(prefix = "spring.jmx", name = "enabled", havingValue = "true") public class DataSourceJmxConfiguration { ... } ``` - 失败原因可能是: - 缺少`MBeanExporter`类(JMX依赖未引入) - `spring.jmx.enabled=false` - 没有`DataSource` Bean ### 二、诊断步骤 1. **启用调试日志** 在`application.properties`中添加: ```properties debug=true # 打印详细条件评估报告 logging.level.org.springframework.boot.autoconfigure=DEBUG ``` 日志将显示类似: ``` DataSourceJmxConfiguration: Did not match: - @ConditionalOnClass did not find required class &#39;javax.management.MBeanServer&#39; (OnClassCondition) ``` 2. **检查条件评估报告** 访问Actuator端点(需先引入`spring-boot-starter-actuator`): ```http GET /actuator/conditions ``` 响应示例: ```json { "contexts": { "application": { "positiveMatches": { "DataSourceAutoConfiguration": { "notMatched": [{ "condition": "OnClassCondition", "message": "@ConditionalOnClass did not find required class &#39;com.zaxxer.hikari.HikariDataSource&#39;" }] } } } } } ``` 3. **依赖树分析** 运行命令检查依赖冲突: ```shell mvn dependency:tree | grep &#39;pool\|jmx\|datasource&#39; # 或Gradle gradle dependencies --configuration runtimeClasspath ``` ### 三、常见解决方案 | 问题类型 | 解决方案 | |---------|---------| | JMX依赖缺失 | 添加`spring-boot-starter-actuator`依赖 | | 连接池未引入 | 添加HikariCP/Tomcat JDBC依赖 | | 属性配置错误 | 检查`spring.jmx.enabled`和`spring.datasource.*`配置 | | Bean冲突 | 使用`@Primary`标注主数据源或排除冲突配置:<br>`@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})` | > 注:这些日志通常是**信息性**的(INFO级别),若应用功能正常则无需处理。Spring Boot会动态跳过不满足条件的配置,属于正常行为。[^1][^2]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值