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:
- Create a proxy bean that will be the used for target bean and class attribute point to ProxyFactoryBean.
- Create a target target bean and set ref bean to proxy bean target attribute (or can be inner anonymous bean).
- 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:
- Annotated @Pointcut on a void function, and define the execution
- Annotated advice type on a function, Eg @Around/@Before/@After/@AfterThrowing/@AfterReturn and etc.
- 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(); }