zoukankan      html  css  js  c++  java
  • Spring学习(八)

    AOP的重要概念

    1、切面 : 切点(Pointcut) + Advice【 在哪里 、加什么 】

    2、Advice: 在 切点 选中的 连接点 "加入" 的 代码 就是 Advice【确定 加什么 】

    3、切点( Pointcut ) : 用来 筛选 连接点 的条件就是切点, 类似于sql语句,where条件就可以理解为切点【 确定 在哪里加 ,在那些方法里面加入代码】

    4、连接点( Join Point ) : 执行点 + 方位信息 所有被拦截的方法被加入了其他的方法,通过代理的方法将想要加入的想要的代码,加入的代码和原有的代码形成了连接点。一个方法有5个连接点,四个方法就有20个

    • 一个正在执行的方法 (执行点) 执行前 (方位信息)
    • 一个正在执行的方法 (执行点) 执行前、后 (方位信息)
    • 一个正在执行的方法 (执行点) 执行并返回后 (方位信息)
    • 一个正在执行的方法 (执行点) 执行抛出异常后 (方位信息)
    • 一个正在执行的方法 (执行点) 执行后 (方位信息)

    5、方位信息

    • 方法执行前 ( before ) 、
    • 执行前和执行后,执行前后都有环绕,两个结合起来才可以使用,两者之间是有联系的 ( around ) 、
    • 正常执行并返回后 ( after-returning ) 、
    • 方法抛出异常后 ( after-throwing ) 、
    • 方法执行后 ( after )

    6、执行点:一个可以执行的方法在执行时就是一个执行点

    使用AOP的方式

    1、使用 XML 文件中的 aop 命名空间:

    • 使用 aop:advisor 标签来声明切面
    • 使用 aop:aspect 标签来声明一组切面
    <?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"
        xmlns:p="http://www.springframework.org/schema/p"
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
      <aop:config proxy-target-class="true" >
        ...................
      </aop:config>
    </beans>

    proxy-target-class 指示 采用 那种方式 创建 代理对象
    proxy-target-class="false" 不采用 cglib 方式,而是采用 JDK 动态代理方式 ,若没有实现接口会通过 cglib 方式
    proxy-target-class="true" 采用 cglib 方式(当一个类没有实现任何接口时,必须采用这种方式)动态产生字节码

    2、使用 注解 声明

    使用 aop:advisor 标签来声明切面

    1、这种方式是基于 XML 文件中的 aop 命名空间,并通过Spring的API实现AOP。

    2、主要步骤

    • 写一个代理目标Mokey
      package ecut.aop.xml;
      
      public class Monkey {
          
          private String name ;
          
          public void eat( String food ) {
              System.out.println( this.name +  " 吃 " + food );
          }
          
          public void run(){
              System.out.println( this.name +  " 在跑步 " );
          }
          
          public void sleep(){
              System.out.println( this.name +  " 在睡觉 " );
          }
          
          public void fly(){
              System.out.println( this.name +  " 很牛逼,可以腾云驾雾 " );
          }
      
          public String getName() {
              return name;
          }
      
          public void setName(String name) {
              this.name = name;
          }
          
      }
    • 通过实现Spring提供了相应的Advice接口的方式编写Advice

      MonkeyBeforeAdvice:

      package ecut.aop.xml;
      
      import java.lang.reflect.Method;
      
      import org.springframework.aop.MethodBeforeAdvice;
      
      // org.springframework.aop.BeforeAdvice 接口 继承了 org.aopalliance.aop.Advice
      // org.springframework.aop.MethodBeforeAdvice 接口 继承了 BeforeAdvice
      public class MonkeyBeforeAdvice implements MethodBeforeAdvice {
      
          @Override
          public void before( Method method , Object[] args , Object target) throws Throwable {
              System.out.println( "MonkeyBeforeAdvice 提示 : 方法[ " + method.getName() + " ]将要执行了" );
          }
      
      }

      org.springframework.aop.BeforeAdvice 接口 继承了 org.aopalliance.aop.Advice,org.springframework.aop.MethodBeforeAdvice 接口 继承了 BeforeAdvice,实现MethodBeforeAdvice的接口并重写before方法。

      MonkeyAroundAdvice:
      package ecut.aop.xml;
      
      import org.aopalliance.intercept.MethodInterceptor;
      import org.aopalliance.intercept.MethodInvocation;
      
      
      // org.aopalliance.intercept.Interceptor 继承了 org.aopalliance.aop.Advice
      // org.aopalliance.intercept.MethodInterceptor  继承 org.aopalliance.intercept.Interceptor
      public class MonkeyAroundAdvice implements MethodInterceptor{
      
          @Override
          public Object invoke( MethodInvocation invocation ) throws Throwable {
              
              System.out.println( "around advice : before" );
              Object result = invocation.proceed(); // 让被 "拦截" 的方法执行 
              System.out.println( "around advice : after" );
              
              /*
              Method m = invocation.getMethod(); //获取被连接的 方法 对应的 Method 对象
              
              Object[] args = invocation.getArguments(); // 获得 即将被执行的 方法的参数列表
              
              Object target = invocation.getThis(); // 获取 代理目标 ( 被别的对象所代理的那个对象 )
              // System.out.println( target );
              
              System.out.println( "around advice : before" );
              Object result = m.invoke( target ,  args ); // 调用 target 对象的 m 对应的方法,并传入参数 args
              System.out.println( "around advice : after" );
              */
              
              return result ;
          }
      
      }

      org.aopalliance.intercept.Interceptor 继承了 org.aopalliance.aop.Advice,org.aopalliance.intercept.MethodInterceptor 继承 org.aopalliance.intercept.Interceptor实现MethodInterceptor的接口并重写invoke方法。这个Advice需要继续向后传递结果,因此需要将结果返回。

      MonkeyAfterAdvice:

      package ecut.aop.xml;
      
      import java.lang.reflect.Method;
      
      import org.springframework.aop.AfterReturningAdvice;
      
      public class MonkeyAfterAdvice implements AfterReturningAdvice {
      
          @Override
          public void afterReturning( Object returnValue , Method method , Object[] args , Object target ) 
                  throws Throwable {
              System.out.println( "方法[ " + method.getName() + " ]执行后返回了: " + returnValue );
          }
      
      
      }

      需要实现AfterReturningAdvice 接口重写afterReturning方法。

    • 配置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"
          xmlns:p="http://www.springframework.org/schema/p"
          xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
              http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
      
          <bean id="wk" class="ecut.aop.xml.Monkey" p:name="孙悟空" />
          
          <bean id="beforeAdvice" class="ecut.aop.xml.MonkeyBeforeAdvice" />
          
          <bean id="aroundAdvice" class="ecut.aop.xml.MonkeyAroundAdvice" />
          
          <bean id="afterAdvice" class="ecut.aop.xml.MonkeyAfterAdvice" />
      
          <!-- proxy-target-class 指示 采用 那种方式 创建 代理对象 -->
          <!-- proxy-target-class="false" 不采用 cglib 方式,而是采用 JDK 动态代理方式 ,若没有实现接口会通过 cglib 方式-->
          <!-- proxy-target-class="true" 采用 cglib 方式(当一个类没有实现任何接口时,必须采用这种方式)动态产生字节码-->
          <aop:config proxy-target-class="true" >
          
              <!-- 声明一个切点 -->
              <!-- 参数个数不确定的 ,..可以匹配任意个数的参数-->
              <aop:pointcut  id="my-pointcut" expression="execution(* ecut.aop.xml.Monkey.*(..))" />
              <!-- 切面 ( Advisor ) : 切点(Pointcut) + Advice              【 在哪里 、加什么 】 -->
              <!-- 一个 aop:advisor 就表示一个 切面   -->
              <aop:advisor pointcut-ref="my-pointcut" advice-ref="beforeAdvice" />
              
              <!-- pointcut=“切点表达式” 切点表达式 execution(修饰符 返回类型 包名.类名.方法名(参数列表) 异常类型) -->
              <!-- <aop:advisor pointcut="execution(* ecut.aop.xml.Monkey.*(..))" advice-ref="beforeAdvice" /> -->
              
              <aop:advisor pointcut-ref="my-pointcut" advice-ref="aroundAdvice" />
              
              <aop:advisor pointcut-ref="my-pointcut" advice-ref="afterAdvice" />
              
          </aop:config>
      
      </beans>

      需要在配置文件中声明切点和Advice,然后aop:advisor声明切面Advice,声明切点需要通过expression属相来指定切点表达execution(修饰符 返回类型 包名.类名.方法名(参数列表) 异常类型)。

    • 编写测试类
      package ecut.aop.xml;
      
      import org.springframework.context.support.AbstractApplicationContext;
      import org.springframework.context.support.ClassPathXmlApplicationContext;
      
      public class TestAdvisor {
      
          public static void main(String[] args) {
              
              String configLocations = "classpath:ecut/**/xml/advisor.xml" ;
              
              AbstractApplicationContext container = new ClassPathXmlApplicationContext( configLocations );
              
              Monkey m = container.getBean( "wk" , Monkey.class );
              //获取的是代理类的类型,不再是Monkey
              System.out.println( m.getClass() );
              System.out.println("~~~~~~~~~~~~~~~~~~~~~~");
              m.eat( "苹果" );
              System.out.println("~~~~~~~~~~~~~~~~~~~~~~");
              m.run();
              System.out.println("~~~~~~~~~~~~~~~~~~~~~~");
              String name = m.getName() ;
              //和filter,interceptor 不一样,需继续传递才会往下执行,而after,before拦截下来之后会继续往下执行,无需继续传递
              System.out.println( "name: " + name );
              
              container.close();
      
          }
      
      }

      通过m.getClass可以获得代理类的类型。测试代码运行结果如下:

      class ecut.aop.xml.Monkey$$EnhancerBySpringCGLIB$$44ed5ead
      ~~~~~~~~~~~~~~~~~~~~~~
      MonkeyBeforeAdvice 提示 : 方法[ eat ]将要执行了
      around advice : before
      孙悟空 吃 苹果
      方法[ eat ]执行后返回了: null
      around advice : after
      ~~~~~~~~~~~~~~~~~~~~~~
      MonkeyBeforeAdvice 提示 : 方法[ run ]将要执行了
      around advice : before
      孙悟空 在跑步 
      方法[ run ]执行后返回了: null
      around advice : after
      ~~~~~~~~~~~~~~~~~~~~~~
      MonkeyBeforeAdvice 提示 : 方法[ getName ]将要执行了
      around advice : before
      方法[ getName ]执行后返回了: 孙悟空
      around advice : after
      name: 孙悟空

      由结果可以看出,Advice按照配置文件中引用的顺序输出了。默认执行顺序为before 一定在 after之前执行,若为方位信息一致,谁在前谁先执行。

    3、织入顺序:

    • 对于在同一个 方位中 织入 Advice 的多个 <aop:advisor>  ,可以使用 order 属性来确定织入顺序
    • 测试案例

      MonkeyAnotherAdvice :

      package ecut.aop.xml;
      
      import java.lang.reflect.Method;
      
      import org.springframework.aop.MethodBeforeAdvice;
      
      public class MonkeyAnotherAdvice implements MethodBeforeAdvice {
          
          @Override
          public void before( Method method , Object[] args , Object target) throws Throwable {
              System.out.println( "MonkeyAnotherAdvice 提示 : 方法[ " + method.getName() + " ]将要执行了" );
          }

      配置文件:

      <?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"
          xmlns:p="http://www.springframework.org/schema/p"
          xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
              http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
      
          <bean id="wk" class="ecut.aop.xml.Monkey" p:name="孙悟空" />
          
          <bean id="beforeAdvice" class="ecut.aop.xml.MonkeyBeforeAdvice" />
          
          <bean id="anotherAdvice" class="ecut.aop.xml.MonkeyAnotherAdvice" />
          
          <aop:config proxy-target-class="true" >
          
              <aop:pointcut  id="my-pointcut" expression="execution(* ecut.aop.xml.Monkey.*(..))" />
              
              <!-- 一个 aop:advisor 就表示一个 切面  -->
              <aop:advisor pointcut-ref="my-pointcut" advice-ref="beforeAdvice" order="10000"/>
              
              <aop:advisor pointcut-ref="my-pointcut" advice-ref="anotherAdvice" order="0"/>
              
          </aop:config>
      
      </beans>

      数字越小越先执行,order属性所指定的数字可以不按照顺序,只要数字之前有大小之分就可以决定两个Advice的执行顺序

      测试类:

      package ecut.aop.xml;
      
      import org.springframework.context.support.AbstractApplicationContext;
      import org.springframework.context.support.ClassPathXmlApplicationContext;
      
      public class TestAdvisorOrder {
      
          public static void main(String[] args) {
              
              String configLocations = "classpath:ecut/**/xml/advisor-order.xml" ;
              
              AbstractApplicationContext container = new ClassPathXmlApplicationContext( configLocations );
              
              Monkey m = container.getBean( "wk" , Monkey.class );
              
              System.out.println( m.getClass() );
              
              m.eat( "苹果" );
              
              container.close();
      
          }
      
      }

      运行结果如下:

      class ecut.aop.xml.Monkey$$EnhancerBySpringCGLIB$$b4a506ad
      MonkeyAnotherAdvice 提示 : 方法[ eat ]将要执行了
      MonkeyBeforeAdvice 提示 : 方法[ eat ]将要执行了
      孙悟空 吃 苹果

      织入顺序由order决定,若没有指定order属性会由配置文件中引用顺序来执行即MonkeyBeforeAdvice 先执行

    使用 aop:aspect 标签来声明一组切面

    1、这种方式是基于 XML 文件中的 aop 命名空间,并通过自定义类来实现AOP。

    2、主要步骤

    • 写一个代理目标Panda
      package ecut.aop.xml;
      
      public class Panda {
          
          private String name ;
          
          public void eat( String food ) {
              System.out.println( this.name +  " 吃 " + food );
          }
          
          public void run(){
              System.out.println( this.name +  " 在跑步 " );
          }
          
          public void sleep(){
              System.out.println( this.name +  " 在睡觉 " );
          }
          
          public void fly(){
              System.out.println( this.name +  " 很牛逼,可以腾云驾雾 " );
          }
          
          public int div( int a , int b ) {
              return a / b ;
          }
      
          public String getName() {
              return name;
          }
      
          public void setName(String name) {
              this.name = name;
          }
          
      }
    • 不实现任何借口自定义一个Advice 类
      package ecut.aop.xml;
      
      import org.aspectj.lang.JoinPoint;
      import org.aspectj.lang.ProceedingJoinPoint;
      import org.aspectj.lang.Signature;
      
      public class PandaAdvices {
          
          /**
           * 不要静态的,所有Advices都是实例化之后去植入这个方法
           * 通过 JoinPoint 类型的对象 可以访问连接点 信息
           * 如果不需要访问连接点信息,那么 可以不写 这个参数
           * @param point
           */
          public void before( JoinPoint point  ) {
              //方法签名方法签名int ecut.aop.xml.Panda.div(int,int)
              Signature signature = point.getSignature(); // 获得当前的连接点对应的方法的签名
                      
              String name =  signature.getName() ; // 获得方法的名称
              
              System.out.println( "PandaAdvices 提示你 : 方法[ " + name + " ]将要执行了" );
              
          }
          
          // ProceedingJoinPoint 是 JoinPoint 接口的子接口 ,可以通过 ProceedingJoinPoint访问连接点 信息,也可以通过ProceedingJoinPoint使方法执行
          public Object around( ProceedingJoinPoint point ) throws Throwable {
              System.out.println( "around : before" );
              Object result = point.proceed();
              System.out.println( "around : after" );
              return result ;//得将值返回,不然没法往后传值了
          }
      
          public void afterReturn( JoinPoint point , Object result ) {
              
              Signature signature = point.getSignature(); // 获得当前的连接点对应的方法的签名
              
              String name =  signature.getName() ; // 获得方法的名称
              
              System.out.println( "方法[ " + name + " ]执行后返回: " + result );
              
          }
          
          public void afterThrow( JoinPoint point , Throwable ex ) {
              
              Signature signature = point.getSignature(); // 获得当前的连接点对应的方法的签名
              
              String name =  signature.getName() ; // 获得方法的名称
              
              System.out.println( "方法[ " + name + " ]执行时发生异常: " + ex );
              
          }
          
          public void after( JoinPoint point  ) {
              
              Signature signature = point.getSignature(); // 获得当前的连接点对应的方法的签名
              
              String name =  signature.getName() ; // 获得方法的名称
              
              System.out.println( "方法[ " + name + " ]执行结束" );
              
          }
      
      }

      连接点JoinPoint 是执行点+方位信息, 通过 JoinPoint 类型的对象 可以访问连接点信息 ,如果不需要访问连接点信息,那么 可以不写 这个参数,afterThrow和afterReturn中的参数名称要和配置文件中的名称保持一致。ProceedingJoinPoint 是 JoinPoint 接口的子接口 ,可以通过 ProceedingJoinPoint访问连接点 信息,也可以通过ProceedingJoinPoint使方法执行。

    • 配置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"
          xmlns:p="http://www.springframework.org/schema/p"
          xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
              http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
      
          <bean id="panda" class="ecut.aop.xml.Panda" p:name="阿宝" />
          
          <!-- pandaAdvices 中包含了要在 某些连接点 中需要织入的代码 -->
          <bean id="pandaAdvices" class="ecut.aop.xml.PandaAdvices" />
          
          <aop:config proxy-target-class="true" >
          
              <!-- 声明一个切点 ( 属于 整个 aop:config 标记内都可以使用,因此称作 全局切点 ) -->
              <aop:pointcut  id="my-pointcut" expression="execution(* ecut.aop.xml.Panda.*(..))" />
              <!-- 声明的是一组切面 -->
              <aop:aspect ref="pandaAdvices">
              
                  <!-- 在 aop:aspect 标记内部的 aop:pointcut 被称作 局部切点,只能在同一个 aop:aspect 标记内使用-->
                  
                  <aop:before pointcut-ref="my-pointcut" method="before"/> <!-- pandaAdvices.before() method所引用的方法就是确定加什么代码 ,一个method就是一个Advice-->
                  
                  <aop:around pointcut-ref="my-pointcut" method="around"/>
                  
                  <aop:after-throwing pointcut-ref="my-pointcut" method="afterThrow" throwing="ex"/>
                  <!-- 通过指定returning这个属哦,将返回值绑定到这个参数上,需要和method中的参数名一致 -->
                  <aop:after-returning pointcut-ref="my-pointcut" method="afterReturn" returning="result"/>
                  
                  <aop:after  pointcut-ref="my-pointcut" method="after" /> <!-- pandaAdvices.after() 是无论抛并不抛出异常都要执行的Advice(类似finally)-->
                  
              </aop:aspect>
              
          </aop:config>
      
      </beans>

      通过<aop:aspect ref="pandaAdvices">来引用自定义的Advice 类,通过<aop:aspect>标记内的aop标记来指定方位信息,pointcut-ref来引用相应的切点,并通过method属性来调用Advice 类中的Advice,method所引用的方法就是确定加什么代码 ,一个method就是一个Advice。<aop:after-throwing/>标签中的throwing属性需要和afterThrow方法中的Throwable参数名称保持一致,<aop:after-returning/>标签中

    • 编写测试类
      package ecut.aop.xml;
      
      import org.springframework.context.support.AbstractApplicationContext;
      import org.springframework.context.support.ClassPathXmlApplicationContext;
      
      public class TestAspect {
      
          public static void main(String[] args) {
              
              String configLocations = "classpath:ecut/**/xml/aspect.xml" ;
              
              AbstractApplicationContext container = new ClassPathXmlApplicationContext( configLocations );
              
              Panda m = container.getBean( "panda" , Panda.class );
              System.out.println(m.getClass());
              //表面上调用的panda的eat,方法实际上m只是一个代理对象
              m.eat( "面条" );
              
              System.out.println( "~~~~~~~~~~~~~~~~~" );
              
              String name = m.getName() ;
              
              System.out.println( "name : " + name  );
              
              System.out.println( "~~~~~~~~~~~~~~~~~" );
              
              int r = m.div( 100 ,  0 );
              
              System.out.println( r );
              
              container.close();
      
          }
      
      }

      运行结果如下:

      
      
      class ecut.aop.xml.Panda$$EnhancerBySpringCGLIB$$dac36ee3
      PandaAdvices 提示你 : 方法[ eat ]将要执行了
      around : before
      阿宝 吃 面条
      around : after
      方法[ eat ]执行后返回: null
      方法[ eat ]执行结束
      ~~~~~~~~~~~~~~~~~
      PandaAdvices 提示你 : 方法[ getName ]将要执行了
      around : before
      around : after
      方法[ getName ]执行后返回: 阿宝
      方法[ getName ]执行结束
      name : 阿宝
      ~~~~~~~~~~~~~~~~~
      PandaAdvices 提示你 : 方法[ div ]将要执行了
      around : before
      方法[ div ]执行时发生异常: java.lang.ArithmeticException: / by zero
      方法[ div ]执行结束
      Exception in thread "main" java.lang.ArithmeticException: / by zero
          at ecut.aop.xml.Panda.div(Panda.java:24)

    3、织入顺序

    • 对于多个 <aop:aspect> 来说 可以通过 order 属性的值 来织入顺序 ( 按照 从小 到大 排列 ) 无需按照顺序排只要有大小之分就好了, 数字越大越靠后执行。
    • 测试案例

      AnimalAdvices:

      package ecut.aop.xml;
      
      import org.aspectj.lang.JoinPoint;
      import org.aspectj.lang.Signature;
      
      public class AnimalAdvices {
          
          public void before1( JoinPoint point  ) {
              
              Signature signature = point.getSignature(); // 获得当前的连接点对应的方法的签名
              
              String name =  signature.getName() ; // 获得方法的名称
              
              System.out.println( "AnimalAdvices  的 before1 提示你 : 方法[ " + name + " ]将要执行了" );
              
          }
          
          public void before2( JoinPoint point  ) {
              
              Signature signature = point.getSignature(); // 获得当前的连接点对应的方法的签名
              
              String name =  signature.getName() ; // 获得方法的名称
              
              System.out.println( "AnimalAdvices  的 before2 提示你 : 方法[ " + name + " ]将要执行了" );
              
          }
      
      }

      配置文件:

      <?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"
          xmlns:p="http://www.springframework.org/schema/p"
          xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
              http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
      
          <bean id="panda" class="ecut.aop.xml.Panda" p:name="阿宝" />
          
          <bean id="pandaAdvices" class="ecut.aop.xml.PandaAdvices" />
          
          <bean id="animalAdvices" class="ecut.aop.xml.AnimalAdvices" />
          
          <aop:config proxy-target-class="true" >
          
              <!-- 声明一个切点 ( 属于 整个 aop:config 标记内都可以使用,因此称作 全局切点 ) -->
              <aop:pointcut  id="my-pointcut" expression="execution(* ecut.aop.xml.Panda.*(..))" />
              
              <aop:aspect ref="pandaAdvices" order="150">
                  <aop:before pointcut-ref="my-pointcut" method="before"/> <!-- pandaAdvices.before() -->
              </aop:aspect>
              
              <aop:aspect ref="animalAdvices" order="-250">
                  <aop:before pointcut-ref="my-pointcut" method="before2"/> <!-- pandaAdvices.before() -->
                  <aop:before pointcut-ref="my-pointcut" method="before1"/>
              </aop:aspect>
              
          </aop:config>
      
      </beans>

      测试类:

      package ecut.aop.xml;
      
      import org.springframework.context.support.AbstractApplicationContext;
      import org.springframework.context.support.ClassPathXmlApplicationContext;
      
      public class TestAspectOrder {
      
          public static void main(String[] args) {
              
              String configLocations = "classpath:ecut/**/xml/aspect-order.xml" ;
              
              AbstractApplicationContext container = new ClassPathXmlApplicationContext( configLocations );
              
              Panda m = container.getBean( "panda" , Panda.class );
              
              m.eat( "面条" );
              
              container.close();
      
          }
      
      }

      运行结果如下:

      AnimalAdvices  的 before2 提示你 : 方法[ eat ]将要执行了
      AnimalAdvices  的 before1 提示你 : 方法[ eat ]将要执行了
      PandaAdvices 提示你 : 方法[ eat ]将要执行了
      阿宝 吃 面条

    使用注解声明切面

    1、步骤

    • 写一个StudentController类
      package ecut.aop.annotation;
      
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.beans.factory.annotation.Qualifier;
      import org.springframework.stereotype.Controller;
      
      /**
       * 控制层 ( Controller ) : 对 表示层 的用户操作 做出响应
       * 实现技术可以是: Servlet 、Struts 、Spring MVC
       * @Controller("sc") value ="sc" 相当于bean的名称是sc,默认名称是 studentController
       */
      @Controller
      public class StudentController {
          
          @Autowired
          @Qualifier( "studentService" )
          private StudentService studentService ;
          
          public String regist( Student s ){
              System.out.println( "StudentController # regist ( Student ) " );
              studentService.save( s );
              return "success" ;
          }
          
          public String logout() {
              
              throw new RuntimeException( "抛出异常" );
              
          }
      
          public StudentService getStudentService() {
              return studentService;
          }
      
          public void setStudentService(StudentService studentService) {
              this.studentService = studentService;
          }
          
      }
    • StudentAspect声明一组切面

      package ecut.aop.annotation;
      
      import org.aspectj.lang.JoinPoint;
      import org.aspectj.lang.ProceedingJoinPoint;
      import org.aspectj.lang.Signature;
      import org.aspectj.lang.annotation.After;
      import org.aspectj.lang.annotation.AfterReturning;
      import org.aspectj.lang.annotation.AfterThrowing;
      import org.aspectj.lang.annotation.Around;
      import org.aspectj.lang.annotation.Aspect;
      import org.aspectj.lang.annotation.Before;
      import org.aspectj.lang.annotation.Pointcut;
      import org.springframework.stereotype.Component;
      
      @Component
      @Aspect
      public class StudentAspect {
          
          //@Pointcut( "execution(* ecut..StudentController.*(..))" )
          @Pointcut( "execution(* ecut.aop.annotation.StudentController.*(..))" )
          private void myPointcut(){
              // 用声明方法的形式 来声明 一个切点,切点名称就是 方法名 , 切点表达式在 注解中指定
          }
          
          //在 方位信息的注解内部 通过  方法调用的 形式 来引用已经声明的切点
          @Before( "myPointcut()" )
          public void before( JoinPoint point  ) {
              Signature signature = point.getSignature(); // 获得当前的连接点对应的方法的签名
              String name =  signature.getName() ; // 获得方法的名称
              System.out.println( "StudentAspect 的 before 提示你 : 方法[ " + name + " ]将要执行了" );
          }
          
          @Around( "myPointcut()" )
          public Object around( ProceedingJoinPoint point ) throws Throwable {
              System.out.println( "StudentAspect 的 around 提示你  : before" );
              Object result = point.proceed();
              System.out.println( "StudentAspect 的 around 提示你  : after" );
              return result ;
          }
          
          //@AfterReturning( value = "myPointcut()" , returning = "result" )
          @AfterReturning( pointcut = "myPointcut()" , returning = "result" )
          public void afterReturn( JoinPoint point , Object result ) {
              Signature signature = point.getSignature(); // 获得当前的连接点对应的方法的签名
              String name =  signature.getName() ; // 获得方法的名称
              System.out.println( "StudentAspect 的 afterReturn 提示你  : 方法[ " + name + " ]执行后返回: " + result );
          }
          
          @AfterThrowing( pointcut = "myPointcut()" , throwing = "ex" )
          public void afterThrow( JoinPoint point , Throwable ex ) {
              Signature signature = point.getSignature(); // 获得当前的连接点对应的方法的签名
              String name =  signature.getName() ; // 获得方法的名称
              System.out.println( "StudentAspect 的 afterThrow 提示你  : 方法[ " + name + " ]执行时发生异常: " + ex );
          }
          
          @After( "myPointcut()" )
          public void after( JoinPoint point  ) {
              Signature signature = point.getSignature(); // 获得当前的连接点对应的方法的签名
              String name =  signature.getName() ; // 获得方法的名称
              System.out.println( "StudentAspect 的 after 提示你 : 方法[ " + name + " ]执行结束" );
          }
      
      }

      首先使用Component注解要让spring容器知道有一个类存在,其次要使用Aspect注解声明时一组切面,在类中通过声明方法的形式 来声明 一个切点,切点名称就是方法名, 切点表达式在 注解中指定。在 方位信息的注解内部 通过 方法调用的 形式 来引用已经声明的切点。@AfterThrowing注解中的throwing属性需要和afterThrow方法中的Throwable参数名称保持一致,@AfterReturning注解中的returning属相需要和afterReturn方法中的Object 参数名称保持一致。

    • 配置文件

      <?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"
          xmlns:p="http://www.springframework.org/schema/p"
          xmlns:context="http://www.springframework.org/schema/context"
          xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
              http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
              http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
          
          <!--  
          <bean id="studentDao" class="ecut.aop.annotation.StudentDao" />
          <bean id="studentService" class="ecut.aop.annotation.StudentService" autowire="byType"/>
          <bean id="studentController" class="ecut.aop.annotation.StudentController" autowire="byType"/>
          
          扫描所有加了注解的类自动完成下面的配置
          <bean id="studentDao" class="ecut.aop.annotation.StudentDao" />
          <bean id="studentService" class="ecut.aop.annotation.StudentService" p:studentDao-ref="studentDao"/>
          <bean id="studentController" class="ecut.aop.annotation.StudentController" p:studentService-ref="studentService"/>
          -->
          <!-- 启用注解的支持 -->
          <!-- <context:annotation-config /> -->
          <!-- 扫描这个包的所有组件 -->
          <context:component-scan base-package="ecut.aop.annotation" />
          
          <!-- 为 注解 提供 自动产生 代理对象的 支持 -->
          <aop:aspectj-autoproxy proxy-target-class="true" />
          
      </beans>

      配置文件中通过<context:component-scan base-package="ecut.aop.annotation" />扫描base-package所指定包及其子包的所有组件,若有注解就自动增加其配置,通过<aop:aspectj-autoproxy proxy-target-class="true" />为 注解 提供 自动产生 代理对象的 支持

    • 测试类
      package ecut.aop.annotation;
      
      import org.springframework.context.support.AbstractApplicationContext;
      import org.springframework.context.support.ClassPathXmlApplicationContext;
      
      public class TestStudentController {
      
          public static void main(String[] args) {
              
              Student s = new Student();
              s.setId( 100 );
              s.setName( "张三" );
              
              String configLocations = "classpath:ecut/**/annotation/ioc-aop.xml" ;
              
              AbstractApplicationContext container = new ClassPathXmlApplicationContext( configLocations );
              
              StudentController sc = container.getBean( "studentController" , StudentController.class );
              
              System.out.println( sc );
              
              sc.regist( s );
              
              System.out.println( "~~~~~~~~~~~~~~~~~~~~~~" );
              
              sc.logout();
              
              container.close();
      
          }
      
      }

    2、相关注解说明

    • @org.aspectj.lang.annotation.Pointcut 用来单独声明一个切点
    • @org.aspectj.lang.annotation.Before 相当于 <aop:before>
    • @org.aspectj.lang.annotation.Around 相当于 <aop:around>
    • @org.aspectj.lang.annotation.AfterReturning 相当于 <aop:after-returning>
    • @org.aspectj.lang.annotation.AfterThrowing 相当于 <aop:after-returning>
    • @org.aspectj.lang.annotation.After 相当于 <aop:after>  

    3、织入顺序

    • 在 注解中通过 @org.springframework.core.annotation.Order 用来指定 织入顺序
    • 在有多个Aspect时,在类声明前使用order注解可以控制织入顺序,在同一个Aspect,在方法中使用order属性不起作用
    • 因此使用注解织入顺序的控制没有配置文件的好。

    转载请于明显处标明出处:

    https://www.cnblogs.com/AmyZheng/p/9277832.html

  • 相关阅读:
    [转载] set IDENTITY_INSERT on 和 off 的设置
    固定GridView的表头和某几列
    图形文件格式小常识
    MVP——ModelViewerPresenter [ZT]
    VB.NET 中图形旋转任意角度 [ZT]
    工厂方法模式和抽象工厂模式之比较 [Z]
    Visual C# 语言概念数据类型(C# 与 Java)
    LINQ 中使用 Distinct 的 Compare过滤重复的字段
    How to load AJAX content into current Colorbox window?
    解决FTP服务器FileZilla server中文乱码问题
  • 原文地址:https://www.cnblogs.com/AmyZheng/p/9277832.html
Copyright © 2011-2022 走看看