Spring AOP
- AspectJ:Java 社区里最完整最流行的 AOP 框架.
- 在 Spring2.0 以上版本中, 可以使用基于 AspectJ 注解或基于 XML 配置的 AOP
在 Spring 中启用 AspectJ 注解支持
- 要在 Spring 应用中使用 AspectJ 注解, 必须在 classpath 下包含 AspectJ 类库: aopalliance.jar、aspectj.weaver.jar 和 spring-aspects.jar
- 将 aop Schema 添加到 < beans> 根元素中.
- 要在 Spring IOC 容器中启用 AspectJ 注解支持, 只要在 Bean 配置文件中定义一个空的 XML 元素 < aop:aspectj-autoproxy>
- 当 Spring IOC 容器侦测到 Bean 配置文件中的 < aop:aspectj-autoproxy> 元素时, 会自动为与 AspectJ 切面匹配的 Bean 创建代理.
用 AspectJ 注解声明切面
- 要在 Spring 中声明 AspectJ 切面, 只需要在 IOC 容器中将切面声明为 Bean 实例. 当在 Spring IOC 容器中初始化 AspectJ 切面之后, Spring IOC 容器就会为那些与 AspectJ 切面相匹配的 Bean 创建代理.
- 在 AspectJ 注解中, 切面只是一个带有 @Aspect 注解的 Java 类.
- 通知是标注有某种注解的简单的 Java 方法.
- AspectJ 支持 5 种类型的通知注解:
-
-
-
- @AfterRunning: 返回通知, 在方法返回结果之后执行
-
- @AfterThrowing: 异常通知, 在方法抛出异常之后
-
package com.atguigu.spring.aop;
import java.util.Arrays;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
/**
* 可以使用 @Order 注解指定切面的优先级,数字越小优先级越高
* @author hasee
*
*/
@Order(2)
@Aspect
@Component
public class LoggingAspect {
/**
* 定义一个方法,用于声明切入点表达式.一般地,该方法中再不需要添入其他的代码
* 使用 @Pointcut 来声明切入点表达式.
* 后面的其他通知直接使用方法名来引用当前的切入点表达式.
*
*/
@Pointcut("execution(public int com.atguigu.spring.aop.ArithmeticCalculator.*(..))")
public void declareJointPointExpression() {}
/**
* 在每一个接口的实现类的每一个方法开始之前执行一段代码
*/
@Before("declareJointPointExpression()")
public void beforeMethod(JoinPoint joinPoint) {
// TODO Auto-generated method stub
String methodName = joinPoint.getSignature().getName();
Object[] args = joinPoint.getArgs();
System.out.println("The method " + methodName + "begins with " + Arrays.asList(args));
}
@After("declareJointPointExpression()")
public void afterMethod(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
System.out.println("The method " + methodName + "ends");
}
/**
* 在方法正常结束后执行的代码
* 返回通知是可以访问到方法的返回值的!
* @param joinPoint
*/
@AfterReturning(value = "declareJointPointExpression()",returning="result")
public void afterReturning(JoinPoint joinPoint, Object result) {
String methodName = joinPoint.getSignature().getName();
System.out.println("The method " + methodName + "ends with :" + result);
}
/**
* 异常通知
* 在方法出现异常时会执行的代码.
* 可以访问到异常对象;且可以指定出现特定异常时再执行通知代码
* @param joinPoint
* @param ex
*/
@AfterThrowing(value = "declareJointPointExpression()",throwing="ex")
public void afterReturning(JoinPoint joinPoint, Exception ex) {
String methodName = joinPoint.getSignature().getName();
System.out.println("The method " + methodName + "occurs excetion :" + ex);
}
/**
* 环绕通知需要携带 PriceedingJoinPoint 类型的参数.
* 环绕通知类似于动态代理的全过程: ProceedingJoinPoint 类型的参数可以决定是否执行目标方法.
* 且环绕通知必须有给返回值,返回值即为目标方法的返回值
* @param pjd
*/
@Around("declareJointPointExpression()")
public Object aroundMethod(ProceedingJoinPoint pjd) {
Object result = null;
String methodName = pjd.getSignature().getName();
//执行目标方法
// System.out.println("aroundMethod");
try {
//前置通知
System.out.println("The method " + methodName + "begins with " + Arrays.asList(pjd.getArgs()));
result = pjd.proceed();
} catch (Throwable e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return result;
}
}
用基于 XML 的配置声明切面
- 除了使用 AspectJ 注解声明切面, Spring 也支持在 Bean 配置文件中声明切面. 这种声明是通过 aop schema 中的 XML 元素完成的.
- 正常情况下, 基于注解的声明要优先于基于 XML 的声明. 通过 AspectJ 注解, 切面可以与 AspectJ 兼容, 而基于 XML 的配置则是 Spring 专有的. 由于 AspectJ 得到越来越多的 AOP 框架支持, 所以以注解风格编写的切面将会有更多重用的机会.
<?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-4.0.xsd">
<!-- 配置 bean -->
<bean id="arithmeticCalculator"
class="com.atguigu.spring.aop.xml.ArithmeticCalculatorImpl"></bean>
<!-- 配置切面的 bean -->
<bean id="loggingAspect"
class="com.atguigu.spring.aop.xml.LoggingAspect"></bean>
<bean id="vlidationAspect"
class="com.atguigu.spring.aop.xml.VlidationAspect"></bean>
<!-- 配置 AOP -->
<aop:config>
<!-- 配置切点表达式 -->
<aop:pointcut
expression="execution(* com.atguigu.spring.aop.xml.ArithmeticCalculator.*(int,int))"
id="pointcut" />
<!-- 配置切面及通知 -->
<aop:aspect ref="loggingAspect" order="2">
<aop:before method="beforeMethod" pointcut-ref="pointcut" />
<aop:after method="afterMethod" pointcut-ref="pointcut"/>
<aop:after-returning method="afterReturning" pointcut-ref="pointcut" returning="result"/>
<aop:after-throwing method="afterThrowing" pointcut-ref="pointcut" throwing="ex"/>
</aop:aspect>
</aop:config>
</beans>