Micrometer #Timed for method in pojo not working - java

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

Related

How does Spring auto convert objects to json for #RestController

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.

How to set the attributes to a generic #Service class in Spring Boot?

I'm new to Spring Boot, so bare me with my basic question here.
I want to build a generic #Service class that has well defined methods that don't even need to be overwritten.
The only thing this class needs is to adjust its attributes based on which Controller method was called. Basically, this class works as a Job handler that needs to adjust some parameters so its methods can perform what they're supposed to compute. The job will always have the same workflow, calling the methods in the same order, but it will obtain different results depending on the parameters/attributes it receives, which, as I said before, are defined by the controller methods.
The only attribute it has beside the ones that adjust the job's workflow is an autowired #Repository object that will save the results of the job in a database.
Maybe I could simply instantiate an Job Handler object and call a constructor with the paramaters I need for the job, but I don't know what is the "Spring way" of doing this, considering how Spring works with dependency injection and I need a #Repository object embbeded into the Job Handler service.
I would really appreciate if anyone could write a sample code/example so I could understand how this can be done with Spring Boot so I don't have to duplicate code or Service Classes.
The Spring way for this case would be to create a Bean of your JobHandler, where you inject the necessary dependencies, like your Repository:
#Configuration
class MyConfiguration {
#Bean
MyJobHandler myJobHandler(MyRepository myRepository) {
return new MyJobHandler (myrepository);
}
}
Alternatively, if you do not want a configuration class, you could declare your JobHandler as a Component and inject the repository in the constructor:
#Component
class MyJobHandler {
private MyRepository myRepository;
public MyJobHandler myJobHandler(MyRepository myRepository) {
this.myRepository = myRepository;
}
}

Why Spring's #Transactional works without proxy?

I got interested in how Spring's #Transactional works internally, but everywhere I read about it there's a concept of proxy. Proxies are supposed to be autowired in place of real bean and "decorate" base method with additional transaction handling methods.
The theory is quite clear to me and makes perfect sense so I tried to check how it works in action.
I created a Spring Boot application with a basic controller and service layers and marked one method with #Transactional annotation. Service looks like this:
public class TestService implements ITestService {
#PersistenceContext
EntityManager entityManager;
#Transactional
public void doSomething() {
System.out.println("Service...");
entityManager.persist(new TestEntity("XYZ"));
}}
Controller calls the service:
public class TestController {
#Autowired
ITestService testService;
#PostMapping("/doSomething")
public ResponseEntity addHero() {
testService.doSomething();
System.out.println(Proxy.isProxyClass(testService.getClass()));
System.out.println(testService);
return new ResponseEntity(HttpStatus.OK);
}}
The whole thing works, new entity is persisted to the DB but the whole point of my concern is the output:
Service...
false
com.example.demo.TestService#7fb48179
It seems that the service class was injected explicitly instead of proxy class. Not only "isProxy" returns false, but also the class output ("com.example.demo.TestService#7fb48179") suggests its not a proxy.
Could you please help me out with that? Why wasn't the proxy injected, and how does it even work without proxy? Is there any way I can "force" it to be proxied, and if so - why the proxy is not injected by default by Spring ?
There's not much to be added, this is a really simple app. Application properties are nothing fancy either :
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=superSecretPassword
spring.datasource.url=jdbc:mysql://localhost:3306/heroes?serverTimezone=UTC
spring.jpa.hibernate.ddl-auto=create-drop
Thank you in advance!
Your understanding is correct, but your test is flawed:
When the spring docs say "proxy", they are referring to the pattern, not a particular implementation. Spring supports various strategies for creating proxy objects. One of these is the java.lang.reflect.Proxy you tested for, but by default spring uses a more advanced technique that generates a new class definition at runtime that subclasses the actual implementation class of the service (and overrides all methods to apply transaction advice). You can see this in action by checking testService.getClass(), which will refer to that generated class, or by halting execution in a debugger, and inspecting the fields of targetService.
The reason that toString() refers to the original object is that the proxy implements toString() by delegating to its target object, which uses its class name to build the String.

Mule - call a controller from inside onCall() method of java class

I am working on a project which is a mavenized web application having Mule support in it. There is requirement to call controller from inside a java class which implements Callable interface. I achieved it by creating an object of controller but it is against MVC rules. Then i tried to use #Autowired annotation but it doesn't work in onCall() method. Is there any solution by which i can call a controller method from java class?
Use Spring to inject the controller into a field of the Callable bean.

Unit Test using the DomainClassConverter feature of Spring

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.

Categories