http://www.javacodegeeks.com/2010/07/aspect-oriented-programming-with-spring.html
Spring framework comes with AOPsupport. In fact, as stated in Spring reference documentation,
“One of the key components of Spring is the AOP framework. While the Spring IoC container does not depend on AOP, meaning you do not need to use AOP if you don't want to, AOP complements Spring IoC to provide a very capable middleware solution. AOP is used in the Spring Framework to...
- ... provide declarative enterprise services, especially as a replacement for EJB declarative services. The most important such service is declarative transaction management.
- ... allow users to implement custom aspects, complementing their use of OOP with AOP.”
Nevertheless Spring AOP framework comes with certain limitations in comparison to a complete AOP implementation, such as AspectJ. The most common problems people encounter while working with Spring AOP framework derive from the fact that Spring AOPis “proxy – based”. In other words when a bean is used as a dependency and its method(s) should be advised by particular aspect(s) the IoC container injects an “aspect – aware” bean proxy instead of the bean itself. Method invocations are performed against the proxy bean instance, transparently to the user, in order for aspect logic to be executed before and/or after delegating the call to the actual “proxy–ed” bean.
Furthermore Spring AOP framework uses either JDK dynamic proxies or CGLIB to create the proxy for a given target object. The first one can create proxies only for the interfaces whilst the second is able to proxy concrete classes but with certain limitations. In particular, as stated in Spring reference documentation,
“If the target object to be proxied implements at least one interface then a JDK dynamic proxy will be used. All of the interfaces implemented by the target type will be proxied. If the target object does not implement any interfaces then a CGLIB proxy will be created.”
To summarize, when working with Spring AOP framework you should have two important things in mind :
- If your “proxy–ed” bean implements at least one interface, the proxy bean can ONLY be casted to that interface(s). If you try to cast it to the “proxy–ed” bean class, then you should expect a ClassCastException to be thrown at runtime. NeverthelessSpring AOP framework provides the option to force CGLIB proxying but with the aforementioned limitations (please refer to the relevant chapter of Spring reference documentation)
- Aspects do not apply to intra–operation calls. Meaning that there is no way for the proxy to intercept the calling of a method originated from another method of the same “proxy–ed” bean
- compile time weaving – compile either target source or aspect classes via theAspectJ compiler
- post compile weaving – inject aspect instructions to already compiled classes
- load time weaving – inject aspect instructions to the byte code during class loading
Spring users who have already implemented aspects for their bean services can switch toAspectJ transparently, meaning that no special code needs to be written since Spring AOPframework uses a subset of AspectJ pointcut expression language, and @AspectJ Springaspects are fully eligible for AspectJ weaving.
Our preferred development environment is Eclipse, so as a prerequisite you must haveEclipse with Maven support installed. The installation of Maven plugin for Eclipse is out of the scope of this tutorial and will not be discussed. Nevertheless you will need the following components : We will be using Eclipse Galileo, „m2eclipse“ Maven Integration for Eclipse Plugin version 0.10.0, Spring version 3.0.1, aspectjrt version 1.6.7 and aspectj-maven-plugin version 1.3 for this tutorial.
Lets begin,
- Create a new Maven project, go to File → Project → Maven → Maven Project
- In the „Select project name and location“ page of the wizard, make sure that „Create a simple project (skip archetype selection)“ option is unchecked, hit „Next“ to continue with default values
- In the „Select an Archetype“ page of the wizard, select „Nexus Indexer“ at the „Catalog“ drop down list and after the archetypes selection area is refreshed, select the „webapp-jee5“ archetype from „org.codehaus.mojo.archetypes“ to use. You can use the „filter“ text box to narrow search results. Hit „Next“ to continue
- In the „Enter an artifact id“ page of the wizard, you can define the name and main package of your project. We will set the „Group Id“ variable to „com.javacodegeeks“ and the „Artifact Id“ variable to „aspectjspring“. The aforementioned selections compose the main project package as „com.javacodegeeks.aspectjspring “ and the project name as „aspectjspring“. Hit „Finish“ to exit the wizard and to create your project
Let's recap a few things about the Maven Web project structure
- /src/main/java folder contains source files for the dynamic content of the application
- /src/test/java folder contains all source files for unit tests
- /src/main/webapp folder contains essential files for creating a valid web application, e.g. „web.xml“
- /target folder contains the compiled and packaged deliverables
- The „pom.xml“ is the project object model (POM) file. The single file that contains all project related configuration.
- Locate the „Properties“ section at the „Overview“ page of the POM editor and perform the following changes :
- Create a new property with name org.springframework.version and value 3.0.1.RELEASE
- Create a new property with name maven.compiler.source and value according to the version of your Java runtime environment, we will use1.6
- Create a new property with name maven.compiler.target and value according to the version of your Java runtime environment, we will use1.6
- Navigate to the „Dependencies“ page of the POM editor and create the following dependencies (you should fill the „GroupId“, „Artifact Id“ and „Version“ fields of the „Dependency Details“ section at that page) :
- Group Id : org.springframework Artifact Id : spring-web Version :${org.springframework.version}
- Group Id : org.aspectj Artifact Id : aspectjrt Version : 1.6.7
- Navigate to the “Plugins” page of the POM editor and create the following plugin (you should fill the „GroupId“, „Artifact Id“ and „Version“ fields of the „Plugin Details“ section at that page) :
- Group Id : org.codehaus.mojo Artifact Id : aspectj-maven-pluginVersion : 1.3
- At the “Plugins” page of the POM editor, select the newly created plugin (from the “Plugins” section), and bind it to the compile execution goal. To do so, locate the “Execution” section and create a new execution. At the “Execution Details” section create a new goal and name it “compile”
- The newly created plugin needs one final configuration change. We must define what version of Java runtime environment we are using in order for the AspectJ compiler to properly weave aspect classes. We need to edit the “pom.xml” file to perform the change. Select the “pom.xml” page of the POM editor, locate the newly created plugin and alter it as follows :
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.3</version>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
6. Finally change the “maven-compiler-plugin” as shown below :
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.0.2</version>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
</configuration>
</plugin>
As you can see Maven manages library dependencies declaratively. A local repository is created (by default under {user_home}/.m2 folder) and all required libraries are downloaded and placed there from public repositories. Furthermore intra – library dependencies are automatically resolved and manipulated.
The next step is to provide hooks for the web application so as to load the Spring context upon startup.
Locate the „web.xml“ file under /src/main/webapp/WEB-INF and add the following :
For loading the Spring context upon startup,
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
Now lets create the applicationContext.xml file that will drive Spring container. Create the file under /src/main/webapp/WEB-INF directory. An example „applicationContext.xml“ is presented below
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.0.xsd">
<context:component-scan base-package="com.javacodegeeks.aspectjspring" />
<bean class="com.javacodegeeks.aspectjspring.aspects.GreetingAspect" factory-method="aspectOf">
<property name="message" value="Hello from Greeting Aspect"/>
</bean>
</beans>
Things to notice here :
- Change the base-package attribute of the context:component-scan element to whatever is the base package of your project so as to be scanned for Springcomponents
- We have to define our aspects in “applicationContext.xml” only if we want to inject dependences to them
- AspectJ denotes the term of “aspect association”. It defines how to manage aspect state. The following state associations are supported:
- Per JVM – one shared aspect instance is constructed and used (default)
- Per object – aspect has its own state per every advised object
- Per control flow – aspect has its own state per particular control flow
All AspectJ aspect classes have “hasAspect()” and “aspectOf()” static methods. These methods are implicitly generated by AspectJcompiler/load time weaver. So, for the default aspect state there is a single aspect instance that can be retrieved using the “aspectOf()” method
- Per JVM – one shared aspect instance is constructed and used (default)
Lets create now the “greeting” Spring service and the relevant “greeting” AspectJ aspect. Create a sub – package named “services” under your main package and place the “GreetingService” class there. An example “greeting” service is shown below :
package com.javacodegeeks.aspectjspring.services;
import org.springframework.stereotype.Service;
@Service("greetingService")
public class GreetingService {
public String sayHello() {
return "Hello from Greeting Service";
}
}
Create a sub – package named “aspects” under your main package and place the “GreetingAspect” class there. An example “greeting” aspect is shown below :
package com.javacodegeeks.aspectjspring.aspects;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class GreetingAspect {
private String message;
public void setMessage(String message) {
this.message = message;
}
@Around("execution(* com.javacodegeeks.aspectjspring.services.GreetingService.*(..))")
public Object advice(ProceedingJoinPoint pjp) throws Throwable {
String serviceGreeting = (String) pjp.proceed();
return message + " and " + serviceGreeting;
}
}
Finally locate the main Web page of your project, “index.jsp”, under /src/main/webapp folder and alter it as follows :
<%@ page language="java" import="org.springframework.web.context.WebApplicationContext, org.springframework.web.context.support.WebApplicationContextUtils, com.javacodegeeks.aspectjspring.services.GreetingService"%>
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<%
WebApplicationContext webApplicationContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
GreetingService greetingService = (GreetingService) webApplicationContext.getBean("greetingService");
%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>JSP Page</title>
</head>
<body>
<h1>Test service invoked and greets you by saying : <%=greetingService.sayHello()%></h1>
</body>
</html>
Things to notice here :
- Upon page load, we retrieve the Spring Web application context, and lookup our “greeting” service. All we have to do is to invoke the “sayHello()” method to see the combined greeting message from our aspect and the service
To build the application right click on your project → Run As → Maven package
To deploy the web application just copy the „.war“ file from the „target“ directory to Apache – Tomcat “webapps” folder
To lunch the application point your browser to the following address
http://localhost:8080/{application_name}/
If all went well you should see your main web page displaying the following :
“Test service invoked and greets you by saying : Hello from Greeting Aspect and Hello from Greeting Service”
You can download the project from here
Hope you liked it
Justin