在软件开发中,分布于应用多处的功能被称为横切关注点。将这些横切关注点和业务逻辑分离是面向切面锁要解决的问题。横切关注点可以被模块化为特殊的类,这些类就叫做切面。这样做的好处:每个关注点集中于一处,而不是分散到多出代码中。服务模块更简洁,它们只要关心核心功能就行了,其他转移到切面中去了。
1.1定义AOP术语
描述aop的常用术语有通知(advice)、切点(pointcut)、和连接点(join point)。
通知:
通知定义了切面是什么以及何时执行,有5中通知类型。
1.Before:在方法执行前调用通知。
2.After:在方法执行后调用通知,不管方法执行是否成功。
3.After-returning:在方法成功执行后调用通知。
4.After-throwing:在方法抛出异常时调用。
5.Around:通知包含了被通知的方法,再被通知的方法调用之前和之后执行相应的自定义工作。
连接点:连接点就是应用执行过程中能够插入切面的一个点。这个点可以是调用方法时、调用方法后、抛出异常时等等,切面代码可以利用这些点插入到应用程序的正常流程中,并添加新的行为。
切点:如果通知定义了什么和何时,那么切点就定义了何处。切点有助于缩小连接点的范围,因为一个切面不需要通知所有的连接点。
切面(aspect):通知和切点的结合。它们定义了切面是什么,在何时何处执行。
引入(Introduction):引入允许向现有的类添加新方法或属性。
织入(weaving):织入是将切面应用到目标对象来生成心得代理对象的过程。切面在指定的连接点中被织入目标对象。目标对象的生命周期中有多个点可以进行织入。
1.编译期:切面在目标类编译时被织入。这种方式需要特殊的编译器,AspectJ的织入编译器就是这个时候织入的。
2.类加载期:切面在类加载到jvm是被织入。这种方式需要特殊的类加载器,它可以再目标类被引入应用之前加强目标类的字节码。AspectJ的LTW就是这种方式织入的。
3.运行期:切面在应用运行到某一时刻时被织入。一般情况下,在织入切面时,AOP容器会为目标对象创建一个代理对象。SPRing AOP就是这种方式织入的。
1.2 Spring对AOP的支持
并不是所有的aop框架都是一样的,它们在连接点模型上有强弱之分。有三个AOP框架:AspectJ,JBoss AOP,Spring AOP。
spring提供4中具有特殊的aop支持:
1.基于代理的经典AOP。
2.@AspectJ注解驱动的切面。
3.纯POJO切面。
4.注入式AspectJ切面(适合spring个版本)
前三种方法都是基于代理的aop的变种,spring对aop的支持局限于方法的拦截。如果对aop的需求超过了对简单方法的拦截(比如对构造方法或属性拦截),那么需要在AspectJ里实现切面,利用依赖注入把Spring Bean注入到切面中。
2.使用切点选择连接点
spring支持的AspectJ切点指示器。
args()---------限制连接点匹配参数为指定类型的执行方法。
@args()---- 限制连接点匹配参数为指定注解标注的执行方法。
execution() 用于匹配时连接点的执行方法
this()--------- 限制连接点匹配AOP代理的Bean的引用为指定类型的类
target()-------限制连接点匹配目标对象为指定类型的类
@target() 限制连接点匹配特定的执行对象,这些对象对应的类要有指定的注解
within() 限制连接点匹配指定的类型
@within() 限制连接点匹配注解所标注的类型
@annotation 限制匹配带有指定注解的连接点
在Spring使用其他AspectJ指示器时会报illegalArgumentException。只有execution是唯一的执行匹配,其他都是限制匹配。
2.1编写切点
execution(* com.xxx.A.get(..))上面第一个*表示返回类型,*表示不关心返回类型,随便什么返回类型都可以。然后是类的全限定名和方法名。(..)表示使用任意参数都可以,就是说所有的get()方法。execution(xxx) && within(xxx) || execution(xxx) && arg(xxx) ||!execution(xxx) 2.2使用spring的bean指示器这个指示器允许在切点表达式中使用Bean的id来识别Bean。
execution(xxx) && bean(id)3 在xml中声明切面<aop:advisor>:定义AOP通知器
<aop:after>:定义AOP后置通知
<aop:after-returning>:定义AOP after-returning通知
<aop:after-throwing>:定义AOP after-throwing通知
<aop:around>:定义AOP环绕通知
<aop:aspect>:定义切面
<aop:aspectj-autoproxy>:启动@AspectJ注解驱动的切面
<aop:before>:定义前置通知
<aop:config>:顶层的aop配置元素,大多数<aop:*>都在这里面
<aop:declear-parents>:为被通知的对象引入额外的接口,并透明的实现。
<aop:pointcut>:定义切点
<aop:config> <aop:aspect id="bean的id">//定义切面 <aop:pointcut id="切点id" expression="execution()"/>//定义切点 <aop:before pointcut-ref="上面的切点id" method="bean里面的方法名"/>//定义前置通知 </aop:aspect></aop:config>如果pointcut想被多个切面引用,就把pointcut定义在config中。3.1 声明环绕通知
<aop:around pointcut-ref="" method=""/> 环绕通知就是把前置通知和后置通知合起来变为一个方法。如果前置通知和后置通知有联系,那么可以用环绕通知来实现,环绕通知的方法参数要有被通知的对象,这样可以调用被通知的方法。3.2为通知传递参数
<aop:before pointcut-ref="" method="" arg-names="name" />3.3 通过切面引入新功能<aop:aspect> <aop:declear-parents types-matching=""//匹配类 implement-interface=""//实现的接口 default-impl=""//接口的实现类 delegate-ref=""//引入接口实现类的Bean id和上面的区别就是它是引用外部的一个Bean/></aop:aspect>4.注解切面@AspectJ//定义切面class A{@Pointcut("execution()")//定义切点 public void a(){} @Before("a()") public void before(){System.out.println("before")} @AfterReturning("a()") public void afterReturning(){System.out.pringln("afterReturning")}}然后要在xml中加上<aop:aspectj-autoproxy/>,这个元素仅仅使用@AspectJ注解作为指引来创建基于代理的切面。本质还是spring风格的切面。
<aop:aspect>和@AspectJ注解都是把一个POJO转变为一个切面的有效方式。<aop:aspect>可以引用任意一个Bean,而注解就要实现一个类。
4.1注解环绕通知@Around("a()")public void around(ProceedingJoinPoint a){ a.proceed();}@DeclearParents(value=""defaultImpl="")private A a;
新闻热点
疑难解答