Using Spring AspectJ
In Java, a library called “AspectJ” is widely used to implement AOP. This article explains AOP processing in Spring AOP by using AspectJ.
AspectJ and Modifying pom.xml
If you look into AOP, you will probably find a lot of information about software called “AspectJ”. AspectJ is so widely used that it can be considered the de facto standard Java AOP software.
Spring AOP also includes features for implementing AOP by using AspectJ. Since it can implement AOP in a different form from the Spring AOP used previously, let’s learn how to use this approach as well.
First, prepare to use AspectJ. Open pom.xml and add the following content inside the <dependencies> tag. Do not delete the spring-core and spring-aop tags that are already written.
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.6</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.6</version>
</dependency>
In this example, the version is specified for Spring Framework 4.1.7. If you use a different version, adjust <version> accordingly.
Two libraries are added here. “AspectJ RT” is the AspectJ runtime program. Adding this library makes AspectJ features available. “AspectJ Weaver” is a utility program required to generate code that applies aspects based on aspect information. To use AspectJ in Spring AOP, you need to prepare these two together.
Create the Aspect Class
Create a class that contains the processing inserted as an Aspect. This time, because AspectJ is used, the style is different from the previous one.
The following is the sample code. Create a class named “SampleAspect” in the com.devkuma.spring.aop package.
package com.devkuma.spring.aop;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class SampleAspect {
@Before("execution(* com.devkuma.spring.aop.SampleAopBean.*(..))")
public void before() {
System.out.println("before:");
}
@After("execution(* com.devkuma.spring.aop.SampleAopBean.*(..))")
public void after() {
System.out.println("after:");
}
}
As you can see, this is a very ordinary POJO class. It does not implement any interface or extend any class. The only difference is that annotations are added.
@Aspect
Indicates that this class is an Aspect class. Classes used by AspectJ are given this annotation.
@Before
This annotation is placed before a method. It indicates processing inserted before method execution. You can think of it as similar to “before” in Spring AOP.
@After
This annotation is placed before a method. It indicates processing inserted after method execution. You can think of it as corresponding to “afterRunning” in Spring AOP.
The annotations themselves are simple. What is harder to understand is the content written inside the parentheses after @Before and @After. It is written as follows.
("execution (...... method specification to be assigned ...)")
Inside the parentheses after execution, specify which method this method should be inserted into. This must accurately specify the method from the package name onward. However, that does not mean every name must be written completely.
Wildcards (*) can be used here, so you can use them to specify all methods in a specific package and class. You can also use the .. symbol for method arguments to specify any arguments.
The expression written here is as follows.
* com.devkuma.spring.aop.SampleAopBean.*(..)
To make it easier to understand, if you write an unspecified value as XX, it looks like this.
XX com.devkuma.spring.aop.SampleAopBean.XX(XX)
The first XX is included to account for public, private, and similar modifiers. The XX after SampleAopBean specifies that any method in this class is targeted. The (XX) means that any form of arguments should match.
Once you understand how to write execution expressions to some extent, you will be able to specify the methods targeted by AOP processing as you want.
Write aopbean.xml
Next, prepare the Bean configuration file. This time, add a new file named “aopbean.xml”.
Create “aopbean.xml” in the “resources” folder and write the following content.
<?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.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="sampleAspect" class="com.devkuma.spring.aop.SampleAspect" />
<bean id="sampleAopBean" class="com.devkuma.spring.aop.SampleAopBean">
<property name="message" value="this is AOP bean!" />
</bean>
<aop:aspectj-autoproxy />
</beans>
Compared with the bean.xml used so far, there are many more things to write this time. The content is summarized below.
Attributes of the <beans> Tag
First, look at the <beans> tag. The following attribute has been added.
xmlns : aop = "http://www.springframework.org/schema/aop"
Also, values indicating this schema location are added to the xsi:schemaLocation value. Specifically, these are the following parts.
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
This is required so that <aop:aspectj-autoproxy/> used in the configuration file can be used. It is somewhat long, but it must be added.
Bean Registration
<bean id = "sampleAspect"class = "comdevkuma.spring.aop.SampleAspect"/>
<bean id = "sampleAopBean"class = "com.devkuma.spring.aop.SampleAopBean">
<property name = "message"value = "this is AOP bean!"/>
</bean>
This is the Bean registration tag that has already been written several times. Register the SampleAspect class and the SampleAopBean class as Beans.
AspectJ Auto Proxy
<aop:aspectj-autoproxy/>
This is a tag for AspectJ. It automatically creates the equivalent of ProxyFactoryBean used earlier in Spring AOP. By writing this, the feature prepared by ProxyFactoryBean is automatically included.
Run the Program
Now AspectJ is ready to use. Let’s use the Bean. Modify the App class in the com.devkuma.spring.aop package as follows.
package com.devkuma.spring.aop;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App {
public static void main(String[] args) {
ApplicationContext app = new ClassPathXmlApplicationContext("aopbean.xml");
SampleAopBean bean = (SampleAopBean) app.getBean("sampleAopBean");
String msg = bean.getMessage();
bean.setMessage("<<" + msg + ">>");
bean.printMessage();
}
}
Execution result:
before :
after :
before :
after :
before :
message : [<< this is AOP bean! >>
after :
Here, getMessage obtains the message, setMessage sets the message based on it, and printMessage outputs the content. You can see that before / after is printed before and after each method call.
The decisive difference from the previous Spring AOP example is the class obtained and executed with getBean. In Spring AOP, the modified SampleAopBean could be obtained through ProxyFactoryBean.
This time, what is obtained by getBean is SampleAopBean. In other words, the SampleAopBean class itself has methods with inserted processing.
After checking the behavior, open aopbean.xml and delete the <aop : aspectj-autoproxy/> tag. If you run it again, only “message:[« this is AOP bean! »]” is displayed. You can see that the methods inserted before / after have disappeared.
In this way, using AspectJ makes it easy to insert and remove processing by simply turning auto proxy ON or OFF. However, the Bean used cannot be returned from the proxy Bean to the original.
Using a Bean Configuration Class
Now the basic usage of AspectJ is clear. In Spring Framework, Beans could be defined by using classes instead of configuration files. Let’s also configure AspectJ with a definition class.
Create a class named “SampleAspectConfig” in the com.devkuma.spring.aop package. Then write the following code.
package com.devkuma.spring.aop;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@EnableAspectJAutoProxy
public class SampleAspectConfig {
@Bean
SampleAopBean sampleAopBean() {
return new SampleAopBean("this is AspectJ bean.");
}
@Bean
SampleAspect sampleAspect() {
return new SampleAspect();
}
}
This is the AspectJ configuration class. After writing it, open App.java and modify the following statement.
Before modifying App
ApplicationContext app =
new ClassPathXmlApplicationContext ( "aopbean.xml");
After modifying App
ApplicationContext app =
new AnnotationConfigApplicationContext (SampleAspectConfig.class);
Now, when you run it, processing is inserted using AspectJ as before, and processing is executed before and after method calls. In this class, in addition to @Configuration, which indicates that it is a configuration class, the following annotation is added.
@EnableAspectJAutoProxy
This annotation corresponds to the <aop:aspectj-autoproxy/> tag that was put in the Bean definition file. By writing this, AspectJ’s auto proxy feature is turned ON, and methods in the Aspect class are automatically inserted.
The Bean definitions are the same as before. The only difference is that adding @EnableAspectJAutoProxy turns AspectJ’s features ON. Even when changing to a configuration class, you can see that using AspectJ remains simple.