@ResourceLock
to my asynchronous method.
@Component public class StringProcessingLogic implements OperationLogic<string> { private final Logger logger = LogManager.getLogger(StringProcessingLogic.class); @ResourceLock @Async("singleOperationExecutor") public Future<boolean> executeOperation(OperationData<string> data) throws OperationFailedException { boolean result = false; try { long startTime = System.currentTimeMillis(); do { logger.info("processing " + data); Thread.sleep(500); } while (startTime + 10000 > System.currentTimeMillis()); result = true; } catch (InterruptedException e) { logger.error(e); } return new AsyncResult<boolean>(result); } }Let's check the result
20:37:10.603 [main] INFO pl.mariusz.marciniak.async.AsyncMainTest - starting asynchronous test 20:37:10.629 [main] INFO pl.mariusz.marciniak.async.AsyncMainTest - executing other logic ... 20:37:10.635 [singleOperationExecutor-1] DEBUG pl.mariusz.marciniak.locking.aop.ResourceLockAspect - acquiring lock on really long calculations::calculation data 20:37:10.635 [singleOperationExecutor-1] INFO pl.mariusz.marciniak.async.StringProcessingLogic - processing really long calculations::calculation data ... 20:37:20.137 [singleOperationExecutor-1] INFO pl.mariusz.marciniak.async.StringProcessingLogic - processing really long calculations::calculation data 20:37:20.638 [singleOperationExecutor-1] DEBUG pl.mariusz.marciniak.locking.aop.ResourceLockAspect - releasing lock on really long calculations::calculation data 20:37:20.638 [singleOperationExecutor-1] DEBUG pl.mariusz.marciniak.locking.aop.ResourceLockAspect - acquiring lock on transformation::transformed data 20:37:20.638 [singleOperationExecutor-1] INFO pl.mariusz.marciniak.async.StringProcessingLogic - processing transformation::transformed data ... 20:37:30.138 [singleOperationExecutor-1] INFO pl.mariusz.marciniak.async.StringProcessingLogic - processing transformation::transformed data 20:37:30.638 [singleOperationExecutor-1] DEBUG pl.mariusz.marciniak.locking.aop.ResourceLockAspect - releasing lock on transformation::transformed data
I’ve used executor with single thread so the result is as desired. The most important thing is that asynchronous execution takes precedence before locking. That is very good, in other way resources will be locked, method invoked asynchronously will immediately returned and resources released before finishing asynchronous execution. So that’s REALLY great. Nice. Yeah, but why?
What exactly is defining order of different aspects invocation at the same join point? Well Spring documentation states:
When two pieces of advice defined in different aspects both need to run at the same join point, unless you specify otherwise the order of execution is undefined. You can control the order of execution by specifying precedence. This is done in the normal Spring way by either implementing theFor test purpose I will change my locking annotation order to be executed beforeorg.springframework.core.Ordered
interface in the aspect class or annotating it with theOrder
annotation. Given two aspects, the aspect returning the lower value fromOrdered.getValue()
(or the annotation value) has the higher precedence.
@Async
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Order(Ordered.HIGHEST_PRECEDENCE) public @interface ResourceLock { }In fact setting
@ResourceLock
to have the highest precedence (@Order
equal the lowest integer), doesn’t change application’s behaviour. This is because AsyncAnnotationBeanPostProcessor
has set beforeExistingAdvisors
property to true
and always adds @Async
advistor before others. According to Spring creators:
@Async
always needs to be the first Advisor in the chain in order to provide meaningful around-invocation semantics
It means that Spring provides correct ordering and I don't need to change anything in my code.
No comments:
Post a Comment