您的位置:首页 > 编程语言 > Java开发

Spring-AOP详解

2016-12-29 22:29 267 查看
AOP术语

AspectJ注解和Spring原生的基于 XML 配置的 AOP
启用AspectJ注解支持

用AspectJ注解声明切面

前置通知和后置通知

返回通知异常通知环绕通知开发中常用

指定切面的优先级

基于XML方式配置AOP

AOP术语

切面(Aspect): 横切关注点(跨越应用程序多个模块的功能)被模块化的特殊对象

通知(Advice): 切面必须要完成的工作

目标(Target): 被通知的对象

代理(Proxy): 向目标对象应用通知之后创建的对象

连接点(Joinpoint):程序执行的某个特定位置:如类某个方法调用前、调用后、方法抛出异常后等。连接点由两个信息确定:方法表示的程序执行点;相对点表示的方位。例如 ArithmethicCalculator#add() 方法执行前的连接点,执行点为 ArithmethicCalculator#add(); 方位为该方法执行前的位置

切点(pointcut):每个类都拥有多个连接点:例如 ArithmethicCalculator 的所有方法实际上都是连接点,即连接点是程序类中客观存在的事务。AOP 通过切点定位到特定的连接点。类比:连接点相当于数据库中的记录,切点相当于查询条件。切点和连接点不是一对一的关系,一个切点匹配多个连接点,切点通过 org.springframework.aop.Pointcut 接口进行描述,它使用类和方法作为连接点的查询条件。

AspectJ注解和Spring原生的基于 XML 配置的 AOP

AspectJ:Java 社区里最完整最流行的 AOP 框架.

在 Spring2.0 以上版本中, 可以使用基于 AspectJ 注解或基于 XML 配置的 AOP

启用AspectJ注解支持

要在 Spring 应用中使用 AspectJ 注解, 必须在 classpath 下包含 AspectJ 类库: aopalliance.jar、aspectj.weaver.jar 和 spring-aspects.jar

将 aop Schema 添加到 根元素中.

要在 Spring IOC 容器中启用 AspectJ 注解支持, 只要在 Bean 配置文件中定义一个空的 XML 元素

当 Spring IOC 容器侦测到 Bean 配置文件中的 元素时, 会自动为与 AspectJ 切面匹配的 Bean 创建代理.

用AspectJ注解声明切面

要在 Spring 中声明 AspectJ 切面, 只需要在 IOC 容器中将切面声明为 Bean 实例. 当在 Spring IOC 容器中初始化 AspectJ 切面之后, Spring IOC 容器就会为那些与 AspectJ 切面相匹配的 Bean 创建代理.

在 AspectJ 注解中, 切面只是一个带有 @Aspect 注解的 Java 类.

通知是标注有某种注解的简单的 Java 方法.

AspectJ 支持 5 种类型的通知注解:

@Before: 前置通知, 在方法执行之前执行

@After: 后置通知, 在方法执行之后执行

@AfterRunning: 返回通知, 在方法返回结果之后执行

@AfterThrowing: 异常通知, 在方法抛出异常之后

@Around: 环绕通知, 围绕着方法执行

前置通知和后置通知

使用JoinPoint 类,会对目标方法执行各种操作。

JoinPoint jp

jp.getTarget().getClass().getName() //获取类名

jp.getSignature().getName() //获取方法名

jp.getArgs() //获取参数

StringBuilder sb = new StringBuilder();
sb.append(jp.getTarget().getClass().getName());
sb.append(".");
sb.append(jp.getSignature().getName());
sb.append("(");
sb.append(Arrays.asList(jp.getArgs());
sb.append(")");
//保存到日期数据库
sb.toString(); //com.shxt.service.impl.ArithmeticServiceImpl.sub([10,2]) 类似这样的日志


这俩个通知的特点是:无论什么情况都会执行。

需要配置:

<!-- 使 AspectJ 的注解起作用 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>


<aop:aspectj-autoproxy proxy-target-class="true" ></aop:aspectj-autoproxy>


  注意:proxy-target-class属性值决定是基于接口的还是基于类的代理被创建。如果proxy-target-class 属性值被设置为true,那么基于类的代理将起作用(这时需要cglib库)。如果proxy-target-class属值被设置为false或者这个属性被省略,那么标准的JDK 基于接口的代理将起作用。

即使你未声明 proxy-target-class=”true” ,但运行类没有继承接口,spring也会自动使用CGLIB代理。

高版本spring自动根据运行类选择 JDK 或 CGLIB 代理

来源: http://blog.csdn.net/zhousenshan/article/details/51511619

/**
* AOP 的 helloWorld
* 1. 加入 jar 包
* com.springsource.net.sf.cglib-2.2.0.jar
* com.springsource.org.aopalliance-1.0.0.jar
* com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
* spring-aspects-4.0.0.RELEASE.jar
*
* 2. 在 Spring 的配置文件中加入 aop 的命名空间。
*
* 3. 基于注解的方式来使用 AOP
* 3.1 在配置文件中配置自动扫描的包: <context:component-scan base-package="com.atguigu.spring.aop"></context:component-scan>
* 3.2 加入使 AspjectJ 注解起作用的配置: <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
* 为匹配的类自动生成动态代理对象.
*
* 4. 编写切面类:
* 4.1 一个一般的 Java 类
* 4.2 在其中添加要额外实现的功能.
*
* 5. 配置切面
* 5.1 切面必须是 IOC 中的 bean: 实际添加了 @Component 注解
* 5.2 声明是一个切面: 添加 @Aspect
* 5.3 声明通知: 即额外加入功能对应的方法.
* 5.3.1 前置通知: @Before("execution(public int com.atguigu.spring.aop.ArithmeticCalculator.*(int, int))")
execution(* com..service.*Service.*(..))
* @Before 表示在目标方法执行之前执行 @Before 标记的方法的方法体.
* @Before 里面的是切入点表达式:
*
* 6. 在通知中访问连接细节: 可以在通知方法中添加 JoinPoint 类型的参数, 从中可以访问到方法的签名和方法的参数.
*
* 7. @After 表示后置通知: 在方法执行之后执行的代码.
*/

//通过添加 @Aspect 注解声明一个 bean 是一个切面!
@Aspect
@Component
public class LoggingAspect {

@Before("execution(* com..service.*Service.*(..))")
public void beforeMethod(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
Object [] args = joinPoint.getArgs();

System.out.println("The method " + methodName + " begins with " + Arrays.asList(args));
}

@After("execution(* com.atguigu.spring.aop.*.*(..))")
public void afterMethod(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
System.out.println("The method " + methodName + " ends");
}

}


返回通知&异常通知&环绕通知(开发中常用)

返回通知的特点:正确执行的情况会执行通知

异常通知的特点:只有异常的情况会执行通知。

使用JoinPoint 类,会对目标方法执行各种操作。

JoinPoint jp

jp.getTarget().getClass().getName() //获取类名

jp.getSignature().getName() //获取方法名

jp.getArgs() //获取参数

StringBuilder sb = new StringBuilder();
sb.append(jp.getTarget().getClass().getName());
sb.append(".");
sb.append(jp.getSignature().getName());
sb.append("(");
sb.append(Arrays.asList(jp.getArgs());
sb.append(")");
//保存到日期数据库
sb.toString(); //com.shxt.service.impl.ArithmeticServiceImpl.sub([10,2]) 类似这样的日志


import java.util.Arrays;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
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 注解指定切面的优先级, 值越小优先级越高
*/
@Order(2)
@Aspect
@Component
public class LoggingAspect {

/**
* 定义一个方法, 用于声明切入点表达式. 一般地, 该方法中再不需要添入其他的代码.
* 使用 @Pointcut 来声明切入点表达式.
* 后面的其他通知直接使用方法名来引用当前的切入点表达式.
*/
@Pointcut("execution(* com..service.*Service.*(..))")
public void declareJointPointExpression(){}

/**
* 在 com.atguigu.spring.aop.ArithmeticCalculator 接口的每一个实现类的每一个方法开始之前执行一段代码
*/
@Before("declareJointPointExpression()")
public void beforeMethod(JoinPoint joinPoint){
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");
}

/**
* 在方法法正常结束受执行的代码
* 返回通知是可以访问到方法的返回值的!
*/
@AfterReturning(value="declareJointPointExpression()",
="result")
public void afterReturning(JoinPoint joinPoint, Object result){
String methodName = joinPoint.getSignature().getName();
System.out.println("The method " + methodName + " ends with " + result);
}

/**
* 在目标方法出现异常时会执行的代码.
* 可以访问到异常对象; 且可以指定在出现特定异常时在执行通知代码
*/
@AfterThrowing(value="declareJointPointExpression()",
throwing="e")
public void afterThrowing(JoinPoint joinPoint, Exception e){
String methodName = joinPoint.getSignature().getName();
System.out.println("The method " + methodName + " occurs excetion:" + e);
}

/**
* 环绕通知需要携带 ProceedingJoinPoint 类型的参数.
* 环绕通知类似于动态代理的全过程: ProceedingJoinPoint 类型的参数可以决定是否执行目标方法.
* 且环绕通知必须有返回值, 返回值即为目标方法的返回值
*/
/*
@Around("execution(public int com.atguigu.spring.aop.ArithmeticCalculator.*(..))")
public Object aroundMethod(ProceedingJoinPoint pjd){

Object result = null;
String methodName = pjd.getSignature().getName();

try {
//在这个位置是前置通知
System.out.println("The method " + methodName + " begins with " + Arrays.asList(pjd.getArgs()));
//执行目标方法
result = pjd.proceed();
//在这个位置是返回通知
System.out.println("The method " + methodName + " ends with " + result);
} catch (Throwable e) {
//在这个位置是异常通知
System.out.println("The method " + methodName + " occurs exception:" + e);
throw new RuntimeException(e);
}
//在这个位置是后置通知
System.out.println("The method " + methodName + " ends");

return result;
}
*/
}


指定切面的优先级

在同一个连接点上应用不止一个切面时, 除非明确指定, 否则它们的优先级是不确定的.

切面的优先级可以通过实现 Ordered 接口或利用 @Order 注解指定.

实现 Ordered 接口, getOrder() 方法的返回值越小, 优先级越高.

若使用 @Order 注解, 序号出现在注解中

基于XML方式配置AOP

<bean id="loggingAspect" class="com.spring.aop.xml.LoggingAspect"></bean>
<bean id="vlidationAspect" class="com.spring.aop.xml.VlidationAspect"></bean>

<!-- 配置 AOP -->
<aop:config>
<!-- 配置切点表达式 -->
<aop:pointcut expression="execution(* com..service.*Service.*(..))"  id="pointcut"/>
<!-- 配置切面及通知 -->
<aop:aspect ref="loggingAspect" order="2">
<aop:before method="beforeMethod" pointcut-ref="pointcut"/>
<aop:after method="afterMethod" pointcut-ref="pointcut"/>
<aop:after-throwing method="afterThrowing" pointcut-ref="pointcut" throwing="e"/>
<aop:after-returning method="afterReturning" pointcut-ref="pointcut" returning="result"/>
<!--
<aop:around method="aroundMethod" pointcut-ref="pointcut"/>
-->
</aop:aspect>
<aop:aspect ref="vlidationAspect" order="1">
<aop:before method="validateArgs" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  spring