Spring 1.x中AOP的使用

本文通过一个“ Hello World”级别的横切性功能介绍 Spring1.X中 AOP的使用,并结合 Spring的经典的声明式事务管理给出 Spring AOP配置中的经典方案。在 Spring2出来以后, Spring1.X的 AOP使用方式已经“不合时宜”了,因此如果你是在新项目中采用 Spring AOP,建议使用 Spring2中的 AOP使用方式。关于 Spring2.X中 AOP的使用,参考该文的姊妹文章 Spring2.X中AOP的使用

        一提到AOP的应用,人们就会本能地提起日志功能,它就像一门语言的“Hello World”一样被人们无数次提起。也许有人会疑问除了“不实用”的日志功能,AOP还能做些什么?可能在很多时候我们并不需要自己实现一个AOP功能,尤其是在拥有了很多优秀的AOP应用框架来解决通用的横切性问题的情况下(比如Spring的事务管理、比如Acegi的安全管理、比如WebWork的拦截功能)。但问题总是层出不穷的,总会有些问题可能需要我们自己AOP一下。

        在月言月,进入到该文的主题(写完上面的一段使我想起俞平伯,那老头很多文章的前几段总是些不知所以的文字,直到“ 在月言月”一出,方进入文章主题;在看完文章后回头一瞧,和主题相关的文字竟不到文章的半数!)。这个Sample要实现的AOP功能是最简单的日志功能,在调用每个Service方法之前输出被调用方法的简单信息。

        我们知道,Aspect=Pointcut+Advice(如果您不知道的话,需要看一看Spring文档了)。在Spring1.X中,不能以AspectJ的语法书写复杂的切入点表达式(这是因为Spring1.X中的切入点是Java类)。Spring1.X可以使用正则表达式切入点以声明的方式来简化编程。这里假定要求对Service层中所有Service方法在调用之前输出和方法相关的信息。切入点声明如下:


< bean  id ="businessServicePointcut"  class ="org.springframework.aop.support.JdkRegexpMethodPointcut" >

< property  name ="patterns" >

< value > * value>

property>

bean>

        应该说明的是,在不使用通知器自动代理的情况下,声明的切入点的作用域实在小的可怜,它不过是方法级别的。我觉得这也是Spring1.X中很不友好的一点,通过ProxyFactoryBean代理的方式实现AOP,需要对每个应用了AOP特性的Bean都做代理,即便是在Bean继承的情况下也不友好。

        下面要创建一个超简单的Before通知:


public   class  LogAdvice  implements  MethodBeforeAdvice{

public   void  before(Method arg0, Object[] arg1, Object arg2)  throws  Throwable {

// no content

}

}

        关于通知和切面的配置如下:

 

< bean  id ="logAdvice"  class ="hibernatesample.service.util.LogAdvice" >bean>

<bean id="logAspect" class="org.springframework.aop.support.DefaultPointcutAdvisor">

<property name="pointcut" ref="businessServicePointcut">property>

<property name="advice" ref="logAdvice">property>

bean>

 

        spring1.X中,AdvisorAspect的同义词,这是spring1.X独有的。坦率的说,我觉得这个词真不应该出现,它会给AOP初学者造成很大的困惑,尤其是对AspectJ不了解的情况下。在spring1.X中,我们并不需要自己编写一个Advisor,只需要使用Spring提供的Advisor封装切入点和通知就行了。如果你不使用通知器自动代理并且要通知作用的类的所有方法,Advisor甚至是不需要的。好了,上面就是我们自定义的一个日志切面,我们还要加一个切面--经典的对于Service必不可少的事务切面。配置文件如下:


< bean  id ="transactionManager"

class
="org.springframework.orm.hibernate3.HibernateTransactionManager" >

< property  name ="sessionFactory" >

< ref  bean ="sessionFactory"   />

property>

bean>

<bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor">

<property name="transactionManager"><ref bean="transactionManager"/>property>

<property name="transactionAttributes">

<props>

<prop key="get*">PROPAGATION_REQUIRED,readOnlyprop>

<prop key="*">PROPAGATION_REQUIREDprop>

props>

property>

bean>

       
        你也看到了,transactionInterceptor是个拦截器,它只是对方法级别上做拦截.将transactionInterceptor和logAspect以ProxyFactoryBean方式作用到
Service 上,配置文件如下:

< bean  id ="accountServiceTarget"  class ="hibernatesample.service.impl.AccountServiceImpl" >

< property  name ="accountDAO"  ref ="accountDAO" >property>

bean>


<bean id="accountService" class="org.springframework.aop.framework.ProxyFactoryBean">

<property name="proxyInterfaces">

<value>hibernatesample.service.AccountServicevalue>

property>

<property name="interceptorNames">

<list>

<value>logAspectvalue>

<value>transactionInterceptorvalue>

list>

property>

<property name="target"><ref bean="accountServiceTarget"/>property>

bean>


        如果我再定义一个
Service,我还是需要在使用了ProxyFactoryBeanService Bean中拷贝如下的内容而不同的是,它们之间只是更换了target属性:

< property  name ="proxyInterfaces" >

< value > hibernatesample.service.AccountService value>

property>

<property name="interceptorNames">

<list>

<value>logAspectvalue>

<value>transactionInterceptorvalue>

list>

property>

          
        一个好的解决配置重复的办法是使用
Spring配置中继承特性,但此继承可不是OO中的继承,它只是将父Bean中的未声明的属性推到子Bean声明(target了),而父Bean去声明那些配置子Bean相同的内容(就是上面的内容)。简化后的内容如下:


< bean  id ="baseServiceProxy"  class ="org.springframework.aop.framework.ProxyFactoryBean"  abstract ="true" >

< property  name ="interceptorNames" >

< list >

< value > logAspect value>

<value>transactionInterceptorvalue>

list>

property>

bean>


<bean id="accountService" parent="baseServiceProxy">

<property name="proxyInterfaces"><value>hibernatesample.service.AccountServicevalue>property>

<property name="target">

<bean class="hibernatesample.service.impl.AccountServiceImpl">

<property name="accountDAO" ref="accountDAO">property>

bean>

property>

bean>

        对于事务声明,除了采用通用的ProxyFactoryBean来拦截transactionInterceptor外,也可以采用TransactionProxyFactoryBean,它应该是变相的ProxyFactoryBeantransactionInterceptor。另外,可以使用TransactionProxyFactoryBeanpostInterceptors或者preInterceptors属性来配置其他切面(通知)。


< bean  id ="baseServiceProxy"

class
="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"

abstract
="true" >

< property  name ="transactionManager"  ref ="transactionManager"   />

< property  name ="transactionAttributes" >

< props >

< prop  key ="get*" > PROPAGATION_REQUIRED,readOnly prop>

<prop key="*">PROPAGATION_REQUIREDprop>

props>

property>

<property name="postInterceptors">

<ref bean="logAspect"/>

property>

bean>


    在简化Spring1.X文件配置方面,更好的选择是使用自动代理。这里介绍一下两个很好用的自动代理方式。

    第一种自动代理是使用BeanNameAutoProxyCreator,只需要指定它的beanNamesinterceptorNames,便可将interceptorNames列表中的切面(拦截器、通知)作用到匹配beanNamesBean。注意的是,beanNames可不是类名,而是配置文件中Bean名。示例如下:


< bean  id ="serviceNameAutoProxy"  class ="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator" >
< property  name ="beanNames" >
< value > *Service value>
property>
<property name="interceptorNames">
<list>
<value>transactionInterceptorvalue>
<value>logAspectvalue>
list>
property>
bean>

     第二种自动代理方式是使用DefaultAdvisorAutoProxyCreator这在思想上已经是切面级的,而不是通知(拦截器)级的;也就是说,只有切面是一等公民,通知和拦截器已经不能脱离切入点而独立工作了。还是上面的例子,使用DefaultAdvisorAutoProxyCreator的配置如下:


< bean  id ="businessServicePointcut"  class ="org.springframework.aop.support.JdkRegexpMethodPointcut" >

< property  name ="patterns" >

< value > hibernatesample.service.* value>

property>

bean>

<bean id="logAdvice" class="hibernatesample.service.util.LogAdvice">bean>

<bean id="logAspect" class="org.springframework.aop.support.DefaultPointcutAdvisor">

<property name="pointcut" ref="businessServicePointcut">property>

<property name="advice" ref="logAdvice">property>

bean>

<bean id="serviceClassFilter" class="hibernatesample.service.util.ServiceClassFilter">bean>

<bean id="txAdvisor"

class
="org.springframework.transaction.interceptor.TransactionAttributeSourceAdvisor">

<property name="transactionInterceptor">

<ref bean="transactionInterceptor"/>

property>

<property name="classFilter" ref="serviceClassFilter">property>

bean>

<bean id="accountService" class="hibernatesample.service.impl.AccountServiceImpl">

<property name="accountDAO" ref="accountDAO">property>

bean>

<bean id="advisorAutoProxy" class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator">

bean>



        相比于前面的配置方式,使用DefaultAdvisorAutoProxyCreator在配置文件上有了不少变化。首先是businessServicePointcut

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值