Around Advice Example With ProxyFactory

Example 1: ProxyFactory + Advice

The first example will show you how to use ProxyFactory with advice and target object.

1) Create a target object need to be advice.
2) Create an advice class and add it into ProxyFactory.
3) Create a new proxy object via ProxyFactory getProxy() method.
4) Perform method interception with with proxy object called.

FooAndBar.java

public FooAndBar {

    public String foo() {
        System.out.println(“foo () “);
        return "foo";
    }

    public String bar() {
        System.out.println(“bar () “);
        return "bar";
    }
}

FooBarAdvice.java

public class FooBarAdvice implements MethodInterceptor {

   public Object invoke(MethodInvocation invocation ) throws Throwable {

     StopWatch timer = new StopWatch();
     timer.start();

     Object method = invocation.proceed();

     timer.stop();

     String MethodName = invocation.getMethod().getName();
     System.out.println(MethodName + ” executed ” + timer.getTotalTimeSeconds());

     return method;
   }
}

Main.java

public static void main(String[] args) {

   FooAndBar target = new FooAndBar();
   Advice advice = new FooBarAdvice();

   ProxyFactory pf = new ProxyFactory();
   pf.addAdvice(advice);
   pf.setTarget(target);

   FooAndBar proxy = (FooAndBar)pf.getProxy();

   proxy.foo();
   proxy.bar();

}

Result

foo()
foo executed 0.04
bar()
bar executed 0.0

 

Example 2: ProxyFactory + Pointcut

Example above behind the scenes will eventually delegates to addAdvisor(). A default implementation DefaultPointcutAdvisor that pointcut all method.

Advisor = Pointcut + Advice

Spring provide few Pointcut implementation. And the following example will show you how to use ProxyFactory with pointcut, advice and target object.

  • StaticMethodMatcherPointcut
  • DynamicMethodMatcherPointcut
  • AnotationMatchingPointcut
  • NameMatcherPointcut
  • AspectJExpressionPointcut
  • JdkRegexpMethodPointcut
  • ControlFlowPointcut
  • ComposablePointcut

 

Now we implement our custom pointcut, let’s say we extends one of the spring provide pointcut implementation.

MyPointCut.java

public class MyPointCut extends StaticMethodMatcherPointcut {

    public boolean matches(Method method, Class<?> clazz) {
		
	String targetMethod = "foo";
	boolean intercept = false;
		
	if (targetMethod.equals(method.getName())) {
		intercept = true;
	}
	return intercept;
    }
	
}

 

Main.java

public static void main(String[] args) {
		
    FooAndBar target = new FooAndBar();
		
    Pointcut pointcut = new MyPointCut();
    Advice advice = new FooBarAdvice();
    Advisor advisor = new DefaultPointcutAdvisor(pointcut, advice);
		
    ProxyFactory pf = new ProxyFactory();
    pf.setTarget(target);
    pf.addAdvisor(advisor);
		
    FooAndBar proxy = (FooAndBar)pf.getProxy();
		
    proxy.foo();
    proxy.bar();
		
}

Result

The result show the only foo() method were intercepted instead of all methods. This is because we filters manually from our pointcut implementations and only return true when the method foo() is called otherwise return as false.

foo()
foo executed 0.039
bar()

More specifically we can override the getClassFilter() method to filter by which class name with method name. Example,

public class MyPointCut extends StaticMethodMatcherPointcut {

    // code omitted

    public ClassFilter getClassFilter() {
        return new ClassFilter() {
            public boolean matches(Class<?> clazz) {
                return (clazz.equals(FooAndBar.class));
            }
        };
    }

}

 

ProxyFactoryBean

The above example are not using much spring features, now we will move on with ProxyFactoryBean together with applicationContext. General usage:

  1. Create a proxy bean that will be the used for target bean and class attribute point to ProxyFactoryBean.
  2. Create a target target bean and set ref bean to proxy bean target attribute (or can be inner anonymous bean).
  3. Access  application with proxy bean.

 

Advice example:

public class Work {

	private String workId;
	private String workDescription;
	private String workHour;
	
        // getter and setter 
}

WorkManager.java

public class WorkManager {

    private Work work;
	
    public void doWorkOverNight() {
        System.out.println("   Work ID: " + work.getWorkId());
        System.out.println("   Work Desc: " + work.getWorkDescription());
        System.out.println("   Work Hour: " + work.getWorkHour());
    }

    public Work getWork() {
        return work;
    }

    public void setWork(Work work) {
        this.work = work;
    }
	
}

WorkAdvice.java

public class WorkAdvice implements MethodInterceptor{

    public Object invoke(MethodInvocation invocation) throws Throwable {

	System.out.println("[Begin] ************************** ");
	System.out.println(invocation.getMethod().getName());
	Object method = invocation.proceed();
		
	System.out.println("[End] **************************** ");
	return method;
     }

}

ApplicationContext.xml

<bean id="target" class="com.octworks.aop.Work">
    <property name="workId" value="AA123"/>
    <property name="workDescription" value="Event Setup"/>
    <property name="workHour" value="5 hours"/>
</bean>
	
<bean id="manager" class="com.octworks.aop.WorkManager">
    <property name="work" ref="proxyBean" />
</bean>
	
<bean id="proxyBean" class="org.springframework.aop.framework.ProxyFactoryBean">
    <property name="target">
        <ref bean="target"/>
    </property>
    <property name="interceptorNames">
	 <list>
	     <value>advice</value>
	 </list>
    </property>
</bean>
	
<bean id="advice" class="com.octworks.aop.WorkAdvice"/>

Main.java

public static void main(String[] args) {
		
  ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("proxybeanfactory-Context.xml");
  WorkManager manager = ctx.getBean(WorkManager.class);
		
  manager.doWorkOverNight();
  ctx.close();
}

Result

[Begin] ************************** 
getWorkId
[End] **************************** 
   Work ID: AA123
[Begin] ************************** 
getWorkDescription
[End] **************************** 
   Work Desc: Event Setup
[Begin] ************************** 
getWorkHour
[End] **************************** 
   Work Hour: 5 hours

 

Advisor

Instead of using advice, we can change to using advisor.

1) First, add pointcut and advisor

// code omitted

<bean id="pointcut" class="org.springframework.aop.aspectj.AspectJExpressionPointcut">
    <property name="expression">
        <value>execution(* getWorkHour*(..)))</value>
    </property>
</bean>
	
<bean id="advisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
    <property name="advice" ref="advice" />
    <property name="pointcut" ref="pointcut" />
</bean>

 

2) Change the interceptorNames value from advice to advisor

<bean id="proxyBean" class="org.springframework.aop.framework.ProxyFactoryBean">
    <property name="target">
        <ref bean="target"/>
    </property>
    <property name="interceptorNames">
        <list>
            <value>advisor</value>
        </list>
    </property>
</bean>

Result

   Work ID: AA123
   Work Desc: Event Setup
[Begin] ************************** 
getWorkHour
[End] **************************** 
   Work Hour: 5 hours

** Pointcut can be declare as inner bean due to it’s not suppose to instantiated as use it directly.

 

Reduce declaration by using AOP Namespace

You may observe there are too many AOP related bean required to manual declare. We can further simplify by using namespace. The following example, we are going to target the work manager and advice the doWorkOverNight().

 

WorkManagerAdvice.java 

Instead of implements the advice interface, this time we will apply namespace and let spring handle for us. For around advice, first arg1 will proceedingJoinPoint, arg2 parameters and etc.

WorkManagerAdvice.java

public class WorkManagerAdvice {

    public Object intercept(ProceedingJoinPoint pJoinpoint) throws Throwable {
		
	System.out.println("[Begin] ************************** ");
	System.out.println(pJoinpoint.getSignature().getName());
		
	Object method = pJoinpoint.proceed();

	System.out.println("[End] **************************** ");
			
	return method;
    }
}

applicationContext.xml

ApplicationContext will required to add the aop namespace on beans tag.

<beans ...>
// code omitted

<aop:config>
  <aop:pointcut  
    id="workExecution"  
    expression="execution(* com.octworks..doWorkOverNight*(..)))"/>

  <aop:aspect ref="advice" >
    <aop:around method="intercept" pointcut-ref="workExecution"/>
  </aop:aspect>
		
</aop:config>


<bean id="advice" class="com.octworks.aop.WorkManagerAdvice"/>

<bean id="work" class="com.octworks.aop.Work">
	<property name="workId" value="AA123"/>
	<property name="workDescription" value="Event Setup"/>
	<property name="workHour" value="5 hours"/>
</bean>

<bean id="manager" class="com.octworks.aop.WorkManager">
	<property name="work" ref="work" />
</bean>

Result

[Begin] ************************** 
doWorkOverNight
   Work ID: AA123
   Work Desc: Event Setup
   Work Hour: 5 hours
[End] ****************************

 

Expression and Execution

execution ( * com.octworks.aop.*Manager.doWorkOverNight*(..) )

Explains

*** Modifier suppose should be visible type and that include subclass and interface.

execution designator
* return type * – any type or can be specify
void return type
com.octworks.aop package (optional) com.octworks.*.work.*.*(..) – one directory in between
com.octworks..work.*.*(..) – 1 or more directory in between
*..work.*.*(..) – any sub package named work
*Manager Class (optional) Suffix with Manager classname
doWorkOverNight* method Prefix with doWorkOverNight
(..) arguments 0 or more arguments
(*)  – single parameter
(int, ..) – first int argus and 0 or more subsequence
(String) – single string argus

Chain together composite pointcut

Chain with composite pointcut  && (and)
|| (or)
! (not)
expression execution with annotation execute(@com.octworks.custom.ReadOnly void read*(..))- Any void method start with read prefix with annotated @ReadOnly

Annotation

Aspect = encapsulate pointcut and advice, so this class contains @Pointcut and advice @Around.

General usage:

  1. Annotated @Pointcut on a void function, and define the execution
  2. Annotated advice type on a function, Eg @Around/@Before/@After/@AfterThrowing/@AfterReturn and etc.
  3. On the Advice annotation, pass in pointcut parameter, which is (1) @Pointcut function’s name and it’s parameter if any.

 

@Component
@Aspect
public class LogMsgInterceptor {

  @Pointcut("execution(* com.octworks.aop..doWorkOverNight(..))")
  public void pointcutExecution() {
		// leave blank
  }
	
  @Around("pointcutExecution()")
  public Object intercept(ProceedingJoinPoint pJoinpoint) throws Throwable {
		
	System.out.println("[Begin] ************************** ");
	System.out.println(pJoinpoint.getSignature().getName());
		
	Object method = pJoinpoint.proceed();
	System.out.println("[End] **************************** ");
			
	return method;
  } 
}

Work.java

@Component("work")
public class Work {

    private String workId;
    private String workDescription;
    private String workHour;

    // getter setter

}

WorkManager.java

@Component("manager")
public class WorkManager {

   @Autowired
   private Work work;
	
   public void doWorkOverNight() {
		
	System.out.println("   Work ID: " + work.getWorkId());
	System.out.println("   Work Desc: " + work.getWorkDescription());
	System.out.println("   Work Hour: " + work.getWorkHour());
		
   }
  
   // code omitted

}

applicationContext.xml

<?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:context="http://www.springframework.org/schema/context"
  xmlns:aop="http://www.springframework.org/schema/aop"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd">
	
  <aop:aspectj-autoproxy />
	
  <context:component-scan base-package="com.octworks.aop" />
	
</beans>

Main.java

public class Main {

  public static void main(String[] args) {
		
	ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("annotation-Context.xml");
		
	Work work = ctx.getBean(Work.class);
	work.setWorkId("AAA");
	work.setWorkDescription("Setup Exhibition");
	work.setWorkHour("6 hours");
		
	WorkManager manager = ctx.getBean(WorkManager.class);
	manager.setWork(work);
		
	manager.doWorkOverNight();
	ctx.close();
		
		
  }
}

Result

[Begin] **************************
doWorkOverNight
   Work ID: AAA
   Work Desc: Setup Exhibition
   Work Hour: 6 hours
[End] ****************************

 

JavaConfig Application Context

You can simplified even in javaConfig application context

@Configuration
@ComponentScan(basePackages="com.octworks.aop.proxyfactorybean")
@EnableAspectJAutoProxy
public class AnnotationApplicationConfig {

	
}

Main.java

public static void main(String[] args) {

    AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
    ctx.register(AnnotationApplicationConfig.class);
    ctx.refresh();
		
    Work work = ctx.getBean(Work.class);
    work.setWorkId("AAA");
    work.setWorkDescription("Setup Exhibition");
    work.setWorkHour("6 hours");
		
    WorkManager manager = ctx.getBean(WorkManager.class);
    manager.setWork(work);
		
    manager.doWorkOverNight();
    ctx.close();
}
Around Advice Example With ProxyFactory

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.