I am new to the concept of Aspect-Oriented Programming. I am writing this following aspect using AspectJ in my spring boot application:
#Aspect
public class MyAspect {
private final AspectUtil aspectUtil;
public MyAspect(AspectUtil aspectUtil)) {
this.aspectUtil = aspectUtil;
}
#Pointcut("#within(org.springframework.stereotype.Service)")
public void applicationServicePointcut() {
}
#AfterReturning(value = ("applicationServicePointcut()"))
public void process(JoinPoint joinPoint) {
HttpServletRequest request = aspectUtil.getRequest();
aspectUtil.getHttpVerb(request);
processUtil(request, joinPoint);
}
public void processUtil(HttpServletRequest request) {
...
}
All of the services defined in my application are annotated with #Transactional, one example is given below:
#Service
#Transactional
public class MyService {
..
}
So, here my question is if any exception I throw from my aspect as defined above, then would the associated transaction in the service layer get rolled back?
Actually, my goal is to roll back the entire transaction in case, any exception occurs at this aspect.
An example for a similar requirement 1.4.8. Advising Transactional Operations is available in the documentation.
Exerpt :
The ordering of advice is controlled through the Ordered interface.
.........
The result of the preceding configuration is a fooService bean that
has profiling and transactional aspects applied to it in that order.
If you want the profiling advice to run after the transactional advice
on the way in and before the transactional advice on the way out, you
can swap the value of the profiling aspect bean’s order property so
that it is higher than the transactional advice’s order value...
Please do go through : Advice Ordering to understand the concept better and control the order of advice.
Also go through the conceptual view of calling a method on a transactional proxy:
Basically the idea would be to throw the exception from the custom aspect that should result in the rollback from the Transaction Advisor.
The short answer is it depends. Aspects are ordered in Spring, meaning that they run with a specific order. That means that if your aspect is set to run before the transactional aspect, then no transaction will be created and there would be nothing to actually rollback.
On the other hand, if you set this to run after the transaction aspect and it indeed throws an exception, this would automatically mean that the transaction would have ran and would be committed.
I believe you need to check on ordering of aspects to find the pattern that suits your needs the best.
Related
Working with Springboot 2.7.0. I had a a working application and I made these changes on top of it
Aspect Configuration
#Configuration
#EnableAspectJAutoProxy
#ComponentScan
public class AspectConfig {}
Aspect Interface
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface Timed { }
Aspect Class to Measure method execution time
#Around("#annotation(Timed)")
public Object measureExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
Object proceed = joinPoint.proceed();
LOG.info("Time taken for {} is {} ms, joinPoint.getSignature(), System.currentTimeMillis() - start,);
return proceed;
}
Added the new #Timed annotation to an existing method in a bean (omitting non relevant code)
#Component
#ConditionalOnExpression("${oauth.enabled}")
public class JwtAuthFilter extends OncePerRequestFilter {
#Timed
public boolean verifySignatureAndExpiry(String bearerToken){
// method logic
}
}
This causes the Springboot application to fail startup.
I can get it to start if I add #Aspect to the JwtAuthFilter class.
but why would I need to do that? It makes the #Timed annotation limited use if I have to annotate every class that needs to use it with #Aspect. Not to mention, though there are no errors, the functionality won't work because an Aspect cannot work on another Aspect.
#Timed works on my controller method though.
#RestController
#RequestMapping(value = "/api/v1", produces = MediaType.APPLICATION_JSON_VALUE)
public class HealthController {
#GetMapping("/health")
#Timed
public Map<String, String> health(){
return Map.of("status", "up");
}
}
This causes the Spring Boot application to fail startup.
You should always post error messages and relevant stack traces, not just say "fails to start up". You are lucky that in this case, I remember the situation, so I can answer your question. Normally, I would be unable to do so without further information.
I can get it to start if I add #Aspect to the JwtAuthFilter class.
That does not make any sense. Why would you add #Aspect to something which is not an aspect? Of course, it makes the start-up error go away, but it also makes your real aspect not fire, because one Spring AOP aspect cannot advise another one, as you already mentioned. Therefore, this approach is - with all due respect - complete nonsense.
The reason for the exception is: You cannot advise your filter by Spring AOP, because it is derived from GenericFilterBean, which has some final methods. Final methods cannot be overriden, therefore not be proxied either. This has the effect of those methods being called upon the proxy instance directly instead of being delegated to the target object, i.e. if such a method accesses an instance field, it shall find it uninitialised, because the proxy's fields are not meant to be initialised, only the target object's ones. See also my answer here for more info.
In this case, final method org.springframework.web.filter.GenericFilterBean#init is trying to access this.logger, which leads to the NPE which makes Spring Boot's Tomcat fail to start up. This has been reported and briefly explained in this comment in Spring issue #27963, which has been closed as invalid.
#Timed works on my controller method though.
Yes, because your controller does not have a problem with accessing an instance field from a final method.
If you absolutely think that you need to measure your filter method's execution time from an aspect, you can switch from Spring AOP to native AspectJ, either for the whole project via load-time weaving or selectively for some target classes via compile-time weaving. I have tried locally, it works with the right pointcut. Then you can also advise your filter. FYI, the pointcut would be something like:
// Annotated class
#Around("execution(* *(..)) && !within(MyAspect) && #target(Timed)")
// Annotated method
#Around("execution(* *(..)) && !within(MyAspect) && #annotation(Timed)")
AspectJ is more powerful than Spring AOP, so you explicitly need to limit matching to method executions, otherwise other joinpoints such as method calls, constructor calls and others would be affected, too. You also need to make sure that the aspect does not advise itself or other aspects, which is perfectly possible in AspectJ.
Context
I am using AOP to create/remove Spring ACL records for managed entities by intercepting Spring Data repositories' save and delete methods. All my repositories are #RepositoryRestResource annotated interfaces extending either PagingAndSortingRepository or CrudRepository. This has been working perfectly in the past. Unfortunately I can't identify exactly the point in time (or code change) where it stopped working.
Expected behaviour
The following advice should fire on all save methods.
#Pointcut("execution(* org.springframework.data.repository.*.save(*))")
public void defaultRepoSave() {}
// may be useful for overridden methods
#Pointcut("execution(* com.xxx.yyy.repository.*.save(..))")
public static void customRepoSave() {}
#Pointcut("defaultRepoSave() || customRepoSave()")
public static void repoSave() {}
#Around(value="repoSave() && args(entity)", argNames="entity")
public Object aroundSave(ProceedingJoinPoint pjp, Object entity) throws Throwable{
log.info("Saving ...");
return pjp.proceed();
}
Note: I tried various combinations of #EnableAspectJAutoProxy(exposeProxy=true/false, proxyTargetClass=true/false), but it doesn't seem to have any effect on this particular advice
The issue
The advice fires for some repositories, and not for others. Both repositories are in the same package. Debugging shows that both repositories are proxied, but the execution for the one on the left is missing advice-related interceptors altogether. The one on the right proceeds as expected.
To eliminate the possibility of pointcut mismatch I created a custom annotation and added to the .save() method in both repositories.
Annotation:
#Retention(RUNTIME)
#Target(METHOD)
public #interface AclManaged {}
Used as:
#Override
#AclManaged
Entity1 save(Entity1 entity);
And the advice:
#Around("#annotation(aclManaged)")
public Object aclManaged(ProceedingJoinPoint joinPoint, AclManaged aclManaged) throws Throwable {
log.info("Acl managed");
return joinPoint.proceed();
}
Same story - the annotation works in one repository but doesn't fire for the repository where the execution(..save..) pointcut had failed.
For testing purposes I am invoking both methods by POSTing an empty entity from Postman to each respective repository rest endpoint. However the same issue happens when the repository is invoked directly from my code.
The repositories code for the sake of completeness (the inconsistent behaviour occurs even with the most basic repo implementation):
--- EDIT: I simplified the repositories to the bare minimum ---
#RepositoryRestResource(collectionResourceRel = "entity1s", path = "entity1s")
public interface Entity1Repository extends PagingAndSortingRepository<Entity1, Long> {
// this is the method that is supposed to fire two distinct advices
#Override
#AclManaged
Entity1 save(Entity1 entity);
}
#RepositoryRestResource(collectionResourceRel = "entity2s", path = "entity2s")
public interface Entity2Repository extends PagingAndSortingRepository<Entity2, Long> {
// both aspects match perfectly
#Override
#AclManaged
Entity2 save(Entity2 ics);
}
Questions
What can be blocking the AOP aspects? How does Spring populate the invocation interceptor chain?
What's the best way to troubleshoot AOP aspects? (given both execution and annotation pointcuts fail)
On a slightly different note - is it advisable to use JPA auditing for ACL management instead of AOP?
Versions
spring 5.0.8.RELEASE, spring data rest 3.0.9.RELEASE, spring data jpa 2.0.9.RELEASE (all managed by Spring Boot 2.0.4)
The issue seems to be caused by an unfortunate combination of Spring Booot's #Enable... annotations and my #Configuration classes. The framework seems to have a few different ways to determine class/interface proxying (stemming from #EnableCaching, #EnableTransactionManagement, #EnableAspectJAutoProxy, #EnableAsync). In certain situations they seem to hijack the expected behaviour. I was able to restore the expected behaviour by:
Adding proxyTargetClass=true to all #Enable.. annotations
Moving the #Enable... annotations to relevant #Configuration classes
I wasn't able to determine which combination in particular was causing the inconsistent behaviour i.e. I don't have a minimal replicable test case.
I am still interested in a more erudite insight as to the root causes behind the inconsistent MethodInvocationInterceptor chains.
I need to somehow flag a transaction. Need some method like: TransactionAspectSupport.setData(someObject); Then till the transaction is alive I would like to be able to read those data.
I need it to check in Aspect class that some operation on current transaction has been already proceeded.
EDIT:
To illustrate what I mean. Let's have two classes.
Service class:
class Service {
#Transactional
public void serviceA(){
// do something
serviceB();
}
#Transactional
public void serviceB(){
// do something
}
}
Aspect class:
#Aspect
class ServiceAspect {
#Pointcut("execution(public * service*)")
private void checkTransaction(){};
#Around("checkTransaction()")
public Object _checkTransaction(ProceedingJoinPoint joinPoint) throws Throwable {
if (!isTransactionFlagged())
doTheCheckingJob()
transactionInfo.setData(Boolean.True);
}
private boolean isTransactionFlagged() {
if (transactionInfo.getData != null)
return true;
return false;
}
}
As you can see I need to flag the transaction somehow, so the aspect do not fire multiple times in one transaction. But still need the aspect to fire for each service method. Just not fire on the inner call of service method.
You can achieve what you want without manual bookkeeping using cflow() or cflowbelow() pointcuts. Unfortunately they are unavailable in Spring AOP, but if you configure Spring to use AspectJ with LTW (load-time weaving) instead, you can use them.
If you want to stick with Spring AOP, though, you indeed need to do manual bookkeeping, e.g. using a ThreadLocal member (assuming that each transaction is executed in a separate thread) or whatever else is appropriate. But maybe there are on-board means in the Spring framework to enable the behaviour you need. I am not a Spring user, so I cannot help you there, though. I only know about AOP.
Feel free to ask follow-up questions and/or provide more code, then I can get more concrete.
I'm trying to get the clear picture of how this works:
-What is the advantage of using DAO class with DAO interface?
-How to handle Hibernate exceptions i.e
public String doSomething(){
Session session = sessionFactory.getCurrentSession();
session.beginTransaction();
Query query = session.createQuery("");
Object o = query.uniqueResult();
session.close();
return "success";
}
I'm not "forced" to try/catch, so how do I catch/intercept any exceptions that may occur?
-Is opening session everytime for new DAO method more expensive than getting the current session? Should I close the session if I use get?
Question Update:
I have #Service annotation in my service classes and for each method using dao interface I've got #Transactional above. Also I've added #Repository to all my DAO classes
Update II:
I'm considering opening bounty for this questions because I want to know more details and this time I'll provide some.
Spring context
Controller
Service Interface
Service Implementation
DAO interface
DAO implementation
So I want to utilize spring MVC as much as possible, how do I make session opening/closing handled by #Transactional?
How do I catch the exceptions(i.e. non existing record or database failed) if any.
What I'm doing wrong? Can anyone suggest some improvements?
A few things on the hibernate session side...
1.) I would take a look a integrating Spring's transaction management into your project. This way you don't have to worry about opening and closing your session's because Spring will handle this for you with intercepts using the #Transactional annotation.
2.) If spring is handling your transactions you won't have to worry about doing the finally calls to make sure everything is closed or rolled back.
3.) If you decide not to use Spring to manage the sessions you should not keep it open for any extended period of time but again, if you use Spring you don't have to worry about it.
As far as the interface on the DAO classes i offer this...
1.) It is considered a good design practice to code to interfaces (see comments left below) and here are a few good reasons why.
lets say you have a...
public interface ShoppingCartService{
public void doStuff(Object obj);
}
You could expose this service as a servlet and deal with the just the 'contract' your interface creates or even hide the fact your using Hibnerate, JDBC or anything else...
#Service
public class PetShopShoppingCartService implements ShoppingCartService{
#Transactional(propagation=Propagation.REQUIRED)
public void doStuff(Object obj){
//The Pet Shop service impl uses hibernate!;
}
}
or...
public class DrugStoreShoppingCartService implements ShoppingCartService{
public void doStuff(Object obj){
//The Drug Store service uses JDBC;
}
}
Or even...
public class NextBigThingShoppingCartService implements ShoppingCartService{
public void doStuff(Object obj){
//do stuff with next big thing;
}
}
I think you get the picture. If you developing public api's or exposing services this becomes pretty important.
Lastly, another good reason to have the interfaces is with working in a team of more than a couple developers. You can quickly stub out an interface, check-in and tell everyone else that this is how it's going to look. This allows them to see what's important and even mock their own impl if they need to (ThingServiceMockDataImpl)
Just because you're not forced to catch exceptions when using Spring's HibernateTemplate doesn't mean that they will not get thrown. They will just be RuntimeExceptions instead of checked exceptions. Also, getCurrentSession() does not open a new session each time you call it, it returns the local Thread's Session.
There are lots of advantages, including lack of code coupling, encapsulation, and transaction demarcation for using the DAO strategy instead of putting your data access code directly into your controller. See http://java.sun.com/blueprints/corej2eepatterns/Patterns/DataAccessObject.html for more info.
Environment: Spring 3, Custom Transaction Management, JDBC Transactions
I just read the Spring docs on using the transaction template to handle transaction management. It seemed overly complex so I want to ask:
Most of my transactions are JDBC related, meaning I just declare an #Transactional on my service. But now I am making a REST service call to another site which needs to rollback if any of the following JDBC operations fail, I'll provide the rollback code in this case.
As I progress in my method, in my transaction - I want to save a reference to the REST service call (needed to roll back that action), and upon exception I just want a method myCustomRollback() called which can access the previously stored object.
Why not just provide a map in the transactionTemplate for storing stuff and define a custom rollback method on the #Transactional annotation?
This is the way I think about it, I'm not following the way Spring thinks about this. Can someone help me bridge the gap between what I want and how I accomplish it most efficiently in Spring? I only need to do this for a few special case operations.
To anyone still reading this:
I solved a similar problem with spring events - as suggested by Den Roman in option 3.
Here's the basic idea (scenario is fictional):
Whenever I perform external operations that need to be rolled back together with the transaction, I publish an event inside my #Transactional method using support from spring (org.springframework.context.ApplicationEventPublisher):
#Transactional
public String placeOrder(Order order) {
String orderId = orderServiceGateway.createOrder(order);
applicationEventPublisher.publishEvent(new OrderCreatedEvent(orderId));
workflowService.startWorkflow(orderId);
return orderId;
}
The event itself can be any object - I created a POJO with details about the remote entity to be deleted.
Then I registered a special event listener that is bound to a transaction phase - in my case to the rollback:
#TransactionalEventListener(phase = TransactionPhase.AFTER_ROLLBACK)
public void rollBackOrder(OrderCreatedEvent orderCreatedEvent) {
String orderId = orderCreatedEvent.getOrderId();
orderServiceGateway.deleteOrder(orderId);
}
Of course, it's recommended to catch & log the exception from rollback operation, not to lose the original exception from the placeOrder() method.
By default these events are synchronous, but they can be made async by additional configuration.
Here's a very good article on this mechanism, including detailed configuration and pitfalls: Transaction Synchronization and Spring Application Events (DZone)
While I don't like the solution 100% because it clutters the business logic with event publishing stuff and binds to spring, it definitely does what I expect it to do and makes it possible to pass context from the transactional method to the rollback method - which is not available through a traditional try/catch block outside of the transactional method (unless you put your context in the exception itself, which is not very nice).
I've re-read your question a few times and am not sure I understand your question completely. I assume your executing someCode and if that fails you would like to execute myCustomRollback which has some information about someCode. So I'll try to provide a Generic answer.
If you want spring to rollback some code. It will only rollback that which is rollBackAble, like jdbc transactions. Assume you have a method which performs 2 calls.
#Transactional
public void doStuff(SomeEntity entity, File file) {
persist(entity);
customFileService.createOnFileSystem(file);
throw new RunTimeException();
}
So the code above will always rollback. It will undo the persisting of your entity, but not the creation of your file, since that is not managed by Spring transactions, unless you provide custom implementation for it to be.
Second, Spring provides 2 ways of working with transactions:
Spring AOP: a proxy is created at runtime which will decorate your code with transactional stuff. If your class would be named MyClass, then Spring will create a class names MyClassProxy, which will wrap your code in transactional code.
AspectJ: at compile time your .class file will be adjusted and transactional code will be embedded inside your method.
The aspectJ approach seems harder to configure, but isn't so much and is way easier to use. Since anything which is annotated with #Transactional will be embedded (weaved) with code. For Spring AOP this is not the case. Transactional inner method calls for instance in Spring will be ignored! So aspectJ provides a more intuitive approach.
Back to what I think your question is (the code is all in 1 class):
public void doSomeCode() {
Object restCall = initialize();
try {
execute(restCall);
} catch (CustomException e) {
myCustomRollback(restCall; e);
}
}
#Transactional(rollbackFor = CustomException.class)
private void execute(Object restCall) throws CustomException {
// jdbc calls..
restCall = callRest(restCall);
throw new CustomException();
}
void myCustomRollback(Object restCall, CustomException e) {
...
}
The code above will only work with AspectJ! Since your making inner method calls which also seems to be private! AOP at runtime cannot handle this.
So what happens is everything (which is rollbackAble) in execute will be rollbacked. And in doStuff you have information about the objects which were used in execute, you now can use in myCustomRollback to rollback your REST stuff manually.
Not sure if I answered this question properly, but I hope it helps someone with a similar problem.
1 solution is to implement your own transactional manager by extending a one
2 solution is to use TransactionSynchronizationManager class
3 solution is to use #TransactionalEventListener in case you have Spring 4
Spring transaction management the default behavior for automatic rollback is for unchecked exceptions
so for a custom exception,
#Transactional(rollbackFor = CustomException.class, noRollbackFor = RuntimeException.class)
public void doSomething(...
)
the transaction be rolled back if it there is an exception that matches the specified. If an exception not matches, it is propagated to caller of the service or TransactionRolledBackException wrapper
if you use use the org.springframework.transaction.PlatformTransactionManager it is more manageable handling exceptions than template
check the documentation http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/transaction.html
you can use the AfterThrowing advice (when an exception is thrown) & call your method (myCustmRollback()) there, you can use TransactionSynchronizationManager class to get thecurrent transaction & roll it back...
alternatively.. you can use the AroundAdvice to begin & commit/rollback your transaction (this way you can use the spring provided transaction manager by using the TransactionSynchronizationManager class)