Spring framework is one of the most widely used framework in Enterprise application development. It has so many features such as Dependency injection, Data access integration, MVC, AOP which takes care of most of the boilerplate part of project, and developers can then focus on business logic only.
One of the important feature in Spring is AOP. It is used by almost every enterprise application which is being developed using Spring.
This article is for those who know AOP, have used AOP in their projects but do not know how Spring or other DI frameworks provide this functionality.
Now lets take an example, suppose we want to process user related data. But we also want to mandate authorized users access only. So we add an interface UserService to our project.
You must have noticed AuthorizationRequired annotation. Well we use this annotation to specify required authorization for current user.
Now, we bind the dependency in application context.
Lets define our Aspect as
If you know the Proxy class and has worked with reflection then you should have an idea that how Spring would be doing this internally. Even if you don't, no problem. Lets see how Spring or any DI framework would be doing this.
To intercept the calls we need to have a Proxy, which will perform some operation which we specify and then delegate the call to real worker(in our case class). So we use proxy pattern to perform this, but what is Proxy pattern? It is -
There are two ways to create Proxy classes in Java
JDK Proxy allows us to create proxy classes at runtime with specified interfaces. To use Proxy class, we also need an InvocationHandler. When calls to a proxy class are made, calls are delegated to InvocationHandler. In our case, we already have an interface UserService and implementation UserServiceImpl. Lets create an InvocationHandler first,
UserServiceInvocationHandler accepts UserServiceImpl as we want to delegate the calls to real class after intercepting the calls. Now to create a Proxy class of UserService we use static method of Proxy class,
But as stated earlier, JDK proxy allows us to create proxy class using interfaces only. So what if we have a method in the implementation which is not declared in the interface? Well, calls to these methods cannot be intercepted using JDK proxy.
To create CGLib based proxy, we need InvocationHandler same as JDK proxy InvocationHandler but from CGLib package.
Lets create a proxy class,
Although this proxy will intercept the calls but our logic to check authorization will fail which you can figure out why :)
One of the important feature in Spring is AOP. It is used by almost every enterprise application which is being developed using Spring.
AOP
So what is AOP? Definition of AOP -Aspect oriented programming is a programming paradigm which aims to increase modularity by allowing the separation of cross cutting concerns.Well, my understanding of AOP is - AOP allows us to introduce/join new modules in your project at pre-specified dynamic locations without having to code for it. Traditional examples of AOP is Transaction management, logging etc. You can read more about AOP at here.
This article is for those who know AOP, have used AOP in their projects but do not know how Spring or other DI frameworks provide this functionality.
Now lets take an example, suppose we want to process user related data. But we also want to mandate authorized users access only. So we add an interface UserService to our project.
@AuthorizationRequired(role=ADMIN) public interface UserService { public User getUserById(Long id); } // dummy implementation public class UserServiceImpl implements UserService { public User getUserById(Long id) { // logic to get user information from database } }
You must have noticed AuthorizationRequired annotation. Well we use this annotation to specify required authorization for current user.
Now, we bind the dependency in application context.
Lets define our Aspect as
@Pointcut("execution(* com.techsach.example.services..*.*(..))") public void beforeAnyServiceMethod(){} @Before("beforeAnyServiceMethod()") public void before(JoinPoint joinPoint) throws AuthorizationException { MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); Method method = methodSignature.getMethod(); AuthorizationRequired annotation = method.getAnnotation(AuthorizationRequired.class); // now we check whether current user had role specified in Annotation, if not then throw exception }This will tell Spring to intercept all the methods of all classes inside services package. Before executing any method from services package, execute our code which validates that current user has authorization to perform current operation.
If you know the Proxy class and has worked with reflection then you should have an idea that how Spring would be doing this internally. Even if you don't, no problem. Lets see how Spring or any DI framework would be doing this.
To intercept the calls we need to have a Proxy, which will perform some operation which we specify and then delegate the call to real worker(in our case class). So we use proxy pattern to perform this, but what is Proxy pattern? It is -
Proxy pattern is an interface which is functioning as something elseGenerally, interfaces are used in code and concrete implementations are bound in the application context. By using this abstraction we can replace the different implementation without changing the code.
There are two ways to create Proxy classes in Java
- JDK Proxy
- CGLib Proxy
JDK Proxy
JDK Proxy allows us to create proxy classes at runtime with specified interfaces. To use Proxy class, we also need an InvocationHandler. When calls to a proxy class are made, calls are delegated to InvocationHandler. In our case, we already have an interface UserService and implementation UserServiceImpl. Lets create an InvocationHandler first,
public class UserServiceInvocationHandler implements InvocationHandler { private UserServiceImpl userServiceImpl; public UserServiceInvocationHandler(UserServiceImpl userServiceImpl) { this.userServiceImpl = userServiceImpl; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // here we can invoke our own logic before executing method of real class // We can execute code from 'before' method which we defined earlier in our Aspect return method.invoke(userServiceImpl, args); } }
UserServiceInvocationHandler accepts UserServiceImpl as we want to delegate the calls to real class after intercepting the calls. Now to create a Proxy class of UserService we use static method of Proxy class,
// implementing class UserServiceImpl userServiceImpl = new UserServiceImpl(); // proxy UserService userServiceProxy = Proxy.newProxyInstance( UserServiceImpl.class.getClassLoader(), UserServiceImpl.getInterfaces(), new UserServiceInvocationHandler(userServiceImpl));This will return the proxy instance of UserService, which will intercept the calls to UserServiceImpl and execute our code before invoking real method.
But as stated earlier, JDK proxy allows us to create proxy class using interfaces only. So what if we have a method in the implementation which is not declared in the interface? Well, calls to these methods cannot be intercepted using JDK proxy.
CGLib Proxy
CGLib allows us to create proxy classes at runtime by creating sub class of specified class using Byte code generation. CGLib proxies are used in the case where Proxy is to be created for those class which does not have any interfaces or have methods which are not declared in the implementing interface.To create CGLib based proxy, we need InvocationHandler same as JDK proxy InvocationHandler but from CGLib package.
public class UserServiceImplInvocationHandler implements net.sf.cglib.proxy.InvocationHandler { private UserServiceImpl userServiceImpl; public UserServiceImplInvocationHandler(UserServiceImpl userServiceImpl) { this.userServiceImpl = userServiceImpl; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // we invoke 'before' method of our Aspect here to check user authorization. return method.invoke(userServiceImpl, args); } }This invocation handler will be used to create proxy class of UserServiceImpl.
Lets create a proxy class,
// object of implementing class UserServiceImpl userServiceImpl = new UserServiceImpl(); // create proxy using CGLIB Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(UserServiceImpl.class); enhancer.setCallback(new UserServiceImplInvocationHandler(userServiceImpl)); UserServiceImpl userServiceImplProxy = (UserServiceImpl)enhancer.create();Above code will create UserServiceImpl proxy, which will intercept all public methods of UserServiceImpl class.
Although this proxy will intercept the calls but our logic to check authorization will fail which you can figure out why :)
A good concise article.
ReplyDeleteThanks dc. Hong Zhang :)
Delete