Spring ─ AOP FrameworkOne of the key components of Spring Framework is the Aspect-Oriented Programming (AOP) framework. Aspect-Oriented Programming entails breaking down program logic into distinct parts called so-called concerns. The functions that span multiple points of an application are called cross-cutting concerns and these cross-cutting concerns are conceptually separate from the application's business logic. There are various common good examples of aspects like logging, auditing, declarative transactions, security, caching, etc. The key unit of modularity in OOP is the class, whereas in AOP the unit of modularity is the aspect. Dependency Injection helps you decouple your application objects from each other and AOP helps you decouple cross-cutting concerns from the objects that they affect. AOP is like triggers in programming languages such as Perl, .NET, Java, and others. Spring AOP module provides interceptors to intercept an application. For example, when a method is executed, you can add extra functionality before or after the method execution. AOP Terminologies Before we start working with AOP, let us become familiar with the AOP concepts and terminology. These terms are not specific to Spring, rather they are related to AOP.
Spring aspects can work with five kinds of advice mentioned as follows:
Spring supports the @AspectJ annotation style approach and the schemabased approach to implement custom aspects. These two approaches have been explained in detail in the following sections.
To use the AOP namespace tags described in this section, you need to import the spring- AOP schema as described:
<?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:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd "> <!-- bean definition & AOP specific configuration --> </beans> You will also need the following AspectJ libraries on the CLASSPATH of your application. These libraries are available in the 'lib' directory of an AspectJ installation, otherwise you can download them from the internet.
An aspect is declared using the <aop:aspect> element, and the backing bean is referenced using the ref attribute as follows:
<aop:config>
<aop:aspect id="myAspect" ref="aBean"> ... </aop:aspect> </aop:config> <bean id="aBean" class="..."> ... </bean> Here "aBean" will be configured and dependency injected just like any other Spring bean as you have seen in the previous chapters. Declaring a Pointcut A pointcut helps in determining the join points (ie methods) of interest to be executed with different advices. While working with XML Schema-based configuration, pointcut will be defined as follows:
<aop:config>
<aop:aspect id="myAspect" ref="aBean"> <aop:pointcut id="businessService" expression="execution(* com.xyz.myapp.service.*.*(..))"/> ... </aop:aspect> </aop:config> <bean id="aBean" class="..."> ... </bean> The following example defines a pointcut named 'businessService' that will match the execution of getName() method available in the Student class under the package com.jtc:
<aop:config>
<aop:aspect id="myAspect" ref="aBean"> <aop:pointcut id="businessService" expression="execution(* com.jtc.Student.getName(..))"/> ... </aop:aspect> </aop:config> <bean id="aBean" class="..."> ... </bean> Declaring Advices You can declare any of the five advices inside an <aop:aspect> using the <aop:{ADVICE NAME}> element as follows:
<aop:config>
<aop:aspect id="myAspect" ref="aBean"> <aop:pointcut id="businessService" expression="execution(* com.xyz.myapp.service.*.*(..))"/> <!-- a before advice definition --> <aop:before pointcut-ref="businessService" method="doRequiredTask"/> <!-- an after advice definition --> <aop:after pointcut-ref="businessService" method="doRequiredTask"/> <!-- an after-returning advice definition --> <!--The doRequiredTask method must have parameter named retVal --> <aop:after-returning pointcut-ref="businessService" returning="retVal" method="doRequiredTask"/> <!-- an after-throwing advice definition --> <!--The doRequiredTask method must have parameter named ex --> <aop:after-throwing pointcut-ref="businessService" throwing="ex" method="doRequiredTask"/> <!-- an around advice definition --> <aop:around pointcut-ref="businessService" method="doRequiredTask"/> ... </aop:aspect> </aop:config> <bean id="aBean" class="..."> ... </bean> You can use the same doRequiredTask or different methods for different advices. These methods will be defined as a part of aspect module. XML Schema Based AOP Example To understand the above-mentioned concepts related to XML Schema-based AOP, let us write an example which will implement few of the advices. To write our example with few advices, let us have a working Eclipse IDE in place and take the following steps to create a Spring application:
package com.jtc;
public class Logging { /** * This is the method which I would like to execute * before a selected method execution. */ public void beforeAdvice(){ System.out.println("Going to setup student profile."); } /** * This is the method which I would like to execute * after a selected method execution. */ public void afterAdvice(){ System.out.println("Student profile has been setup."); } /** * This is the method which I would like to execute * when any method returns. */ public void afterReturningAdvice(Object retVal){ System.out.println("Returning:" + retVal.toString() ); } /** * This is the method which I would like to execute * if there is an exception raised. */ public void AfterThrowingAdvice(IllegalArgumentException ex){ System.out.println("There has been an exception: " + ex.toString()); } } Following is the content of the Student.java file
package com.jtc;
public class Student { private Integer age; private String name; public void setAge(Integer age) { this.age = age; } public Integer getAge() { System.out.println("Age : " + age ); return age; } public void setName(String name) { this.name = name; } public String getName() { System.out.println("Name : " + name ); return name; } public void printThrowException(){ System.out.println("Exception raised"); throw new IllegalArgumentException(); } } Following is the content of the MainApp.java file
package com.jtc;
import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MainApp { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml"); Student student = (Student) context.getBean("student"); student.getName(); student.getAge(); student.printThrowException(); } } Following is the configuration file Beans.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:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd "> <aop:config> <aop:aspect id="log" ref="logging"> <aop:pointcut id="selectAll" expression="execution(* com.jtc.*.*(..))"/> <aop:before pointcut-ref="selectAll" method="beforeAdvice"/> <aop:after pointcut-ref="selectAll" method="afterAdvice"/> <aop:after-returning pointcut-ref="selectAll" returning="retVal" method="afterReturningAdvice"/> <aop:after-throwing pointcut-ref="selectAll" throwing="ex" method="AfterThrowingAdvice"/> </aop:aspect> </aop:config> <!-- Definition for student bean --> <bean id="student" class="com.jtc.Student"> <property name="name" value="Zara" /> <property name="age" value="11"/> </bean> <!-- Definition for logging aspect --> <bean id="logging" class="com.jtc.Logging"/> </beans> Once you are done creating the source and bean configuration files, let us run the application. If everything is fine with your application, it will print the following message:
Going to setup student profile.
Name : Zara Student profile has been setup. Returning:Zara Going to setup student profile. Age : 11 Student profile has been setup. Returning:11 Going to setup student profile. Exception raised Student profile has been setup. There has been an exception: java.lang.IllegalArgumentException ..... other exception content The above defined <aop:pointcut> selects all the methods defined under the package com.jtc. Let us suppose, you want to execute your advice before or after a particular method, you can define your pointcut to narrow down your execution by replacing stars (*) in pointcut definition with the actual class and method names. Following is a modified XML configuration file to show the concept:
<?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:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd "> <aop:config> <aop:aspect id="log" ref="logging"> <aop:pointcut id="selectAll" expression="execution(* com.jtc.Student.getName(..))"/> <aop:before pointcut-ref="selectAll" method="beforeAdvice"/> <aop:after pointcut-ref="selectAll" method="afterAdvice"/> </aop:aspect> </aop:config> <!-- Definition for student bean --> <bean id="student" class="com.jtc.Student"> <property name="name" value="Zara" /> <property name="age" value="11"/> </bean> <!-- Definition for logging aspect --> <bean id="logging" class="com.jtc.Logging"/> </beans> If you execute the sample application with these configuration changes, it will print the following message:
Going to setup student profile.
Name : Zara Student profile has been setup. Age : 11 Exception raised ..... other exception content @AspectJ-based AOP with Spring @AspectJ refers to a style of declaring aspects as regular Java classes annotated with Java 5 annotations. The @AspectJ support is enabled by including the following element inside your XML Schema-based configuration file.
<aop:aspectj-autoproxy/>
You will also need the following AspectJ libraries on the classpath of your application. These libraries are available in the 'lib' directory of an AspectJ installation, otherwise you can download them from the internet.
Aspects classes are like any other normal bean and may have methods and fields just like any other class, except that they will be annotated with @Aspect as follows:
package org.xyz;
import org.aspectj.lang.annotation.Aspect; @Aspect public class AspectModule { } They will be configured in XML like any other bean as follows:
<bean id="myAspect" class="org.xyz.AspectModule">
<!-- configure properties of aspect here as normal --> </bean> Declaring a Pointcut A pointcut helps in determining the join points (ie methods) of interest to be executed with different advices. While working with @AspectJ-based configuration, pointcut declaration has two parts:
import org.aspectj.lang.annotation.Pointcut;
@Pointcut("execution(* com.xyz.myapp.service.*.*(..))") // expression private void businessService() {} // signature The following example defines a pointcut named 'getname' that will match the execution of getName() method available in the Student class under the package com.jtc:
import org.aspectj.lang.annotation.Pointcut;
@Pointcut("execution(* com.jtc.Student.getName(..))") private void getname() {} Declaring Advices You can declare any of the five advices using @{ADVICE-NAME} annotations as given in the code snippet. This assumes that you already have defined a pointcut signature method businessService():
@Before("businessService()")
public void doBeforeTask(){ ... } @After("businessService()") public void doAfterTask(){ ... } @AfterReturning(pointcut = "businessService()", returning="retVal") public void doAfterReturnningTask(Object retVal){ // you can intercept retVal here. ... } @AfterThrowing(pointcut = "businessService()", throwing="ex") public void doAfterThrowingTask(Exception ex){ // you can intercept thrown exception here. ... } @Around("businessService()") public void doAroundTask(){ ... } You can define a pointcut inline for any of the advices. Following is an example to define inline pointcut for before advice:
@Before("execution(* com.xyz.myapp.service.*.*(..))")
public doBeforeTask(){ ... } @AspectJ-based AOP Example To understand the above-mentioned concepts related to @AspectJ based AOP, let us write an example which will implement few of the advices. To write our example with few advices, let us have a working Eclipse IDE in place and take the following steps to create a Spring application:
package com.jtc;
import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.Around; @Aspect public class Logging { /** Following is the definition for a pointcut to select * all the methods available. So advice will be called * for all the methods. */ @Pointcut("execution(* com.jtc.*.*(..))") private void selectAll(){} /** * This is the method which I would like to execute * before a selected method execution. */ @Before("selectAll()") public void beforeAdvice(){ System.out.println("Going to setup student profile."); } /** * This is the method which I would like to execute * after a selected method execution. */ @After("selectAll()") public void afterAdvice(){ System.out.println("Student profile has been setup."); } /** * This is the method which I would like to execute * when any method returns. */ @AfterReturning(pointcut = "selectAll()", returning="retVal") public void afterReturningAdvice(Object retVal){ System.out.println("Returning:" + retVal.toString() ); } /** * This is the method which I would like to execute * if there is an exception raised by any method. */ @AfterThrowing(pointcut = "selectAll()", throwing = "ex") public void AfterThrowingAdvice(IllegalArgumentException ex){ System.out.println("There has been an exception: " + ex.toString()); } } Following is the content of the Student.java file
package com.jtc;
public class Student { private Integer age; private String name; public void setAge(Integer age) { this.age = age; } public Integer getAge() { System.out.println("Age : " + age ); return age; } public void setName(String name) { this.name = name; } public String getName() { System.out.println("Name : " + name ); return name; } public void printThrowException(){ System.out.println("Exception raised"); throw new IllegalArgumentException(); } } Following is the content of the MainApp.java file
package com.jtc;
import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MainApp { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml"); Student student = (Student) context.getBean("student"); student.getName(); student.getAge(); student.printThrowException(); } } Following is the configuration file Beans.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:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd "> <aop:aspectj-autoproxy/> <!-- Definition for student bean --> <bean id="student" class="com.jtc.Student"> <property name="name" value="Zara" /> <property name="age" value="11"/> </bean> <!-- Definition for logging aspect --> <bean id="logging" class="com.jtc.Logging"/> </beans> Once you are done creating the source and bean configuration files, let us run the application. If everything is fine with your application, it will print the following message:
Going to setup student profile.
Name : Zara Student profile has been setup. Returning:Zara Going to setup student profile. Age : 11 Student profile has been setup. Returning:11 Going to setup student profile. Exception raised Student profile has been setup. There has been an exception: java.lang.IllegalArgumentException ..... other exception content |