前言
有些老项目使用Spring MVC里面有写好的数据库连接池,比如redis/mongodb/mybatis(mysql其他Oracle同理)。在这些项目迁入spring boot框架时,会报错。
原因是我们业务写好了连接池,但spring boot在jar包存在的时候会主动加载spring boot的autoconfiguration创建连接池,但我们并未配置Spring Boot参数,也不需要配置。
1. mongodb
mongodb自动配置错误如下:
org.mongodb.driver.cluster : Exception in monitor thread while connecting to server localhost:27017
com.mongodb.MongoSocketOpenException: Exception opening socket
Caused by: java.net.ConnectException: Connection refused (Connection refused)
但是我没有引入spring-boot-starter-data-mongodb的jar包,后来发现我引入了spring-data-mongodb的jar
检查spring-boot-starter-data-mongodb的jar,包括3部分,如下:
我的jar包都有,相当于这些jar拼装成了 spring-boot-starter-data-mongodb
在Spring Boot中自动引入了自动配置功能
需要手动排除自动配置的数据源,在SpringBootApplication中exclude
@SpringBootApplication(exclude = {MongoAutoConfiguration.class, MongoDataAutoConfiguration.class})
启动不再报错连接localhost:27017,业务正常。原理见Spring Boot官方文档
2. mybatis
mybatis同理
Failed to auto-configure a DataSource: 'spring.datasource.url' is not specified and no embedded data
***************************
APPLICATION FAILED TO START
***************************
Description:
Cannot determine embedded database driver class for database type NONE
Action:
If you want an embedded database please put a supported one on the classpath. If you have database settings to be loaded from a particular profile you may need to active it (no profiles are currently active).
需要排除
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
3. 原理讲解
原理是EnableAutoConfiguration
进一步跟踪:
AutoConfigurationImportSelector这个类有自动加载与排除的逻辑
public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; } AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader .loadMetadata(this.beanClassLoader); AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata); return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); }
注意加载代码
getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);
/** * Return the {@link AutoConfigurationEntry} based on the {@link AnnotationMetadata} * of the importing {@link Configuration @Configuration} class. * @param autoConfigurationMetadata the auto-configuration metadata * @param annotationMetadata the annotation metadata of the configuration class * @return the auto-configurations that should be imported */ protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return EMPTY_ENTRY; } AnnotationAttributes attributes = getAttributes(annotationMetadata); Listconfigurations = getCandidateConfigurations(annotationMetadata, attributes); configurations = removeDuplicates(configurations); Set exclusions = getExclusions(annotationMetadata, attributes); checkExcludedClasses(configurations, exclusions); configurations.removeAll(exclusions); configurations = filter(configurations, autoConfigurationMetadata); fireAutoConfigurationImportEvents(configurations, exclusions); return new AutoConfigurationEntry(configurations, exclusions); }
里面
getExclusions(annotationMetadata, attributes);
/** * Return any exclusions that limit the candidate configurations. * @param metadata the source metadata * @param attributes the {@link #getAttributes(AnnotationMetadata) annotation * attributes} * @return exclusions or an empty set */ protected SetgetExclusions(AnnotationMetadata metadata, AnnotationAttributes attributes) { Set excluded = new LinkedHashSet<>(); excluded.addAll(asList(attributes, "exclude")); excluded.addAll(Arrays.asList(attributes.getStringArray("excludeName"))); excluded.addAll(getExcludeAutoConfigurationsProperty()); return excluded; }
看到了,exclude或者excludeName,当然还有一种方法
private ListgetExcludeAutoConfigurationsProperty() { if (getEnvironment() instanceof ConfigurableEnvironment) { Binder binder = Binder.get(getEnvironment()); return binder.bind(PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE, String[].class).map(Arrays::asList) .orElse(Collections.emptyList()); } String[] excludes = getEnvironment().getProperty(PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE, String[].class); return (excludes != null) ? Arrays.asList(excludes) : Collections.emptyList(); }
通过application.properties文件配置spring.autoconfigure.exclude
private static final String PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE = "spring.autoconfigure.exclude";
总结
出现这种错误多半发生在引入了spring-boot-starter-mongodb等这样的starter插件jar,没有配置数据源url;或者旧业务升级spring boot(笔者就是这种情况)
解决方法
不需要的jar不要引入即可解决问题
使用exclude排除,有三种实现方式exclude、excludeName、spring.autoconfigure.exclude
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。