Spring AOP - aspect loop execution - spring

in first and foremost i need to say that I'm new in Spring AOP (well, I'm new in AOP at all).
In my application I have service class which is advised by aspect, unitil this point is everyting fine. Aspect is triggered, everyting works. But I need to call that service method from my aspect, and there is problem. My Aspect is (logicaly) triggered for each call and everyting end on StackOwerflow error.
It is possible to prevent that aspect looping ?
I have idea to create IAspectSandbox interface (or class) and method invocations from class which will implement this interface do not trigger aspects. But I really don't know how to achieve this goal :)
My class schema:
#Service
public class MyService
{
public BarObject update( FooObject item )
{
BarObject barObject = new BarObject();
// save FooObject to database and detect changes against old row
// information about fields, that was changed is in BarObject
return barObject;
}
}
--------------------------------------------------------------------------
#Aspect
public class MyServicePointcut
{
#Pointcut("execution(* cz.package.service.MyService.update(..))")
public void myServiceItemChanged() {}
}
--------------------------------------------------------------------------
#Component
#Aspect
public class PraceZadaniChangeAspect
{
#AutoWire
private MyService myService;
#AfterReturning("cz.package.pointcuts.MyServicePointcut.myServiceItemChanged()", returning = "returnVal")
public void execute( BarObject returnVal )
{
// do something with BarObject ... mostly check changes
// .....
// .....
// at the end I need to save changes
myService.update( returnVal.getFooObject() ); // after this call is this aspect triggered again. I know why, but I don't want to :)
}
}

Answer #1: Calling Advised Method Only (Around Advice)
If you autowire your service back into your aspect, you're still invoking Spring's proxy mechanism, including the AOP aspect that you've applied to your service.
See "Around Advice" in the Spring AOP chapter:
http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/aop.html#aop-ataspectj-around-advice
Basically, do something like this:
#AfterReturning("...")
public void execute(ProceedingJoinPoint p, BarObject returnVal)
{
// do something with BarObject
// ...
// call original method with original args
p.proceed(p.getArgs());
}
I am not 100% sure on the code, but proceed() should call the target method directly without invoking the AOP proxy recursively.
Answer #2: Calling Multiple Target Object Methods
If you need to call multiple methods from that service object within your aspect, you'll need access to the unproxied object via getTarget():
#AfterReturning("...")
public void execute(JoinPoint p, BarObject returnVal)
{
// do something with BarObject
// ...
// call various service methods without triggering this AOP proxy again
// by using getTarget() to get the unproxied object:
MyService myService = (MyService) p.getTarget();
myService.update(...); // does not trigger AOP interceptor
myService.otherMethod(...); // neither does this
myService.thirdMethod(...); // nor this
}

Related

Does Transactional.TxType.REQUIRES_NEW start a new transaction when called from the same bean?

AFAIK
In proxy mode (which is the default), only 'external' method calls
coming in through the proxy will be intercepted. This means that
'self-invocation', i.e. a method within the target object calling some
other method of the target object, won't lead to an actual transaction
at runtime even if the invoked method is marked with #Transactional!
But here is: Transactional.TxType.REQUIRES_NEW
Will be the second transaction created?
#Service
public class SomeService {
#Transactional
public void doSomeLogic() {
// some logic
doOtherThings();
// some logic
}
#Transactional(Transactional.TxType.REQUIRES_NEW)
private void doOtherThings() {
// some logic
}
To get an answer to this question, you need to know how a proxy works.
When you annotate a method inside a bean, the proxy will wrap that bean with the appropriate logic. This means that if you call that annotated method of your bean, the request will first be sent to the proxy object (named with $), which will then call the bean's method. If this method calls another method of the same bean, it will call it without invoking a proxy which has a logic, e.x., of transaction management.
Example: Here is the code which will be wrapped by proxy and an appropriate diagram of its work.
#Service
public class SomeService {
#Transactional
public void foo() {
// this next method invocation is a direct call on the 'this' reference
bar();
}
#Transactional(Transactional.TxType.REQUIRES_NEW)
public void bar() {
// some logic...
}
}
Source
Hence, the answer is No.
Hope that's a little bit more clear now.

Any way to find other annotations on methods that are annotated with EventListeners using the resulting ApplicationListener returned from the context?

It looks like the ApplicationListenerMethodAdapter hides the method it is annotated for making it impossible to look if that method potentially contains other Annotations. There is some other way around this?
if i have an event listener like this
#EventListener
#SomeOtherAnnotation
public void onSomeEvent(SomeEvent e) {
...
}
and a custom event multicaster
public class CustomEventMulticaster extends SimpleApplicationEventMulticaster {
public <T extends ApplicationEvent> void trigger(final T event,
Function<ApplicationListener<T>, Boolean> allowListener) {
...
}
}
i'd like to do something like trigger only if some annotation exists
customEventMulticaster.trigger(someEvent, (listener) -> {
return listener.getClass().getAnnotation(SomeOtherAnnotation.class) == null;
})
There is a hacky solution - just as case study - but please don't go that way.
Since your application listener is in fact ApplicationListenerMethodAdapter you can use reflection to get method or targetMethod from that class. From there you can get method annotations.
More or less (not checked, pure notepad here)
customEventMulticaster.trigger(someEvent, (listener) -> {
Field f=((ApplicationListenerMethodAdapter)listener).getDeclaredField("method"); // or 'targetMethod' - consult ApplicationListenerMethodAdapter to get the difference
f.setAccessible(true);
Method m=f.get(listener); // cast again if required
anno=m.getAnnotation(yourAnno); // here you can access annotation
return anno == null;
})
To make this at least to pretend ot be safe, add nullchecks and check if listener is indeed castable to ApplicationListenerMethodAdapter

Spring nested transactions

In my Spring Boot project I have implemented following service method:
#Transactional
public boolean validateBoard(Board board) {
boolean result = false;
if (inProgress(board)) {
if (!canPlayWithCurrentBoard(board)) {
update(board, new Date(), Board.AFK);
throw new InvalidStateException(ErrorMessage.BOARD_TIMEOUT_REACHED);
}
if (!canSelectCards(board)) {
update(board, new Date(), Board.COMPLETED);
throw new InvalidStateException(ErrorMessage.ALL_BOARD_CARDS_ALREADY_SELECTED);
}
result = true;
}
return result;
}
Inside this method I use another service method which is called update:
#Transactional(propagation = Propagation.REQUIRES_NEW)
public Board update(Board board, Date finishedDate, Integer status) {
board.setStatus(status);
board.setFinishedDate(finishedDate);
return boardRepository.save(board);
}
I need to commit changes to database in update method independently of the owner transaction which is started in validateBoard method. Right now any changes is rolling back in case of any exception.
Even with #Transactional(propagation = Propagation.REQUIRES_NEW) it doesn't work.
How to correctly do this with Spring and allow nested transactions ?
This documentation covers your problem - https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/data-access.html#transaction-declarative-annotations
In proxy mode (which is the default), only external method calls coming in through the proxy are intercepted. This means that self-invocation, in effect, a method within the target object calling another method of the target object, will not lead to an actual transaction at runtime even if the invoked method is marked with #Transactional. Also, the proxy must be fully initialized to provide the expected behaviour so you should not rely on this feature in your initialization code, i.e. #PostConstruct.
However, there is an option to switch to AspectJ mode
Using "self" inject pattern you can resolve this issue.
sample code like below:
#Service #Transactional
public class YourService {
//... your member
#Autowired
private YourService self; //inject proxy as an instance member variable ;
#Transactional(propagation= Propagation.REQUIRES_NEW)
public void methodFoo() {
//...
}
public void methodBar() {
//call self.methodFoo() rather than this.methodFoo()
self.methodFoo();
}
}
The point is using "self" rather than "this".
The basic thumb rule in terms of nested Transactions is that they are completely dependent on the underlying database, i.e. support for Nested Transactions and their handling is database dependent and varies with it.
In some databases, changes made by the nested transaction are not seen by the 'host' transaction until the nested transaction is committed. This can be achieved using Transaction isolation in #Transactional (isolation = "")
You need to identify the place in your code from where an exception is thrown, i.e. from the parent method: "validateBoard" or from the child method: "update".
Your code snippet shows that you are explicitly throwing the exceptions.
YOU MUST KNOW::
In its default configuration, Spring Framework’s transaction
infrastructure code only marks a transaction for rollback in the case
of runtime, unchecked exceptions; that is when the thrown exception is
an instance or subclass of RuntimeException.
But #Transactional never rolls back a transaction for any checked exception.
Thus, Spring allows you to define
Exception for which transaction should be rollbacked
Exception for which transaction shouldn't be rollbacked
Try annotating your child method: update with #Transactional(no-rollback-for="ExceptionName") or your parent method.
Your transaction annotation in update method will not be regarded by Spring transaction infrastructure if called from some method of same class. For more understanding on how Spring transaction infrastructure works please refer to this.
Your problem is a method's call from another method inside the same proxy.It's self-invocation.
In your case, you can easily fix it without moving a method inside another service (why do you need to create another service just for moving some method from one service to another just for avoid self-invocation?), just to call the second method not directly from current class, but from spring container. In this case you call proxy second method with transaction not with self-invocatio.
This principle is useful for any proxy-object when you need self-invocation, not only a transactional proxy.
#Service
class SomeService ..... {
-->> #Autorired
-->> private ApplicationContext context;
-->> //or with implementing ApplicationContextAware
#Transactional(any propagation , it's not important in this case)
public boolean methodOne(SomeObject object) {
.......
-->> here you get a proxy from context and call a method from this proxy
-->>context.getBean(SomeService.class).
methodTwo(object);
......
}
#Transactional(any propagation , it's not important in this case)public boolean
methodTwo(SomeObject object) {
.......
}
}
when you do call context.getBean(SomeService.class).methodTwo(object); container returns proxy object and on this proxy you can call methodTwo(...) with transaction.
You could create a new service (CustomTransactionalService) that will run your code in a new transaction :
#Service
public class CustomTransactionalService {
#Transactional(propagation= Propagation.REQUIRES_NEW)
public <U> U runInNewTransaction(final Supplier<U> supplier) {
return supplier.get();
}
#Transactional(propagation= Propagation.REQUIRES_NEW)
public void runInNewTransaction(final Runnable runnable) {
runnable.run();
}
}
And then :
#Service
public class YourService {
#Autowired
private CustomTransactionalService customTransactionalService;
#Transactional
public boolean validateBoard(Board board) {
// ...
}
public Board update(Board board, Date finishedDate, Integer status) {
this.customTransactionalService.runInNewTransaction(() -> {
// ...
});
}
}

Unit testing with Mockito

I am writing unit tests for service layer in my spring application.
Here is my service class
#Service
public class StubRequestService implements RequestService {
#Autowired
private RequestDao requestDao;
#Transactional(propagation = Propagation.REQUIRED, readOnly = true)
#Override
public Request getRequest(Long RequestId) {
Request dataRequest = requestDao.find(requestId);
return dataRequest;
}
}
Here is my test class
#RunWith(MockitoJUnitRunner.class)
#ContextConfiguration(locations = { "/META-INF/spring/applicationContext.xml" })
public class StubRequestServiceTest {
#Mock
public RequestDao requestDao;
StubRequestService stubRequestService; // How can we Autowire this ?
#org.junit.Before
public void init() {
stubRequestService = new StubRequestService(); // to avoid this
stubRequestService.setRequestDao(dataRequestDao);
// Is it necessary to explicitly set all autowired elements ?
// If I comment/remove above setter then I get nullPointerException
}
#Test
public void testGetRequest() {
Request request = new Request();
request.setPatientCnt("3");
when(requestDao.find(anyLong())).thenReturn(request);
assertEquals(stubRequestService.getRequest(1234L).getPatientCnt(),3);
}
}
Its working fine but I have few questions
How can we Autowire service class in test ? I am using constructor in init() method to create service object.
Do we have to set all Autowire element for service class ? For ex StubRequestService have autowired RequestDao which I need to set explicitly before calling test method otherwise it giveds nullPointerException as requestDao is null in StubRequestService.getRequest method.
Which are the good practices to follow while unit testing Spring service layer ? (If I am doing anything wrong).
Your test is fine. It doesn't even have to have the #ContextConfiguration annotation.
The whole point of dependency injection frameworks like Spring is to be able to unit test services by simply instantiating them, setting mock dependencies, and then call their methods.
You're doing it correctly. You don't need to have a Spring context for such unit tests. That's why they're called unit tests: they test it in isolation of all their actual dependencies, Spring included.
Side note: assuming you're using JUnit, the arguments of the assertXxx method should be swapped. The expected value comes before the actual value. It becomes important when the assertion fails and you have a message like "expecting 6 but was 3" rather than "expecting 3 but was 6".
If you really feel that it will make your tests easier to understand - you can initialize a spring context and fetch all of the objects from there. However, usually it will require creating a separate spring configuration XML file specifically for tests therefore I would not recommend it.
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("testApplicationContext.xml");
stubRequestService = (RequestService)applicationContext.getBean("myRequestServiceBean");
(and 3) Basically, I prefer testing each component of my application in total isolation from eachother and that's why I do not recommend what I described in [1].
What that means, is you take a separate logical slice of your application and test only it, while fully mocking up everything it tries to access.
Let's say you have three classes:
//Fetches stuff from some webservice and converts to your app domain POJOs
class DataAccessLayer {
public void setWebservice(Webservice ws) {...};
public MyObject getMyObject() {...};
}
//Formats the domain POJOs and sends them to some kind of outputstream or stuff.
class ViewLayer {
public void setOutputStream(OutputStream os) {...};
public void viewMyObject(MyObject mo) {...};
}
//Main entry point of our MyObject fetch-process-display workflow
class Controller {
public void setDataAccessLayer(DataAccessLayer dal) {...};
public void setViewLayer(ViewLayer vl) {...};
public void showMyObject() {
MyObject mo = dal.getMyObject();
...some processing here maybe...
vl.viewMyObject(mo);
}
}
Now, what tests can we write here?
Test if DataAccessLayer properly converts the object from mocked up WS to our domain object.
Test if ViewLayer properly formats the object given to him and writes it to mocked up output stream.
Test if Controller takes an object from mocked up DataAccessLayer processes it properly and sends it to mocked up ViewLayer.
Or You can use springockito
https://bitbucket.org/kubek2k/springockito/wiki/Home, it will make your tests cleaner

Is the ArroundInvoke method gets invoked if it were invoked from another ejb method?

I've the following case:
#Interceptors(MyInterceptClass.class)
public void ejbMethod1()
{
}
#Interceptors(MyInterceptClass.class)
public void ejbMethod2()
{
ejbMethod1();
}
Is calling ejbMethod2 causes TWO interceptor calls to be executed?
Thanks.
I'll assume you mean #Interceptors (plural) annotation which defines the interceptor class which will be invoked upon annotated method invocation. #Interceptor annotation (singular) is for annotating a class which is an interceptor.
If so, then short answer is: no.
The interceptor is executed by the container. If your method invocation will not go through the container, then it will not be intercepted.
Therefore the following call to ejbMethod1():
#Interceptors(MyInterceptClass.class)
public void ejbMethod2() {
ejbMethod1();
}
won't activate the MyInterceptClass as it's the local call (non-EJB one).
If you'd like to call the interceptor once again, you should use business interface, so something like:
// Somewhere in the class
#Resource
SessionContext ctx;
#Interceptors(MyInterceptClass.class)
public void ejbMethod2() {
// This is explicit call which will go through the EJB Container
ctx.getBusinessObject(YourEJBClass.class).ejbMethod1();
}
This will make the EJB-aware call and will hit the interceptor while invoking ejbMethod1().

Resources