Besides Dependency Injection (DI), another core feature that the Spring Framework offers is aspect-oriented programming (AOP).
AOP is a tool for implementing crosscutting concerns.
The crosscutting concerns refers to logic in an application that cannot be decomposed from the rest of the application and may result in code duplication and tight coupling.
AOP is a programming paradigm that aims to increase modularity by allowing the separation of cross-cutting concerns.
Adding Log methods to every single method is a cross-cutting concern. A logging strategy affects every logged part of the system. The log methods would cross-cut all logged classes and methods.
Spring AOP framework modularizes cross-cutting concerns in aspects. When executing a method in Spring IoC container, Spring AOP can hijack the executing method, and add extra functionality before or after the method execution.
AOP comes with its own specific set of concepts and terms.
The following are the core concepts of AOP:
Spring AOP has four type of advices.
Advice is an action to take either before or after the method execution.
Add the following new dependency for AOP coding to POM.xml.
<dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>2.2.2</version> </dependency>
The following code defines a Java bean for a printer service.
package com.java2s.common; public class PrinterService { private String name; private String url; public void setName(String name) { this.name = name; } public void setUrl(String url) { this.url = url; } public void printName() { System.out.println("Printer Name : " + this.name); } public void printURL() { System.out.println("Printer URL : " + this.url); } public void printThrowException() { throw new IllegalArgumentException(); } }
Here is the Spring-Customer.xml file for bean configuration.
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> <bean id="myService" class="com.java2s.common.PrinterService"> <property name="name" value="printerName" /> <property name="url" value="http://www.java2s.com" /> </bean> </beans>
The following is the main class to run the code above.
package com.java2s.common; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class App { public static void main(String[] args) { ApplicationContext appContext = new ClassPathXmlApplicationContext( new String[] { "SpringBeans.xml" }); PrinterService cust = (PrinterService) appContext.getBean("myService"); cust.printName(); cust.printURL(); try { cust.printThrowException(); } catch (Exception e) { } } }
Output
The following code shows how to add Before advice.
A Before advice is executed before the method execution.
First, create a class which implements MethodBeforeAdvice interface.
package com.java2s.common; import java.lang.reflect.Method; import org.springframework.aop.MethodBeforeAdvice; public class AOPBeforeMethod implements MethodBeforeAdvice { @Override public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println("AOPBeforeMethod : Before method call."); } }
Then, we create the AOPBeforeMethod bean in the xml configuration file.
<bean id="aopBeforeMethodBean" class="com.java2s.aop.AOPBeforeMethod" />
To use the AOPBeforeMethod
class we have to install it by using
org.springframework.aop.framework.ProxyFactoryBean
as follows.
<bean id="myServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target" ref="myService" /> <property name="interceptorNames"> <list> <value>aopBeforeMethodBean</value> </list> </property> </bean>
Full source code
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> <bean id="myService" class="com.java2s.common.PrinterService"> <property name="name" value="printerName" /> <property name="url" value="http://www.java2s.com" /> </bean> <bean id="aopBeforeMethodBean" class="com.java2s.common.AOPBeforeMethod" /> <bean id="myServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target" ref="myService" /> <property name="interceptorNames"> <list> <value>aopBeforeMethodBean</value> </list> </property> </bean> </beans>
Run the main function again.
From the result we can see that the AOPBeforeMethod's before() method is executed before every myService's methods.
The following code shows how to use After returning advice. After returning advice will execute after the method is returned a result.
First, create a class which implements AfterReturningAdvice interface.
package com.java2s.common; import java.lang.reflect.Method; import org.springframework.aop.AfterReturningAdvice; public class AOPAfterMethod implements AfterReturningAdvice { @Override public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable { System.out.println("AOPAfterMethod : After method call."); } }
Then, install the AOPAfterMethod
class in the
Bean configuration file.
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> <bean id="myService" class="com.java2s.common.PrinterService"> <property name="name" value="printerName" /> <property name="url" value="http://www.java2s.com" /> </bean> <bean id="aopAfterMethodBean" class="com.java2s.common.AOPAfterMethod" /> <bean id="myServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target" ref="myService" /> <property name="interceptorNames"> <list> <value>aopAfterMethodBean</value> </list> </property> </bean> </beans>
Run the main function again. Here is the output.
From the result we can see that the AOPAfterMethod's afterReturning() method is executed after every myService's methods returns a result.
The following code shows how to use After throwing advice. After throwing advice will execute after the method throws an exception.
First, create a class which implements ThrowsAdvice interface, and create a afterThrowing method to hijack the IllegalArgumentException exception.
package com.java2s.common; import org.springframework.aop.ThrowsAdvice; public class AOPThrowException implements ThrowsAdvice { public void afterThrowing(IllegalArgumentException e) throws Throwable { System.out.println("AOPThrowException : Throw exception call."); } }
Then, install the AOPThrowException
in Bean configuration file.
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> <bean id="myService" class="com.java2s.common.PrinterService"> <property name="name" value="printerName" /> <property name="url" value="http://www.java2s.com" /> </bean> <bean id="aopThrowExceptionBean" class="com.java2s.common.AOPThrowException" /> <bean id="myServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target" ref="myService" /> <property name="interceptorNames"> <list> <value>aopThrowExceptionBean</value> </list> </property> </bean> </beans>
Run the code and here is the output.
Printer Name : printerName Printer URL : http://www.java2s.com AOPThrowException : Throw exception call.
From the result we can see that Spring IoC container runs the AOPThrowException's afterThrowing() method when myService's methods throws an exception.
The following code shows how to use Around advice. Around advice combines all three advices above, and execute during method execution.
First, create a class which implements MethodInterceptor interface.
public Object invoke(MethodInvocation methodInvocation) throws Throwable method is called for each method call and we have to call the "methodInvocation.proceed();" to run the original method, otherwise the original method will not run.
package com.java2s.common; import java.util.Arrays; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; public class AOPAroundMethod implements MethodInterceptor { @Override public Object invoke(MethodInvocation methodInvocation) throws Throwable { System.out.println("Method name : " + methodInvocation.getMethod().getName()); System.out.println("Method arguments : " + Arrays.toString(methodInvocation.getArguments())); System.out.println("AOPAroundMethod : Before method call."); try { // proceed to original method call Object result = methodInvocation.proceed(); // same with AfterReturningAdvice System.out.println("AOPAroundMethod : after call."); return result; } catch (IllegalArgumentException e) { System.out.println("AOPAroundMethod : Throw exception call."); throw e; } } }
Here is the xml Bean configuration file to install the Around Advice AOP.
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> <bean id="myService" class="com.java2s.common.PrinterService"> <property name="name" value="printerName" /> <property name="url" value="http://www.java2s.com" /> </bean> <bean id="aopAroundMethodBean" class="com.java2s.common.AOPAroundMethod" /> <bean id="myServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target" ref="myService" /> <property name="interceptorNames"> <list> <value>aopAroundMethodBean</value> </list> </property> </bean> </beans>
Run the main function, here is the output.