I'm looking at code in which I'm assuming spring decides to use Jackson behind the scenes to auto convert an object to json for a #RestController
#RestController
#RequestMapping("/api")
public class ApiController {
private RoomServices roomServices;
#Autowired
public ApiController(RoomServices roomServices) {
this.roomServices = roomServices;
}
#GetMapping("/rooms")
public List<Room> getAllRooms() {
return this.roomServices.getAllRooms();
}
}
The Room class is just a plain java class with some fields, getters/setters. There is no Jackson or any other explicit serialization going on in the code. Although this does return json when checking the url. I tried looking through the spring documentation but I'm not quite sure what I'm looking for. What is the name for this process in spring / how does it work? I tried with just #Controller and it broke. Is this functionality coming from #RestController?
If you are using Spring Boot Starter Web, you can see that it's using Spring Boot Starter JSON through the compile dependencies, and Jackson is the dependency of the Start JSON library. So, you're assumption is right (Spring is using Jackson for Json convertion by default)
Spring use it's AOP mechanism to intercept the mapping methods in #Controller (you can see that #RestController is actually a #Controller with #ResponseBody), spring create a proxy object (using JDK proxy or through cglib) for the class that annotated with #Controller.
When the request flow is processing, the program who really call the mapping method will be lead to the proxy first, the proxy will invoke the real #Controller object's method and convert it's returning value to Json String using Jackson Library (if the method is annotated with #ResponseBody) and then return the Json String back to the calling program.
Related
I am trying to instrument a method in a POJO class. When I use micrometer's #Timed annotation for a method in Spring controller class, it works fine and I can see metrics in prometheus dashboard(I have micrometer-registry-prometheus configured in pom). But when I use same annotation for a method in a POJO class(in same spring application) I don't see metrics in prometheus dashboard.
Do I need some extra config to get it working for a method in POJO class?
Edit: I have named metrics as
#Timed(value="myCustomMethod_responseTime")
Controllers are instrumented automatically, you don't need #Timed on them.
Also, you need a few things to make this work:
You need to create a TimedAspect #Bean
The method you are instrumenting should be public and belong to a #Bean
I'm using a bunch of microservices in my project. These microservices all share one common endpoint that returns an object which has been parsed through XML.
Now I'd love to have some kind of default method defined within the Annotation like this:
#RestController
public #interface ComaModule {
Class<? extends Object> clazz();
#RequestMapping("/descriptor")
default ModuleDescriptor getDescriptor() {
ModuleXmlUnmarshaller moduleXmlUnmarshaller = new ModuleXmlUnmarshaller(clazz());
Optional<ModuleDescriptor> moduleDescriptorOptional = moduleXmlUnmarshaller.findModuleDescriptor();
return moduleDescriptorOptional.orElse(null);
}
}
That does not work since I am not able to have a method definition in my annotation. So the hard stuff is that I want to keep #RequestMapping("/descriptor") for this.
In fact I want some kind of aspect for every RestController I use. I read about AOP for Spring and Proxy but thought I might be able to achieve this with Annotations.
May be you can try adding annotation processor class, where you can write the code which have in your post and achieve what your goal.
I have an annotation based Spring web application controller which maps XML to an annotated plain old java object. Exemplary code as follows:
#Controller
#RequestMapping("/")
public class Endpoint {
#RequestMapping
public void send(...) {
...
}
...
The mapping is automatically done via Jaxb2RootElementHttpMessageConverter and at the moment it is possible to reference and replace content in the received XML.
My question is, is it possible -- for example via beans.xml -- to configure the Jaxb2RootElementHttpMessageConverter such that the replacement of entities is prevented?
The converter is automatically created by Spring.
If you want to overwrite a bean created by spring you just define your own bean of the same type. Additionally you can mark it #Primary. I haven't tried it with Jaxb2RootElementHttpMessageConverter, but it worked with ObjectMapper as described here :
http://docs.spring.io/spring-boot/docs/current/reference/html/howto-spring-mvc.html#howto-customize-the-jackson-objectmapper
I'm trying to code a unit test for a method defined in a controller.
The method is like this:
#RestController
#RequestMapping("/products")
public class RestProductController {
#RequestMapping(value="/{product}/skus", produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.GET)
public List<SkuSummaryVO> getSkuByProduct(#Valid #PathVariable Product product){
List<SkuSummaryVO> skusByProductVOs = skuService.getSkusByProduct(product);
return skusByProductVOs;
}
}
We use in our Configuration class the annotation #EnableSpringDataWebSupport to enable the DomainClassConverter feature. So we can use the JPA entity as a #PathVariable. So when a product id will be set in the URL, we will get the product (with a request behind the scene).
We are developing unit test without enabling the Spring App Context and using Mockito.
So we initialize the mockMvcBuilders like this:
public class RestProductControllerTest {
...
#Before
public void setUp() {
RestProductController restProductController = new RestProductController();
...
mockMvc = MockMvcBuilders.standaloneSetup(restProductController).build();
}
}
and the test method is like this:
#Test
public void testGetProductById() throws Exception {
...
String jsonResult = ...;
mockMvc.perform(get("/products/123/skus").contentType(MediaType.APPLICATION_JSON)).andExpect(status().isOk()).andExpect(content().string(jsonResult));
}
And I get a 500 for the HttpCode (the status)
And the unit tests work fine for the controller methods witch are not using the DomainClassConverter feature (for example if I use a Long productId instead of a Product product as a parameter of getSkuByProduct, it will work)
UPDATE: on second thought, what I originally proposed below could never work since DomainClassConverter requires that your Spring Data Repositories be present in the ApplicationContext, and in your example you are using StandaloneMockMvcBuilder which will never create an ApplicationContext that contains your Spring Data Repositories.
The way I see it, you have two options.
Convert your test to an integration test using a real ApplicationContext (loaded via #ContextConfiguration as demonstrated in the reference manual) and pass that to MockMvcBuilders.webAppContextSetup(WebApplicationContext). If the configured ApplicationContext includes your Spring Data web configuration, you should be good to go.
Forgo the use of DomainClassConverter in your unit test, and instead set a custom HandlerMethodArgumentResolver (e.g., a stub or a mock) via StandaloneMockMvcBuilder.setCustomArgumentResolvers(HandlerMethodArgumentResolver...). Your custom resolver could then return whatever Product instance you desire.
You'll have to register an instance of the DomainClassConverter with the ConversionService in the StandaloneMockMvcBuilder that is created when you invoke MockMvcBuilders.standaloneSetup(Object...).
In SpringDataWebConfiguration.registerDomainClassConverterFor(), you can see that the DomainClassConverter is instantiated (and indirectly registered) like this:
DomainClassConverter<FormattingConversionService> converter =
new DomainClassConverter<FormattingConversionService>(conversionService);
converter.setApplicationContext(context);
And you can set your own FormattingConversionService via StandaloneMockMvcBuilder.setConversionService(). See WebMvcConfigurationSupport.mvcConversionService() for an example of how to configure the ConversionService for a web environment.
The challenge then is how to obtain a reference to the ApplicationContext. Internally, StandaloneMockMvcBuilder uses a StubWebApplicationContext, but as far as I can see (prior to Spring 4.1) there is no way to access it directly without subclassing StandaloneMockMvcBuilder.
As of Spring Framework 4.1, you could implement a custom MockMvcConfigurer (which gives you access to the WebApplicationContext via its beforeMockMvcCreated() method.
So hopefully that's enough information to get you on the right track!
Good luck...
Sam
You can mock the string to entity conversion through WebConversionService. Check this answer for some code example and more details.
If I use spring configuration to construct an object, is it possible for me to instantiate that object for clients without requiring them to import my spring configuration?
If this can't be done, does one client using spring always necessitate any of its clients use spring?
If this can be done, what is the correct way to do it? Something like this...
Library code:
public MyFactory() {
#Autowired
InitializedObject obj
public getInstance() {
return obj;
}
}
Client code:
import com.code.package.something.myfactory.MyFactory;
...
InitializedObject obj = MyFactory.getInstance();
One of the option is to avoid Spring annotations altogether, working only with spring configuration files to have constructor or setter based injection. That way your classes become independent of Spring code and your client can use your code as long as he provide the dependencies correctly using factory or whatever.
Autowiring dependency is done by the Spring container. If there's none, then it won't get autowired. You need to either ensure the client has a Spring container configured properly to autowire your dependency, or setup one yourself (eg: using ClassPathXmlApplicationContext)