Spring Boot:如何外部化 JDBC 数据源配置?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/20044292/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Spring Boot: How to externalize JDBC datasource configuration?
提问by zeodtr
I have following Spring Boot controller code that works. (Some sensitive text was replaced)
我有以下有效的 Spring Boot 控制器代码。(部分敏感文字已替换)
package com.sample.server;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import org.apache.commons.dbcp.BasicDataSource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestMapping;
@RestController
public class DetailReportController
{
@RequestMapping(value="/report/detail", method=RequestMethod.GET)
public List<UFGroup> detailReport()
{
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName("net.sourceforge.jtds.jdbc.Driver");
dataSource.setUrl("jdbc:jtds:sqlserver://111.11.11.11/DataBaseName;user=sa;password=password");
JdbcTemplate jt = new JdbcTemplate(dataSource);
List<UFGroup> results = jt.query(
"select NID, SCode, SName from UFGroup",
new RowMapper<UFGroup>()
{
@Override
public UFGroup mapRow(ResultSet rs, int rowNum) throws SQLException
{
return new UFGroup(rs.getInt("NID"), rs.getString("SCode"),
rs.getString("SName"));
}
});
return results;
}
private static class UFGroup
{
public int nid;
public String scode;
public String sname;
public UFGroup(int nid, String scode, String sname)
{
this.nid = nid;
this.scode = scode;
this.sname = sname;
}
}
}
Now I want to externalize the configuration of the datasource. That is, BasicDataSource class, driver class name, datasource URL should be placed into application.properties. How can I do that?
现在我想外部化数据源的配置。也就是说,BasicDataSource 类、驱动类名、数据源 URL 应该放在 application.properties 中。我怎样才能做到这一点?
BTW, I am a newbie of Spring, Spring Boot and even Java Beans. All I have is some Java programming experience, mainly for mobile devices. I've spent a few days to study Spring Boot environment, but I was literally overwhelmed. So please, give me the exact instruction with concrete example.
顺便说一句,我是 Spring、Spring Boot 甚至 Java Beans 的新手。我所拥有的只是一些 Java 编程经验,主要针对移动设备。我花了几天时间研究 Spring Boot 环境,但我真的不知所措。所以请给我具体的例子的确切说明。
UPDATE: when I applied the answer of M. Deinum, following error occured when I ran the application:
更新:当我应用 M. Deinum 的答案时,运行应用程序时出现以下错误:
2013-11-18 19:37:54.789 INFO 6868 --- [ main] com.logicplant.uflow.server.Application : Starting Application on zeo-PC with PID 6868 (C:\Projects\uFlow\Dev\Server\Spring\uFlowServer\build\libs\uFlowServer-1.0.0.jar started by zeo)
2013-11-18 19:37:54.830 INFO 6868 --- [ main] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@7f83698f: startup date [Mon Nov 18 19:37:54 KST 2013]; root of context hierarchy
2013-11-18 19:37:55.931 INFO 6868 --- [ main] o.apache.catalina.core.StandardService : Starting service Tomcat
2013-11-18 19:37:55.932 INFO 6868 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet Engine: Apache Tomcat/7.0.42
2013-11-18 19:37:56.009 INFO 6868 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2013-11-18 19:37:56.010 INFO 6868 --- [ost-startStop-1] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 1183 ms
2013-11-18 19:37:56.165 INFO 6868 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring FrameworkServlet 'dispatcherServlet'
2013-11-18 19:37:56.165 INFO 6868 --- [ost-startStop-1] o.s.web.servlet.DispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization started
2013-11-18 19:37:56.242 INFO 6868 --- [ost-startStop-1] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2013-11-18 19:37:56.388 INFO 6868 --- [ost-startStop-1] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/report/detail],methods=[GET],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public java.util.List<com.logicplant.uflow.server.DetailReportController$UFGroup> com.logicplant.uflow.server.DetailReportController.detailReport()
2013-11-18 19:37:56.438 INFO 6868 --- [ost-startStop-1] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2013-11-18 19:37:56.439 INFO 6868 --- [ost-startStop-1] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2013-11-18 19:37:56.788 INFO 6868 --- [ost-startStop-1] o.s.web.servlet.DispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization completed in 622 ms
2013-11-18 19:37:56.881 INFO 6868 --- [ main] o.apache.catalina.core.StandardService : Stopping service Tomcat
java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:53)
at java.lang.Thread.run(Unknown Source)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'detailReportController': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private org.springframework.jdbc.core.JdbcTemplate com.logicplant.uflow.server.DetailReportController.jt; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.jdbc.core.JdbcTemplate] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:292)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1139)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:537)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475)
at org.springframework.beans.factory.support.AbstractBeanFactory.getObject(AbstractBeanFactory.java:299)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:295)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:195)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:665)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:760)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:482)
at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:122)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:509)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:278)
at com.logicplant.uflow.server.Application.main(Application.java:17)
... 6 more
Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private org.springframework.jdbc.core.JdbcTemplate com.logicplant.uflow.server.DetailReportController.jt; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.jdbc.core.JdbcTemplate] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:505)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:87)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:289)
... 20 more
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.jdbc.core.JdbcTemplate] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java:1051)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:919)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:820)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:477)
... 22 more
For reference, the content of build.grade file (I use Gradle) is as follows: (following M. Deinum's suggestion, I removed the dependency for org.apache.commons.dbcp.)
作为参考,build.grade文件(我使用Gradle)的内容如下:(按照M. Deinum的建议,我去掉了对org.apache.commons.dbcp的依赖。)
buildscript {
repositories {
maven { url "http://repo.spring.io/libs-snapshot" }
mavenLocal()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:0.5.0.M5")
}
}
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'
apply plugin: 'spring-boot'
jar {
baseName = 'uFlowServer'
version = '1.0.0'
}
repositories {
mavenCentral()
maven { url "http://repo.spring.io/libs-snapshot" }
}
dependencies {
compile("org.springframework.boot:spring-boot-starter-web:0.5.0.M5")
compile("com.fasterxml.Hymanson.core:Hymanson-databind")
compile("org.springframework:spring-jdbc:4.0.0.M3")
runtime("net.sourceforge.jtds:jtds:1.3.1")
testCompile("junit:junit:4.11")
}
task wrapper(type: Wrapper) {
gradleVersion = '1.8'
}
And this is Application.java file, which is the main source file.
这是 Application.java 文件,它是主要的源文件。
package com.sample.server;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableAutoConfiguration
@ComponentScan
public class Application
{
public static void main(String args[])
{
SpringApplication app = new SpringApplication(Application.class);
app.setShowBanner(false);
app.run(args);
}
}
What can be done for the error?
可以为错误做些什么?
UPDATE: As M. Deinum suggested, when I changed build.gradle file as follows, the application worked!
更新:正如 M. Deinum 所建议的,当我按如下方式更改 build.gradle 文件时,该应用程序工作正常!
buildscript {
repositories {
maven { url "http://repo.spring.io/libs-snapshot" }
mavenLocal()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:0.5.0.M6")
}
}
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'
apply plugin: 'spring-boot'
jar {
baseName = 'uFlowServer'
version = '1.0.0'
}
repositories {
mavenCentral()
maven { url "http://repo.spring.io/libs-snapshot" }
}
dependencies {
compile("org.springframework.boot:spring-boot-starter-web:0.5.0.M6")
compile("org.springframework.boot:spring-boot-starter-jdbc:0.5.0.M6")
compile("com.fasterxml.Hymanson.core:Hymanson-databind")
runtime("net.sourceforge.jtds:jtds:1.3.1")
testCompile("junit:junit:4.11")
}
task wrapper(type: Wrapper) {
gradleVersion = '1.8'
}
回答by M. Deinum
Change your controller to the following
将您的控制器更改为以下内容
@RestController
public class DetailReportController {
@Autowired
private JdbcTemplate jt;
@RequestMapping(value="/report/detail", method=RequestMethod.GET)
public List<UFGroup> detailReport() {
List<UFGroup> results = jt.query(
"select NID, SCode, SName from UFGroup",
new RowMapper<UFGroup>(){
@Override
public UFGroup mapRow(ResultSet rs, int rowNum) throws SQLException {
return new UFGroup(rs.getInt("NID"), rs.getString("SCode"), rs.getString("SName"));
}
});
return results;
}
private static class UFGroup
{
public int nid;
public String scode;
public String sname;
public UFGroup(int nid, String scode, String sname)
{
this.nid = nid;
this.scode = scode;
this.sname = sname;
}
}
}
In src/main/resourcesadd an application.propertieswith the following
在src/main/resources添加application.properties与以下
spring.datasource.driverClassName=net.sourceforge.jtds.jdbc.Driver
spring.datasource.url=jdbc:jtds:sqlserver://111.11.11.11/DataBaseName
spring.datasource.username=sa
spring.datasource.password=password
And simply start your application. No xml needed. Spring boot will create the DataSourceand will add a default JdbcTemplateinstance.
只需启动您的应用程序。不需要 xml。Spring boot 将创建DataSource并添加一个默认JdbcTemplate实例。
Tip: Remove the dependency for org.apache.commons.dbcpspring-boot will give you the newer (and IMHO better) tomcat connection pool (which despite the name can be used entirely on its own).
提示:删除对org.apache.commons.dbcpspring-boot的依赖将为您提供更新(恕我直言更好)的 tomcat 连接池(尽管名称可以完全独立使用)。
回答by Shoaib Chikate
I will remodify your code in better approach.
我会以更好的方式重新修改您的代码。
Firstly no need of using new operator in your code as you are using Spring so you can use the powerful feature of Spring i.e Dependency Injection.
首先,在使用 Spring 时无需在代码中使用 new 运算符,因此您可以使用 Spring 的强大功能,即依赖注入。
@RestController
public class DetailReportController
{ @Required
private JdbcTemplate jt;
//setter for the same
@RequestMapping(value="/report/detail", method=RequestMethod.GET)
public List<UFGroup> detailReport()
{
List<UFGroup> results = jt.query(
"select NID, SCode, SName from UFGroup",
new RowMapper<UFGroup>()
{
@Override
public UFGroup mapRow(ResultSet rs, int rowNum) throws SQLException
{
return new UFGroup(rs.getInt("NID"), rs.getString("SCode"),
rs.getString("SName"));
}
});
return results;
}
private static class UFGroup
{
public int nid;
public String scode;
public String sname;
public UFGroup(int nid, String scode, String sname)
{
this.nid = nid;
this.scode = scode;
this.sname = sname;
}
}
}
application-context.xml
应用程序上下文.xml
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<property name="maxActive" value="100"/>
<property name="maxIdle" value="30"/>
<property name="maxWait" value="16000"/>
<property name="minIdle" value="0"/>
</bean>
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location">
<value>database.properties</value>
</property>
</bean>
<bean id="jt" class="org.springframework.jdbc.core.JdbcTemplate;">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="detailReportController" class="your class">
<property name="jt" ref="jt"/>
</bean>
Another suggestion that you can move your code related to database in DAO classes,its very bad practice to have the same in Controller.Else if you want to stick to your approach use Properties class of java.util package
另一个建议,您可以在 DAO 类中移动与数据库相关的代码,如果您想坚持使用 java.util 包的 Properties 类,在 Controller.Else 中使用相同的代码是非常糟糕的做法

