CDI AOP Tutorial: Java Standard Method Interception Tutorial - Java EE
Before we added AOP support when we looked up the atm, we looked up the object directly as shown in figure 1, now that we applied AOP when we look up the object we get what is in figure 2. When we look up the atm in the application context, we get the AOP proxy that applies the decoration (advice, method interceptor) to the atm target by wrapping the target and delegating to it after it invokes the series of method interceptors.
Victroy lap
The last code listing works just like you think. If you use simulateLogin, atm.deposit does not throw a SecurityException. If you use simulateNoAccess, it does throw a SecurityException. Now let's weave in a few more "Aspects" to the mix to drive some points home and to show how interception works with multiple interceptors.
I will go quicker this time.
LoggingInterceptor
package org.cdi.advocacy;
import java.util.Arrays;
import java.util.logging.Logger;
import javax.interceptor.AroundInvoke;
import javax.interceptor.Interceptor;
import javax.interceptor.InvocationContext;
@Logable @Interceptor
public class LoggingInterceptor {
@AroundInvoke
public Object log(InvocationContext ctx) throws Exception {
System.out.println("In LoggingInterceptor");
Logger logger = Logger.getLogger(ctx.getTarget().getClass().getName());
logger.info("before call to " + ctx.getMethod() + " with args " + Arrays.toString(ctx.getParameters()));
Object returnMe = ctx.proceed();
logger.info("after call to " + ctx.getMethod() + " returned " + returnMe);
return returnMe;
}
}
Now we need to define the Logable interceptor binding annotation as
follows:
package org.cdi.advocacy;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.*;
import javax.interceptor.InterceptorBinding;
@InterceptorBinding
@Retention(RUNTIME) @Target({TYPE, METHOD})
public @interface Logable {
}
Now to use it we just mark the methods where we want this logging.
AutomatedTellerMachineImpl.java using Logable
package org.cdi.advocacy;
...
@Secure
public class AutomatedTellerMachineImpl implements AutomatedTellerMachine {
...
@Logable
public void deposit(BigDecimal bd) {
System.out.println("deposit called");
transport.communicateWithBank(null);
}
public void withdraw(BigDecimal bd) {
System.out.println("withdraw called");
transport.communicateWithBank(null);
}
}
Notice that we use the @Secure at the class level which will applies the security interceptor to every mehtod in the AutomatedTellerMachineImpl. But, we use @Logable only on the deposit method which applies it, you guessed it, only on the deposit method.
Now you have to add this interceptor to the beans.xml:
META-INF/beans.xml
<beans
...
<interceptors>
<class>org.cdi.advocacy.LoggingInterceptor</class>
<class>org.cdi.advocacy.security.SecurityAdvice</class>
</interceptors>
</beans>
When we run this again, we get something like this in our console
output:
May 15, 2011 6:46:22 PM org.cdi.advocacy.LoggingInterceptor log INFO: before call to public void org.cdi.advocacy.AutomatedTellerMachineImpl.deposit(java.math.BigDecimal) with args [1.00] May 15, 2011 6:46:22 PM org.cdi.advocacy.LoggingInterceptor log INFO: after call to public void org.cdi.advocacy.AutomatedTellerMachineImpl.deposit(java.math.BigDecimal) returned nullNotice that the order of interceptors in the beans.xml file determines the order of execution in the code. (I added a println to each interceptor just to show the ordering.) When we run this, we get the following output.
Output:
In LoggingInterceptor In SecurityAdviceIf we switch the order in the beans.xml file, we will get a different order in the console output.
META-INF/beans.xml
<beans
...
<interceptors>
<class>org.cdi.advocacy.security.SecurityAdvice</class>
<class>org.cdi.advocacy.LoggingInterceptor</class>
</interceptors>
</beans>
In SecurityAdvice In LoggingInterceptorThis is important as many interceptors can be applied. You have one place to set the order.
Conclusion
AOP is neither a cure all or voodoo magic, but a powerful tool that needs to be in your bag of tricks. The Spring framework has brought AOP to the main stream masses and Spring 2.5/3.x has simplified using AOP. CDI brings AOP and DI into the standard's bodies where it can get further mainstreamed, refined and become part of future Java standards like JCache, Java EE 6 and Java EE 7.
You can use Spring CDI to apply services (called cross-cutting
concerns) to objects using AOP's interception model. AOP need not seem like
a foreign concept as it is merely a more flexible version of the
decorator design pattern. With AOP you can add additional behavior to
an existing class without writing a lot of wrapper code.
This can be a real time saver when you have a use case where you need to apply
a cross cutting concern to a slew of classes.
To reiterate...
CDI is the Java standard for dependency injection and interception (AOP). It is evident from the popularity of DI and AOP that Java needs to address DI and AOP so that it can build other standards on top of it. DI and AOP are the foundation of many Java frameworks. I hope you share my excitement of CDI as a basis for other JSRs, Java frameworks and standards.
CDI is a foundational aspect of Java EE 6. It is or will be shortly supported by Caucho's Resin, IBM's WebSphere, Oracle's Glassfish, Red Hat's JBoss and many more application servers. CDI is similar to core Spring and Guice frameworks. However CDI is a general purpose framework that can be used outside of JEE 6.
CDI simplifies and sanitizes the API for DI and AOP. I find that working with CDI based AOP is easier and covers the most common use cases. CDI is a rethink on how to do dependency injection and AOP (interception really). It simplifies it. It reduces it. It gets rid of legacy, outdated ideas.
CDI is to Spring and Guice what JPA is to Hibernate, and Toplink. CDI will co-exist with Spring and Guice. There are plugins to make them interoperate nicely (more on these shortly).
This is just a brief taste. There is more to come.Resources
- CDI Source
- CDI advocacy group
- CDI advocacy blog
- CDI advocacy google code project
- Google group for CDI advocacy
- Manisfesto version 1
- Weld reference documentation
- CDI JSR299
- Resin fast and light CDI and Java EE 6 Web Profile implementation
- CDI & JSF Part 1 Intro by Andy Gibson
- CDI & JSF Part 2 Intro by Andy Gibson
- CDI & JSF Part 3 Intro by Andy Gibson
About the Author
This article was written with CDI advocacy in mind by Rick Hightower with some collaboration from others.
Rick Hightower has worked as a CTO, Director of Development and a Developer for the last 20 years. He has been involved with J2EE since its inception. He worked at an EJB container company in 1999. He has been working with Java since 1996, and writing code professionally since 1990. Rick was an early Spring enthusiast. Rick enjoys bouncing back and forth between C, Python, Groovy and Java development. Although not a fan of EJB 3, Rick is a big fan of the potential of CDI and thinks that EJB 3.1 has come a lot closer to the mark. Rick Hightower is CTO of Mammatus and is an expert on Java and Cloud Computing.
There are 18 code listings in this article(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)





Comments
Jonathan Fisher replied on Wed, 2011/05/25 - 1:52pm
Reza Rahman replied on Thu, 2011/05/26 - 3:11pm
Krzysztof Kowalczyk replied on Fri, 2011/05/27 - 5:55am
class InterceptedBean{ @MethodReturnOne <-- interceptor, always returns 1 Integer x(){ return 2L; } Integer y(){ return x(); } }x() would return 1 when called outside of the bean, but when called inside the bean -> y() will return 2L. So interceptor is not applied on all method invocations. In CDI it's quite easy to create scope-cached functions for instance, what is really handy considering JSF bindings behavior. Unfortunately in practice private or protected functions cannot be cached in that way and public functions cannot be reused in the same bean. That's annoying.Reza Rahman replied on Fri, 2011/05/27 - 4:45pm
in response to:
Krzysztof Kowalczyk
The trivial fix would be:
class InterceptedBean{ @MethodReturnOne <-- interceptor, always returns 1 Integer x(){ return 2L; }@MethodReturnOne <-- interceptor, always returns 1 Integer y(){ return x(); } }The big benefit to using proxying vis-a-vis AspectJ style byte-code injection is class-loader simplicity/performance. CDI interceptors are also way easier than arcane AspectJ point-cut syntax.
Krzysztof Kowalczyk replied on Sat, 2011/05/28 - 12:18pm
in response to:
Reza Rahman
Reza Rahman replied on Sat, 2011/05/28 - 8:22pm
in response to:
Krzysztof Kowalczyk
I think you are overstating the issue a bit.
Even in the Spring world, AspectJ use is very rare. Take a look at this graph. As you can see, AspectJ use is quite insigficant, even in the Spring world.
If the issue you noted is really that critical, you'd see more AspectJ use, right :-)?
Krzysztof Kowalczyk replied on Sun, 2011/05/29 - 8:21am
in response to:
Reza Rahman
Reza Rahman replied on Mon, 2011/05/30 - 3:07pm
I thought I was being very clear about the rationale, but I'll try again for you:
CDI is based on proxying, exactly like Spring AOP (so it too will have the issue you are concerned about). CDI (and Java EE in general) does not mandate direct byte-code manulation/code-generation like AspectJ does (that is the only way of implementing what you are looking for -- that is interception at the class instance level rather than injection proxy level). This is done primarily because of class-loading/deployment/implemention simplicity as well as performance (manipulating byte code/generating code is a lot more performance intensive rather than simple Java proxying via sub-classing).
CDI/Java EE is crystal clear that interceptors are only applied via injected proxy -- it is not applied when making a self-call or a call on a direct class instance (likely created via "new" instead of injection). Just like anyone using Spring AOP, anyone using CDI interceptors should be aware of this before the fact. If interceptors are really needed during self-call the solution is quite simple -- do a self-injection like this:
class InterceptedBean {@MethodReturnOne <-- interceptor, always returns 1 Integer x(){ return 2L; }Integer y(){ return self.x(); } }As I said, in practice this case is very rare which is why Spring AOP and CDI interceptors are sufficient for most cases as compared to full-blown AspectJ. For example, I have not come across it once in ten years of real-world enterprise developement and I have seen a self-call example only in the Java EE compatibility test kit (and was surprised when I saw it).
Hope this helps.
Krzysztof Kowalczyk replied on Mon, 2011/05/30 - 5:29pm
You claim that you never have seen such use case, so you say that you never invoke a method from another method? (joke ;) ) CDI is general purpose solution and should not constrain itself to procedural style code.
You mentioned subclassing. If we used exactly such style of aop, it would behave exactly as I expect it to behave. The virtual dispatch would be fulfilled, it would not change behavior of not managed instances and it is also one of fastest(runtime) solutions . It is semantically equivalent to a class:
//Proxy class ReturnOne extends InterceptedBean{ Integer x(){return 1; } }This would cause y() to return 1 (because x is a virtual method)! It can be implemented at injection proxy level as subclass, the original class is not changed in any way. So again, this has nothing to do with AspectJ way. Subclassing is actually easiest way to implement aop afaik, but behaves differently than EJB interceptors.
The behavior of EJB interceptor points rather to a "subclass and delegate" implementation style, or proxy with dynamic filters on method invocations handlers, both are slower in theory, but in practice it should not make a big difference. So I cannot agree with your comments about performance issues.
Reza Rahman replied on Tue, 2011/05/31 - 3:49am
in response to:
Krzysztof Kowalczyk
Spring AOP and CDI interceptors are good enough for the vast majority of real world use cases and both AOP system are clear that they require interception only on injected proxy calls. For the remaining cases, it is possible to use AspectJ with CDI, Java EE or pretty much anything else.
Now, I believe I've said everything that needs to be said on this. I don't have the time or patience to explain to you why the imaginary code you have above makes little sense in a real-world proxy-based container. You'll have to find that out on your own by looking at Weld, OpenWebBeans, CanDI or Spring AOP. The way I see it, I have paid this more attention than most people would.
If you really feel that strongly about it, feel free to bring it to the attention of the CDI 1.1 EG that is just currently underway: http://www.jcp.org/en/jsr/proposalDetails?id=346. The JCP page references a JIRA instance for CDI/Weld that you can also use.
Hope it helps.
Krzysztof Kowalczyk replied on Tue, 2011/05/31 - 3:16am
Btw. I've checked JBoss interceptor implementation. It is using load time bytecode manipulation. So CDI does mandate bytecode manipulation, whitout it one cannot create dynamic proxy for classes, only for interfaces. It also uses "delegate" style proxy. Spring AOP does the same, but it actually call the self-invocation behavior as "issue" and is explicit about this behavior in documentation. Thought you'd like to know.
Regards, Krzysztof Kowalczyk
Reza Rahman replied on Tue, 2011/05/31 - 3:54am
Krzysztof Kowalczyk replied on Tue, 2011/05/31 - 6:22am
Krzysztof Kowalczyk replied on Tue, 2011/05/31 - 6:37am
Reza Rahman replied on Tue, 2011/05/31 - 1:00pm
in response to:
Krzysztof Kowalczyk
I think you should look at real world implementations instead of simply thinking in a void of how this should be. There is more to any of this than unfortunately readily meets the eye. I mentioned some of those concerns already -- implementation (both simplicity and flexibility)/class-loading/deployment/code-generation/byte-code manipulation/proxying/performance/overall container architecture/innovation and so on.
However, as I promised, I'll bring up the issue for you anyway.
Krzysztof Kowalczyk replied on Wed, 2011/06/01 - 1:28pm
https://issues.jboss.org/browse/CDI-44
https://issues.jboss.org/browse/CDI-74
A bit similar discussion:
http://lists.jboss.org/pipermail/weld-dev/2010-May/002506.html
http://lists.jboss.org/pipermail/weld-dev/2010-May/002513.html (I like the example)
I did not found those links earlier. The implementation will be changed, some things are not yet specified.
I do check real world implementations, I know differences between some of them, especially considering performance and some issues caused by them (as in the case of subclass and delegate (proxy) pattern of aop), that's why it caught my attention.
Reza Rahman replied on Sat, 2011/06/11 - 7:45pm
in response to:
Krzysztof Kowalczyk
Carla Brian replied on Tue, 2012/04/03 - 12:53pm