Я хочу использовать внешний файл свойств для загрузки базы данных и информации о конечной точке REST. Я пытаюсь избежать конфигурации XML и предпочитаю конфигурацию на основе аннотаций.
Я создал 2 класса, каждый из которых помечен @Configuration
, и использую аннотацию @Value
в своих конструкторах для загрузки свойств:
RestConfiguration.java
@Configuration
public class RestConfiguration {
private final String grantType;
private final AuthenticationScheme authenticationScheme;
private final String clientId;
private final String clientSecret;
private final String accessTokenUri;
private final boolean useProxy;
private final String proxyHost;
private final int proxyPort;
@Autowired
public RestConfiguration(
@Value("${api.oauth2.grant-type}") String grantType,
@Value("${api.oauth2.authentication-scheme}") AuthenticationScheme authenticationScheme,
@Value("${api.oauth2.client-id}") String clientId,
@Value("${api.oauth2.client-secret}") String clientSecret,
@Value("${api.oauth2.url}") String accessTokenUri,
@Value("${net.proxy}") boolean useProxy,
@Value("${net.proxy.host}") String proxyHost,
@Value("${net.proxy.port}") int proxyPort) {
this.grantType = grantType;
this.authenticationScheme = authenticationScheme;
this.clientId = clientId;
this.clientSecret = clientSecret;
this.accessTokenUri = accessTokenUri;
this.useProxy = useProxy;
this.proxyHost = proxyHost;
this.proxyPort = proxyPort;
}
}
PersistenceConfiguration.java
@Configuration
public class PersistenceConfiguration {
private final String host;
private final String port;
private final String database;
private final String schema;
private final String user;
private final String password;
@Autowired
public PersistenceConfiguration(
@Value("${db.host}") String host,
@Value("${db.port}") String port,
@Value("${db.database}") String database,
@Value("${db.schema}") String schema,
@Value("${db.user}") String user,
@Value("${db.password}") String password) {
this.host = host;
this.port = port;
this.database = database;
this.schema = schema;
this.user = user;
this.password = password;
}
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource) {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource);
em.setPackagesToScan("ch.example.rest.entities");
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
em.setJpaProperties(additionalProperties());
return em;
}
@Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("org.postgresql.Driver");
dataSource.setUrl("jdbc:postgresql://" + host + ":" + port + "/" + database);
dataSource.setUsername(user);
dataSource.setPassword(password);
dataSource.setSchema(schema);
return dataSource;
}
@Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory emf) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(emf);
return transactionManager;
}
@Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
return new PersistenceExceptionTranslationPostProcessor();
}
Properties additionalProperties() {
Properties properties = new Properties();
properties.setProperty("hibernate.hbm2ddl.auto", "none");
properties.setProperty("hibernate.dialect", "org.hibernate.dialect.PostgreSQL95Dialect");
properties.setProperty(
"hibernate.physical_naming_strategy",
"ch.example.rest.configurations.SnakeCaseNamingStrategy");
properties.setProperty(
"spring.datasource.hikari.data-source-properties", "stringtype=unspecified");
return properties;
}
}
Оба файла конфигурации находятся в одном пакете (подпакет configurations
).
Класс, который инициализирует контекст Spring, выглядит следующим образом:
@Configuration
@ComponentScan(basePackages = "ch.example.rest")
@EnableJpaRepositories("ch.example.rest.repositories")
@PropertySource("classpath:application.properties")
public class RestClient {
private CommandLineController commandLineController;
@Autowired
public RestClient(CommandLineController commandLineController) {
this.commandLineController = commandLineController;
}
private static void main(String[] args) {
// ... some parsing of command line arguments
// Initialize context
ApplicationContext ctx = new AnnotationConfigApplicationContext(RestClient.class);
RestClient restClient = ctx.getBean(RestClient.class, uploadCommand);
restClient.runCommand(parsedCommand, uploadCommand);
}
public void runCommand(String command, UploadBillsCommand uploadCommand) {
// Some calls to a controller
commandLineController....;
}
}
Интересно, что класс RestConfiguration
получает свойства, а PersistenceConfiguration
- нет. Во время отладки я заметил, что класс PersistenceConfiguration
создается почти сразу, тогда как RestConfiguration
загружается через некоторое время, когда сделан первый вызов RestTemplate.
Я подозреваю, что это может что-тоэто связано с тем, что Spring JPA пытается подключить репозитории и поэтому требует, чтобы при запуске было установлено соединение SQL.
Я обнаружил этот вопрос, который, по-видимому, предполагает, что это невозможнопредоставлять конфигурацию базы данных извне без дополнительного шаблонного кода. Поскольку этому вопросу уже 5 лет, мне было интересно, может быть, есть другое элегантное решение, чтобы решить эту проблему, не создавая второй контекст.