Stonefishy Blog - by Andrew Shi

Scribbles of a front and back ends developer

Spring AOP - Static Pointcut

Let’s first to see the three very technical terms as below.

  • Adivce - Indicate the action to take either before or after the method execution.
  • Pointcut - Indicate which method should be intercept, by method name or regular expression pattern.
  • Advisor - Group ‘Advice’ and ‘Pointcut’ into a single unit, and pass it to a proxy factory object

No pointcut example

MyLogicClass
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package spring.test.aop;

public class MyLogicClass {

  public void logicMethodOne() {
      System.out.println("This is from logicMethodOne");
  }
  
  public void logicMethodTwo() {
      System.out.println("This is from logicMethodTwo");
  }
  
  public void testLogic() {
      System.out.println("This is from testLogic");
  }
}
MyAroundAdvice.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package spring.test.aop;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class MyAroundAdvice implements MethodInterceptor {

  @Override
  public Object invoke(MethodInvocation invocation) throws Throwable {
      System.out.println("Before method execuation");

      Object result = invocation.proceed();
      
      System.out.println("After method execuation");

      return result;
  }

}
App.java
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
package spring.test.aop;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App {

  private static ApplicationContext applicationContext;

  public static void main(String[] args) {

      applicationContext = new ClassPathXmlApplicationContext("springConfig.xml");
      
      MyLogicClass logicClass = (MyLogicClass)applicationContext.getBean("proxyFactoryBean");

      System.out.println("\n--------------------\n");
      logicClass.logicMethodOne();
      
      System.out.println("\n--------------------\n");
      logicClass.logicMethodTwo();
      
      System.out.println("\n--------------------\n");
      logicClass.testLogic();
      System.out.println("\n--------------------\n");

  }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN"
         "http://www.springframework.org/dtd/spring-beans-2.0.dtd">

<beans>
  <bean id="myAroundAdvice" class="spring.test.aop.MyAroundAdvice"/>
  
  <bean id="myLogicClass" class="spring.test.aop.MyLogicClass"/>
  
  <bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">
      <property name="interceptorNames">
          <list>
              <value>myAroundAdvice</value>
          </list>
      </property>
      <property name="target" ref="myLogicClass"/>
  </bean>
</beans>
output
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
--------------------

Before method execuation
This is from logicMethodOne
After method execuation

--------------------

Before method execuation
This is from logicMethodTwo
After method execuation

--------------------

Before method execuation
This is from testLogic
After method execuation

--------------------

In above example, all logic methods of the class has been intercepted due to the application doesn’t use the pointcut to specific.

Name match example

And now, we just want to intercept the method testLogic, so we need to create NameMatchMethodPointcut bean in spring config xml. and put the method name you want to intercept to the mappedName property value.

1
2
3
<bean id="nameMatchPointcut" class="org.springframework.aop.support.NameMatchMethodPointcut">
      <property name="mappedName" value="testLogic"/>
</bean>

And also you can mapped multiple methods with mappedNames, like below.

1
2
3
4
5
6
7
8
<bean id="nameMatchPointcut" class="org.springframework.aop.support.NameMatchMethodPointcut">
      <property name="mappedNames">
          <list>
              <value>logicMethodOne</value>
              <value>logicMethodTwo</value>
          </list>
      </property>
</bean>

Then create DefaultPointcutAdvisor bean to group and associate the pointcut and advice.

1
2
3
4
<bean id="myPointcutAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
      <property name="pointcut" ref="nameMatchPointcut"/>
      <property name="advice" ref="myAroundAdvice"/>
</bean>

Replace the interceptorNames of ProxyFactoryBean with the new Advisor.

1
2
3
4
5
6
7
8
<bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">
      <property name="interceptorNames">
          <list>
              <value>myPointcutAdvisor</value>
          </list>
      </property>
      <property name="target" ref="myLogicClass"/>
</bean>

It’s done, let’s see the full spring config file and output.

springConfig.xml
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
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN"
         "http://www.springframework.org/dtd/spring-beans-2.0.dtd">

<beans>
  <bean id="myAroundAdvice" class="spring.test.aop.MyAroundAdvice"/>
  
  <bean id="myLogicClass" class="spring.test.aop.MyLogicClass"/>
  
  <bean id="nameMatchPointcut" class="org.springframework.aop.support.NameMatchMethodPointcut">
      <property name="mappedName" value="testLogic"/>
      <!-- <property name="mappedNames">
          <list>
              <value>logicMethodOne</value>
              <value>logicMethodTwo</value>
          </list>
      </property> -->
  </bean>
  
  <bean id="myPointcutAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
      <property name="pointcut" ref="nameMatchPointcut"/>
      <property name="advice" ref="myAroundAdvice"/>
  </bean>
  
  <bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">
      <property name="interceptorNames">
          <list>
              <value>myPointcutAdvisor</value>
          </list>
      </property>
      <property name="target" ref="myLogicClass"/>
  </bean>
</beans>
output
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
--------------------

This is from logicMethodOne

--------------------

This is from logicMethodTwo

--------------------

Before method execuation
This is from testLogic
After method execuation

--------------------

Regular expression example

Also we can use regular expression to match methods which should be intercepted. Create RegexpMethodPointcutAdvisor bean and set the pattern property value with regular expression and combine with advice.

1
2
3
4
5
<bean id="regexPointcutAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
      <property name="pattern" value=".*Method.*"/>
      
      <property name="advice" ref="myAroundAdvice"/>
</bean>

And you can specific multiple patterns with patterns property.

1
2
3
4
5
6
7
8
9
<bean id="regexPointcutAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
      <property name="patterns">
          <list>
              <value>.*Method.*</value>
          </list>
      </property>
      
      <property name="advice" ref="myAroundAdvice"/>
</bean>

Finally pass the advisor to the ProxyFactoryBean.

1
2
3
4
5
6
7
8
<bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">
      <property name="interceptorNames">
          <list>
              <value>regexPointcutAdvisor</value>
          </list>
      </property>
      <property name="target" ref="myLogicClass"/>
</bean>
output
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
--------------------

Before method execuation
This is from logicMethodOne
After method execuation

--------------------

Before method execuation
This is from logicMethodTwo
After method execuation

--------------------

This is from testLogic

--------------------

In practice, you can use it to manage DAO layer, where you can declare “.*DAO.*” to intercept all your DAO classes to support transaction.