Tuesday, May 29, 2018

Use Spring AOP

You can apply AOP on classes directly.
 <bean id="myService" class="com.example.MyService">
  ...
 </bean>

 <bean id="MethodHijackerAdvice" class="com.example.MethodHijacker" />

 <bean id="myAdvisor"
  class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
  <property name="mappedName" value="methodNameToBeHijacked" />
  <property name="advice" ref="MethodHijackerAdvice" />
 </bean>

 <bean
  class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
  <property name="beanNames">
   <list>
    <value>*Service</value>
   </list>
  </property>
  <property name="interceptorNames">
   <list>
    <value>myAdvisor</value>
   </list>
  </property>
 </bean>

With this configuration, all the classes whose name ends with "Service" will be hijacked by this AOP. The myService class is one of them.

MethodHijacker.java
====================
public class MethodHijacker implements MethodInterceptor
{
 @Override
 public Object invoke(MethodInvocation methodInvocation) throws Throwable {
  System.out.println("MethodHijacker : Before method hijacked!");
  Object result = methodInvocation.proceed();
  System.out.println("MethodHijacker : After method hijacked!");
  return result;
 }
}

In the example above, the AOP proxies will be created on the classes that match the name pattern. Spring has another more powerful proxy creator DefaultAdvisorAutoProxyCreator. This one does not need to know the class names. It just creates the proxies for all the classes that match any advisor configured.

<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" />

You can also apply AOP on interfaces.
  1. Use Spring AOP factory to create bean instance.
        
        <bean id="abstractParent" class="org.springframework.aop.framework.ProxyFactoryBean" abstract="true">
       <property name="interceptorNames">
        <list>
         <value>myIntroductionAdvisor</value>
        </list>
       </property>
      </bean>
    

    Note that it has the value "true" for the attribute "abstract". So this does not actually create any bean by itself.

    To actually create a bean, one example is below that makes use of the abstract bean as the parent:

       
         <bean id="myAOPbean" parent="abstractParent">
            <property name="proxyInterfaces" value="com.example.myBeanInterface" />
            <property name="target">
            <bean parent="abstractParentTarget">
               <constructor-arg value="com.example.Abc" />
            </bean>
            </property>
         </bean>
      
         <bean id="abstractParentTarget" class="com.example.CommonBehavior" abstract="true"/> 
    

    Note that com.example.myBeanInterface is an interface. This makes sense because the interface can then be proxied by AOP.

  2. In the configuration for AOP factory, specify the interceptor name (myIntroductionAdvisor in this case)
  3. The interceptor is an advisor. The example below extends the Spring Advisor DefaultIntroductionAdvisor:
       MyIntroductionAdvisor
       ===========================
        import org.springframework.aop.support.DefaultIntroductionAdvisor;
     
        public class MyIntroductionAdvisor extends DefaultIntroductionAdvisor {
     
     public MyIntroductionAdvisor() {
      super(new MyInterceptor());
     }
    
     @Override
     public boolean matches(Class clazz) {
      return MyMatcher.class.isAssignableFrom(clazz);
     }
    }
    

    In this advisor, you specify two things:
    (1) What classes will be AOP'ed. ( The MyMatcher classes )
    (2) What class will perform the AOP action. ( MyInterceptor in this case)

     
    public interface MyMatcher<T> {
     // can optionally define common behavior here. For example the method below:
     // List<T> process(String method, Object[] queryArguments);
    }  
    

    MyInterceptor can now apply behavior on the methods of all the MyMatcher classes. The interceptor API method is below.

    public Object invoke(MethodInvocation methodInvocation) throws Throwable 
    

    CommonBehavior implements the MyMatcher interface.

  4. The essential thing of the whole configuration is the interceptor that defines what you want to accomplish. The interceptor will defines what methods will be intercepted and what actions will be performed.