How do constructor calls with the #Value annotation work? - java

Hi I'm new to Java and Spring, and currently have a situation that is similar to this:
In Message.java
public class Message {
final String text;
public Message(#Value("${message.text}") String text) {
this.text = text;
}
}
In application.properties:
message.text = "This is some text."
Now, I have a test file where I want to write a test that checks the value of the String text, akin to something like
assertEquals(text, "This is some text.");
How would I go about doing this? It seems I can't manually invoke the constructor by doing new Message("...") because that overrides the #Value injection.

The #Value annotation here is an instruction to the Spring container, so if you're wanting to test that it operates as expected, the only way to do that is to use SpringRunner or equivalent to actually start a context and feed it the value in question. Typically, this annotation isn't directly explicitly tested; rather, when the bean is covered as part of a medium-scale test like #SpringBootTest, its value will be set somewhere and is expected to be used in some business manner, the effect of which is tested.

Related

How to extract the url from application.properties?

I have this application.properties:
url.up=${url:http://localhost:8080/upload}
And I want to extract the url "http://localhost:8080/upload". How can I extract it?
I tried something like this, but the url is null:
String url = config.getPropertyValue("url");
Where getPropertyValue:
public String getPropertyValue(String propertyKey) {
return properties.getProperty(propertyKey);
}
U can use #Value in your class
in your property file U can define
url.up=http://localhost:8080/upload
In Your class
#Value("${url.up}")
private String url;
then U can access the value using variable url
Unfortunately I do not know what class the config object is instatiated from which makes it hard to understand what the properties.getProperty() method call is doing. Therefore, I'll give you a a more general answer. As you are using Spring, you basically have two very elegant solutions to retrieve data from your application property files. If you just need a single value (as in your example the url.up field), then you would typically directly inject that value into the class that needs this data as in the following short Kotlin snippet (in java this would be very similar, just look up the #Value annotation on the internet).
#Component
class PropertyPrinter(
#Value("\${url.up}") private val url: String
) {
#PostConstruct
fun postConstruct() {
println("URL is: $url")
}
}
The other way would be to create a dedicated config class that hold a bunch logically connected fields and add the #ConfigurationProperties annotation. See here for a more in depth explanation.
You should use #Value annotation. For example:
#Value("${url}")
private String url;
The url variable holds http://localhost:8080/upload.
You should use #Value or appContext:
https://stackoverflow.com/a/29744955/21233858

How to pass parameter to a #Component class which can't be autowired in Spring Boot

I am building a desktop application using JavaFx and Spring Boot and I am stuck at some problem
I have #Component class Translator which extends Task<String> and makes an api call to google translation api and I want to pass an autowired bean class and some string parameters such as source and target language to the Translator constructor.
#Component
class Translator extends Task<String>{
private TextPreprocessor textPreprocessor;
private Stirng strUrl;
private String from;
private String to;
private String text;
public Translator(#Value("${translator.api.url}" strUrl,TextPreprocessor textPreprocessor)){
this.strUrl=strUrl;
this.textPreprocessor=textPreprocessor;
}
protected String call() throws Exception{
//some code
}
}
#Component
class TextPreprocessor{
//some code
}
So my problem is I need to pass parameters from,to and text to Translator constructor but I can't do that here because these varibles can't be autowired. How can I fix this?
According to your requirements the class Translator should not belong to Spring-context and it should not be a spring-bean. Instead it seems that this class should be instantiated manually by you in the code each time you need it so that you can pass dynamically the fields from, to, text.
In the class where you will manually instantiate Translator instances, you could have autowired strUrl and textPreprocessor as fields, so that they are always available for you to pass in the new instance of Translator that you will create.
There is 1 case where I would see it as a spring-bean and this would be if you moved from, to, text from been fields of this class into been parameters of method call. This way you could have Translator as a singleton retrieved from Spring-context and you could use dynamic values for from, to, text any time you would like to invoke call method.
The whole picture of your question seems like a design problem.

Get Spring #Value from groovy script

Inside Java code:
#Value("${myVar}") private String myVar;
Inside Groovy code:
import org.springframework.beans.factory.annotation.Value;
import groovy.transform.Field;
#Field #Value('${myVar}') String myVar;
return myVar;
returns null.
How to get myVar from Groovy script, assuming the Beans and myVar are not passed in?
If I understand correctly, you want Spring context to be available during your Groovy script execution and perform the usual injection on your script properties.
Groovy scripts are compiled to Groovy classes, and so you should be able to simply use beanFactory.autowireBean(this). However, to obtain a reference to a BeanFactory in the first place, you need to manually start a Spring context like so:
new ClassPathXmlApplicationContext('path/to/applicationContext.xml').withCloseable { context ->
context.autowireCapableBeanFactory.autowireBean(this)
... //the rest of your script goes here
}
(You might need to adjust the new ClassPathXmlApplicationContext("path/to/applicationContext.xml") part to suit your needs, depending on what method you prefer to use to bootstrap your Spring context; also, if the script is part of a larger codebase, you might want to craft a smaller application context than for the rest of the app, to save startup time and avoid the usual side effects. Depends on the use case, though).
The only part I'm unsure of is whether #Field retains the original annotations when transforming a variable to a field, but if you're not getting any errors, I would assume that to be the case.
In case annotations are not retained, you can always declare a simple annotated POJO bean acting as a container for the injectable properties and call def myPojo = new Pojo(); context.getBeanFactory()).autowireBean(myPojo) instead.
You need to make sure the groovy script is managed by spring, you can either add the #Service or #Component annotations on the class. see example below
#Service
class Demo {
public #Value('${valueA}') String valueA
String showValue() {
return valueA
}
}

How do I use a variable from an application.yml file in my normal code?

For example, let's say that in my yml file I had a variable called indicator. And based on what the indicator variable's value was I want the code to do something different. How would I access the yml variable in the regular code and use it accordingly?
You can use this:
#Value("${your.path.yml.string}")
private String x;
YML:
your:
path:
yml:
string: hello
x will be "hello"
You need to use Spring Expression Language which says we should write it as
#Value("${spring.application.name}")
private String appName;
For Default value if key is not present in yaml/yml or properties file
#Value("${spring.application.name: defaultValue}")
private String appName;
The last way you can fetch value is using environment object
#Autowired
private Environment environment;
String appName = environment.get("spring.application.name");
You can add #Value annotation to any field in your beans.
#Value("$(path.to.your.variable)")
String myString;
Works with constructors as well.
public MyClass(#Value("$(path.to.your.variable)") String myString) {
You can use #Value on fields or parameters to assign the property to some variable.
Property example:
#Value("${indicator}")
private String indicator
Parameter example:
private void someMethod(#Value("${indicator}") String indicator) {
...
}
Then you can use indicator as you want.
Note: the class where you use #Value should be a Spring Component
With Spring-Boot, you have the file application.yml automatically provided for you. What you can do is adding a property in this file, for instance:
my.properties: someValue
Then, in one of your Spring Bean (either define with #Component or #Bean) you can retrieve this value using the annotation #Value. Then, do whatever you want with this variable.
For instance:
#Component
public class MyClass {
#Value("${my.properties"}
private String myProp; // will get "someValue" injected.
...
// Just use it in a method
public boolean myMethod() {
if(myProp.equals("someValue") {
// do something
} else {
// do something else
}
}
}
The best way to do this is not to have a tight coupling between Spring and your "normal" code at all, but instead to use the normal Java features like constructors along with Spring #Bean methods:
class MyService {
final String indicatorName;
MyService(String indicatorName) {
this.indicatorName = indicatorName;
}
}
... in your configuration class...
#Bean
MyService myService(#Value("indicator.name") String indicatorName) {
return new MyService(indicatorName);
}
Two notes for Spring Boot specifically:
The #ConfigurationProperties feature allows you to map properties onto structured Java data classes and is typically cleaner than using #Value by hand.
Always namespace properties that you define yourself to avoid future collisions with other libraries, so instead of indicator.name use company.project.indicator.name. I recommend looking at DataSourceProperties in Boot to see an example of how to set all this up.
More broadly, though, when you say that you want the code to "do something different", it sounds like the better option might be to have different classes that get activated under different circumstances. Both Spring profiles and Spring Boot auto-configuration help to do this.
The problem statement can be re-defined as Configuration Management in Java.
You should have a component like ConfigManager that gets instantiated as part of your application start up. That component will read a properties file, a yaml in your use case. Subsequent app logic will fetch these values from the ConfigManager exposed as simple key/value pairs.
All that is left for you to identify how to read and parse values from yaml file. This is already answered here:
Parse a YAML file

Internal working of field injection in spring and why is it not recommended to use

As the title suggests , I want to know how does field injection internally works in spring , I read many articles on this and got to know few things like below but didn't understood the exact reason behind it :
-> It should not be used because when you do unit testing then you are dependent upon the spring
container to instantiate the class in case of field injection.
-> You cannot use "final" keyword in case of field injection , means you cannot make the field immutable.
-> It internally uses reflection
I want to know how exactly does #Autowired works internally , how does it uses reflection , I am trying to understand the exact reason behind all the above mentioned points, what happens behind the scenes when we write the below code :
#Component
public class B {
#Autowired
private A a1;
}
I have read similar questions on stack overflow about this topic , but I couldn't find the exact explanation that I am looking.
Spring has a concept of Bean Post Processors.
When spring builds a bean it applies registered bean post processors that help to "initialize" the bean.
So, there is org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor that handles autowiring.
Basically it works with an newly created object. Spring introspects the fields of the beans (by using reflection). The fields that have #Autowired is a subject for processing with this bean post processor. It finds the candidate for injection in the application context and actually injects the value.
Now given this information, its understandable why final fields cannot be autowired. Leave alone spring, In pure Java, final fields must be instantiated directly right during the declaration (final int i = 123) or in the constructor of the class. But the autowiring happens after constructor, so its impossible to autowire the final fields.
As for the unit testing, the private properties must be somehow configured from the test. But since they're encapsulated (yes, spring kind of breaks encapsulation in this case for its usage), its impossible to write a good test for the class that contains fields injection. That's is a reason to switch to constructor injection.
public class FieldInjection {
#Autowired
private A a;
}
VS.
public class ConstructorInjection {
private final A a;
// this can be generated by lombok, you don't have to put #Autowired on constructor in the case of single constructor, spring will use it to create a bean
public ConstructorInjection(A a) {
this.a = a;
}
}
Now the test for FieldInjection class is impossible:
public class FieldInjectionTest {
#Test
void test() {
FieldInjection underTest = new FieldInjection();
how do you know that you should instantiate A a. ????
}
}
However in the case of constructor injection its a trivial task:
public class ConstructorInjectionTest {
#Test
void test() {
A a = mock(A.class);
ConstructorInjection underTest = new ConstructorInjection(a);
// the dependencies must be supplied in the constructor
// otherwise its impossible to create an object under test
}
}

Categories

Resources