Spring异常分析
异常报错
2019-01-14 10:40:18.427 ERROR 11776 --- [ost-startStop-1] o.s.b.w.e.t.TomcatStarter : Error starting Tomcat context.
Exception: org.springframework.beans.factory.BeanCreationException. Message: Error creating bean with name 'servletEndpointRegistrar' defined in class path resource [org/springframework/boot/actuate/autoconfigure/endpoint/web/ServletEndpointManagementContextConfiguration$WebMvcServletEndpointManagementContextConfiguration.class]:
Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException:
Failed to instantiate [org.springframework.boot.actuate.endpoint.web.ServletEndpointRegistrar]:
Factory method 'servletEndpointRegistrar' threw exception; nested exception is org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'healthEndpoint' defined in class path resource [org/springframework/boot/actuate/autoconfigure/health/HealthEndpointConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.boot.actuate.health.HealthEndpoint]: Factory method 'healthEndpoint' threw exception; nested exception is org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'org.springframework.boot.actuate.autoconfigure.jdbc.DataSourceHealthIndicatorAutoConfiguration':
Bean instantiation via constructor failed; nested exception is org.springframework.beans.BeanInstantiationException:
Failed to instantiate [org.springframework.boot.actuate.autoconfigure.jdbc.DataSourceHealthIndicatorAutoConfiguration$$EnhancerBySpringCGLIB$$caa88ea6]: Constructor threw exception; nested exception is org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'dataSource': Post-processing of FactoryBean's singleton object failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'scopedTarget.dataSource' defined in class path resource [org/springframework/boot/autoconfigure/jdbc/DataSourceConfiguration$Hikari.class]:
Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.zaxxer.hikari.HikariDataSource]: Factory method 'dataSource' threw exception; nested exception is org.springframework.boot.autoconfigure.jdbc.DataSourceProperties$DataSourceBeanCreationException: Failed to determine a suitable driver class
关键在最后一句Factory method 'dataSource' threw exception; nested exception is org.springframework.boot.autoconfigure.jdbc.DataSourceProperties$DataSourceBeanCreationException: Failed to determine a suitable driver class
我这里是有配置mysql驱动的。所以定位应该是配置问题。
根据提示,可以看出是从DataSourceProperties这里抛出来的异常。读取spring.datasource开头的配置项。如果spring.datasource.driverClassName为空,会抛出异常,提示Failed to determine a suitable driver class
我的配置文件里是有配置的,该配置在读取时会将driver-class-name转换为驼峰式写法driverClassName。所以问题应该是Springboot没有加载application.properties的问题,可以来验证一下。
启动后,异常信息变成
Factory method 'dataSource' threw exception; nested exception is org.springframework.boot.autoconfigure.jdbc.DataSourceProperties$DataSourceBeanCreationException: Failed to determine suitable jdbc url
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
@ConfigurationProperties(prefix = "spring.datasource")
public class DataSourceProperties implements BeanClassLoaderAware, InitializingBean {
<span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">determineDriverClassName</span><span class="hljs-params">()</span> </span>{
<span class="hljs-keyword">if</span> (StringUtils.hasText(<span class="hljs-keyword">this</span>.driverClassName)) {
Assert.state(driverClassIsLoadable(),
() -> <span class="hljs-string">"Cannot load driver class: "</span> + <span class="hljs-keyword">this</span>.driverClassName);
<span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>.driverClassName;
}
String driverClassName = <span class="hljs-keyword">null</span>;
<span class="hljs-keyword">if</span> (StringUtils.hasText(<span class="hljs-keyword">this</span>.url)) {
driverClassName = DatabaseDriver.fromJdbcUrl(<span class="hljs-keyword">this</span>.url).getDriverClassName();
}
<span class="hljs-keyword">if</span> (!StringUtils.hasText(driverClassName)) {
driverClassName = <span class="hljs-keyword">this</span>.embeddedDatabaseConnection.getDriverClassName();
}
<span class="hljs-keyword">if</span> (!StringUtils.hasText(driverClassName)) {
<span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> DataSourceBeanCreationException(
<span class="hljs-string">"Failed to determine a suitable driver class"</span>, <span class="hljs-keyword">this</span>,
<span class="hljs-keyword">this</span>.embeddedDatabaseConnection);
}
<span class="hljs-keyword">return</span> driverClassName;
}
}
测试加载的配置文件
public class KzfApiAlipayApplication {
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>{
ConfigurableApplicationContext context = SpringApplication.run(KzfApiAlipayApplication.class, args);
System.out.println(context.getEnvironment().getProperty(<span class="hljs-string">"spring.datasource.driver-class-name"</span>));
}
}
public class ServletEndpointManagementContextConfiguration {
@Configuration
@ConditionalOnClass(DispatcherServlet.class)
public static class WebMvcServletEndpointManagementContextConfiguration {
private final ApplicationContext context;
public WebMvcServletEndpointManagementContextConfiguration(
ApplicationContext context) {
this.context = context;
}
@Bean
public ServletEndpointRegistrar servletEndpointRegistrar(
WebEndpointProperties properties,
ServletEndpointsSupplier servletEndpointsSupplier) {
DispatcherServletPath dispatcherServletPath = this.context
.getBean(DispatcherServletPath.class);
return new ServletEndpointRegistrar(
dispatcherServletPath.getRelativePath(properties.getBasePath()),
servletEndpointsSupplier.getEndpoints());
}
}
}
SpringBoot启动过程
@SpringBootApplication
public class CrawlerDiscreditApplication {
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>{
SpringApplication.run(CrawlerDiscreditApplication.class, args);
}
}
SpringApplication类
public class SpringApplication {
<span class="hljs-keyword">private</span> ResourceLoader resourceLoader;
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">SpringApplication</span><span class="hljs-params">(Class<?>... primarySources)</span> </span>{
<span class="hljs-keyword">this</span>(<span class="hljs-keyword">null</span>, primarySources);
}
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">SpringApplication</span><span class="hljs-params">(ResourceLoader resourceLoader, Class<?>... primarySources)</span> </span>{
<span class="hljs-keyword">this</span>.resourceLoader = resourceLoader;
Assert.notNull(primarySources, <span class="hljs-string">"PrimarySources must not be null"</span>);
<span class="hljs-keyword">this</span>.primarySources = <span class="hljs-keyword">new</span> LinkedHashSet<>(Arrays.asList(primarySources));
<span class="hljs-keyword">this</span>.webApplicationType = deduceWebApplicationType();
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
<span class="hljs-keyword">this</span>.mainApplicationClass = deduceMainApplicationClass();
}
}