Spring学习之AOP总结帖

AOP(面向方面编程),也可称为面向切面编程,是一种编程范式,提供从另一个角度来考虑程序结构从而完善面向对象编程(OOP)。

在进行 OOP 开发时,都是基于对组件(比如类)进行开发,然后对组件进行组合,OOP 最大问题就是无法解耦组件进行开发,比如我们上边举例,而 AOP 就是为了克服这个问题而出现的,它来进行这种耦合的分离。AOP 为开发者提供一种进行横切关注点(比如日志关注点)分离并织入的机制,把横切关注点分离,然后通过某种技术织入到系统中,从而无耦合的完成了我们的功能。

1 AOP概述

  AOP(Aspect-Oriented Programming, 面向切面编程): 是一种新的方法论, 是对传统 OOP(Object-Oriented Programming, 面向对象编程) 的补充。AOP 的主要编程对象是切面(aspect), 而切面模块化横切关注点。在应用 AOP 编程时, 仍然需要定义公共功能, 但可以明确的定义这个功能在哪里, 以什么方式应用, 并且不必修改受影响的类. 这样一来横切关注点就被模块化到特殊的对象(切面)里。AOP更多概念

AOP 的好处:

每个事物逻辑位于一个位置, 代码不分散, 便于维护和升级

业务模块更简洁, 只包含核心业务代码

2 Spring AOP

  在 Spring2.0 以上版本中,可以使用基于 AspectJ 注解或基于 XML 配置的 AOP,AspectJava是Java社区中最完整最流程的AOP框架。

2.1 在Spring中使用AspectJava注解支持

  要在 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注解中,切面只是一个带有@Aspect注解的Java类,通知是标注有某种注解的简单Java方法,AspectJ支持以下5种类型的通知注解:

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

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

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

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

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

2.2 利用方法签名编写 AspectJ 切入点表达式

  最典型的切入点表达式时根据方法的签名来匹配各种方法:

execution(public void com.aop.HelloImpl.hi()):匹配HelloImpl中返回值为void且无参数的hi方法

execution(public void com.aop.HelloImpl.*()):匹配HelloImpl中返回值为void且无参数的所有public方法

execution(public void com.aop.HelloImpl.*(..)):匹配HelloImpl中返回值为void的所有public方法

execution(public * com.aop.HelloImpl.*(..)):匹配HelloImpl中所有public方法

  在AspectJ中,切入点表达式可以使用操作符&&、||、!结合起来。

// 声明该方法为前置通知 @Before("execution(public void com.aop.HelloImpl.hi()) || execution(public void com.aop.HelloImpl.hihi(String))") public void beforeMethod(JoinPoint point) { String methodName = point.getSignature().getName(); System.out.println("HelloAspect beforeMethod : " + methodName); }

2.3 使用AOP程序示例,使用了全部的5种通知

  首先定义一个Hello接口,代码如下:

public interface Hello { public void hi(); }

  然后定义一个Hello实现类HelloImpl,代码如下:

@Component public class HelloImpl implements Hello { @Override public void hi() { System.out.println("HelloImp hi()..."); } }

  然后定义一个Hello的切面类HelloAspect,然后如下所示,注意:Hello接口、HelloImpl类、HelloAspect类都是在com.aop包下的。

@Aspect @Component public class HelloAspect {   // 声明该方法为前置通知   @Before("execution(public void com.aop.HelloImpl.hi()) || execution(public void com.aop.HelloImpl.hihi(String))")   public void beforeMethod(JoinPoint point) {   String methodName = point.getSignature().getName();   System.out.println("HelloAspect beforeMethod : " + methodName);   } // 声明该方法为后置通知,无论该方法是否发生异常 @After("execution(public void com.aop.HelloImpl.*())") public void endMethod(JoinPoint point) { String methodName = point.getSignature().getName(); System.out.println("HelloAspect afterMethod : " + methodName); } // 返回通知,正常返回时的通知,不包括异常,可以访问到方法的方绘制 @AfterReturning(value = "execution(public void com.aop.HelloImpl.*())", returning = "result") public void afterReturning(JoinPoint point, Object result) { String methodName = point.getSignature().getName(); System.out.println("HelloAspect afterReturning : " + methodName + ", result: " + result); } // 异常通知 @AfterThrowing(value = "execution(public void com.aop.HelloImpl.*())", throwing = "ex") public void afterThrowing(JoinPoint point) { String methodName = point.getSignature().getName(); System.out.println("HelloAspect afterReturning : " + methodName); } /** * 环绕通知需携带 ProceedingJoinPoint 类型的参数。 * 环绕通知类似于动态代理的全过程, ProceedingJoinPoint 类型的参数可以决定是否执行目标方法。 * 环绕通知必须有返回值,且返回值是目标方法的返回值 */ @Around("execution(public void com.aop.HelloImpl.*())") public Object aroundMethod(ProceedingJoinPoint point) { Object result = null; // 环绕通知(前通知) System.out.println("HelloAspect aroundMethod start..."); try { // 前置通知 result = point.proceed(); // 目标方法执行 } catch (Throwable throwable) { // 异常通知 throwable.printStackTrace(); } // 环绕通知(后通知) System.out.println("HelloAspect aroundMethod end..."); // 后置通知 // 返回通知 return result; } }

  配置aopContext.xml文件,如下所示:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/0935020e20a7d2007f3d71ebf62dc710.html