Getting a SpringBoot property in a non-Spring class - java

I've got a number of properties defined in my application.properties file. These are loaded into a number of different configuration files across the system via #Configuration, #PropertySource, and #ConfigurationProperties annotations.
In addition, I have a library, separate from this system, that has no dependence on Spring (and ideally will stay that way). At some point in the execution of the system, it initializes an instance of a class from this library via reflection and a no args constructor. However, in that initialization, I want to get a Spring property and assign it to a local field - however, as I mentioned before, this library class is not spring configured, and is in fact in an entirely different project. How can this be done?
My current solution is that when the property is initialized in the config class, the setter for the property also sets a system property (via System.setProperty("someProp", propValue)), and then in the no args constructor of the library class I call System.getProperty("someProp"). However, this feels really hacky, particularly the part where I set the variable. Perhaps there is some way to configure SpringBoot to automatically propagate that particular property up to become a System property as well?
My code atm
ServiceConfig.class
#Configuration
#PropertySource("classpath:application.properties")
#ConfigurationProperties(prefix = "service")
public class ServiceConfig {
private String serviceUrl;
public String getServiceUrl() {
return serviceUrl;
}
public void setServiceUrl(String serviceUrl) {
this.serviceUrl = serviceUrl;
System.setProperty("SERVICE_URL", serviceUrl);
}
}
My application.properties
service.serviceUrl=http://localhost:8000
LibraryClass.class
public class LibraryClass {
private final String serviceUrl;
public LibraryClass() {
this.serviceUrl = getProperty("OAUTH_SERVICE_URL");
}
...
}

if your LibraryClass has a setter method - in the example below named setServiceUrl(...) - to set this configuration property you could add this to your existing ServiceConfig configuration class:
#Bean
public LibraryClass getLibraryClass(#Value(${"OAUTH_SERVICE_URL"}) String serviceUrl) {
LibraryClass libraryClass = new LibraryClass();
libraryClass.setServiceUrl(serviceUrl);
return libraryClass;
}
Other than that - if you cannot modify LibraryClass because it's parts of a 3rd party library or so... you could use Spring's Environment instance, to read all needed properties that - later - will be accessed inside of the constructor of LibraryClass and set them just the way you did as System properties. Also add this to your config class:
#Autowired
public void setSystemPropsNeededForLibraryClassConstruction(Environment environment) {
System.setProperty("serviceUrl", environent.getProperty("serviceUrl"));
}

Related

Is it OK to inject value directly inside Bean function?

I was wondering how to do dependency injection in the most effective way inside my code.
I have this code:
#Configuration
public class SomeName {
#Autowired
private Other other;
#Bean
public void method() {
other.someMethod();
// some code
}
}
Can this code be changed into the following code(other will be used only inside this function)?
#Configuration
public class SomeName {
#Bean
public void method(Other other) {
other.someMethod();
// some code
}
}
You should avoid #Autowired if possible and inject using a constructor or method.
Starting with Java 9 and java modules (project jigsaw) there are some strict rules that make it harder for your framework to change the values of a private field.
What Spring is doing in the first example is essentially that - it breaks encapsulation to change the value of a private value. (There is a way to overcome this with "opens" directive in module-info..)
You are also becoming dependent on the framework you are using and your code becomes harder to test compared to when using a simple setter.
You are also not explicitly declaring that your class depends on another class since I can easily instantiate it and "Other" will be null.
Some resources:
https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-scanning-autodetection (search for jigsaw)
https://blog.marcnuri.com/field-injection-is-not-recommended/
PS: You are probably missing #Configuration on your class

What´s a good practice for accessing external configurations in Spring Boot?

I´m setting up a Spring Boot application where certain configurations are being read from my application.yaml-file. I´ve done this a few times before and it works well, but I wondered whether there is a better way to access this configuration during runtime or whether I´m creating possible issues by not following some best practice.
Right now the class that extracts the configuration is simply defined as a Component like this:
#Component
#EnableConfigurationProperties
#ConfigurationProperties("myPrefix")
public class MyExternalConfiguration{
private HashMap<String, Boolean> entries= new HashMap<String, Boolean>();
public Boolean getConfigurationForKey(String key) {
return this.entries.get(key);
}
}
And then autowired to several other classes that need to access this configuration like this:
#Component
public class MyClass{
#Autowired
private MyExternalConfiguration myExternalConfiguration;
public void doSomething(){
//...
Boolean someEntry = myExternalConfiguration.getConfigurationForKey(someKey);
}
}
Now, this does work just fine. It´s just that I have seen examples of where configurations like this are handled as a singleton for example (although not in a Spring-Boot environment). I would just like to ask, whether there is some commonly accepted way to access external configurations or whether you see an issue with the way i access it in my project.
Thank you in advance!
There is a whole chapter about configuration in the Spring Boot Reference Manual:
https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-external-config
Simply said there are two options to access configuration:
With the Value annotation:
#Value("${name}")
private String name;
Or typesafe with a configuration class:
#ConfigurationProperties(prefix="my")
public class Config {
private List<String> servers = new ArrayList<String>();
public List<String> getServers() {
return this.servers;
}
}
So there is no need to read the configuration file by your own.

Spring jUnit Testing properties file

I have a jUnit Test that has its own properties file(application-test.properties) and its spring config file(application-core-test.xml).
One of the method uses an object instantiated by spring config and that is a spring component. One of the members in the classes derives its value from application.properties which is our main properties file. While accessing this value through jUnit it is always null. I even tried changing the properties file to point to the actual properties file, but that doesnt seem to work.
Here is how I am accessing the properties file object
#Component
#PropertySource("classpath:application.properties")
public abstract class A {
#Value("${test.value}")
public String value;
public A(){
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
}
public A(String text) {
this();
// do something with text and value.. here is where I run into NPE
}
}
public class B extends A {
//addtnl code
private B() {
}
private B(String text) {
super(text)
}
}
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations={"classpath:META-INF/spring/application-core-test.xml",
"classpath:META-INF/spring/application-schedule-test.xml"})
#PropertySource("classpath:application-test.properties")
public class TestD {
#Value("${value.works}")
public String valueWorks;
#Test
public void testBlah() {
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
B b= new B("blah");
//...addtnl code
}
}
Firstly, application.properties in the #PropertySource should read application-test.properties if that's what the file is named (matching these things up matters):
#PropertySource("classpath:application-test.properties ")
That file should be under your /src/test/resources classpath (at the root).
I don't understand why you'd specify a dependency hard coded to a file called application-test.properties. Is that component only to be used in the test environment?
The normal thing to do is to have property files with the same name on different classpaths. You load one or the other depending on whether you are running your tests or not.
In a typically laid out application, you'd have:
src/test/resources/application.properties
and
src/main/resources/application.properties
And then inject it like this:
#PropertySource("classpath:application.properties")
The even better thing to do would be to expose that property file as a bean in your spring context and then inject that bean into any component that needs it. This way your code is not littered with references to application.properties and you can use anything you want as a source of properties. Here's an example: how to read properties file in spring project?
As for the testing, you should use from Spring 4.1 which will overwrite the properties defined in other places:
#TestPropertySource("classpath:application-test.properties")
Test property sources have higher precedence than those loaded from the operating system's environment or Java system properties as well as property sources added by the application like #PropertySource
I faced the same issue, spent too much calories searching for the right fix until I decided to settle down with file reading:
Properties configProps = new Properties();
InputStream iStream = new ClassPathResource("myapp-test.properties").getInputStream();
InputStream iStream = getConfigFile();
configProps.load(iStream);
If you are using a jar file inside a docker container, and the resource properties file, say application.properties is packaged within the same classes directory that contains the java(this is what IntelliJ IDE does automatically for resources file stored in /src/main/resources), this is what helped me:
public static Properties props = new Properties();
static {
try {
props.load(
Thread
.currentThread()
.getContextClassLoader()
.getResourceAsStream("application.properties")
);
} catch (Exception e) {
e.printStackTrace();
}
}
Most other methods either only worked inside the IDE or inside the Docker. This one works in both.
if you want to load a few properties, I found a good way in the spring
ReflectionTestUtils.
#Before
Public void setup(){
ReflectionTestUtils.setField(youclassobject, "value", "yourValue")
}
>you can follow this link as well for more details https://roytuts.com/mock-an- autowired-value-field-in-spring-with-junit-mockito/

How can I get my config file from class to class?

Just a quick and simple question. I have a program with several classes that read information off of a .properties file. Is it better practice to pass the file from class to class as an argument in the constructor, or open the file directly in each class?
If you're going to do this by hand, I would recommend you create a configuration class, that takes the file via the constructor, and reads the property values into member variables. Then every other class that needs configuration takes a Configuration class via it's constructor. However, almost no one does this, and instead uses a framework like spring, which handles property injection for you.
In spring, it would look something like this:
<!-- application context xml file -->
<context:property-placeholder location="file:///some/path/to/file" />
Then in your java classes:
public class SomeClass {
#Value("${some.property}")
private String someProp;
#Value("${some.other.prop}")
private Integer someOtherProp;
// ...
}
At application startup the properties get injected into your class.
My suggestion is to have a Util class which loads the properties file and get values from that Util to the required classes.
Note: I dont think you have any issues on loading the properties file.
I would suggest that you create an immutable class that takes in the file as a constructor argument and sets all the instance variables. I'd call it PropertyConfiguration. Then since the class is immutable, you won't have to worry about passing it to everyone. You could even have a class that holds it.
For example, the code below would set you up to have a nice set up to have several things available project wide. I would just ensure that anything that's shared be immutable to ensure thread safety.
public class ClientUtils {
private static ClientContext _clientContext = null;
public static void setClientContext(ClientContext cc) {
_clientContext = cc;
}
public static ClientContext getContext() {
return _clientContext;
}
}
public class ClientContext {
private final Configuration _configuration;
public ClientContext(Configuration config){
_configuration = config;
}
public Configuration getClientContext() {
return _configuration;
}
}
If your program contains data which need not be a part of compilation and can vary from deployment to deployment, you must add it to the properties file : ( Things like database connection string, email addresses ).
Just in case you need this, I'm adding the code for accessing the properties files.
Drop the file in build directory.
Properties properties = new Properties();
properties.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("credentials.properties"));

properties at application level

I am currently having a properties file and I am loading this file in each class where there is a need to get the properties
static PropertiesConfiguration config = null;
config = new PropertiesConfiguration("Interface.properties");
This is working fine. But I know this is not the efficient way to load properties file multiple times.
Can anyone help me how to create the properties file at application level and create a java file (say config.java) + calling the method which returns the property.
One way to solve this is to utilize the dependency injection. You can have a singleton bean that holds the property instance and then you inject this bean as a dependency to each of your class that needs to access those properties.
One way to implement dependency injection to use the Spring framework. For instance, you can achieve loading the property file using Spring's PropertyPlaceHolderConfigurer. Then this bean becomes your reference bean for rest of the application. A tutorial on this can be found here.
Another choice for you is to load the property file in the constructor of the main entry class or the main method of your application and then pass the object to the classes that needs it. However, this will make your application more tightly coupled and maintenance would be harder in the future.
Another option is to create a singleton class that loads the properties and have a method that returns the values as needed.
I used this approach to set the properties at application level.
Define a properties file (say configure.properties).
Create a java class Config:
public class Config {
private static Config instance;
private PropertiesConfiguration configure;
private Config() throws ConfigurationException {
configure = new PropertiesConfiguration("configure.properties");
}
public static Config getInstance() {
if (null == instance) {
try {
instance = new Config();
} catch (ConfigurationException ex) {
throw new RuntimeException(ex);
}
}
return instance;
}
public PropertiesConfiguration getConfigure() {
return configure;
}
public void setConfig(PropertiesConfiguration configure) {
this.configure = configure;
}
}
This java class load the properties file at the startup and calls the getInstance method to get the value of the property. To get the value of the property anywhere else in the application import Config and
Config.getInstance().getConfig().getString("property.given.in.properties");
Sorry if variable names doesn't make much sense.
Thanks all for your input.
I just load my properties file in the System Properties:
public void loadStreamToSysProperties(InputStream in) throws IOException
{
Properties p = System.getProperties();
p.load(in);
}
Then I can get them out of System.getProperty where ever I am in the code.
In this example the InputStream is a Stream I created from the file name/path.

Categories