I'm having some problem with Spring MVC 4.2.6 and Jackson (2.7.3). I created an absctract #RestController class with a method that uses a generic type. I implement this abstract class passing the type of generic object. I use #RequestBody and the DispatcherServlet throws JsonMappingException (can not construct instance of my class). The MappingJackson2HttpMessageConverter in application context is configured.
Edit: More info added.
public abstract class AbsctractAnimalResource<DTO extends AnimalDTO> {
// Doesn't work. Throws JsonMappingException. (#RequestBody)
#RequestMapping(value = "/bar", method = POST, produces = APPLICATION_JSON_VALUE)
public ResponseEntity<?> bar(#RequestBody DTO dto) throws Exception {
return new ResponseEntity(dto, HttpStatus.CREATED);
}
}
#RestController
#RequestMapping("/cat")
public class CatResource extends AbsctractAnimalResource<CatDTO> {
}
#RestController
#RequestMapping("/dog")
public class DogResource extends AbsctractAnimalResource<DogDTO> {
}
interface AnimalDTO { }
public class CatDTO implements AnimalDTO { }
public class DogDTO implements AnimalDTO { }
After some debugs in Spring 4.2.6 classes I found this commentary:
https://github.com/spring-projects/spring-framework/blob/4.2.x/spring-web/src/main/java/org/springframework/http/converter/json/AbstractJackson2HttpMessageConverter.java#L51
Spring 4.2.X and Jackson 2.7.X don't work together. I downgraded my Jackson version to 2.6.6 and now everything works. Spring 4.3.X works well with Jackson 2.7+.
Related
I have two (more in the future) implementations of ImportantService – VeryImportantService and LessImportantService:
public interface ImportantService<T extends ImportantRequest> {}
#Service
public class VeryImportantService implements ImportantService<VeryImportantRequest> {}
#Service
public class LessImportantService implements ImportantService<LessImportantRequest> {}
And then I have a controller, in which I want to inject all of the implementations of ImportantService:
#RequiredArgsConstructor
#RestController
#RequestMapping("/api/important")
public class ImportantController<T extends ImportantRequest> {
private final ImportantService<T> importantService;
#PostMapping
public ResponseEntity<ImportantResponse> create(#RequestBody #Valid T request) {
// very important code here
}
}
Obviously, such king of injecting fails:
UnsatisfiedDependencyException: Error creating bean with name 'importantController' defined in file ...
...
Consider marking one of the beans as #Primary, updating the consumer to accept multiple beans, or using #Qualifier to identify the bean that should be consumed
What I want is:
Inject all of the implementations of ImportantService, and then, based on the T automatically select required bean. I know I can add method to ImportantService, which returns the type that implementation works with and then inject ImportantService as List<ImportantService> importantServices and then filter like this:
importantServices.stream()
.filter(importantService -> importantService.getType().equals(request.getClass()))
.findFirst()
.ifPresent(importantService -> importantService.doImportantJob(request));
BUT! I have hundreds of services to refactor like this and I really don't want to write additional logic to controllers.
I know about #Conditional annotation and Condition interface, but AFAIK there's no way to make them do what I want.
Why not implement the proxy pattern?
example:
#Service
#Primary
#RequiredArgsConstructor
public class ImportantServiceProxy implements ImportantService<T extends ImportantRequest> {
private final List<ImportantService> importantServices;
private ImportantService getImportantService(ImportantRequest request){
return this.importantServices.stream()
.filter(importantService -> importantService.getType().equals(request.getClass()))
.findFirst()
.get();
}
public void doImportantJob(ImportantRequest request){
this.getImportantService(request).doImportantJob(request);
}
}
Then in your controller you can call the function without check the type.
#RequiredArgsConstructor
#RestController
#RequestMapping("/api/important")
public class ImportantController<T extends ImportantRequest> {
private final ImportantService<T> importantService;
#PostMapping
public ResponseEntity<ImportantResponse> create(#RequestBody #Valid T request) {
importantService.doImportantJob(request);
}
}
what you want is a list of beans which are of type ImportantService
so you have to declare a variable like this.
final List<ImportantService> importantServices;
demoController(List<ImportantService> importantServices) {
this.importantServices = importantServices;
}
My interface:
package com.demo.dependency;
#RestController
#RequestMapping(value = "#{'${api.baseUrl}'}")
public interface BaseController<Response> {
#PostMapping(value = "#{'${api.interface}'}")
#ResponseBody
public Response process(#RequestBody #Valid Request request) throws Exception;
}
Implementation:
package com.demo.application;
public class BarController implements BaseController<BarResponse> {
#Override
public BarResponse process(Request request) throws Exception {
// do something
}
}
I'm new to Spring Boot. I wonder whether these annotations can work properly in implementation class:
#RestController and #RequestMapping on interface class
#PostMapping and #ResponseBody on interface method
#RequestBody and #Valid on method parameters
#{'${api.baseUrl}'} and #{'${api.interface}'} to read configure from application.properties
My spring boot version is 2.2.6.
It seems that my SpringApplication from package com.demo.application failed to auto scan BarController. However, this answer says that "the annotation should apply to all subclasses", including #Service. Is anything wrong with my code?
Thanks in advance.
This is my jersey config class
#ApplicationPath("services")
public class JerseyApplication extends ResourceConfig{
public JerseyApplication() {
packages("com.ems");
register(EmployeeService.class);
}
}
Here autowiring of employeeService is giving a null pointer exception
#Path("/ems")
#Component
public class EmployeeRestController {
#Autowired
private EmployeeService employeeService;
#GET
#Path("/employees")
#Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public List<Employee> getEmployees() {
return employeeService.getEmployees();
}
}
I have tried everything
In my employeeServiceImpl I have #service annotation
Still, it is not working.
To configure the dependency injection using the built in DI framework (HK2), you should use an AbstractBinder, as mentioned in some answers in Dependency injection with Jersey 2.0.
#ApplicationPath("services")
public class JerseyApplication extends ResourceConfig {
public JerseyApplication() {
packages("com.ems");
register(new AbstractBinder() {
#Override
protected void configure() {
bind(EmployeeService.class)
.to(EmployeeService.class)
.in(Singleton.class);
}
});
}
}
Secondly, you do not use the #Autowired annotation. This annotation is specifically for Spring. For standard injection with Jersey, just use the #Inject annotation. Also remove the #Component annotation, as this is also for Spring.
As an aside, if you do want to integrate Spring with Jersey, you should read Why and How to Use Spring With Jersey. It will break down what you need to understand about integrating the two frameworks.
You should register Controller not Service class.
Sample
#ApplicationPath("services")
public class JerseyApplication extends ResourceConfig{
public JerseyApplication() {
packages("com.ems");
register(EmployeeRestController.class);
}
}
I have the following unit test:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = {EqualblogApplication.class})
#WebAppConfiguration
#TestPropertySource("classpath:application-test.properties")
public class PostServiceTest {
// ...
#Test(expected = ConstraintViolationException.class)
public void testInvalidTitle() {
postService.save(new Post()); // no title
}
}
The code for save in PostService is:
public Post save(#Valid Post post) {
return postRepository.save(post);
}
The Post class is marked with #NotNull in most fields.
The problem is: no validation exception is thrown.
However, this happens only in testing. Using the application normally runs the validation and throws the exception.
Note: I would like to do it automatically (on save) and not by manually validating and then saving (since it's more realistic).
This solution works with Spring 5. It should work with Spring 4 as well. (I've tested it on Spring 5 and SpringBoot 2.0.0).
There are three things that have to be there:
in the test class, provide a bean for method validation (PostServiceTest in your example)
Like this:
#TestConfiguration
static class TestContextConfiguration {
#Bean
public MethodValidationPostProcessor bean() {
return new MethodValidationPostProcessor();
}
}
in the class that has #Valid annotations on method, you also need to annotate it with #Validated (org.springframework.validation.annotation.Validated) on the class level!
Like this:
#Validated
class PostService {
public Post save(#Valid Post post) {
return postRepository.save(post);
}
}
You have to have a Bean Validation 1.1 provider (such as Hibernate Validator 5.x) in the classpath. The actual provider will be autodetected by Spring and automatically adapted.
More details in MethodValidationPostProcessor documentation
Hope that helps
This is how I did it by loading ValidationAutoConfiguration.class into context:
#SpringBootTest
#ContextConfiguration(classes = { MyComponent.class, ValidationAutoConfiguration.class
public class MyComponentValidationTest {
#Autowired
private MyComponent myComponent;
#Test
void myValidationTest() {
String input = ...;
// static import from org.assertj.core.api.Assertions
assertThatThrownBy(() -> myComponent.myValidatedMethod(input))
.isInstanceOf(ConstraintViolationException.class)
.hasMessageContaining("my error message");
}
}
And MyComponent class:
#Component
#Validated
public class MyComponent {
public void myValidatedMethod(#Size(min = 1, max = 30) String input) {
// method body
}
)
I'm trying to create a simple REST service with JAX-RS (Jersey), without using Spring. And I'm using Joda as date fields in my entity.
To configure automatic json mapping, I create a JsonMapperProvider, where I add JodaModule:
#Provider
#Produces(MediaType.APPLICATION_JSON)
public class JsonMapperProvider implements ContextResolver<ObjectMapper> {
final ObjectMapper objectMapper;
public JsonMapperProvider() {
objectMapper = new ObjectMapper();
objectMapper.registerModule(new JodaModule());
}
#Override
public ObjectMapper getContext(Class<?> arg0) {
return objectMapper;
}
}
This is my Resource class:
#Path("users")
public class UserController {
#Inject
private UserService userService;
#GET
#Path("/{id}")
#Produces(MediaType.APPLICATION_JSON)
public User getUserById(#PathParam("id") Long id) {
return userService.findById(id);
}
}
And I'm using a "no web.xml" configuration, with this class:
#ApplicationPath("api")
public class RestApplication extends ResourceConfig {
}
But it doesn't work... the LocalDate field in User entity is always returned empty.
The only workaround I found is to register all the components (including JacksonFeature class from jersey-media-json-jackson) in the ResourceConfig class, like this:
#ApplicationPath("api")
public class RestApplication extends ResourceConfig {
public RestApplication() {
super(
UserController.class,
JsonMapperProvider.class,
JacksonFeature.class
);
}
}
Is there another solution to this problem? I'd rather not to register all my services and other stuff in this class manually...
No, this is how it's supposed to work. You can also override the methods in javax.ws.rs.core.Application instead of extending ResourceConfig