With my continued effort to annihilate the use of CGLib library, I reached the point of dealing with Spring Framework's Transaction management. Like with other support libraries from Spring Framework, transaction support is enabled via AOP proxies.
These proxies are often created using CGLib and as such they require our beans to violate Item 13 (Minimize the accessibility of classes and members) from "Effective Java (2nd Edition) by Joshua Bloch". CGLib proxy creation requires a non-final class with default empty constructor which prevents us from declaring our fields as final (Item 15?).
I could try and use Java dynamic proxy (supported by Spring Framework) but sometimes even dynamic proxies have their limitations (we reached such limitation by declaring a type @Transanctional and @ManagedResource - both tried to create different proxy types). Simply by looking at Spring TX schema I noticed it supports AspectJ mode. After reading some posts on bytecode weaving, comparing Spring AOP with AspectJ and performance charts (you got to read this) - I decided to use AspectJ to replace any AOP usage we have in our context (other than transaction support, we use advices). Another decision I made was to use compile-time weaving instead of load-time. Spring Framework has a very easy configuration if we wish to enable load-time weaving but it requires us to modify our application server configuration.
pom.xml
First of all, to enable compile time weaving, we need to add an AspecJ compiler to our build cycle. Maven has an aspectj maven plugin that can be easily added to enable compile time weaving.
Spring Framework Application Context Configuration
Now comes the part where we tell Spring that we want transaction support using the aspectj mode.
Notice that I also included </tx:advice> which tells Spring what are the basic transaction attributes to apply for each transactional call (e.g. method calls starting with count shouldn't be allowed to modify the database state).
Bean
Note that now we can declare our bean as final and reduce the implementation visibility to package only. Not needing the default empty constructor we can also declare our fields as final. Let's review the affect made by adding the @Transanctional to our service: If we were to leave the declaration on the type we would have to wrap each and every method with a transaction support (even private methods and even when calling this from within our service). If our service implementation contains only public methods, it wouldn't make any difference, but I would still recommend annotating only the public methods and by that proxy the concrete flow with transaction support.
In conclusion, we started with a runtime retention annotation and transformed its behavior to function like it is compile time. The performance benefits are immediately shown and even other aspects enjoy the benefits of compile time weaving. I think anyone who uses Spring Framework transaction management should consider using the above solution. Now go ahead and apply it on your project!
These proxies are often created using CGLib and as such they require our beans to violate Item 13 (Minimize the accessibility of classes and members) from "Effective Java (2nd Edition) by Joshua Bloch". CGLib proxy creation requires a non-final class with default empty constructor which prevents us from declaring our fields as final (Item 15?).
I could try and use Java dynamic proxy (supported by Spring Framework) but sometimes even dynamic proxies have their limitations (we reached such limitation by declaring a type @Transanctional and @ManagedResource - both tried to create different proxy types). Simply by looking at Spring TX schema I noticed it supports AspectJ mode. After reading some posts on bytecode weaving, comparing Spring AOP with AspectJ and performance charts (you got to read this) - I decided to use AspectJ to replace any AOP usage we have in our context (other than transaction support, we use advices). Another decision I made was to use compile-time weaving instead of load-time. Spring Framework has a very easy configuration if we wish to enable load-time weaving but it requires us to modify our application server configuration.
pom.xml
First of all, to enable compile time weaving, we need to add an AspecJ compiler to our build cycle. Maven has an aspectj maven plugin that can be easily added to enable compile time weaving.
Spring Framework Application Context Configuration
Now comes the part where we tell Spring that we want transaction support using the aspectj mode.
Notice that I also included </tx:advice> which tells Spring what are the basic transaction attributes to apply for each transactional call (e.g. method calls starting with count shouldn't be allowed to modify the database state).
Bean
Note that now we can declare our bean as final and reduce the implementation visibility to package only. Not needing the default empty constructor we can also declare our fields as final. Let's review the affect made by adding the @Transanctional to our service: If we were to leave the declaration on the type we would have to wrap each and every method with a transaction support (even private methods and even when calling this from within our service). If our service implementation contains only public methods, it wouldn't make any difference, but I would still recommend annotating only the public methods and by that proxy the concrete flow with transaction support.
In conclusion, we started with a runtime retention annotation and transformed its behavior to function like it is compile time. The performance benefits are immediately shown and even other aspects enjoy the benefits of compile time weaving. I think anyone who uses Spring Framework transaction management should consider using the above solution. Now go ahead and apply it on your project!
No comments:
Post a Comment