Spring 2.x AOP声明式配置+声明式事务管理

本文详细介绍了基于Spring框架的声明式AOP配置方法,包括如何声明切面、切入点、通知类型(前置、后置、返回后、异常、环绕通知),以及如何配置事务管理器和事务拦截器。并通过实例展示了如何在实际应用中应用这些配置。


 Spring 2.x AOP声明式配置

不同于之前的一篇日志《IOC与AOP及四种拦截方式》,它是基于编码式的拦截实现。
具体语法,可看最后面的“总结分析”.
 
实例:
在例子中,我们使用<aop:config/>配置一个切面并拦截目标对象Peoples的SayHello()方法,在它执行前输出提示信息。
新建目标类People
package aop.test; 

public class People{ 
                public String SayHello(String str){ 
                                System.out.println(this.getClass().getName()+ "说:"+str); 
                                return str; 
                } 
} 

 

创建含有main()方法的测试类TestMain,从Spring IoC容器中获取Peoples对象,并调用其SayHello()方法,

代码如下:

package aop.test; 
// import省略 
public class TestMain { 
                public static void main(String[] args) { 
                                // 实例化Spring IoC容器 
       ApplicationContext ac = new ClassPathXmlApplicationContext( 
                     "applicationContext.xml"); 
                                // 获取受管Bean的实例 
                 People p = (People) ac.getBean("TestBean"); 
                  p.SayHello("传入的参数值"); 
                } 
} 

 

创建MyAspect类,添加一个beforeAdvice()方法作为前置通知方法,

代码如下:

package aop.test; 
import org.aspectj.lang.JoinPoint; 
public class MyAspect { 
public void beforeAdvice(JoinPoint point) { 
     System.out.println("前置通知被触发:" + point.getTarget().getClass().getName()+ "将要" + point.getSignature().getName()); 
                } 
} 

 

修改xml配置文件,为其添加aop命名空间,并把MyAspect注册为一个受管Bean,作为我们下面定义切面的backing bean。

代码如下:

<?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" 
                xsi:schemaLocation="http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd    
                             http://www.springframework.org/schema/aop    
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"> 
                <bean id="MyAspect" class="aop.test.MyAspect" /> 
                <bean id="TestBean" class="aop.test.People" /> 
                 
                <aop:config proxy-target-class="true"> 
                                <aop:aspect ref="MyAspect" order="0" id="Test"> 
                                                <aop:pointcut id="testPointcut" 
                                                                expression="execution(* aop..*(..))" /> 
                                                <aop:before pointcut-ref="testPointcut" 
                                                                method="beforeAdvice" /> 
                                </aop:aspect> 
                </aop:config> 
</beans> 

即可.

实例分析AOP声明式配置:

1. 声明一个切面
      在基于AOP命名空间的Spring AOP中,要声明一个切面,需要使用<aop:config/>的子标签<aop:aspect>。<aop: aspect>标签有一个ref属性必须被赋值,它用于指定和该切面关联的受管Bean.正如上例所示, MyAspect该Bean对应的java类是一个普通的java类,在该类中定义了切面的通知方法。此外,<aop:aspect>标签还有两个可选的order属性和id属性,order属性用于指定该切面的加载顺序,id属性用于标识该切面。
2. 声明一个切入点
      要声明一个切入点,可以使用<aop:aspect>的子标签<aop:pointcut>,在Spring2.5中它有两个属性id和expression,分别用于标示该切入点和设定该切入点表达式。如上例.
3. 声明一个通知
      和@AspectJ一样,基于AOP命名空间的配置也可以定义五种通知类型,并且使用方式和特性类似。与@AspectJ不同的是,配置信息从Annotation中转移到了xml配置文件。
 
1)、前置通知
    声明一个前置通知可以使用<aop:aspect>的子标签<aop:before/>。该标签的属性说明如下表:
    pointcut:指定该通知的内置切入点
    pointcut-ref:通过id 引用已定义的切入点
    method:指定通知对应的方法,该方法必须已在切面的backing bean中被声明
    arg-names:通过方法的参数名字来匹配切入点参数
 
      对于一个通知来说,切入点和对应的通知方法是必须的。也就是说,在这些属性中,method属性是必须的,我们必须要给通知指定一个对应的方法;pointcut属性和pointcut-ref必须有一个被指定,以此确定该通知的切入点。如:
<aop:aspect ref="MyAspect" order="0" id="Test"> 
<aop:pointcut id="testPointcut" 
    expression="execution(* aop.test.TestBean.*(..))"/> 
<aop:before pointcut-ref="testPointcut" method="beforeAdvice"/> 
</aop:aspect>
 
2)、 后置通知
     声明一个后置通知使用<aop:after/>标签,它的属性等和<aop:before/>标签类似,下面是范例:
<aop:aspect ref="MyAspect" order="0" id="Test"> 
  <aop:pointcut id="testPointcut" 
    expression="execution(* aop.test.TestBean.*(..))" /> 
  <aop:after    pointcut-ref="testPointcut" method="AfterAdvice"/> 
</aop:aspect>

3)、 返回后通知
      <aop:after-returning/>标签可以声明一个返回后通知,该标签的属性和<aop:before/>相比它多了一个returning属性。该属性的意义类似于@AfterReturning注解的returning属性,用于将链接点的返回值传给通知方法。用法如下:
<aop:aspect ref="MyAspect" order="0" id="Test"> 
  <aop:pointcut id="testPointcut" 
    expression="execution(* aop.test.TestBean.*(..))" /> 
  <aop:after-returning pointcut-ref="testPointcut" 
    method="AfterReturnAdvice" returning="reVlue" /> 
</aop:aspect>


4)、 异常通知
        声明一个异常通知使用<aop:after-throwing />标签,它有一个类似于throwing属性又来指定该通知匹配的异常类型。用法如下:

<aop:aspect ref="MyAspect" order="0" id="Test"> 
    <aop:pointcut id="testPointcut" 
    expression="execution(* aop.test.TestBean.*(..))" /> 
    <aop:after-throwing pointcut-ref="testPointcut" 
    method="afterThrowingAdvice" throwing="throwable" /> 
</aop:aspect>


 5)、 环绕通知
      环绕通知是所有通知中功能最强大的通知,用<aop:around/>标签来声明。用法如下:

<aop:aspect ref="MyAspect" order="0" id="Test"> 
  <aop:pointcut id="testPointcut" 
    expression="execution(* aop.test.TestBean.*(..))" /> 
                <aop:around pointcut-ref="testPointcut" method="aroundAdvice"/> 
</aop:aspect>


 

Spring 2.x AOP声明式事务配置

 

例子:
例子中有 两个数据源,两个数据库事务拦截器,两个数据库事物切点。
execution组合表达式表述数据库事务切点:
    大部分service类的方法使用数据源txManager-datasourceone,对应事物切点txPointcut-datasourceone,事物拦截器txAdvice-datasourceone;
    service层PublishService类的几个方法使用数据源txManager-datasourcetwo,对应事物切点txPointcut-datasourcetwo,事物拦截器txAdvice-datasourcetwo;
    一个自定义方法拦截器RuntimeLogInterceptor(拦截每个方法,并记录每个方法的执行日志),拦截切点runtimeLogInterceptorPoint;
<!-- 数据源1事务管理器 --> 
<bean id="txManager-datasourceone" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 
    <property name="dataSource" ref="DataSource"/> 
</bean> 

<!-- 数据源2事务管理器 --> 
<bean id="txManager-datasourcetwo" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 
    <property name="dataSource" ref="srcDataSource"/> 
</bean> 
        
<!-- 数据源1事务拦截器 --> 
<tx:advice id="txAdvice-datasourceone" transaction-manager="txManager-datasourceone" > 
    <tx:attributes> 
        <tx:method name="get*" read-only="true"/> 
        <tx:method name="*"/> 
    </tx:attributes> 
</tx:advice> 

<!-- 数据源2事务拦截器 --> 
<tx:advice id="txAdvice-datasourcetwo" transaction-manager="txManager-datasourcetwo" > 
    <tx:attributes> 
        <tx:method name="get*" read-only="true"/> 
        <tx:method name="*"/> 
    </tx:attributes> 
</tx:advice> 

<!-- aop配置 强制使用cglib代理 --> 
<aop:config proxy-target-class="true"> 

    <!-- datasourceone 数据库事务管理切点    
             包含的方法:service包(或子包)下所有名字以 'Service' 结尾的类内所有的方法。 
             不包含的方法:x.y.service包下PublishService类的getResCategory(..)方法, 
             getEditorResList(..)方法,updateResbackrmd(..)方法 
    --> 
    <aop:pointcut id="txPointcut-datasourceone"    
        expression="execution(* x.y.service..*Service.*(..))    
            and !execution(* x.y.service.PublishService.getResCategory(..))    
            and !execution(* x.y.service.PublishService.getEditorResList(..))    
            and !execution(* x.y.service.PublishService.updateResbackrmd(..))"/> 

    <!-- datasourcetwo 数据库事务管理切点    
             包含的方法:x.y.service包PublishService类的getResCategory(..)方法, 
             getEditorResList(..)方法,updateResbackrmd(..)方法。 
    -->                                
    <aop:pointcut id="txPointcut-datasourcetwo"    
        expression="execution(* x.y.service.PublishService.getResCategory(..))    
            or execution(* x.y.service.PublishService.getEditorResList(..))    
            or execution(* x.y.service.PublishService.updateResbackrmd(..))"/> 

    <!-- 运行日志拦截点    
             包含的方法:service包(或子包)下所有名字以 'Service' 结尾的类内所有的方法。 
             不包含的方法:x.y.service包RuntimeLogService类createRuntimeLogBeforeRequest(..)方法, 
  getRuntimeLog(..)方法,setRuntimeLog(..)方法,completeRuntimeLogAfterRequest(..)方法。 
    --> 
    <aop:pointcut id="runtimeLogInterceptorPoint"    
        expression="execution(* x.y.service..*Service.*(..))    
            and !execution(* x.y.service.RuntimeLogService.createRuntimeLogBeforeRequest(..))    
            and !execution(* x.y.service.RuntimeLogService.getRuntimeLog(..))    
            and !execution(* x.y.service.RuntimeLogService.setRuntimeLog(..))    
            and !execution(* x.y.service.RuntimeLogService.completeRuntimeLogAfterRequest(..))"/> 
        
    <!-- 运行日志拦截 --> 
    <aop:advisor advice-ref="RuntimeLogInterceptor" pointcut-ref="runtimeLogInterceptorPoint"/> 
    <!-- datasourceone,datasourcetwo数据库事务拦截 --> 
    <aop:advisor advice-ref="txAdvice-datasourceone" pointcut-ref="txPointcut-datasourceone"/> 
    <aop:advisor advice-ref="txAdvice-datasourcetwo" pointcut-ref="txPointcut-datasourcetwo"/> 

</aop:config>

 

总结:
--------------------------------------------------------------------------------
1,pointcut既可以定义在一个接口上面(表示实现该接口的类方法将被拦截),同时也可以定义在一个类上面(无接口的情况,需要强制使用cglib)。在接口上面定义pointcut时无需关心接口实现类的具体位置,只需要定义被拦截的接口及方法位置。

2,常见的情况:
第一种情况:x.y.service..*Service.*(..)
 x.y.service:表示包“x.y.service”
 x.y.service.. :表示包“x.y.service”及其子包例如:“x.y.service.abc”,“x.y.service.def”,“x.y.service.ghi”,“x.y.service.jkl”。。。
 *Service:定义接口(或没有实现接口的类,需要使用cglib代理)表达式;所有以Service结尾的类或接口,注意不是所有以Service结尾的包名。
 *(..) :定义方法名,方法参数表达式;任意方法的名称,任意方法参数。

第二种情况:com.xyz.service.*.*(..)
 com.xyz.service:定义 包“com.xyz.service”
 *.*(..):定义任意接口(或没有实现接口的类,需要使用cglib代理),任意方法,任意参数在service包下定义的任意方法的执行。

第三种情况:com.xyz.service..*.*(..)
 com.xyz.service:定义 包“com.xyz.service”
 com.xyz.service.. :定义包“com.xyz.service”及其子包
 *.*(..):定义任意接口(或没有实现接口的类,需要使用cglib代理),任意方法,任意参数.
 
 

 
==================================================================
 
 
 
 
.
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值