#Value application.properties come with a null URL - java

I'm having a pretty annoying problem with application.properties.
I have a Java 11 project with Spring boot working perfectly, I can get all values ​​normally using :
#Value("${example}")
Except when I try to get this URL coming from properties. This url is passed and called in a RestTemplate, and it always comes null.
#Value("${url}")
private String url
#Autowired
private RestTemplate restTemplate;
restTemplate.postForObject(url,new HttpEntity<>(getBody(),getHeaders()), DTO.class);
By hovering the mouse over the (${"url"}, the http appears: saying it is capturing, but when it reaches the part of the code that goes through #RestTemplate, the value is null.
I put up a log.info(url) and on Springboot's climb it shows the correct value with the url, but when it goes through the code line: the value comes to null
If I go directly to the class:
private static String URL = "http:example.com" , the code runs fine.
My application.properties in folder of project/src/main/resources
server.port=8088
url=http://example.com
spring.rabbitmq.port=5672
spring.rabbitmq.username=user
spring.rabbitmq.password=password
spring.rabbitmq.host=rabbitm.example.com
spring.rabbitmq.template.exchange=test-sender
spring.rabbitmq.template.retry.enabled=true
spring.rabbitmq.template.retry.multiplier=2
My code:
package com.test;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import javax.annotation.PostConstruct;
#Data
#AllArgsConstructor
#NoArgsConstructor
#Service
public class TestService {
#Value("${url}")
private String url; //coming from properties is always null
//private static String URL1 = "http.com.example"; //
// this way works as static
#Autowired
private RestTemplate restTemplate;
#PostConstruct
public void print() {
System.out.println(url);
}
public TestResponseServiceDTO getTest(){
log.info(url) // here also shows a correct url on the console
System.out.println(url) // here also shows a correct url on the console
// Even at this point in my code, the url comes from the line below and it comes null
TestResponseServiceDTO response=restTemplate.postForObject(url, // Here the URL appears null
new HttpEntity<>(getbody(),getHeaders()),TestResponseServiceDTO.class);
return response;
}
}
How can I get around this?

You've declared the property in question as following, which is incorrect
#Value(${"url"})
private String url;
Try changing it to
#Value("${url}")
private String url;
Now if you've mentioned the url in the properties file and the above class is a bean managed by spring (annoated by component or service) then url won't be null unless you set it to null again later in the code prior using it in the rest template call.
Another case: Also, make sure that you should invoke the getTest() method from spring managed bean. So, you shouldn't be doing something like this.
TestService testService = new TestService();
testService.getTest();

Couldn't find any issue in the code you have provided.
First thing we have to understand is if the value was not resolved when application startup, application won't even start. Plus even you have mentioned that when you hover over ("${url}") value is captured!
I can see you have print the value of url to console in your PostConstruct method. First thing what you can do check that.
Only reason I can think of is, value of url is changed at run time by mistake or on purpose. You can avoid this from happening by removing #Data annotation from your TestService class. I don't see an reason why would you need #Data, #AllArgsConstructor, #NoArgsConstructor annotations in the first place. Specially #Data, because it can introduce unnecessary mutability to your TestService class. You can use #Getter if you need to access private variables.

Related

#Value not Injecting ANY value

I'm learning how to create a RESTful API with Springboot, and the #Value tag is not injecting values from application.properties, nor is it injecting values specified in the tag itself. I have my project structured as such:
api
config
controllers
model
services
SpringApplication.java
resources
application.properties
Strangely, this behavior only seems to occur within files located in my "services" folder. The #Value tag works as expected in files located in my "controllers" folder. Below are examples of what I am doing:
#Value("${var}")
String variable
"var" is defined in application.properties as var=some_stringbut variable is still initialized as 'null'
#Value("I am directly assigning a value to this variable, but it still comes out null")
String variable
I believe I am using the correct import: import org.springframework.beans.factory.annotation.Value.
At first I just thought the "services" folder was blind to the directory where application.properties is located, but after trying to directly inject values, I'm not so sure what to think.
Edit
All of the classes in the services folder are annotated with #Service and nothing else. Below is what the class looks like. I've opted to leave out implementations of the methods, other variables, and irrelevant imports. The code/methods all work as expected when hard-coding the variables. My focus is the #Value tag.
package myapi.api.services;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
#Service
public class MyService {
#Value("${var}")
String variable;
public List<Data> getData() {
return new ArrayList<Data>();
}
public void postData() {
}
Edit 2
Below is the APIController class, stored in the "controllers" folder. Again, I've opted to leave out irrelevant methods/imports/variables. I would also like to note, that the #Value tag works as expected in this class.
package myapi.api.controllers;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import myapi.api.services.MyService;
import lombok.RequiredArgsConstructor;
#RestController
#RequestMapping("api")
#RequiredArgsConstructor
public class APIController {
#Autowired
private final static MyService myService = new MyService();
#GetMapping("/getdata")
public List<Data> getData() {
return myService.getData();
}
}
#Autowired
private final static MyService myService = new MyService();
Three issues:
First, you cannot have a new operator. Spring controls the lifecycle of your class instances. It will call new for you in the background. Remove the entire new operator stanza.
Next: your your field cannot be final. After construction of the class, Spring will need to modify that field with a proxy. Remove the final declaration;
Finally: your field cannot be static. static variables have a certain lifecycle with the JVM, and you need to let the Spring framework manage your lifecycle. remove the static operator.
The correct declaration should be:
#Autowired
private MyService myService;

SpringBoot #Autowire why does this example work?

I have created this class:
import org.springframework.stereotype.Component;
...
#Component("notTheNameTestMe") //shouldnt this only work with testMe ?
public class TestMe {
public void showMsg() {
System.out.println("Autowiring works");
}
}
And I'm using it this way in a second class (or better: controller):
import com.example.TestMe; //shouldnt this be not necessary with autowire? But getting error else...
...
#Autowired
private TestMe testMe;
...
this.testMe.showMsg();
But this works perfectly (so maybe Im not really using autowire here?), it even works if I rename the whole TestMe class to TestMeSomething (if I adjust the import in the second class)
I dont really understand what #Autowired does. I thought it just scans for SpringBoot Components (which are named by the string in #Component() and when it finds a match it Injects the dependancy. But in my example the match is impossible and I still can see the message "Autowiring works" in the console. This shouldnt be like this if I would really use autowire here or? What am I understanding in a wrong way? What is the difference to using new TestMe() then? I have the dependancy already with the import or? So not a real dependancy injection, or?
Spring is not operating on the name in the #Component annotation. Rather it's using the name of the class. It's simply finding a class named TestMe because that's the type of the variable you've annotated with #Autowired.

Running Tests with MockitoJUnitRunner fails verify asserts

Is there a way to mock a Repository without the #RunWith(MockitoJUnitRunner) annotation on the class?
I have a test that passed without the annotation but fails with it. Without it, my repo test doesn't work. It's a catch 22.
When I use that annotation, my when() methods in my tests no longer stub behavior, mocks do nothing, and despite setting break ppoints and those breakpoints being hit (indicating the line/method is run), verify(..., times(x)) statements say the mocked object never interacted with that method. I've been pulling my hair out on why using the #RunWith(MockitoJUnitRunner) annotation would make the most simple parts of Mockito not work.
I can't find any threads asking about this but maybe someone knows better keywords to use. Does this sound like a known issue?
Here is my test:
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
import static org.mockito.MockitoAnnotations.initMocks;
// toggling this below annotation is the source of grief.
//#RunWith(MockitoJUnitRunner.class)
public class LoadEditEntityChangeLogServiceImplTest {
#InjectMocks
private ServiceImpl serviceMock;
#Mock
private EditStepRepository editStepRepository;
#Mock
private EditMapper editMapper;
#Before
public void init() {
initMocks(this);
}
#Test // when the RunWith is commented out, this passes. When it is not, the test fails the verify assert.
public void mapEditEntityFromAction_Test() {
EditDTO editDTO = Mockito.mock(EditDTO.class);
when(editDTO.getSysNum()).thenReturn((long)7334);
EditEntity editEntity = new editEntity();
editEntity.setSysNum(editDTO.getSysNum());
when(editMapper.mapToEntity(eq(editDTO))).thenReturn(editEntity);
editEntity response = serviceMock.mapEditEntityFromAction(editDTO);
verify(loadEditMapper, times(1)).mapToEntity(eq(loadEventDTO));
assertEquals(loadEventDTO.getSystemNumber(), response.getSystemNumber());
}
#Test // this will fail without the #RunWith as the mocked repo will be null and throws NullPointerException when used.
public void updateConvertedEventSegment_Test() {
EditEntity editEntity = new EditEntity();
EditStepEntity editStepEntity = new EditStepEntity();
editEntity.setEditStep(editStepEntity);
doReturn(editStepEntity).when(editStepRepository).save(any());
serviceMock.updateEditStep(editEntity);
verify(editEntity, times(1)).getEditStep();
verify(editStepRepository, times(1)).save(eq(editStepEntity));
}
}
You should understand what does this runner actually do:
Basically it allows injecting mocks (prepared by mockito with Mockito.mock(...) ) into the test fields annotated with #Mock. In the question, since you've commented out the runner, all these fields will be null.
When you annotated something with #InjectMocks - it will inject the mocks into the fields of the object of type of the annotated reference.
One more point to clarify here: MockitoAnnotations.initMocks(this) will do the same as the "runner" so no need to include both (you should use initMocks if you can't use the runner for some reason, like if there is already another runner that must be used)
Now, you ask:
Is there a way to mock a Repository without the #RunWith(MockitoJUnitRunner) annotation on the class?
The answer is - yes, you can, in fact you don't have to use the runner, sometimes its more convenient.
So, assuming you really use this runner, the real question is what exactly do you mean by "my repository doesn't work". Does this mean that there exists a reference in the service that points of this repository and its null?
Does it mean that there is a mock of repository but when you execute the call "under the test" the mock is different?
You don't show it in the code, but I assume you have some like this:
public class ServiceImpl {
private final EditStepRepository editStepRepository;
public ServiceImpl(EditStepRepository editStepRepository) {
this.editStepRepository = editStepRepository;
}
...
}
But if so, once you create a mock (and indeed there should be a mock injected into the ServiceImpl class (check this out with debugger or something), There should be expectatations specified on the repository, usually there should be code like this in the test:
Mockito.when(editStepRepository.doSomething(...)).thenReturn(...)
You haven't placed any of these lines, that why it doesn't work.
But all-in-all since the question contains many uncertain technicalities like this, I can't tell more than that other that speculating...

Why Doesn't Setting Jackson to Accept Empty Lists as Null Work?

My company has a Java web service using Spring Web that accepts JSON via a REST API. We're using Maven and our Jackson version is 2.9. We're trying to prevent deserialization exceptions from being thrown when an integrator passes in an empty list when our API isn't expecting one.
For example, here's my application class:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
#EnableWebMvc
#SpringBootApplication
public class ExampleApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(ExampleApplication.class, args);
}
}
Student.java class:
import lombok.Data;
import java.util.Map;
#Data
public class Student {
private String firstName;
private String lastName;
private Map<String, String> classGrades;
}
A StudentController:
package com.example.example.controllers;
import com.example.example.models.Student;
import org.springframework.http.RequestEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.stream.Stream;
#RestController()
#RequestMapping(value = "/Students/", produces = "application/json")
public class StudentController {
#PostMapping(path = "")
public RequestEntity<Student> createNewStudent(#RequestBody Student student) {
return null;
}
}
The application.properties:
spring.jackson.deserialization.accept-empty-array-as-null-object=true
The pom.xml contains all the default dependencies, with the following added:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
</dependency>
Every other project file is default to the project structure generated by the Spring initializer. The expected request body (JSON formed using Postman):
{
"firstName": "Mark",
"lastName": "Twain",
"classGrades": {
}
}
Works just fine. However, if any field (though in our specific case, the classGrades field) receives an empty list, a Jackson deserialization exception is thrown. An example JSON request that fails:
{
"firstName": "Mark",
"lastName": "Twain",
"classGrades": []
}
And the exception that is thrown:
org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize instance of `java.util.LinkedHashMap` out of START_ARRAY token; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `java.util.LinkedHashMap` out of START_ARRAY token
at [Source: (PushbackInputStream); line: 65, column: 28]
According to the project's github page, the option ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT should resolve this issue for us. We've tried setting this directly on an ObjectMapper configuration object and within application.properties using the line:
spring.jackson.deserialization.accept-empty-array-as-null-object=true
Neither method seemed to take effect. We're currently using a workaround using the #JsonDeserialize(using = MyCustomDeserializer.class) annotaion on fields prone to this problem. However, we would like to be able to have all of our fields treat empty lists as null by default.
Are we misunderstanding the configuration option and using it incorrectly? Is there a way to treat empty lists as null within our app, and if so, how can we accomplish this?
For this example case, my problem was the #EnableWebMvc annotation within the ExampleApplication class. Removing that annotation allowed me to successfully send an empty array to my endpoint, which then received it as a null object.
Note
My original problem still exists within my company's application, even after removing the annotation. However, it seems like this may be an issue with a different setting that might be clashing with ...accept-empty-arrays-as-null-object.
A Map is serialized as
{ "k1": "v1", "k2": "v2"}
An empty Map is serialized as
{ }
In your example you try to put a [], which represents an empty list or array, into a Map and that is why Jackson complains. Simply, cannot map an empty list into a Map.
This also explains why enabling ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT has no effect
Update
I was not right. Jackson can map empty array to a Map.
Created a minimal springboot example that uses the spring.jackson.deserialization.accept-empty-array-as-null-object=true property and can map an empty array [] to the Map
Check here if you want.

How can i get spring-boot main-class package name

I want to make a spring-boot starter,so i need to get package name of application main-class.
How can i get it?
Sometimes,you can use listener for spring boot,but it must configure in application.properties.
import org.springframework.boot.autoconfigure.AutoConfigurationPackages;
import org.springframework.context.ApplicationContext;
AutoConfigurationPackages.get(context.getAutowireCapableBeanFactory())
By this method, you can get the package of you current class.
MethodHandles.lookup().lookupClass().getPackage().getName()
You could use the fact that the main class will problably be annotated with #SpringBootApplication and search for that class in the Spring Context. Implementing something like:
#Service
public class ApplicationFinder {
#Autowired private ApplicationContext context;
public String findBootClass() {
Map<String, Object> candidates = context.getBeansWithAnnotation(SpringBootApplication.class);
return candidates.isEmpty() ? null : candidates.values().toArray()[0].getClass();
}
}
Once you jave the class name you can string slice it to extract the package.

Categories

Resources