为什么80%的码农都做不了架构师?>>>   

AspectJ允许使用注解用于定义切面、切入点和增强处理,而Spring框架则可以识别并根据这些注解来生成AOP代理。Spring只是使用了和AspectJ 5一样的注解,但并没有使用AspectJ的编译器或者织入器,底层依然使用SpringAOP来实现,依然是在运行时动态生成AOP代理,因此不需要增加额外的编译,也不需要AspectJ的织入器支持。而AspectJ采用编译时增强,所以AspectJ需要使用自己的编译器来编译Java文件,还需要织入器。

为了启用Spring对@AspectJ切面配置的支持,并保证Spring容器中的目标Bean被一个或多个切面自动增强,必须在Spring配置文件中配置如下内容(第4、9、10、15行):

<?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"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context-3.0.xsd"><!-- 启动@AspectJ支持 --><aop:aspectj-autoproxy/>
</beans>

所谓自动增强,指的是Spring会判断一个或多个切面是否需要对指定的Bean进行增强,并据此自动生成相应的代理,从而使得增强处理在合适的时候被调用。如果不打算使用XML Schema的配置方式,则应该在Spring配置文件中增加如下片段来启用@AspectJ支持(即上面的<aop:aspectj-autoproxy />和下面创建Bean的方式选择一种即可启用@AspectJ支持):

<bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator" />

上面配置的是一个Bean后处理器,该处理器将会为容器中Bean生成AOP代理。

为了在Spring应用中启动@AspectJ支持,还需要在用用的类加载路径下增加两个AspectJ库:aspectweaver.jar和aspectjrt.jar,直接使用AspectJ安装路径下的lib目录下的这两个Jar文件即可,当然,也可以在Spring解压缩文件夹的lib/aspectj路径下找到它们。下面是项目内容的截图:

定义切面Bean

当启用了@AspectJ支持后,只要我们在Spring容器中配置一个带@AspectJ注释的Bean,Spring将会自动识别该Bean,并将该Bean作为切面处理。下面是一个例子:

@Aspect
public class LogAspect {}

切面类(用@Aspect修饰的类)和其他类一样可以有方法和属性的定义,还可能包括切入点、增强处理的定义。当我们使用@Aspect来修饰一个Java类后,Spring将不会把该Bean当成组件Bean处理,因此当Spring容器检测到某个Bean使用了@AspectJ标注之后,负责自动增强的后处理Bean将会忽略该Bean,不会对该Bean进行任何增强处理。

使用Before增强处理

当我们在一个切面类里使用@Before来标注一个方法时,该方法将作为Before增强处理。使用@Before标注时,通常需要指定一个value属性值,该属性值指定一个切入点表达式(既可以是一个已有的切入点,也可以直接定义切入点表达式),用于指定该增强处理将被织入哪些切入点。看例子:

package com.abc.advice;import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;@Aspect
public class BeforeAdviceTest {//匹配com.abc.service下的类中以before开始的方法@Before("execution(* com.abc.service.*.before*(..))")public void permissionCheck() {System.out.println("模拟权限检查");}
}

上面的程序使用@Aspect修饰了BeforeAdviceTest类,这表明该类是一个切面类,在该贴面里定义了一个permissionCheck方法——这个方法本来没有什么特殊之处,但因为使用了@Before来标注该方法,这就将该方法转换成一个Before增强处理。这个@Before注解中,直接指定了切入点表达式,指定com.abc.service包下的类中以before开始的方法的执行作为切入点。现假设我们在com.abc.service下有一个这样一个类:

package com.abc.service;
import org.springframework.stereotype.Component;@Component
public class AdviceManager {//这个方法将被BeforeAdviceTest类的permissionCheck匹配到public void beforeAdvice() {System.out.println("方法: beforeAdviceTest");}
}

从上面的代码来看,这个AdviceManager是一个纯净的Java类,它丝毫不知道将被谁来增强,也不知道将被进行怎样的增强——正式因为AdviceManager类的这种“无知”,才是AOP的最大魅力:目标类可以被无限的增强。

在Spring配置文件中配置自动搜索Bean组件,配置自动搜索切面类,SpringAOP自动对Bean组件进行增强,下面是Spring配置文件代码:

<?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"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context-3.0.xsd"><!-- 启动@AspectJ支持 --><aop:aspectj-autoproxy/><!-- 指定自动搜索Bean组件,自动搜索切面类 --><context:component-scan base-package="com.abc.service,com.abc.advice"><context:include-filter type="annotation" expression="org.aspectj.lang.annotation.Aspect" /></context:component-scan>
</beans>

主程序非常简单,通过Spring容器获取AdviceManager Bean,并调用Bean的beforeAdvice方法:

package com.abc.main;import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;import com.abc.service.AdviceManager;@SuppressWarnings("resource")
public class AOPTest {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");AdviceManager manager = context.getBean(AdviceManager.class);manager.beforeAdvice();}
}

执行主程序,将看到以下结果:

使用Before增强处理只能在目标方法执行之前织入增强,使用Before增强处理无需理会目标方法的执行,所以Before处理无法阻止目标方法的执行。Before增强处理执行时,目标方法还未获得执行机会,所以Before增强处理无法访问目标方法的返回值。

使用AfterReturning增强处理

和使用@Before注解的使用类似,使用@AfterReturning来标注一个AfterReturning增强处理,该处理将在目标方法正常完成后被织入。使用@AfterReturning时可以指定两个属性:

  • pointcut/value:这两个属性的作用是一样的,都用于指定该切入点对应的切入表达式。同样的,既可以是一个已有的切入点,也可以是直接定义的切入点。当指定了pointcut属性后,value的属性值将会被覆盖

  • returning:指定一个返回值形参名,增强处理定义的方法可以通过该形参名来访问目标方法的返回值。

在com.abc.advice包下面增加AfterReturningAdviceTest,这个类定义了一个AfterReturning增强处理:

package com.abc.advice;import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;@Aspect
public class AfterReturningAdviceTest {//匹配com.abc.service下的类中以afterReturning开始的方法@AfterReturning(returning="returnValue", pointcut="execution(* com.abc.service.*.afterReturning(..))")public void log(Object returnValue){System.out.println("目标方法返回值:" + returnValue);System.out.println("模拟日志记录功能...");}
}

并在AdviceManager类中增加以下内容:

//将被AfterReturningAdviceTest的log方法匹配
public String afterReturning() {System.out.println("方法:afterReturning");return "afterReturning方法";
}

正如上面程序中看到的,程序中使用@AfterReturning注解时,指定了一个returning属性,该属性的返回值是returnValue,这表明允许在增强方法log中使用名为returnValue的形参,该形参代表目标方法的返回值。在测试类AOPTest的main方法中增加调用本方法的语句,运行测试类,可以看到以下结果:

@AfterReturning注解的returning属性所指定的形参名必须对应增强处理中的一个形参名,当目标方法执行以后,返回值作为相应的参数传入给增强处理方法。

需要注意的是,使用@AfterReturning属性还有一个额外的作用,它可用于限定切入点之匹配具有对应返回值类型的方法——假设上面的log方法的参数returnValue的类型为String,那么该切入点只匹配com.abc.service.impl包下的返回值为String的所有方法。当然,上面的log方法返回值类型为Object,表明该切入点可匹配任何返回值的方法。除此之外,虽然AfterReturning增强处理可以访问到目标方法的返回值,但它不可改变这个返回值。

使用AfterThrowing增强处理

使用@AfterThrowing注解可用于标注一个AfterThrowing增强处理,这个处理主要用于处理陈旭中未处理的异常。使用这个注解时可以指定两个属性:

  • pointcut/value:这两个属性的作用是一样的,都用于指定该切入点对应的切入表达式。同样的,既可以是一个已有的切入点,也可以是直接定义的切入点。当指定了pointcut属性后,value的属性值将会被覆盖

  • throwing:指定一个返回值形参名,增强处理定义的方法可通过该形参名来访问目标方法中所抛出的异常对象。

在com.abc.advice包下面增加AfterThrowingAdviceTest,这个类定义了一个AfterThrowing增强处理:

package com.abc.advice;import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;@Aspect
public class AfterThrowingAdviceTest {@AfterThrowing(throwing="ex",pointcut="execution(* com.abc.service.*.afterThrow*(..))")public void handleException(Throwable ex) {System.out.println("目标方法抛出异常:" +ex);System.out.println("模拟异常处理");}
}

并在AdviceManager类中增加以下内容:

//将被AfterThrowingAdviceTest的handleException方法匹配
public void afterThrowing() {System.out.println("方法: afterThrowing");try {int a = 10 / 0;} catch (ArithmeticException ae) {System.out.println("算术异常已被处理");}String s = null;System.out.println(s.substring(0,3));
}

正如上面程序中看到的,程序中使用@AfterThrowing注解时,指定了一个throwing属性,该属性的值是ex,这表明允许在增强方法log中使用名为ex的形参,该形参代表目标方法的抛出的异常对象。运行测试类,可以看到以下结果:

需要注意的是:如果一个异常在程序内部已经处理,那么Spring AOP将不会处理该异常。只有当目标方法抛出一个未处理的异常时,该异常将会作为对应的形参传给增强处理的方法。和AfterReturning类似的是,正确方法的参数类型可以限定切点只匹配指定类型的异常——假如上面的handleException方法的参数类型为NullPointerException,那么如果目标方法只抛出了ArithmaticException,则Spring AOP将不会处理这个异常。当然,handleException的参数类型为Throwable,则匹配了所有的Exception。

从测试结果中可以看到,AfterThrowing处理虽然可以对目标方法的异常进行处理,但这种处理与直接使用catch捕捉不同:catch捕捉意味着完全处理该异常,如果catch块中没有重新抛出新异常,则该方法可以正常结束;而AfterThrowing处理虽然处理了该异常,但它不能完全处理该异常,这个异常依然会传播到上一级调用者(本例中为JVM,故会导致程序终止)。

《Spring中的AOP系列三、四、五》的代码在这里:点击下载,欢迎留言提意见。

【未完,待续】

转载于:https://my.oschina.net/itblog/blog/210718

Spring中的AOP(三)——基于Annotation的配置方式(一)相关推荐

  1. java学习day40(Spring)spring中的aop和基于XML以及注解的AOP配置

    第1章 AOP 的相关概念[理解] 1.1AOP 概述 1.1.1 什么是 AOP AOP :全称是 Aspect Oriented Programming 即:面向切面编程. 简单的说它就是把我们程 ...

  2. 手动实现SPring中的AOP(1)

    Spring中的AOP是基于JDK的API动态的在内存中创建代理对象的.所以这里先介绍一些设计模式之----代理模式: a)         代理模式的定义:代理(Proxy)模式是一种提供对目标对象 ...

  3. Spring中的AOP切面编程的三种实现方式

    文章目录 Spring中的AOP切面编程的三种实现方式 1.最基本AOP的实现 a.引入jar包 b.编写通知类,这里以后置通知和环绕通知类为例子进行说明 c.在SpringIOC容器中配置 d.测试 ...

  4. 一文读懂Spring中的AOP机制

    一.前言 这一篇我们来说一下 Spring 中的 AOP 机制,为啥说完注解的原理然后又要说 AOP 机制呢? 1.标记日志打印的自定义注解 @Target({ElementType.METHOD}) ...

  5. Spring中的AOP(二)——AOP基本概念和Spring对AOP的支持

    AOP的基本概念 AOP从运行的角度考虑程序的流程,提取业务处理过程的切面.AOP面向的是程序运行中的各个步骤,希望以更好的方式来组合业务逻辑的各个步骤.AOP框架并不与特定的代码耦合,AOP框架能处 ...

  6. 动态代理——》AOP —— Spring 中的 AOP||AOP 相关术语||学习 spring 中的 AOP 要明确的事

    AOP 概述 什么是 AOP       AOP:全称是 Aspect Oriented Programming 即:面向切面编程 AOP 的作用及优势 作用: 在程序运行期间,不修改源码对已有方法进 ...

  7. java day59【 AOP 的相关概念[理解] 、 Spring 中的 AOP[掌握] 、 Spring 整合 Junit[掌握] 】...

    第1章 AOP 的相关概念[理解] 1.1AOP 概述 1.1.1 什么是 AOP 1.1.2 AOP 的作用及优势 1.1.3 AOP 的实现方式 1.2AOP 的具体应用 1.2.1 案例中问题 ...

  8. spring中的aop术语和细节

    Spring中AOP的细节 说明 我们学习spring的aop,就是通过配置的方式 AOP相关术语 Joinpoint(连接点): 所谓连接点是指那些被拦截到的点.在spring中,这些点指的是方法, ...

  9. Spring中Bean管理操作基于XML配置文件方法实现

    Spring中Bean管理操作基于XML配置文件方法实现 基于XML配置文件方式实现 1.基于`xml`方式创建对象 2.基于`xml`方式注入属性 1.创建类,定义属性和对应的set方法 2.在Sp ...

最新文章

  1. 通俗理解并行性和并发性的区别
  2. size-t数据类型 ssize-t数据类型
  3. Struts2.0第三章(文件上传、ajax开发、json、Fastjson、Jackson、注解开发)
  4. jquery调用asp.net 页面后台方法
  5. “换标”Intel的穷则思变
  6. ABP入门系列(14)——应用BootstrapTable表格插件
  7. [Unity]限制两个物体之间的距离
  8. asp建站系统源码_三款适用于企业建站的CMS建站系统
  9. java类成员方法(成员函数)的初步介绍
  10. js打印线程id_浅谈python中的多线程和多进程(二)
  11. 实现三联tab切换特效
  12. 编辑器未包含main类型_利用 ONLYOFFICE 将在线文档编辑器集成到 Python Web 应用程序中...
  13. 禅道xampp文件夹无法删除_无法删除文件解决方案
  14. python mpi4py multiprocessing_python基于multiprocessing的多进程创建方法
  15. 考公、事业编、央企国企私企外企、校招社招都在这些地方找到信息(精华)
  16. OpenGL Ant Tweak Bar的用户界面库
  17. 数论——佩尔方程变量分解
  18. 加密通信(三):CA证书
  19. android系统如何获得外置卡路径
  20. Nodejs-Express, Koa, Hapi三个框架应如何选择

热门文章

  1. ITK:索引置换序列
  2. DCMTK:部分元素访问API的应用程序
  3. VTK:可视化之VectorOfActors
  4. VTK:图片之ImageDivergence
  5. C++基本输入/输出
  6. QT的QReadLocker类的使用
  7. 经典C语言程序100例之九零
  8. python基于web可视化_Python Selenium实现无可视化界面
  9. php与tcp哪个快,PHP程序员如何理解TCP协议
  10. RocketMQ特性、专业术语(Producer,Producer Group,Consumer Group,Topic,Message,Tag,Broker,Name Server)等