Spring 在XML中声明切面/AOP

来源:互联网 发布:三国志11公孙瓒数据 编辑:程序博客网 时间:2024/05/01 17:40

在Spring的AOP配置命名空间中,我们可以找到声明式切面选择,看下面:

<aop:config><!-- AOP定义开始 --><aop:pointcut/><!-- 定义切入点 --><aop:advisor/><!-- 定义AOP通知器 --><aop:aspect><!-- 定义切面开始 --><aop:pointcut/><!-- 定义切入点 --><aop:before/><!-- 前置通知 --><aop:after-returning/>        <!-- 后置返回通知 --><aop:after-throwing/>           <!-- 后置异常通知 --><aop:after/><!-- 后置通知(不管通知的方法是否执行成功) --><aop:around/><!-- 环绕通知 --><aop:declare-parents/>          <!-- 引入通知  --></aop:aspect><!-- 定义切面结束 --></aop:config><!-- AOP定义结束 -->

一、声明切面

切面就是包含切入点和通知的对象,在Spring容器中将被定义为一个Bean,Schema方式的切面需要一个切面支持Bean,该支持Bean的字段和方法提供了切面的状态和行为信息,并通过配置方式来指定切入点和通知实现。

 

    切面使用<aop:aspect>标签指定,ref属性用来引用切面支持Bean。

 

    切面支持Bean“aspectSupportBean”跟普通Bean完全一样使用,切面使用“ref”属性引用它。

二、  声明切入点

    切入点在Spring中也是一个Bean,Bean定义方式可以有很三种方式:

    1)在<aop:config>标签下使用<aop:pointcut>声明一个切入点Bean,该切入点可以被多个切面使用,对于需要共享使用的切入点最好使用该方式,该切入点使用id属性指定Bean名字,在通知定义时使用pointcut-ref属性通过该id引用切入点,expression属性指定切入点表达式:


<aop:config><aop:pointcut expression="execution(* cn.com.ztz.spring.service.ShowServiceImpl.*(..))" id="pointcut"/><aop:aspect ref="audienceAspect" ><aop:before pointcut-ref="pointcut" method="taskSeats"/></aop:aspect></aop:config>
 2)在<aop:aspect>标签下使用<aop:pointcut>声明一个切入点Bean,该切入点可以被多个切面使用,但一般该切入点只被该切面使用,当然也可以被其他切面使用,但最好不要那样使用,该切入点使用id属性指定Bean名字,在通知定义时使用pointcut-ref属性通过该id引用切入点,expression属性指定切入点表达式:
<aop:config><aop:aspect ref="audienceAspect" ><aop:pointcut expression="execution(* cn.com.ztz.spring.service.ShowServiceImpl.*(..))" id="pointcut"/><aop:before pointcut-ref="pointcut" method="taskSeats"/></aop:aspect></aop:config>

    3)匿名切入点Bean,可以在声明通知时通过pointcut属性指定切入点表达式,该切入点是匿名切入点,只被该通知使用:
<aop:config><aop:aspect ref="audienceAspect" ><aop:before pointcut="execution(* cn.com.ztz.spring.service.ShowServiceImpl.*(..))" method="taskSeats"/></aop:aspect></aop:config>

三、   声明通知

直接来看个例子:
package cn.com.ztz.spring.service;public interface ShowService {public void show();}
package cn.com.ztz.spring.service;public class ShowServiceImpl implements ShowService{@Overridepublic void show() {      showBefore();//showError();//异常测试    showEnd();}public void showBefore(){System.out.println("showBefore============");}public void showError(){System.out.println("showError============");throw new RuntimeException();}public void showEnd(){System.out.println("showEnd===============");}}

package cn.com.ztz.spring.service;public class AudienceAspect {public void taskSeats(){System.out.println("等候节目开始===");}public void applaud(){System.out.println("鼓掌=========");}public void demandRefund(){System.out.println("退钱离场======");}}
   <bean id="showService" class="cn.com.ztz.spring.service.ShowServiceImpl"/>    <bean id="audienceAspect" class="cn.com.ztz.spring.service.AudienceAspect"/><aop:config><aop:pointcut id="pointcut" expression="execution(* cn.com.ztz.spring.service.ShowServiceImpl.*(..))"/><aop:aspect ref="audienceAspect" ><aop:before pointcut-ref="pointcut" method="taskSeats"/><aop:after-returning pointcut-ref="pointcut" method="applaud"/><aop:after-throwing pointcut-ref="pointcut" method="demandRefund"/></aop:aspect></aop:config>
public static void main(String[] args) {ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");      ShowService hs = ctx.getBean("showService", ShowService.class);      System.out.println("======================================");    hs.show();    System.out.println("======================================");}
控制台输出结果:
======================================
等候节目开始===
showBefore============
showEnd===============
鼓掌=========
======================================

四、 为通知传递参数

现在有很多人在观看演出,我想传入一个人姓名,怎么办了,来看下:
public interface ShowService {public void showBefore(String param);}
public class ShowServiceImpl implements ShowService{public void showBefore(String param){System.out.println("showBefore============");}}
<pre name="code" class="java">public class AudienceAspect {public void taskSeats(String param){System.out.println(param+",等候节目开始===");}}
   <bean id="showService" class="cn.com.ztz.spring.service.ShowServiceImpl"/>    <bean id="audienceAspect" class="cn.com.ztz.spring.service.AudienceAspect"/><aop:config><aop:aspect ref="audienceAspect" ><aop:before pointcut="execution(* cn.com.ztz.spring.service.ShowServiceImpl.*(..)) and args(param)" method="taskSeats(java.lang.String)"arg-names="param"/></aop:aspect></aop:config>

public static void main(String[] args) {ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");      ShowService hs = ctx.getBean("showService", ShowService.class);      System.out.println("======================================");    hs.showBefore("张三");    System.out.println("======================================");}
控制台输出结果:
======================================
张三,等候节目开始===
showBefore============
======================================

五、  声明环绕通知

环绕着在切入点选择的连接点处的方法所执行的通知,环绕通知非常强大,可以决定目标方法是否执行,什么时候执行,执行时是否需要替换方法参数,执行完毕是否需要替换返回值,可通过<aop:aspect>标签下的<aop:around >标签声明:
环绕通知第一个参数必须是org.aspectj.lang.ProceedingJoinPoint类型,在通知实现方法内部使用ProceedingJoinPoint的proceed()方法使目标方法执行,proceed 方法可以传入可选的Object[]数组,该数组的值将被作为目标方法执行时的参数。
public interface ShowService {public void showAround(String param);}
public class ShowServiceImpl implements ShowService{public void showAround(String param){System.out.println("showAround============"+param);}}
public class AudienceAspect {public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable {      System.out.println("around before advice===========");      Object retVal = pjp.proceed(new Object[] {"around"});      System.out.println("around after advice===========");      return retVal; }}
   <bean id="showService" class="cn.com.ztz.spring.service.ShowServiceImpl"/>    <bean id="audienceAspect" class="cn.com.ztz.spring.service.AudienceAspect"/><aop:config><aop:aspect ref="audienceAspect" ><aop:around pointcut="execution(* cn.com.ztz.spring.service.ShowServiceImpl.*(..))" method="aroundAdvice"/></aop:aspect></aop:config>
public static void main(String[] args) {<span style="white-space:pre"></span>ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");      ShowService hs = ctx.getBean("showService", ShowService.class);      System.out.println("======================================");    hs.showAround("张三");    System.out.println("======================================");}
控制台输出结果:
======================================
around before advice===========
showAround============around
around after advice===========
======================================

六、引入

一些编程语言,例如Ruby和Groovy,有开放类的理念。它们可以不用直接修改对象或类的定义能够为对象或类增加新的方法。不幸的是,java不是动态的语言,一旦编译完成了,我们很难再为该类添加新的功能了。
自己想想我们现在不是一直在用切面吗?实际上,利用被称为引入的AOP概念,切面可以为spring bean添加新的方法。
 Spring引入允许为目标对象引入新的接口,通过在< aop:aspect>标签内使用< aop:declare-parents>标签进行引入。

<aop:declare-parents  
          types-matching="AspectJ语法类型表达式"  
          implement-interface=引入的接口"               
          default-impl="引入接口的默认实现"  
          delegate-ref="引入接口的默认实现Bean引用"/>

看下例子:我们定义了个新的接口和实现
public interface DeclareService {public void declare();}

public class DeclareServiceImpl implements DeclareService {@Overridepublic void declare() {System.out.println("declare=====================");}}

   <bean id="showService" class="cn.com.ztz.spring.service.ShowServiceImpl"/>    <bean id="declareService" class="cn.com.ztz.spring.service.DeclareServiceImpl"/><aop:config><aop:aspect><aop:declare-parents types-matching="cn.com.ztz.spring.service.ShowServiceImpl+" implement-interface="cn.com.ztz.spring.service.DeclareService" delegate-ref="declareService"/></aop:aspect></aop:config>

public static void main(String[] args) {ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");  DeclareService hs = ctx.getBean("showService", DeclareService.class);      System.out.println("======================================");    hs.declare();    System.out.println("======================================");}

我们获得还是showService的Bean,运行下测试方法输出结果:
======================================
declare=====================
======================================

1 0
原创粉丝点击