xml中 aop:aspect aop:advisor如何配置

我们知道Spring-AOP的核心执行流程是这样的.
1. Spring Ioc初始化所有Bean. 如果初始化Bean的时候发现改Bean满足pointcut中SpEl表达式, 则将该Bean做动态代理.
2. 在Bean的方法执行时,判断该Bean是否为代理对象,若是代理对象的话, 判断当前Method是否满足aop表达式,
3. 如果满足表达式,则按照顺序通知Aop配置的各通知(前,后,环绕,异常等通知)

所以在配置Aop时,我们按照以下步骤:
1. 定义一个Aop通知对象(切面)
2. 配置xml(或者注解), 让被通知对象与aspect通过表达式进行匹配

准备:

1. 先定义Bean对象
public interface ISleepable {
    public void sleep();
}

public class Human implements ISleepable{
   @Override
   public void sleep() {
       System.out.println(" 小主启动睡觉核心程序...........");
       try {
           Thread.sleep(100);
       } catch (InterruptedException e) {
           e.printStackTrace();
       }
   }
}
2. 再定义两个被通知主体:

public class SleepHelper {
  public void afterReturning() throws Throwable {
      System.out.println("业余催眠师   催眠结束, 向家人收钱..........");
  }

  public void before() throws Throwable {
      System.out.println("业余催眠师   开始催眠..........");
  }
}

public class SleepLog {
    public void before() throws Throwable {
        System.out.println(">>>>>>>     小主准备脱衣睡觉    ");
    }

    public void afterReturning() throws Throwable {
        System.out.println(">>>>>>>     小主睡着了, 自动关灯:    ");
    }
}
3. 用表达式粘合:
<!--bean对象-->
  <bean id="human" class="com.spring_aop.Human"></bean>

<!--两个通知主体-->
  <bean id="yeyuSleepHelper" class="com.spring_aop.SleepHelper"></bean>
  <bean id="SleepLog" class="com.spring_aop.SleepLog"></bean>
<aop:config proxy-target-class="true">
   <!--1. 定义默认的全局切点-->
   <aop:pointcut expression="execution(* com.spring_aop.Human.*(..))" id="pointCutDefault"/>

   <aop:aspect ref="yeyuSleepHelper" id="aspect" order="1">
       <aop:before method="before" pointcut-ref="pointCutDefault"/>
       <aop:after-returning method="afterReturning" pointcut-ref="pointCutDefault"/>
   </aop:aspect>

   <aop:aspect ref="SleepLog" id="aspectlog"  order="3">
       <aop:before method="before" pointcut-ref="pointCutDefault"/>
       <aop:after-returning method="afterReturning" pointcut-ref="pointCutDefault"/>
   </aop:aspect>

    <!--============ 一条美丽的分割线 ==========-->

   <aop:aspect ref="yeyuSleepHelper" id="aspect4" order="2">
       <!--2. 这里的pointcut定义在aspect内, -->
       <aop:pointcut expression="execution(* com.spring_aop.Human.*(..))" id="pointCutSelfDefined"/>
       <aop:before method="before" pointcut-ref="pointCutSelfDefined"/>
       <aop:after-returning method="afterReturning" pointcut-ref="pointCutSelfDefined"/>
   </aop:aspect>

</aop:config>

注意: 我们将<aop:pointcut> 直接配置在<aop:config>一级目录, 表示这是个全局默认配置,
这样可以在多个<aop:aspect> 中直接引用此<aop:pointcut>,
当然也可以在<aop:aspect>内部单独定义<aop:pointcut>,如

 <aop:aspect ref="yeyuSleepHelper" id="aspect4" order="2">
     <!--2. 这里的pointcut定义在aspect内, -->
     <aop:pointcut expression="execution(* com.spring_aop.Human.*(..))" id="pointCutSelfDefined"/>
     <aop:before method="before" pointcut-ref="pointCutSelfDefined"/>
     <aop:after-returning method="afterReturning" pointcut-ref="pointCutSelfDefined"/>
 </aop:aspect>

备注: 每个<aop:aspect>都有属性order 用来定义切面的执行顺序, 默认是按照xml解析顺序执行


除了直接使用普通Bean定义切面,然后在xml中手动指定其method接受各类别的通知Advice外,我们也可以专门定义一个类来处理接受到的通知. 如:

public class SleepHelperAdvisor implements MethodBeforeAdvice, AfterReturningAdvice {
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("专业催眠师   催眠结束, 向家人收钱..........");
    }

    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("专业催眠师   催眠师开始催眠..........");
    }
}

注意, 需要实现相应的接口:AfterReturningAdvice,MethodBeforeAdvice,

在xml中配置Advisor:
包含三种方式;

第一种:
可以认为advisor 是一种特殊的Aspet, 其内部包含一个Advice实现类和一个Pointcut 表达式,
所以可以直接在<aop:advisor>中直接配置pointcut表达式.
所以可以如下定义

<aop:config>
          <!-- 注意advisor不能放在aspect的后面-->
  <aop:advisor advice-ref="professionSleeper" 
          pointcut="execution(* com.spring_aop.Human.*(..))" order="4"/>
</aop:config>

第二种:
<aop:advisor>中 使用已经配置好的pointcut

<aop:config>
      <aop:advisor advice-ref="professionSleeper" pointcut-ref="pointCutDefault" order="5"/>
 </aop:config>

第三种:

<aop:advisor>中 使用自定义的pointcut

<aop:config proxy-target-class="true">
    <aop:pointcut id="pointAdvisorDefined" expression="execution(* com.spring_aop.Human.*(..))"/>
    <aop:advisor advice-ref="professionSleeper" pointcut-ref="pointAdvisorDefined" order="1"/>
</aop:config>
advisor使用场景

< aop:advisor>大多用于事务管理。

<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="*" timeout="120" propagation="REQUIRED" rollback-for="Exception" />
    </tx:attributes>
</tx:advice>

<aop:config proxy-target-class="true">
    <aop:pointcut id="txPointCut" expression="..."/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut" />
</aop:config>
小结:

<aop:advisor><aop:aspect>其实都是将通知和切面进行了封装,原理基本上是一样的,只是使用的方式不同而已。

注意点:
  1. Aop动态代理如下遵从如下规则, 如果bean有接口, 则默认使用Jdk动态代理, 否则使用Cglib动态代理.

这就会出现如下问题:

 @Autowired
 Human human;

当我们在Spring中自动注入时使用实现类而非接口时, 因为Human有接口,所以会被动态代理为一个实现其接口的代理对象,从而找不到原始的Human, 找出注入失败.
有两种解决方法
1. 注入时,使用接口如:

 @Autowired    
 ISleepable human;

2.. 定义动态代理时指定强制使用CgLib, 配置"proxy-target-class="true"
因为Cglib动态代理实现的是继承机制, 会返回当前类的子类,

最后附录上全部xml配置文件

<?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.xsd
     http://www.springframework.org/schema/aop
     http://www.springframework.org/schema/aop/spring-aop.xsd
     http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
     http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
     http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">


  <aop:aspectj-autoproxy proxy-target-class="true"/>

  <!--bean对象-->
  <bean id="human" class="com.spring_aop.Human"></bean>

  <!--两个通知主体-->
  <bean id="yeyuSleepHelper" class="com.spring_aop.SleepHelper"></bean>
  <bean id="SleepLog" class="com.spring_aop.SleepLog"></bean>


  <aop:config proxy-target-class="true">
      <!--定义默认的全局切点-->
      <aop:pointcut expression="execution(* com.spring_aop.Human.*(..))" id="pointCutDefault"/>

      <aop:aspect ref="yeyuSleepHelper" id="aspect" order="1">
          <aop:before method="before" pointcut-ref="pointCutDefault"/>
          <aop:after-returning method="afterReturning" pointcut-ref="pointCutDefault"/>
      </aop:aspect>

      <aop:aspect ref="SleepLog" id="aspectlog" order="3">
          <aop:before method="before" pointcut-ref="pointCutDefault"/>
          <aop:after-returning method="afterReturning" pointcut-ref="pointCutDefault"/>
      </aop:aspect>

      <!--============ 一条美丽的分割线 ==========-->
      <aop:aspect ref="yeyuSleepHelper" id="aspect4" order="2">
          <!--这里的pointcut定义在aspect内, -->
          <aop:pointcut expression="execution(* com.spring_aop.Human.*(..))" id="pointCutSelfDefined"/>
          <aop:before method="before" pointcut-ref="pointCutSelfDefined"/>
          <aop:after-returning method="afterReturning" pointcut-ref="pointCutSelfDefined"/>
      </aop:aspect>

  </aop:config>

  <bean id="professionSleeper" class="com.spring_aop.SleepHelperAdvisor"></bean>

  <aop:config>
      <!--advisor不能放在aspect的后面-->
      <aop:advisor advice-ref="professionSleeper" pointcut="execution(* com.spring_aop.Human.*(..))" order="4"/>
      <aop:advisor advice-ref="professionSleeper" pointcut-ref="pointCutDefault" order="5"/>

  </aop:config>


</beans>

源码:
https://gitee.com/yangxulong/gitee-projects/tree/aop/spring-stuffs

完…

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值