Apply same logic in several points in your application is called cross cutting concerns, and in Spring we can implement it using AOP, some basics aspects are:
- Advice: Action taken. Different types of advice include “around,” “before” and “after” advice.
- Pointcut: Where the action should be applied.
In this example we are going to define an AfterThrowingAdvice which means an action is executed after some exception has been thrown, so first we need to define a general exception in our application.
package com.jos.dem.jmailer.exception
import org.springframework.core.NestedRuntimeException
class BusinessException extends NestedRuntimeException {
BusinessException(String msg){
super(msg)
}
BusinessException(String msg, Throwable cause) {
super(msg, cause)
}
}
BusinessException is a general exception in our application defined to react in the same way to the specific exception, some specific exception could be an EmailerException.
package com.jos.dem.jmailer.exception
import java.lang.RuntimeException
class EmailerException extends RuntimeException {
@Override
String getMessage() {
"Emailer exception"
}
}
So our advice look like this:
package com.jos.dem.jmailer.advice
import org.aspectj.lang.annotation.AfterThrowing
import org.aspectj.lang.annotation.Aspect
import org.springframework.stereotype.Component
import com.jos.dem.jmailer.exception.BusinessException
import org.apache.commons.logging.Log
import org.apache.commons.logging.LogFactory
@Aspect
@Component
class AfterThrowingAdvice {
Log log = LogFactory.getLog(this.class)
@AfterThrowing(pointcut = "execution(* com.jos.dem.jmailer.service..**.*(..))", throwing = "ex")
public void doRecoveryActions(RuntimeException ex) {
log.info "Wrapping exception ${ex}"
throw new BusinessException(ex.getMessage(), ex)
}
}
Explanation
- An aspect is defined with the @Aspect annotation
- A bean managed by the spring context is defined with the @Component annotation
- A pointcut is defined as all methods in package com.jos.dem.jmailer.service with any parameter
- The action defined in the advice is thrown an BusinessException
So if in any service we throw an Exception, actually a RuntimeException our advice logic is executed.
package com.jos.dem.jmailer.service
import org.springframework.stereotype.Service
import org.springframework.util.StringUtils
import com.jos.dem.jmailer.exception.EmailerException
import org.apache.commons.logging.Log
import org.apache.commons.logging.LogFactory
@Service
class EmailerService {
Log log = LogFactory.getLog(this.class)
String getTitle(String name) {
log.debug "GETTING title with name : ${name}"
"Hello ${name}"
}
String getDescription() {
log.debug "GETTING description"
"Jmailer is a service for delivering emails"
}
def sendEmail(){
log.debug 'Sending email'
throw new EmailerException()
}
}
As you can see at EmailerService in the sendEmail() method we are throwing an EmailerException so our Advice will be called.
In order to define our AOP in spring we need an aop context file in the following path: src/main/resources/aop-appctx.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/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
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.0.xsd">
<aop:aspectj-autoproxy/>
<context:component-scan base-package="com.jos.dem.jmailer.advice"/>
</beans>
Finally we need to import our aop-appctx.xml in our servlet as well.
File: src/main/webapp/WEB-INF/dispatcher-servlet.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd ">
<import resource="classpath:/aop-appctx.xml" />
<context:component-scan base-package="com.jos.dem.jmailer" />
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/views/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
<mvc:resources mapping="/resources/**" location="/resources/" />
<mvc:annotation-driven />
</beans>
To download the project
git clone https://github.com/josdem/jmailer-bootstrap.git
git fetch
git checkout feature/aop-business-exception