The Aspect-oriented programming (AOP) library AspectJ is useful for intercepting method calls and inserting arbitrary processing in a JVM environment, and frames. It is often used as a work implementation.
For example, there are quite a few cases where AspectJ (CGLIB proxy [^ cglib]) is used to realize transactions with @Transactional
annotation even in the field of Spring Framework [^ aop-impl].
[^ cglib]: Although it is referred to as "CGLIB proxy" in the official Spring documentation, it refers to AspectJ (GCLIB is the name of the code generation library used internally by AspectJ).
[^ aop-impl]: Spring uses by default [java.lang.reflect.Proxy](https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/ lang / reflect / Proxy.html) allows AOP only for interface, and there are quite a few cases where AspectJ is used because of its restrictions.
The widely used AspectJ is actually useful, but in projects where it works ** "My @ Transactional
is ignored after hours of trial and error ... why ...! " I heard the sad cry of ** many times at various sites.
In Spring, if you read through the chapters of the official document Chapter 6. Aspect Oriented Programming with Spring, you can see the background. I know all the working principles and possible options, but if you're having trouble with ** "I want to move my AOP right now !!" **, see below:
Symptoms of AOP not working if the following conditions are met:
--Class or method is final
--static
method
Let's make it possible to override.
Common patterns:
--Called by this. method ()
--A method call is being made to the return this;
--new your class ()
AspectJ achieves AOP by the following steps:
[^ override]: That's why the target method needs to be able to override
The important point is that ** AspectJ creates another class and its instance (wrapper) that inherits from your class **.
So if you call a method on an instance of the wrapper, AOP will work, but if you call a method on an instance of the original class, AOP will not work at all.
The workaround is to get the instance from which the method is called from the framework (which cares for AspectJ).
For example, the specific workaround for the above-mentioned 2)
pattern is as follows:
--Called by this. method ()
--this
in the method of your class is the original instance, not the wrapper
--Get your (wrapper) instance ** from the framework and call it with DI, etc. instead of this
(= you'll get a wrapper for AspectJ)
--A method call is being made to the return this;
--Use the instance obtained from the framework by DI etc. and do not expose this
--new your class ()
--Let's let the framework create the instance (define it as Bean / Component in Spring)
As those who have read all the explanations so far already know, it comes down to the simple principle that ** AspectJ calls the method without going through the wrapper generated by inheritance / override **.
In articles in the world, there are times when there is an explanation and enumeration that "AspectJ does not work well in that case, in such a case, in such a case", and when I saw that, "What a complicated mysterious library. I've seen people despaired many times, but it's essentially just that (I'm likely to explain that in the future, so the text I summarized it in).
Even if the specific examples mentioned so far do not directly apply, you can probably find the cause by thinking of the operating principle of AspectJ mentioned in this article.
@Transactional
's rollbackFor
By default, a transaction will be rolling back on RuntimeException and Error but not on checked exceptions (business exceptions). See DefaultTransactionAttribute.rollbackOn(Throwable) for a detailed explanation.
Although irrelevant to the subject of this article, Spring's @Transactional
does not rollback the checked exception (eg ʻIOException) by default, so" the transaction was committed even though an exception occurred. There is often a story saying "Ta!". Therefore, there is some know-how that it is less confusing to explicitly specify
rollbackFor`.
Recommended Posts