Getting values from yml configuration to dependency - java

I got class:
#WebServiceClient(name = "${service.name}", targetNamespace = "${service.namespace}", wsdlLocation = "${service.wsdlLocation}")
public class ExampleService extends Service {
#Value("${service.wsdlLocation}")
private static String wsdlLocation;
}
It is a part of the mvn project which I compile and use from my local maven repo as a dependency to my other spring-boot app, which have configuration yml:
service:
name: name
namespace: namespace
wsdlLocation: wsdlLocation
Is there a way that this ExampleService class will use configuration of the "parent" project?
EDIT:
Two answers appearred, but I felt I did not ask question clearly. What I want to do now is to use class ExampleService in my "parent" project and make it see configuration from that parent project. So far when for example I write in parent project code like:
ExampleService exampleService = new ExampleService();
System.out.println(exampleService.getWsdlLocation());
Null is printed. So I'm looking for some solution to that.
EDIT 2:
Here are my classes.
Parent project:
package com.example.kris.parentservice;
import com.example.kris.childservice.ExampleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
#RestController
#RequestMapping("/example")
public class ExampleController {
// #Autowired
private ExampleService exampleService;
#Value(value = "${service.wsdlLocation}")
private String wsdlLocation;
#GetMapping(value = "/example")
public void example() {
exampleService = new ExampleService();
System.out.println("ExampleService field: " + exampleService.getWsdlLocation());
System.out.println("#Value: " + wsdlLocation);
}
}
Child project:
package com.example.kris.childservice;
import org.springframework.beans.factory.annotation.Value;
public class ExampleService {
#Value("${service.wsdlLocation}")
private String wsdlLocation;
public String getWsdlLocation() {
return wsdlLocation;
}
}
And output after running parent controller:
ExampleService field: null
#Value:test.wsdl.location

YamlPropertySourceLoader can be helpful in spring boot app inject a bean like this
#Bean
public PropertySource<?> yamlPropertySourceLoader() throws IOException {
YamlPropertySourceLoader loader = new YamlPropertySourceLoader();
PropertySource<?> applicationYamlPropertySource = loader.load("application.yml",
new ClassPathResource("application.yml"), "default");
return applicationYamlPropertySource;
}

Spring Boot uses a very particular PropertySource order that is
designed to allow sensible overriding of values. Properties are
considered in the following order: Source
base on same you can decide which properties are going to be in effect. If a property key is duplicated, the last declared file will ‘win’ and override.
Devtools global settings properties on your home directory (~/.spring-boot-devtools.properties when devtools is active).
#TestPropertySource annotations on your tests.
properties attribute on your tests. Available on #SpringBootTest and
the test annotations for testing a particular slice of your
application.
Command line arguments.
Properties from SPRING_APPLICATION_JSON (inline JSON embedded in
an environment variable or system property).
ServletConfig init parameters.
ServletContext init parameters.
JNDI attributes from java:comp/env.
Java System properties (System.getProperties()).
OS environment variables.
A RandomValuePropertySource that has properties only in
random.*.
Profile-specific application properties outside of your packaged
jar
(application-{profile}.properties and YAML variants).
Profile-specific application properties packaged inside your jar
(application-{profile}.properties and YAML variants).
Application properties outside of your packaged jar
(application.properties and YAML variants).
Application properties packaged inside your jar
(application.properties and YAML variants).
#PropertySource annotations on your #Configuration classes.
Default properties (specified by setting
SpringApplication.setDefaultProperties).
Edit:
As mentioned in your edit, you are creating new object using below code
ExampleService exampleService = new ExampleService();
System.out.println(exampleService.getWsdlLocation())
This will create object from spring, you should using autowiring
#Autowired
private ExampleService exampleService ;
System.out.println(exampleService.getWsdlLocation())

Related

Spring Boot: ComponentScan vs Declared Bean in an Autoconfigured Jar

Suppose I have a jar with a Spring Component called MyComponent. This jar is a Spring Boot "autoconfigured" jar, meaning that it has a Configuration class (annotated with #Configuration), and additionally, a META-INF/spring.factories file on the classpath. This jar is not an executable jar by itself; it is a library that is meant for inclusion in a Spring Boot application.
These files look as follows:
MyComponent.java, in package com.mine.components:
#Component
public class MyComponent {
private static final Logger logger = LoggerFactory.getLogger(MyComponent.class);
#PostConstruct
public void init() {
logger.info("MyComponent inited");
}
}
MyConfiguration.java, in package com.mine.config:
#Configuration
#ComponentScan(basePackages = "com.mine.components")
public class MyConfiguration {
}
spring.factories, in META-INF under src/main/resources:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.mine.config.MyConfiguration
If I include this jar in a Spring Boot project with the above three files, the MyComponent component is NOT detected (the log message never prints).
But if I instead remove the #ComponentScan and declare MyComponent using the #Bean annotation as follows, it is detected:
#Bean
public MyComponent myComponent() {
return new MyComponent();
}
Why?
Difference beetween ComponentScan and declared Bean inside #Configuration class:
#ComponentScan: You enable auto-scanning (default using current folder path), optionally you can specify an basePackage where spring will found yours beans.
#ComponentScan(basePackages = "com.mine.components")
You're saying to Spring that in this package("com.mine.components"), you'll define yours beans typically using annotations (#Component, #Entity, #Controller, #Service, #Repository or more).
#Bean: This way you define your beans manually inside #Configuration class, but Spring has to discover your configuration class, usually using #ComponentScan, #SpringBootApplication.
META-INF/spring.factories: you define an custom autoconfiguration

How to read external properties file In Spring?

Everybody knows if we want to read the properties file, we can do as follows:
#Configuration
#PropertySource("classpath:/application.properties")
public class AppConfig {
#Value("${app.name}")
public String name;
#Bean
public static PropertySourcesPlaceholderConfigurer placeholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
#Bean
public PostService postService() {
return new PostServiceImpl(name);
}
}
But, now I have a framework which is similar to SpringBoot. It can integrate Spring with Mybatis.
The problem is preceding code only can read my project classpath file but I need to read the properties file project using my framework. How I do it?
Update
I'm sorry for everybody. Maybe I don't say clearly, so here is the picture:
I don't use SpringBoot
I want to read the project(using my framework) classpath, not my framework classpath.
Thanks.
Spring provides external configuration. By this you can run your application in different environment.
refer link :
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html
If you do not like application.properties as the configuration file name, you can switch to another file name by specifying a spring.config.name environment property.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
#Configuration
#PropertySource("classpath:db.properties")
#PropertySource("classpath:project.properties")
public class DBConfiguration {
#Autowired
Environment env;
#Bean
public DBConnection getDBConnection() {
System.out.println("Getting DBConnection Bean for
App:"+env.getProperty("APP_NAME"));
DBConnection dbConnection = new DBConnection(env.getProperty("DB_DRIVER_CLASS"),
env.getProperty("DB_URL"), env.getProperty("DB_USERNAME"),
env.getProperty("DB_PASSWORD").toCharArray());
return dbConnection;
}
}
DB.properties:
#Database configuration
DB_DRIVER_CLASS=com.mysql.jdbc.Driver
DB_URL=jdbc:mysql://localhost:3306/Test
DB_USERNAME=root
DB_PASSWORD=root
project.properties:
APP_NAME=TEST APP
Spring framework can read external configuration files from different locations.
It can read the configuration file from your project directory but you would need to remove this line:
#PropertySource("classpath:/application.properties")
that limits it to your application class path.
You can check here to see the different locations spring read configuration files from.
If you are just wanting to read properties yourself from the classpath, you can use
Properties prop = new Properties();
InputStream input = this.getClass().getResourceAsStream("/application.properties")
prop.load(input);
// get the property value and print it out
System.out.println(prop.getProperty("foo"));
For non boot users who want to scan properties external to application classpath:
#PropertySource("file:/path/to/application.properties")
The "file" can be replaced with "http" for webhosted remote properties
I using next option to load properties file from anywhere, and put it into environment to access it via Environment#getProperty or #Value("name"):
#Configuration
public class MVCConfig {
#Autowired
private ConfigurableEnvironment env;
#PostConstruct
public void setup() {
Properties config = new Properties();
try (InputStream stream = this.getClass().getResourceAsStream("/custom.properties")) {
config.load(stream);
}
env.getPropertySources().addLast(new PropertiesPropertySource("mvc", config));
}
}

Spring Boot singleton get Value

Hello i am beginner in JAVA Spring Boot.
I can't get value from aplication.yml or get from service(JpaRepository) in singleton class
#Component
#Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)
public class AuthProviderService {
#Value("${test.value}")
private String testvalue;
#Autowired
private ProductService productService;
}
Spring boot is loading properties in particular way. Please check for spelling mistake in filename (application.yaml)
14.Application properties outside of your packaged jar (application.properties and YAML variants).
15.Application properties packaged inside your jar (application.properties and YAML variants).
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html
Also, please consider encapsulating you're configuration to #ConfigurationProperties

Adding new dependency to my Spring Boot project breaks existing #Autowired setup

I am encountering a baffling #Autowired issue, which only occurred after I added a dependency to one of my own projects.
Here is the situation:
I am extending a service, which has autowired repositories. Here's the simplified version:
package com.opt.custom.domain;
import com.opt.repo.RepositoryOne;
import com.opt.repo.RepositoryTwo;
#Primary
#Service("CustomDomainServiceImpl")
public class CustomDomainServiceImpl extends DomainServiceImpl {
private RepositoryOne repo1;
private RepositorTwo repo2;
#Autowired
public CustomDomainServiceImpl(RepositoryOne repo1
, RepositorTwo repo2) {
super(repo1, repo2);
}
....
}
This has been working fine - the #Autowired tags grab the repositories fine, whether or not I include them as attributes, as I don't use them except to feed into the parent service.
However, I have created another service (with its own service, repositories, etc.). When I add this new service as a dependency of the above project (in the POM file), the #Autowired annotations in the above code stop working, even if I don't reference any of the services, repos, etc. in this class. Specifically, the error is:
Parameter 0 of constructor in com.opt.custom.domain.CustomDomainServiceImpl required a bean of type 'com.opt.repo.RepositoryOne' that could not be found.
Action:
Consider defining a bean of type 'com.opt.repo.RepositoryOne' in your configuration.
I don't know how simply adding a dependency (while not using anything from it) can cause this issue.
I have tried adding a #ComponentScan to the above class:
#ComponentScan(basePackages = {"com.opt.repo"})
But this has not helped.
If it helps, this is the top-level class in the Maven project that I am adding as a dependency:
package com.opt.new.service;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
#SpringBootApplication
#EntityScan(basePackages = { "com.someotherpackage.persistence.*" })
public class PersistenceClasses {
public static void main(String[] args) {
SpringApplication.run(PersistenceClasses .class, args);
}
}
Thank you for any insights you can provide.
#ComponentScan annotation should be added to the spring boot application class. In your case, its PersistenceClasses. Also, make sure to have a #Repository annotation on your RepositoryOne class
#Repository is a spring stereotype, identifying spring components in an application. More information on it can be found here

How to test Classes with #ConfigurationProperties and #Autowired

I want to test small parts of the application that rely on properties loaded with #Autowired and #ConfigurationProperties. I am looking for a solution loading only the required properties and not always the whole ApplicationContext.
Here as reduced example:
#TestPropertySource(locations = "/SettingsTest.properties")
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {TestSettings.class, TestConfiguration.class})
public class SettingsTest {
#Autowired
TestConfiguration config;
#Test
public void testConfig(){
Assert.assertEquals("TEST_PROPERTY", config.settings().getProperty());
}
}
Configuration Class:
public class TestConfiguration {
#Bean
#ConfigurationProperties(prefix = "test")
public TestSettings settings (){
return new TestSettings();
}
}
Settings Class:
public class TestSettings {
private String property;
public String getProperty() {
return property;
}
public void setProperty(String property) {
this.property = property;
}
}
The properties file in the resource folder contains the entry:
test.property=TEST_PROPERTY
In my current setup config is not null, but no fields are available.
The reason the fields are not field should have something to do with the fact that I am not using Springboot but Spring.
So what would be the Springboot way to get this running?
edit:
The reason why I want to do this is: I have a parser that parses Textfiles, the regular expressions used are stored in a properties file.
To test this I would like to load only the properties needed for this parser which are in the exaple above the TestSettings.
While reading the comments I already noticed that this are no Unit tests anymore. However using the full Spring boot configuration for this small test seems a bit too much to me. That's why I asked if there is a posibilty to load only the one class with properties.
You need to annotate your TestConfiguraion with #EnableConfigurationProperties as follows:
#EnableConfigurationProperties
public class TestConfiguration {
#Bean
#ConfigurationProperties(prefix = "test")
public TestSettings settings (){
return new TestSettings();
}
}
Also you only need to include TestConfiguration.class in #ContextConfiguration of you SettingsTest class:
#TestPropertySource(locations = "/SettingsTest.properties")
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = TestConfiguration.class)
public class SettingsTest {
...
A couple points:
You don't need a "TestConfiguration" class in your main package, because all it's doing is configuring the "TestSettings" bean. You can do this simply by annotating the TestSettings class itself.
Normally you would load the context you need for the test using the #SpringApplicationConfiguration annotation, passing the name of your Application class. However, you said you don't want to load the whole ApplicationContext (though it's not clear why), so you need to create a special configuration class to do the loading only for tests. Below I call it "TestConfigurationNew" to avoid confusion with the TestConfiguration class that you had originally.
In the Spring Boot world, all properties are generally kept in the "application.properties" file; but it is possible to store them elsewhere. Below, I have specified the "SettingsTest.properties" file that you proposed. Note that you can have two copies of this file, the one in the main/resources folder, and the one in the test/resources folder for testing.
Change the code as follows:
TestSettings.java (in main package)
#Configuration
#ConfigurationProperties(prefix="test", locations = "classpath:SettingsTest.properties")
public class TestSettings {
private String property;
public String getProperty() {
return property;
}
public void setProperty(String property) {
this.property = property;
}
}
SettingsTest.java (in test package)
#TestPropertySource(locations="classpath:SettingsTest.properties")
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = TestConfigurationNew.class)
public class SettingsTest {
#Autowired
TestSettings settings;
#Test
public void testConfig(){
Assert.assertEquals("TEST_PROPERTY", settings.getProperty());
}
}
TestConfigurationNew.java (in test package):
#EnableAutoConfiguration
#ComponentScan(basePackages = { "my.package.main" })
#Configuration
public class TestConfigurationNew {
}
This should now work the way you wanted.
you can actually just add #EnableConfigurationProperties to your #SpringBootTest directly.
eg:
#ActiveProfiles("test")
#RunWith(SpringRunner.class)
#SpringBootTest(classes = TestConfiguration.class)
#EnableConfigurationProperties
...
If you use Spring Boot, now you only need:
#RunWith(SpringRunner.class)
#SpringBootTest
No extra #ContextConfiguration, no extra class only for tests to EnableAutoConfiguration and EnableConfigurationProperties. You don't have to specify the configuration class to load, they will all be loaded.
But, ensure the properties entries you want to read in main/resources/application.yml is also present in test/resources/application.yml. Repetition is unavoidable.
Another way is:
Define a class of configuration only for tests, along with MyApplicationTest.java, at the same level. This class can be empty.
Like:
#EnableAutoConfiguration
#EnableConfigurationProperties(value = {
ConnectionPoolConfig.class
})
public class MyApplicationTestConfiguration {
}
And, in the class to load the autowired configuration.
Like:
#RunWith(SpringRunner.class)
//#SpringBootTest // the first, easy way
#ContextConfiguration(classes = MyApplicationTestConfiguration.class,
initializers = ConfigFileApplicationContextInitializer.class)
public class ConnectionPoolConfigTest {
#Autowired
private ConnectionPoolConfig config;
Basically, you:
use a specific configuration to #EnableConfigurationProperties and #EnableAutoConfiguration, listing all the #ConfigurationProperties files you want to load
in the test class, you load this configuration file of tests, with an initializer class defined by Spring to load application.yml file.
And, put the values to load in test/resources/application.yml. Repetition is unavoidable. If you need load another file, use #TestProperties() with a location. Note: #TestProperties only supports .properties files.
Both way works for configuration class loading values
either from application.yml/application.properties
or from another properties file, specified by PropertySource, like #PropertySource(value = "classpath:threadpool.properties")
Important
Last notes from Spring doc, as per here
Some people use Project Lombok to add getters and setters automatically. Make sure that Lombok does not generate any particular constructor for such a type, as it is used automatically by the container to instantiate the object.
Finally, only standard Java Bean properties are considered and binding on static properties is not supported.
That means, if you have lombok.#Builder without #NoArgsConstructor nor #AllArgsConstructor, properties injection will not happen because it only sees the invisible constructor created by #Builder. So, be sure to use none, or all of these annotations!
Unit test
To avoid having to load a Spring context, we can use the Binder class, which is also used internally by Spring anyway.
// A map of my properties.
Map<String, String> properties = new HashMap<>();
properties.put("my-prefix.first-property", "foo");
properties.put("my-prefix.second-property", "bar");
// Creates a source backed by my map, you can chose another type of source as needed.
ConfigurationPropertySource source = new MapConfigurationPropertySource(properties)
// Binds my properties to a class that maps them.
Binder binder = new Binder(source);
BindResult<MyConfiguration> result = binder.bind("my-prefix", MyConfiguration.class);
// Should return true if bound successfully.
Assertions.assertTrue(result.isBound);
// Asserts configuration values.
MyConfiguration config = result.get();
Assertions.assertEquals("foo", config.getFirstProperty());
Assertions.assertEquals("bar", config.getSecondProperty());

Categories