Well, in the previous tutorial, we shed light on the concept of Aspect-Oriented Programming (AOP) in the Spring framework. Although Spring AOP works best with AspectJ which is a recommended choice. In this example, we will find out how we can implement Spring AOP by using Spring1.2 old-style AOP. This style is supported by the Spring 3 version.
Before we proceed, we should know that spring1.2 old-style supports four types of advice for aop implementation.
We can better understand the advice interface hierarchy by the view of the below diagram:
In the above diagram, we see the different interfaces that are used by AOP. Now we can study that the Advice interface is divided into three interfaces that are BeforeAdvice, AfterAdvice, and Interceptor. These interfaces are now extended by their sub-interfaces in the following manner:
The before advice method is nothing but the advice that is given to a method before it is executed. It is like a code that is executed before the call method. This method is called before a joinpoint and is used as an interceptor method. This method is not capable of providing an interruption to the flow of execution that proceeds at the joining point unless it throws an exception.
Now, we will create a class named A that includes the string as ‘actual business logic:
package com.intellinuts; public class A { public void m(){System.out.println("actual business logic");} }
Now, we create another class named BeforeAdvisor that implements the MethodBeforeAdvice interface.
package com.intellinuts; import java.lang.reflect.Method; import org.springframework.aop.MethodBeforeAdvice; public class BeforeAdvisor implements MethodBeforeAdvice{ @Override public void before(Method method, Object[] args, Object target)throws Throwable { System.out.println("additional concern before actual logic"); } }
In the XML configuration file, we create three classes for one each for A-class, beforeAdviser, and ProxyFactoryBean class respectively.
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <bean id="obj" class="com.intellinuts.A"></bean> <bean id="ba" class="com.intellinuts.BeforeAdvisor"></bean> <bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target" ref="obj"></property> <property name="interceptorNames"> <list> <value>ba</value> </list> </property> </bean> </beans>
The Spring framework provides the ProxyFactoryBean class as an example of Aspect-Oriented Programming (AOP). It is used to apply interceptor logic to an existing targeted bean in a manner that when methods on that bean are called, the interceptors are executed before and after that method call. ProxyFactoryBean is used so often inside the Spring to generate proxies for different types of reasons like remoting stubs, transaction management. But it is best suited for application logic concerns. It includes two properties that are target and interceptorNames. Here, an instance of A-class and an instance of advisor class will be assumed as target object and interceptor respectively.
As in the XML file mentioned above, you are required to pass the advisor object as the list object. The ProxyFactoryBean class code will be as:
public class ProxyFactoryBean{ private Object target; private List interceptorNames; //getters and setters }
Now, we will call the actual method in the test class:
package com.intellinuts; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.xml.XmlBeanFactory; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; public class Test { public static void main(String[] args) { Resource r=new ClassPathResource("applicationContext.xml"); BeanFactory factory=new XmlBeanFactory(r); A a=factory.getBean("proxy",A.class); a.m(); } }
After running the program successfully, we will see the output as:
additional concern before actual logic actual business logic Printing additional information in MethodBeforeAdvice
We can also display additional information such as method name, method argument, target object, target object class name, proxy class, etc by changing only two classes that are BeforeAdvisor.java and Test.java. This will be done as:
package com.intellinuts; import java.lang.reflect.Method; import org.springframework.aop.MethodBeforeAdvice; public class BeforeAdvisor implements MethodBeforeAdvice{ @Override public void before(Method method, Object[] args, Object target)throws Throwable { System.out.println("additional concern before actual logic"); System.out.println("method info:"+method.getName()+" "+method.getModifiers()); System.out.println("argument info:"); for(Object arg:args) System.out.println(arg); System.out.println("target Object:"+target); System.out.println("target object class name: "+target.getClass().getName()); } }
package com.intellinuts; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.xml.XmlBeanFactory; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; public class Test { public static void main(String[] args) { Resource r=new ClassPathResource("applicationContext.xml"); BeanFactory factory=new XmlBeanFactory(r); A a=factory.getBean("proxy",A.class); System.out.println("proxy class name: "+a.getClass().getName()); a.m(); } }
Here is the output:
proxy class name: com.intellinuts.A$$EnhancerByCGLIB$$409872b1 additional concern before actual logic method info:m 1 argument info: target Object:com.intellinuts.A@11dba45 target object class name: com.intellinuts.A actual business logic
In this example, advice is invoked after the execution of a join point that completes normally. This method will not be called if an exception is thrown. It will run when an execution that is matched returns a value normally. Here, the name of the return attribute must correspond to the name of a parameter and the returned value will be passed to the advice method as the corresponding argument value.
We will create a class A.java that contains actual business logic which is the same as in the previous example.
Now, we will create the AfterAdvisor class that implements the AfterReturningAdvice interface.
package com.intellinuts; import java.lang.reflect.Method; import org.springframework.aop.AfterReturningAdvice; public class AfterAdvisor implements AfterReturningAdvice{ @Override public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable { System.out.println("additional concern after returning advice"); } }
Now, you will need to change only the advisor class in the XML configuration file. The rest of the properties of the XML file applicationContext.xml will be the same as in the previous example.
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <bean id="obj" class="com.intellinuts.A"></bean> <bean id="ba" class="com.intellinuts.AfterAdvisor"></bean> <bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target" ref="obj"></property> <property name="interceptorNames"> <list> <value>ba</value> </list> </property> </bean> </beans>
Now we will create the Test.java class file which is the same as in the previous example.
The output will be displayed as:
actual business logic additional concern after returning advice
MethodInterceptor surrounds a join point. It can perform custom behavior before and after the method is being called. This method can go to the joinpoint or shortcut the advised method execution by its own returned value or throw an exception.
In this example, the class A.java will be created that contains actual business logic which also includes the same properties as in the previous examples.
Next, create the AroundAdviser.java class that implements the MethodInterceptor interface.
package com.intellinuts; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; public class AroundAdvisor implements MethodInterceptor{ @Override public Object invoke(MethodInvocation mi) throws Throwable { Object obj; System.out.println("additional concern before actual logic"); obj=mi.proceed(); System.out.println("additional concern after actual logic"); return obj; } }
Now just create the XML configuration file applicationContext.xml which is the same as in the previous examples except the advisor class that needs to be changed.
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <bean id="obj" class="com.intellinuts.A"></bean> <bean id="ba" class="com.intellinuts.AroundAdvisor"></bean> <bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target" ref="obj"></property> <property name="interceptorNames"> <list> <value>ba</value> </list> </property> </bean> </beans>
Now, we will create the same Test.java class as mentioned in the previous examples.
The output will be displayed as:
additional concern before actual logic actual business logic additional concern after actual logic
The ThrowsAdvice method is used to execute a specific action in case of an exception that is thrown inside a method. Spring IoC container will invoke this method each time a runtime exception occurs.
In this example, we will create a class named Validator.java that contains actual business logic that throws an exception if the age is found less than 18.
package com.intellinuts; public class Validator { public void validate(int age)throws Exception{ if(age<18){ throw new ArithmeticException("Not Valid Age"); } else{ System.out.println("vote confirmed"); } } }
In the next step, create the ThrowsAdvisor.java class that implements the ThrowsAdvice interface.
package com.intellinuts; import org.springframework.aop.ThrowsAdvice; public class ThrowsAdvisor implements ThrowsAdvice{ public void afterThrowing(Exception ex){ System.out.println("additional concern if exception occurs"); } }
Now create the same XML configuration file applicationContext.xml as mentioned in the previous examples except for the changes in the Validator and advisor class only.
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <bean id="obj" class="com.intellinuts.Validator"></bean> <bean id="ba" class="com.intellinuts.ThrowsAdvisor"></bean> <bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target" ref="obj"></property> <property name="interceptorNames"> <list> <value>ba</value> </list> </property> </bean> </beans>
Now we will create the Test.java class to run the program.
package com.intellinuts; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.xml.XmlBeanFactory; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; public class Test { public static void main(String[] args) { Resource r=new ClassPathResource("applicationContext.xml"); BeanFactory factory=new XmlBeanFactory(r); Validator v=factory.getBean("proxy",Validator.class); try{ v.validate(12); }catch(Exception e){e.printStackTrace();} } }
Finally, the output of the above program is:
java.lang.ArithmeticException: Not Valid Age additional concern if exception occurs at com.intellinuts.Validator.validate(Validator.java:7) at com.intellinuts.Validator$$FastClassByCGLIB$$562915cf.invoke(<generated>) at net.sf.cglib.proxy.MethodProxy.invoke(MethodProxy.java:191) at org.springframework.aop.framework.Cglib2AopProxy$CglibMethodInvocation.invoke Joinpoint(Cglib2AopProxy.java:692) at org.springframework.aop.framework.ReflectiveMethodInvocation. proceed(ReflectiveMethodInvocation.java:150) at org.springframework.aop.framework.adapter.ThrowsAdviceInterceptor. invoke(ThrowsAdviceInterceptor.java:124) at org.springframework.aop.framework.ReflectiveMethodInvocation. proceed(ReflectiveMethodInvocation.java:172) at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor. intercept(Cglib2AopProxy.java:625) at com.intellinuts.Validator$$EnhancerByCGLIB$$4230ed28.validate(<generated>) at com.intellinuts.Test.main(Test.java:15)