I have a question about Spring #Async annotation.
I have a controller autowired a service(GnInsuranceDetailsService)
#RequestMapping(value="/agn/AP0W01A_010/insertDetail.do")
public ResponseEntity<?> insertDetail(#ModelAttribute("dto")GnInsuranceDetailsDTO dto,HttpSession session) throws Exception {
gnInsuranceDetailsDTO.setBkFlag(getAgnUserInfo(session).getBkFlag());
gnInsuranceDetailsService.insertGnInsuranceDetail(dto);//open another thread to insert data
MessageDTO dto = new MessageDTO(AgnConstant.INSERT_SUCCESS);
return new ResponseEntity<MessageDTO>(dto,HttpStatus.OK);// increase return time for client
}
And the Service insertGnInsuranceDetail method I declare #Async up method.
#Transactional(readOnly = false)
#Async
public void insertGnInsuranceDetail(GnInsuranceDetailsDTO gnInsuranceDetailsDTO) throws Exception{
GnInsuranceDetails entity = gnInsuranceDetailsDTO.convert();
gnInsuranceDetailsDAO.save(detailsEntity);
}
I put the #Async for the service method to increase controller response time for client side,but it does not work as I think.
Do I lose somethings?Or How can I modify in the easiest way to use?
You would not loose anything, when you put #Async in the method service will be executed in a different thread, Controllers insertDetail method will not be returned until your insertGnInsuranceDetail is returned or thrown any exception.
I put the #Async for the service method to increase controller response time for client side,but it does not work as I think.
#Async - Annotation that marks a method as a candidate for asynchronous execution. Can also be used at the type level, in which case all of the type's methods are considered as asynchronous.
This #Async annotation will not help you in delaying the response time. To introduce a delay, use Thread.sleep(milliseconds);
If you want main thread(controller) to wait for insert data thread to get a result (successful/failure) you can just invoke db layer code from the controller. Whereas if you want the client response to be sent earlier, then create a new thread inside the controller and use that thread to insert data. In the latter approach of creating a new thread for insert data your client will not know about the status of data insertion, since the thread is created in the controller and it will end in the controller, no feedback/response can be given to the client, since we will not be having client details.
Hope it Helps!
Reference: https://www.tutorialspoint.com/java/lang/thread_sleep_millis.htm
#Async should be first enabled in your #Configuration bean:
#Configuration
#EnableAsync
public class AppConfiguration {
[...]
}
for xml configuration add this: <task:annotation-driven>
Related
My Spring Boot application provides the following REST controller which invokes two methods of a Service. One of these methods is annotated with #Async so it should run asynchronously.
The main application class is annotated with #EnableAsync.
The problem I observed is: Basically, the async method is executed. I can see the corresponding log entries in the production system. But it seems as if sometimes the async method does not get invoked. There are file ids in the database which do not appear in the logs.
Do you have any idea when this behavior could occur?
REST controller
#PostMapping(consumes = MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<Void> uploadDocument(#RequestParam("file") MultipartFile multipartFile) {
long fileId = fileService.save(multipartFile);
file.generateThumbnail(fileId);
return ResponseEntity.ok().build();
}
FileService
#Transactional
public long save(MultipartFile multipartFile) {
// saves the file...
return fileId;
}
#Async
#Transactional
public void generateThumbnail(long fileId) {
// generate thumbnail
log.info("Starting thumbnail generation for fileId {}", fileId);
file.setThumbnailId(thumbnailId);
}
Calling a async method from FileService does not work as async. You must create another Service for async method and call it from your service.
Reason:
self-invocation – calling the async method from within the same class
– won't work the method needs to be public so that it can be proxied.
And self-invocation doesn't work because it bypasses the proxy and
calls the underlying method directly.
Resources: https://www.baeldung.com/spring-async
I am autowiring service in controller. And in service, I have a scenario where I need to throw an exception and DB changes also. So, I tried #Async
#Transactional
public void verifyOtp(OtpDto otpDto)
...
if(xyz){
deactivateOtp(emp.getId());
throw new ServException("Mobile no requested is already assigned", "error-code");
}
}
#Async
#Transactional //with or without
public void deactivateOtp(Integer id){
otpRepo.deactivateOtp(id);
}
public interface OtpRepository extends JpaRepository<Otp, Integer> {
#Modifying
#Query("UPDATE Otp SET isActive = 0 WHERE id = :id")
public void deactiveOtp(#Param("id") Integer id);
This is not creating new thread. But, if I gives at repo, it works
public void deactivateOtp(Integer id){
otpRepo.deactivateOtp(id);
}
public interface OtpRepository extends JpaRepository<Otp, Integer> {
#Async
#Transactional
#Modifying
#Query("UPDATE Otp SET isActive = 0 WHERE id = :id")
public void deactiveOtp(#Param("id") Integer id);
First of all check that the service is wrapped into proxy (you can place a breakpoint in controller and see the reference to the service, it will be with proxy). Otherwise there is something wrong with the configuration and #Transactional/#Async won't work till you fix that.
Now, assuming this is not an issue, there is an issue in the code:
When the controller calls service.verifyOtp it goes to the proxy (to handle the transaction) and then to your implementation.
But when it reaches your implementation and you call the method that belongs to the same impl, it doesn't pass through the proxy again, instead it directly goes to the deactivateOtp as if there is no spring at all here. Of course, #Async doesn't work.
In terms of resolution:
Consider using self injection if you work with spring 4.3+. Read this thread for more information.
Alternatively, refactor your code so that the deactivateOtp will be a public method of another class. In this case the call won't be "internal" anymore, it will path through Proxy hence the #Async will work.
This is discussed many times.
Basically, Spring AOP will not intercept local method call
(in your case call deactivateOtp() within the same class)
You can read more about this behavior here: Understanding AOP proxies
Highlight:
The key thing to understand here is that the client code inside the main(..) of the Main class has a reference to the proxy. This means that method calls on that object reference will be calls on the proxy, and as such the proxy will be able to delegate to all of the interceptors (advice) that are relevant to that particular method call. However, once the call has finally reached the target object, the SimplePojo reference in this case, any method calls that it may make on itself, such as this.bar() or this.foo(), are going to be invoked against the this reference, and not the proxy. This has important implications. It means that self-invocation is not going to result in the advice associated with a method invocation getting a chance to execute.
I have used Spring in the past. I moved to a different team where I am getting familiarized with codebase. I found the following code and trying to understand how it works and how spring injects autowired objects in the case. From my basics of Spring, this is definitely not the right way to do. But surprisingly, this code is in production for a long time and no issues were identified.
#Controller
#RequestMapping("/start")
public class AController implements Runnable, InitializingBean {
#Autowired
private StartServiceImpl service = new StartServiceImpl(); // 1
Thread thread;
public void run() {
service.start();
}
public void stop() {
try {
thread.join();
} catch (InterruptedException e) {
}
}
}
#Override
public void afterPropertiesSet() throws Exception {
thread = new Thread(this);
thread.setPriority(Thread.MAX_PRIORITY);
thread.start();
}
}
#Component
public class StartServiceImpl {
//methods
}
Q1) What does localhost:8080/project/start is expected to do. There is NO GET or POST methods defined.
Q2) on the commented line 1, StartServiceImpl is both autowired and constructed with "new". So what happens here. Does the container inject bean or just an object is instantiated.
#Controller
#RequestMapping("/stop")
public class BController {
#Autowired
private StartServiceImpl service = new StartServiceImpl();
#RequestMapping(value = "**", method = RequestMethod.GET)
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try {
service.shutdownRequested();
new AController().stop(); // 2
} catch (Exception e) {
}
}
}
Q3) Again in commented line 2, does calling stop, calls the stop on the bean in the application context or a new object gets created and the stop method is called. What would happen in the latter case? Are we really stopping the service that was started or not? I think we are not stopping the service.
I have read this post. It is very useful. But it did not answer my question.
I will try to answer the questions specifically, as the purpose of the code is hard to understand (for me at least).
Q1) It is unclear for me what this code tries to achieve. As you noticed, it is not a controller, and I suspect that the only reason why it is registered this way is so that it gets auto-scanned (which might as well get done by using #Controller. This is just a hunch, I don't quite understand its purpose.
Q2) The answer is that two instances will be created, one via new, the other as a bean. When running in Spring, the final value of the field is the bean, because dependency injection happens after the construction. Typically this is done when the class is envisioned to be used outside Spring (e.g. a unit test), so that the field can be initialized with a default value.
Q3) stop() will be invoked on a new instance, and not the bean. The service bean is stopped because of the direct call above that line to the injected bean, but the next one will be an NPE, I guess, because afterPropertiesSet is not invoked on the target object created via new. the only reason why this doesn't show an NPE in the logs is because the exception is swallowed below. The thread variable is not initialized and remains NULL.
Hope this helps,
This code is flawed on many levels.
Ever since Java 5, manually starting threads is an antipattern. It's messy and way too low-level. ExecutorServices should be used.
A Rest controller that is a Runnable? That's a monstrous mingling of concerns.
A service is created via new but then overwritten with an autowired dependency? WTF!
etc.
I'd keep the thread running all the time, scheduling the task with the #Scheduled annotation and use the controller to toggle a flag that decides if the thread actually does somethin, e.g.
#Service
class StartService{
private boolean active;
public void setActive(boolean active){this.active=active;}
#Scheduled(fixedRate=5000)
public void doStuff(){
if(!active)return;
// do actual stuff here
}
}
And now all the rest controllers do is toggle the value of the "active" field. Benefits:
every class does one thing only
you always know how many threads you have
The code you posted is very strange.
Q1 ) What does localhost:8080/project/start is expected to do. There
is NO GET or POST methods defined.
I think localhost:8080/project/start will return 404 error (The requested resource is not available). Because there is no mapped methods in AController. #RequestMapping annotation on class level is not enough for make request to controller. There is have to be mapped method.
But service will be started anyway. Because AController implements InitializingBean. Method afterPropertiesSet() will be invoked by Spring after controller will be created and all fields will be initialized.
Q2) on the commented line 1, StartServiceImpl is both autowired and
constructed with "new". so what happens here. does the container
inject bean or just an object is instantiated.
Another strange snippet. Java will create new instance of StartServiceImpl on creation of instance of AController class. But after that, Spring will assign it's own instance(declared as component) to this field. And reference to firs instance (created by constructor) will be lost.
Q3) Again in commented line 2, does calling stop, calls the stop on
the bean in the appication context or a new object gets created and
the stop method is called. what would happen in the latter case? Are
we really stopping the service that was started or not? I think we are
not stopping the service
Actually service will be stopped. Because of invocation of service.shutdownRequested();. But thread in AController bean will continue to work. new AController().stop(); will invoke method of just created instance, but not method of controller (instance created by Spring).
This code is totally wrong usage of Spring framework.
I am using spring jpa transactions in my project.One Case includes inserting a data in a synchronized method and when another thread accesses it the data is not updated.My code is given below :
public UpdatedDTO parentMethod(){
private UpdatedDTO updatedDTO = getSomeMethod();
childmethod1(inputVal);
return updatedDTO;
}
#Transactional
public synchronized childmethod1(inputVal){
//SomeCodes
//Place where update takes place
TableEntityObject obj = objectRepository.findByInputVal(inputVal);
if(obj == null){
childMethod2(inputVal);
}
}
#Transactional
public void childMethod2(inputVal){
//Code for inserting
TableEntityObject obj = new TableEntityObject();
obj.setName("SomeValue");
obj.setValueSet(inputVal);
objectRepository.save(obj);
}
Now if two threads access at the same time and if first thread completes childmethod2 and childmethod1 and without completing parentMethod() after that if second thread comes to the childMethod1() and checks if data exists,the data is null and is not updated by first thread.I have tried many ways like
#Transactional(propagation = Propagation.REQUIRES_NEW)
public synchronized childmethod1(inputVal){
//SomeCodes
//Place where update takes place
TableEntityObject obj = objectRepository.findByInputVal(inputVal);
if(obj == null){
childMethod2(inputVal);
}
}
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void childMethod2(inputVal){
//Code for inserting
TableEntityObject obj = new TableEntityObject();
obj.setName("SomeValue");
obj.setValueSet(inputVal);
objectRepository.save(obj);
}
also tried taking off #transactional in the childMethod1() but nothing works out.I know im doing something wrong here , but couldnt figure out where and what exactly i am doing wrong.Can anyone help me out with this
#Transactional is resolved using proxies on spring beans. It means it will have no effect if your method with #Transactional is called from the same class. Take a look at Spring #Transaction method call by the method within the same class, does not work?
The easiest would be moving those methods into separate service.
Typical checklist I follow in cases like these :
If Java based configuration then make sure
#EnableTransactionManagement annocation is present in the class
containing the #Configuration annotation
Make sure the transactionManager bean is created, again this should be mentioned in the configuration class.
Use of #Transactional annocatio over the method which is calling the repository, typically a class in the DAO layer
Adding the #Service annotation for the class which is invoking the methods in the repository
Nice blog which explains the Transaction configuration with JPA in depth --> http://www.baeldung.com/2011/12/26/transaction-configuration-with-jpa-and-spring-3-1/68954
I get a LazyInitializationException by combining the conversionservice with a #Transactional behavior in a MVC controller.
Following fails:
#RequestMapping(value = "/{userId}", method = RequestMethod.GET)
#ResponseBody
#Transactional
public JsonUser getUser(#PathVariable("userId") User user) {
// convertToJsonUser triggers lazy loading of User.adresses
return mUserPresenter.convertToJsonUser(user);
}
... with following exception:
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: User.adresses, could not initialize proxy - no Session
But the same code without the conversionservice succeeds:
#RequestMapping(value = "/{userId}", method = RequestMethod.GET)
#ResponseBody
#Transactional
public JsonUser getUser(#PathVariable("userId") Long userId) {
User user = mUserRepository.findOne(userId);
// convertToJsonUser triggers lazy loading of User.adresses
return mUserPresenter.convertToJsonUser(user);
}
and the same code without the transactional behavior succeeds:
#RequestMapping(value = "/{userId}", method = RequestMethod.GET)
#ResponseBody
public JsonUser getUser(#PathVariable("userId") User user) {
// changedConvertToJsonUser DOESN'T trigger lazy loading of User.adresses
return mUserPresenter.changedConvertToJsonUser(user);
}
The conversion seems to occur in its own transaction before the main transaction to be opened by the #Transactional annotation. As a result, the User loaded by the conversionmanager is not bound to the main transaction and lazy loading fails for that reason.
Is that behavior known?
How can I get rid of it?
Did I forget something in the configuration?
Thank you in advance!!!
Controller is not a perfect place to put #Transactional annotations. It would be better to add them to service methods. This may cause problems because you may have a proxy object of controller which is mixed up with proxy from transaction manager and you can not predict what the order of execution will be. So my advice is: try to add a service layer.. even a very simple put there #Transactional annotations and test the code again.
... the conversionservice is per se the way of loading entities ...
No, it's not. It's like the name says: conversion. If you load entities, then that's your choice, not Spring's.
if it is not recommanded to open a transaction in the controller-layer, then why did Spring developers have developed the conversionservice feature?
The service in ConversionService implies that we are not in the controller layer.
The conversion seems to occur in its own transaction before the main transaction to be opened by the #Transactional annotation
There is no transaction opened by #Transactional in this case, because it doesn't work.
Transactions usually work on the service layer. You could have something like:
MyUserService implements UserService {
#Override
#Transactional(readonly = true)
public User getUserById(int id) {
But that's not your problem. Your problem is that the User object is not fully initialized. You would need a transaction on convertToJsonUser.
As a side note: The people who develop Spring prefer using the ids directly and load entities in the controller, not before.