<4> 简记aop原理和使用

前面把IOC的主要内容都介绍完了,这篇就是另一个spring的核心:aop

在软件中,有些行为对大多数模块说都是通用的。如登录、日志、性能统计、安全和事务管理都是重要的事情又是公共的功能,与业务模块功能需要配合一起工作,如果把这些公共功能与业务功能模块交织在一起编码,则到处散乱,对代码的维护,修改,响应变化都不方便。
在程序开发中,散布于程序中多个地方的函数被称为“交叉事务”。应该把他们与一般的业务逻辑分开(但实际经常是直接嵌入的),把这些交叉事务与业务逻辑分开正是面向切面编程(即AOP)的作用所在。

一、 AOP术语
Aop的术语都不太直观理解,这完全没有关系,可以先大概知道这些概念,然后在实际应用中去理解。
1. 通知(advice)
就是切面(aspect,后面会介绍)要完成的功能,就是上面引文中提到的”交叉事务”。这些功能需要在指定的位置(就是下面要说的连接点)前(before)执行还是位置后(after)执行还是抛出异常后(throws)执行,这些都可以根据指定的时机在指定的位置来完成特定的功能。
有5种类型的通知
1) Before: org.springframework.aop.MethodBeforeAdvice
2) After-returning: org.springframework.aop.AfterReturningAdvice
3) After-throwing: org.springframework.aop.ThrowsAdvice
4) Around:org.aopalliance.intercept.MethodInterceptor
5) Introduction: org.springframework.aop.IntroductionInterceptor
除了org.aopalliance.intercept.MethodInterceptor外这些接口都是spring框架的。Around通知相当于before,ater-returning和after-throwing三者结合的通知。

2. 连接点(Joinpoint)
就是要把上面的通知功能应用到哪个地方,这个地方可以是指定的方法被调用时,异常被抛出时等。也可以说,通知是发生在特定的连接点上的。Sping只支持方法连接点,如AspectJ和Jboss不仅提供方法连接点还可以是字段和构造器连接点。
3. 切入点(Pointcut)
指定一个通知将被引发的一系列连接点的集合。也就是切入点把连接点聚集起来,指明了要在哪些连接点上执行通知功能。 AOP框架必须允许开发者指定切入点,比如可以通过正则表达式指定。 切入点分静态切入点和动态切入点,动态切入点比静态切入点的演算代价高很多(这两种切入点后面会再提到)。
4. 切面(Aspect)
    切面是通知和切入点的结合。指明了在何时何地执行什么功能。可以用用Spring的 Advisor或拦截器实现,也就是提供通知功能的类。
5. 目标(target)
包含连接点的对象。也被称作 被通知或被代理对象。
6. 代理(proxy)
AOP框架创建的对象,包含通知。 在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。如果目标对象实现的是一个接口,则会采用JDK动态代理,如果目标类不是一个接口而是类,则会使用CGLIB生成目标类的一个子类作为代理。注意,被标记为final的方法不能被通知(因为final方法不能被子类重写)。
7. 织入(weaving)
是把切面(aspect)应用到目标对象(target)来创建新的代理对象(proxy)的过程。
在目标对象的生命周期中有多个时机可以发生织入过程:
1) 编译时。需要特殊的编译器,AspectJ的织入编译器就是这种方式。
2) 类加载时。需要特殊的ClassLoader.AspectJ 5的“加载时织入(LTW)”就是这种方式。
3) 运行时。一般情况下,在织入切面时,AOP容器会动态创建一个代理对象。Spring就是这种方式。Spring2.0从AspectJ项目借鉴了很多对AOP的支持, Spring对AOP的支持比AspectJ要弱,只支持方法的注入。

二、 创建通知,切入点,切面,代理
下面分别介绍spring框架如何支持aop的,需要执行哪些步骤。也方便使用时理解配置,或实现自定义的aop功能。
1. 创建通知:只要让切面类(包含通知功能的类)实现上面5种通知类型的接口,就可以分别实现相应功能的通知。
2. 创建切入点:
Spring提供了几种不同类型的切点,其中两种最有用的是 正则表达式切点和AspectJ表达式切点。
1) 声明正则表达式切点
Jdk1.4之后都是使用org.springframework.aop.support.JdkRegexpMethodPointcut类定义:
<bean id=”performPointcut”
     class=” org.springframework.aop.support.JdkRegexpMethodPointcut”>
<property name=”pattern” value=”.*perform” />
        </bean>
        然后联合通知和切入点
        <bean id=”audienceAdvisor”
class=”org.springframework.aop.support.DefaultPointcutAdvisor”>
<property name=” advice” ref=”audienceAdvice” />--通知类
<property name=” pointcut” ref=”performPointcut” />--切入点
</bean>
audienceAdvisor就是一个完整的切面了。
还有 一个简单点的写法,利用RegexpMethodPointcutAdvisor创建切面。
<bean id=”audienceAdvisor”
Class=”org.springframework.aop.support.RegexpMethodPointcutAdvisor”>
<property name=”advice” ref=” audienceAdvice” />
<property name=”pattern” value=”.*perform” />---这里可以直接定义切入点表达式,不需要再引用切入点的bean了。
</bean>

2)但spring中更加常用的是AspectJ 切点表达式,要利用
AspectJExpressionPointcutAdvisor:
<bean id=”audienceAdvisor”
Class=”org.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor” />
<property name=”advice” ref=”audienceAdvice” />
<property name=”expression” value=”execution(*.*.perform(..))” />
</bean>
注:关于AspectJ切点表达式可以参考:
http://sishuok.com/forum/posts/list/281.html

到现在还没有完成aop功能,切面在spring里是以代理方式实现的,还需要代理bean(proxy)才行.可以使用ProxyFactoryBean实现。
3. 创建代理
<bean id=”dukeTarget” class=”com.springinaction.PoeticJuggler”
autowire=”constructor”>
<constructor-arg ref=”songnet29” />
</bean>----这里定义的是target
<bean id=”duke”
Class=”org.springframework.aop.framework.ProxyFactoryBean”>
<property name=”target” ref=”dukeTarget” />--指明target
<property name=”interceptorNames” value=”audienceAdvisor” />--指明通知
<property name=”proxyInterfaces” value=”com.springinaction.Performer” />
</bean>--这里定义的是proxy

至此, 一个完整的spring AOP功能就完成了,最后是通过创建代理bean来应用切面功能。配置完我们发现这个过程很繁琐,所有的元素都需要配置。不用担心,上面只是分解了spring实现aop需要做的所有步骤,为了理解原理,实际使用时我们可以简化配置,比如让spring自动代理被通知的bean.

4.自动代理
针对上面的《创建代理》步骤,我们可以用自动代理简化,实现自动代理bean的方式有两种
1) 基于spring上下文里声明的通知者bean的基本自动代理:即使用xml配置切点表达式,决定哪个bean和哪个方法要被代理。
2) 基于@AspectJ注解。
这两种方式就是为了消除ProxyFactoryBean.

1. xml配置
查看上面的切入点表达式,里面有足够的信息决定哪个bean的哪个方法需要通知了,我们可以使用Spring提供的DefaultAdvisorAutoProxyCreator(是一种BeanPostProcessor)来自动检查切点是否匹配bean.
为了使用DefaultAdvisorAutoProxyCreator,只需要在spring上下文里声明如下<bean>:
<bean class=”org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator” />
这个bean不需要id,因为我们永远不会直接引用它,spring容器会自动识别这个BeanPostProcessor,利用它创建代理。这样就不需要配置ProxyFactoryBean了。

2. 自动代理 @AspectJ切面
1) 把包含通知的类声明为切面@Aspect
@Aspect         // 声明切面
Public class Audience
{
  @Pointcut(“execution(* *.perform(..))”) //定义切入点,只做定义使用,没有其他作用。
   Public void performance() {}

  @Before(“performance()”)  //定义Before通知
  Public void takeSets() {}
}




2) 然后只需要把切面类定义成一个普通bean. 为了支持自动创建代理bean,需要利用Spring提供的AnnotationAwareAspectJAutoProxyCreator,把它注册成一个bean.spring在aop命名空间提供了一个简单自定义配置元素:
<aop:aspectj-autoproxy />
这个需要在beans的命名空间提供支持:



切面的自动代理极大简化了spring切面的配置工作,同时也让程序透明化,隐藏了切面的很多细节。
至此上面所有内容都可以作为理解spring配置AOP的基础,是最原始的配置方式,也体现了spring处理的过程。下面讲的是目前用的最多的比较方便的配置方式(其实注解也使用的挺多的)。

三、 定义纯粹的POJO切面
使用ProxyFactoryBean配置有些欠优雅,即使使用自动代理配置,包括前面的通知、切入点都还是要配置成bean,整个aop配置不聚集,不直观。在spring2.0里新的xml配置元素体现了改进。Spring2.0在aop命名空间里提供了一些配置元素,简化了把类转化为切面的操作。



通过上面的理论基础,下面这个例子就很容易理解了:



另外还可以自定义AspectJ切面注入到Spring, 这个不常用,这里就不记录了。

最后推荐两篇博文跟本篇内容相关:
http://www.ibm.com/developerworks/cn/java/j-lo-springaopcglib/
http://docs.huihoo.com/spring/zh-cn/aop.html

关于Aspect依赖:
https://www.mkyong.com/spring3/spring-aop-aspectj-annotation-example/
http://blog.xuite.net/franky.chao/wretch/154513858

猜你喜欢

转载自zoroeye.iteye.com/blog/1923701
今日推荐