- @annotation & @decorator
- Use Case
- Spring AOP & AspectJ
- Code Sample:
@annotation & @decorator
I was recently looking into Java @annotation and have always been comparing it with Python @decorator. @annotations and @decorators share the same form, but function differently.
Java @annotation is designed to be meta data marker. It marks certain elements like methods, classes, or variables without changing the actual work of the code. When defining an annotation in Java, a retention policy needs to be specified. The retention policy decides whether if the annotation will exist in the .class files after compilation and should it be accessible during runtime.
There are 3 types of retention policies 1:
- CLASS (default): Annotations are to be recorded in the class file by the compiler but need not be retained by the VM at run time.
- SOURCE: Annotations are to be discarded by the compiler.
- RUNTIME: Annotations are to be recorded in the class file by the compiler and retained by the VM at run time, so they may be read reflectively.
One typical example of annotation is @Override. Its retention policy is SOURCE which means it is only there for developers’ reference, and will not exist in the compiled files. JUnit @Test annotation, on the other hand, has the RUNTIME retention policy and will exist after compiled. @Test leaves a mark on the test functions and so later JUnit can know which functions are tests and should it run.
Python @decorator is an implementation of the Decorator Pattern. When a decorated method is called, a wrapper function elsewhere that defines the decorator will catch the called method, and so can apply some preprocess/postprocess logics to the decorated method. In a nutshell, a @decorator is a function that takes a target function as parameter, and then returns a wrapper function that call the target function and also do some extra works around. Python @decorators are very useful syntactic sugar and are very easy to use. An example decorator looks like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
Decorator Pattern is useful in many cases. For example:
- Log the running time for certain functions.
- RESTful Web Services usually need some user role filtering(such as user login) on some API services.
- Common constrains on database queries.
Most of the Python Web Framework implemented user login functions by decorators. So the developers only need to decorate the functions that require authentication with the provided decorator. This is very easy and keeps the code clear. I’d like to use Java @annotation to do exactly the same thing, which lead us to Spring-AOP and AspectJ.
Spring AOP & AspectJ
Spring AOP(Aspect Oriented Programming) is a technique that enables adding executable blocks to the source code without explicitly changing it. For example, we don’t want to add code blocks and conditions inside the API Web Service codes which pollute our code with multiple conditions check. Instead, we want some other classes to intercept these specific API calls, check the authentication and then decide whether to trigger the service behind the scenes. We want that interceptor to understand the annotations and intercept every call to those specific methods with the annotation. This case perfectly fits the original intent of AOP — to avoid re-implementation of some common behavior in multiple classes. In terms of AOP, our solution can be explained as creating an aspect that cross-cuts the code at certain join points and applies an around advice that implements the desired functionality. 2
Spring-AOP enables you to use @AspectJ annotation style to make aspects. Note that with proper configuration, Spring-AOP will include the @AspectJ aspects into its management scope, and so we can inject beans into aspects too.
Show Me The Code
The following code samples use Spring annotation-based configurations instead of XML-based configs. There are many tutorials and resources online regarding implementing such functions in XML-based Spring, but I didn’t find much resources on annotation-based Spring. So I’m gonna do it with annotation-based configuration and have a quick walkthrough on the code. More details on Spring annotation-based configuration and Spring-AOP including some terminologies can be found at 3.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
This is a simple aspect with a single around advice
around() inside. The aspect is annotated with @Aspect and advice is annotated with @Around. Annotation @Around has one parameter, which — in this case — says that the advice should be applied to a method if:
- its visibility modifier is * (public, protected or private);
- its name is name * (any name);
- its arguments are .. (any arguments);
- it is annotated with
@TestAnnotationwhich will be defined later;
I also inject a bean
appId into this aspect. I will discuss the proper configuration required in order to make such injection works.
When a call to an annotated method is to be intercepted, method
around() executes before executing the actual method. Method
around() receives an instance of class
ProceedingJoinPoint and returns an object.
ProceedingJoinPoint can be seen as the original method, and the return value will be used as the result of the original method. In order to call the original method, the advice has to call
proceed() of the join point object.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
This is another aspect with a single around advice. The only difference with the
TestAspect is that this aspect will read the attribute from the annotation.
1 2 3 4 5 6 7 8 9
1 2 3 4 5 6 7 8 9 10 11
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
This is a standard configuration POJO for annotation-based Spring. In order to use annotation-based configuration, we just need to annotate a config POJO with
ComponentScan, and annotate each beans with
EnableAspectJAutoProxy is required for Spring to auto discover @AspectJ aspects, and perform runtime weaving 3. In addition, the aspects also have to be declared as beans so that Spring will include them into its management scope. With this configuration, our aspects and annotations are ready to go.
1 2 3 4 5 6 7 8 9 10 11 12 13
In the service class, we simply annotate the method with our custom annotations and this method will be intercepted when it gets called.
Let’s run it…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
- @annotation can have attributes, but attribute type can only be primitives and Class<>.
- @annotation attributes can only be assigned with constant expression. Namely, you have to do
this.idis a constant.