New AOP Mechanism

Liferay is heavily using Spring's aop. Spring's aop is doing an amazing job to make your life easy by making your code clean.

So why i am creating something new, if the exist one is good enough? Spring is taking care of general use cases, but Liferay's use case is a little bit special, so it gives me a chance to improve.

Let's see how does Spring do the general aop first.
Basically the aop is a wrapper on top of the real bean, so it gives you a chance to do extra processing before(or after, or throwing, or finally) real logic. And to make this more powerful, the wrapper can wrap another wrapper, which means multiple layers aop support.

Your extra processing is put inside advice class, and the advice will be binded to certain point-cut at runtime. The following picture demonstrates this.


Besides your own logic inside advices, which parts contribute the most overhead? I mean the overhead purely generated by the framework itself.
There are two parts:

  1. The aop wrapper's creation and invoking.
  2. The runtime wiring between advice and point-cut.


For 1, each aop wrapper is a JDK dynamic proxy object(or CGLib proxy), as you all know the creation of proxy object is kind of heavy. And the generated code introduces a few more stack-calls, so the more aops you have, the deeper the stack is. If you have ever seen liferay's thread dump, you will know what i am talking about.
For 2, the wiring between advice and point-cut is using a regular express like pattern matching which is a complex calculating process. So the more point-cuts you have and the more complex the patterns are, the slower your code is.

Now you should see, to improve the performance, we should create aop wapper objects as fewer as possible, we should use fewer point-cuts.
These rules can not be applied to Spring itself, since for general purpose processing, you can not limit the usage for them.

But for liferay, things are different, the key point is, the aop advices are mainly for service beans.

First, we could limit the point-cut to just service beans, if an advice only cares a few service beans, it can do a second matching by bean name or annotation. Either way is cheaper than regex.

Second, since advice itself will take care of secondly matching, all aop wrappers are just against service beans. So why bother creating a new wrapper for each advice? All advices could share the same wrapper which is created by the first invoked advice, then all other advices will be invoked as a chain. The following picture demonstrates this.


So this reduce Na(advice number) aop wrapper to 1, Np(point-cut number) point-cut matching to 1(still needs a few secondly matching, but they are much cheaper).

Since the advices now are chained up as a linked list, it becomes possible to modify aops structures esaily at runtime, just insert or remove elements from the list. By this way you can take out the unused advices complete(not just disable them, even they are disabled, they still cause stack calls) for performance, or apply new advices without restart the server(of course you have to take care of the thread safe property yourself).

So for a complex system(Liferay system) with a lot of aop usage, this can improve performance significantly.

For more detail info, please see LPS-9793 and LPS-9795