Spring 2.x AOP声明式配置

来源:互联网 发布:中国税务网络大学首页 编辑:程序博客网 时间:2024/06/04 00:25
不同于之前的一篇日志《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代理),任意方法,任意参数.

原创粉丝点击