I use a MongoConfiguration class to setup my Sping 4 MongoDB. I want to read properties from application.properties so I use #Value:
....
#Configuration
#EnableMongoRepositories
#ComponentScan(basePackageClasses = {Application.class})
public class MongoConfiguration extends AbstractMongoConfiguration {
#Value("${mongodb.host}")
String mongodb_host;
#Value("${mongodb.port}")
int mongodb_port;
#Value("${mongodb.databasename}")
String mongodb_databasename;
#Override
protected String getDatabaseName() {
return mongodb_databasename;
}
#Override
public Mongo mongo() throws Exception {
return new MongoClient( mongodb_host, mongodb_port );
}
#Override
protected String getMappingBasePackage() {
return "com.example.mongodb01";
}
}
This works fine for a web application -- but when I try the same idea in a command line Java application it fails (it's as if the application.properties was found but #Value never ran). I know I am reading the applications.properties file OK. It must have something to do with the differences in running in a servlet container vs. an application but after much searching and trials I have not been able to resolve this and fix it. I would appreciate any help on this -- Thank you!
I did see a similar question and I tried adding the below to my MongoConfiguration but still had the same problem:
#Bean
public static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
Did you include the #PropertySource
#Configuration
#PropertySource("classpath:application.properties")
#EnableMongoRepositories
#ComponentScan(basePackageClasses = {Application.class})
public class MongoConfiguration extends AbstractMongoConfiguration {
...
Related
I'm new to spring boot and I'm trying to wrap my head around how to make dependency injection work for deployment and testing.
I have a #RestController and a supporting #Service. The service injects another class that is an interface for talking to Kafka. For the Kafka interface I have two implementations: one real and one fake. The real one I want to use in production and the fake in test.
My approach is to use two different configuration for each environment (prod and test).
#Configuration
public class AppTestConfiguration {
#Bean
public KafkaMessagePublisher kafkaMessagePublisher() {
return new KafkaMessagePublisherFakeImpl();
}
}
#Configuration
public class AppConfiguration {
#Bean
public KafkaMessagePublisher kafkaMessagePublisher() {
return new KafkaMessagePublisherImpl();
}
}
Then in my main application I would like to somehow load AppConfiguration.
#SpringBootApplication
public class DeployerServiceApiApplication {
public static void main(String[] args) {
SpringApplication.run(DeployerServiceApiApplication.class, args);
}
// TODO: somehow load here...
}
And in my test load the fake configuration somehow
#SpringBootTest
#AutoConfigureMockMvc(addFilters = false)
public class DeployerServiceApiApplicationTest {
#Autowired private MockMvc mockMvc;
// TODO: somehow load AppTestConfiguration here
#Test
public void testDeployAction() throws Exception {
...
ResultActions resultActions = mockMvc.perform(...);
...
}
}
I've spent the better part of a day trying to figure this out. What I'm trying to accomplish here is fundamental and should be straight forward yet I keep running into issues which makes me wonder if the way I'm thinking about this is all wrong.
Am not sure if i understand your question completely but from description i guess you wish to initialize bean based on environment. Please see below.
#Profile("test")
#Configuration
public class AppTestConfiguration {
#Bean
public KafkaMessagePublisher kafkaMessagePublisher() {
return new KafkaMessagePublisherFakeImpl();
}
}
#Profile("prod")
#Configuration
public class AppConfiguration {
#Bean
public KafkaMessagePublisher kafkaMessagePublisher() {
return new KafkaMessagePublisherImpl();
}
and then you can pass the "-Dspring.profiles.active=prod" argument while starting you application using java command or you can also specify the profile in your test case like below.
#SpringBootTest
#ActiveProfile("test")
#AutoConfigureMockMvc(addFilters = false)
public class DeployerServiceApiApplicationTest
Use spring profiles, you can annotate your test class with #ActiveProfiles("test-kafka") and your test configuration with #Profile("test-kafka").
This is pretty easy task in spring boot world
Rewrite your classes as follows:
#Profile("test")
#Configuration
public class AppTestConfiguration {
#Bean
public KafkaMessagePublisher kafkaMessagePublisher() {
return new KafkaMessagePublisherFakeImpl();
}
}
#Profile("prod")
#Configuration
public class AppConfiguration {
#Bean
public KafkaMessagePublisher kafkaMessagePublisher() {
return new KafkaMessagePublisherImpl();
}
}
This will instruct spring boot to load the relevant configuration when the "prod"/"test" specified.
Then you can start your application in production with --spring.profiles.active=prod and in the Test you can write something like this:
#SpringBootTest
#ActiveProfiles("test")
public class DeployerServiceApiApplicationTest {
...
}
If you want to run all the tests with this profile and do not want to write this ActiveProfiles annotation you can create src/test/resources/application.properties and put into it: spring.active.profiles=test
i met a urger issue, the anotain #Bean method execute before the #PostConstruct method in the same class,
in spring boot oauth2. there is a class named as
AuthorizationServerEndpointsConfiguration.java
There are 2 methods in it.
#PostConstruct
public void init() {
xxxx
}
}
#Bean
public AuthorizationServerTokenServices defaultAuthorizationServerTokenServices() {
return endpoints.getDefaultAuthorizationServerTokenServices();
}
According to my understanding,the init shuold run before defaultAuthorizationServerTokenServices method, but when i debug it in IDEA, the defaultAuthorizationServerTokenServices before init.
As below config can to reproduce this issue.
#Configuration
#Import({ ClientDetailsServiceConfiguration.class, AuthorizationServerEndpointsConfiguration.class })
class MyAuthorizationServerConfigurer extends AuthorizationServerConfigurerAdapter {
.....
}
Could you tell me what's the reason?
Thanks,
We had this issue when debugging a configurer with #RefreshScope.
When we dropped the RefreshScope lazy loading it appeared to fix the issue
// #RefreshScope
public class MyAuthorizationServerConfigurer extends AuthorizationServerConfigurerAdapter {
}
I have a Spring Boot + Apache Camel project that works brilliantly. I just added a new bean though where I wanted to have its implementation be profile-specific. I created Spring tests to verify it, and it works as expected, but when I run the server I get the following stack trace:
Caused by: org.apache.camel.NoSuchBeanException: No bean could be found in the registry for: MyFancyBean
at org.apache.camel.component.bean.RegistryBean.getBean(RegistryBean.java:94)
at org.apache.camel.model.language.MethodCallExpression.createExpression(MethodCallExpression.java:196)
at org.apache.camel.model.language.MethodCallExpression.createPredicate(MethodCallExpression.java:210)
at org.apache.camel.model.language.ExpressionDefinition.createPredicate(ExpressionDefinition.java:148)
at org.apache.camel.model.ValidateDefinition.createProcessor(ValidateDefinition.java:63)
at org.apache.camel.model.ValidateDefinition.createProcessor(ValidateDefinition.java:35)
at org.apache.camel.model.ProcessorDefinition.makeProcessorImpl(ProcessorDefinition.java:545)
at org.apache.camel.model.ProcessorDefinition.makeProcessor(ProcessorDefinition.java:506)
at org.apache.camel.model.ProcessorDefinition.addRoutes(ProcessorDefinition.java:222)
at org.apache.camel.model.RouteDefinition.addRoutes(RouteDefinition.java:1068)
I have an interface and two implementations:
public interface MyFancyBean { ... }
public class FooFancyBean implements MyFancyBean { ... }
public class NonFooFancyBean implements MyFancyBean { ... }
Depending on profile, the correct bean is read instantiated:
#Configuration
public class AppConfig {
#Bean
#Profile("foo")
MyFancyBean fooBean() {
return new FooFancyBean();
}
#Bean
#Profile("!foo")
MyFancyBean nonFooBean() {
return new NonFooFancyBean();
}
}
I've verified this works a couple of ways. First, a couple tests:
#ActiveProfiles("anything-but-foo")
#RunWith(SpringJUnit4ClassRunner.class)
#ComponentScan(basePackages = {"com.example", "com.jtv.spring.boot"})
#EnableAutoConfiguration
#Component
public class NonFooBean_SpringTest {
#Autowired
private MyFancyBean bean;
#Test
// ... here "bean" is instantiated as "NonFooFancyBean"
So the test works.
Further, when I start my app, depending on profile the correct bean in my #Configuration class above is called.
But Camel is still angry and says "NoSuchBeanException" on startup.
FWIW, here's how I'm referencing the bean:
#Component
public class MyCamelRoute extends RouteBuilder {
#Override
public void configure() throws Exception {
// [...]
from("direct:processStuff").
validate().method("MyFancyBean").
process("MyProcessor");
}
}
How do I get Camel to honor this config?
Whoooo... Y'all get to be my rubber duck today. I just autowired it. (This doesn't work for my processor, which is why it didn't occur to me initially.)
#Component
public class MyCamelRoute extends RouteBuilder {
#Override
public void configure() throws Exception {
// [...]
#Autowired MyFancyBean myFancyBean;
from("direct:processStuff").
validate().method(myFancyBean).
process("MyProcessor");
}
}
I was configuring environment variables using spring profiles in my spring boot application. There i did configuration like
My interface is
public interface EnvConfiguration {
String getServerUrl();
}
My development configuration is
#Component
public class DevelopmentConfig implements EnvConfiguration{
#Value("${DEV}")
private String serverUrl;
#Override
public String getServerUrl(){
return serverUrl;
}
}
#Configuration
#Profile("dev")
public class DevelopmentProfile {
#Bean
public EnvConfiguration getDevelopmentConfig(){
return new DevelopmentConfig();
}
}
Same as i did configured for production environment
#Component
public class ProductionConfig implements EnvConfiguration {
#Value("${PROD}")
private String serverUrl;
#Override
public String getServerUrl(){
return serverUrl;
}
}
#Configuration
#Profile("prod")
public class ProductionProfile {
#Bean
public EnvConfiguration getProductionConfig(){
return new ProductionConfig();
}
}
Now i configured environment variable in eclipse using run configurations->agruments
-Dspring.profiles.active="dev"
Now when i trying to run my application,i am getting error:
expected single matching bean but found 2: productionConfig,developmentConfig
So please help me what am i missing there ?
Thanks in advance!
I was adding programming arguments,we have to add vm arguments
Why are you trying to configure environment properties with Java ?
You could put all your configuration into an application.properties.
Then if you want dev environment, you juste override the properties you want in application-dev.properties.
The same for prod in application-prod.properties.
Then you start as you did with -Dspring.profiles.active=dev and you will be able to retrieve value with #Value.
I'm using Spring Boot and have the following Component class:
#Component
#ConfigurationProperties(prefix="file")
public class FileManager {
private Path localDirectory;
public void setLocalDirectory(File localDirectory) {
this.localDirectory = localDirectory.toPath();
}
...
}
And the following yaml properties file:
file:
localDirectory: /var/data/test
I would like to remove the reference of java.io.File (of setLocalDirectory) by replacing with java.nio.file.Path. However, I receive a binding error when I do this. Is there way to bind the property to a Path (e.g. by using annotations)?
To add to jst's answer, the Spring Boot annotation #ConfigurationPropertiesBinding can be used for Spring Boot to recognize the converter for property binding, as mentioned in the documentation under Properties Conversion:
#Component
#ConfigurationPropertiesBinding
public class StringToPathConverter implements Converter<String, Path> {
#Override
public Path convert(String pathAsString) {
return Paths.get(pathAsString);
}
}
I don't know if there is a way with annotations, but you could add a Converter to your app. Marking it as a #Component with #ComponentScan enabled works, but you may have to play around with getting it properly registered with the ConversionService otherwise.
#Component
public class PathConverter implements Converter<String,Path>{
#Override
public Path convert(String path) {
return Paths.get(path);
}
When Spring sees you want a Path but it has a String (from your application.properties), it will lookup in its registry and find it knows how to do it.
I took up james idea and defined the converter within the spring boot configuration:
#SpringBootConfiguration
public class Configuration {
public class PathConverter implements Converter<String, Path> {
#Override
public Path convert(String path) {
return Paths.get(path);
}
}
#Bean
#ConfigurationPropertiesBinding
public PathConverter getStringToPathConverter() {
return new PathConverter();
}
}