Spring 6【异常通知、Schema-based方式包含多个相同通知的执行顺序、AspectJ实现AOP、在通知中获取切入点参数的写法 】(十五)-全面详解(学习总结---从入门到深化)

目录

异常通知

Schema-based方式包含多个相同通知的执行顺序

AspectJ实现AOP

在通知中获取切入点参数的写法 

 纯注解方式实现AOP


6.异常通知

异常通知只有在切入点出现异常时才会被触发。如果方法没有异常,异常通知是不会执行的。

6.1 新建通知类

新建com.tong.advice.MyThrow。

MethodInterceptor接口没有方法,但是我们必须严格提供一个下面的方法:

  • public void afterThrowing:必须相同
  • 必须有Exception参数 

也就是说,虽然ThrowsAdvice虽然没有方法,但是我们必须自己写一个和下面一样的方法。

package com.tong.advice;
import org.springframework.aop.ThrowsAdvice;
public class MyThrow implements ThrowsAdvice {
     public void afterThrowing(Exception e){
         System.out.println("异常通知:"+e.getMessage());
    }
}

 6.2 配置切面

<?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:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       https://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/aop
       https://www.springframework.org/schema/aop/spring-aop.xsd">

     <context:component-scan base-package="com.tong.mapper,com.tong.service.impl"> 
     </context:component-scan>
     <bean id="mybefore" class="com.tong.advice.MyBefore"></bean>
     <bean id="myafter" class="com.tong.advice.MyAfter"></bean>
     <bean id="myaround" class="com.tong.advice.MyAround"></bean>
     <!-- 异常通知 -->
     <bean id="mythrow" class="com.tong.advice.MyThrow"></bean>
     <aop:config>
        <aop:pointcut id="mypoint" expression="execution(*
com.tong.service.impl.PeopleServiceImpl.test())"/>
        <!-- 织入异常通知 -->
       <aop:advisor advice-ref="mythrow" pointcut-ref="mypoint"></aop:advisor>
    </aop:config>
</beans>

6.3 在切入点中写个异常

在PeopleServiceImpl里面随意写个会出现异常的代码

@Service("peopleService")
public class PeopleServiceImpl implements PeopleService {
    @Autowired
    private PeopleMapperImpl peopleMapperImpl123;
    @Override
    public void test() {
        System.out.println("join pointer 切入点。");
        int i = 5/0;// 算数异常
    }
}

7.Schema-based方式包含多个相同通知的执行顺序

如果切面中包含多个通知,执行顺序是按照配置顺序执行。

  • 前置通知:先配置的先执行
  • 后置通知:先配置的后执行
  • 环绕通知:先配置的先执行后结束
  • 异常通知:先配置的后执行(ThrowsAdvice是AfterAdvice的子接口,AfterReturningAdvice是 AfterAdvice的子接口)

AspectJ实现AOP

1.AspectJ介绍

前面学习的Schema-based方式属于Spring 框架提供的一种AOP实现方式。

AspectJ并不是Spring框架提供的技术,而是一个Java的AOP框架,借助专门的编译器来生成Java的字节码文件,编译完成,就形成了对应的切面。也就是说:AspectJ属于编译期增强

Spring Framework 2.0开始对AspectJ方式进行了支持。

可以使用注解方式,也可以使用XML配置文件方式。

在不考虑通知的参数时,AspectJ方式非常简单,只需要使用POJO+特定配置 。但是如果需要在通知中考虑参数问题和返回值时,相对配置会复杂一些。

2. AspectJ方式通知类型

  1. 前置通知before
  2. 后置通知:
  •         after是否出现异常都执行的后置通知。
  •         after-returning切入点不出现异常时才执行的后置通知

    3、环绕通知around

    4、异常通知after-throwing

3.代码实现

3.1创建通知类

Aspectj方式实现AOP的通知类不需要实现任何的接口,直接声明一个普通java类即可,然后在类中直接定义通知方法即可,方法名随意,但是建议方法名见名知意。

package com.tong.advice;
import org.aspectj.lang.ProceedingJoinPoint;
public class MyAdvice {
     //前置通知方法
     public void before(){
          System.out.println("我是前置通知方法...");
     }
     //后置通知方法
     public void after(){
          System.out.println("我是后置通知方法...");
     }
     //环绕通知方法
     public Object round(ProceedingJoinPoint pp) throws Throwable {
          System.out.println("环绕---前");
          //放行
          Object proceed = pp.proceed();
          System.out.println("环绕---后");
          return proceed;
     }
     //异常通知方法
     public void myThrow(Exception ex){
         System.out.println("我是异常通知......"+ex.getMessage());
    }
}

3.2配置AOP

<?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
       https://www.springframework.org/schema/aop/spring-aop.xsd">
      <!--配置真实对象的bean-->
      <bean id="us" class="com.tong.service.impl.UserServiceImpl"></bean>
      <!--配置通知bean-->
      <bean id="advice" class="com.tong.advice.MyAdvice"></bean>
      <!--配置AOP组装-->
      <aop:config>
          <!--基于Aspectj方式配置-->
          <aop:aspect ref="advice">
          <!--声明切点-->
          <aop:pointcut id="mp" expression="execution(* com.tong.*.impl.*.*(..))"/>
          <!--配置通知方法-->
          <aop:before method="before" pointcut-ref="mp"></aop:before>
          <!--切点正常执行才会被执行-->
          <aop:after-returning method="after" pointcut-ref="mp"></aop:afterreturning>
          <!--切点是否正常执行都会执行-->
          <!--<aop:after method="after" pointcut-ref="mp"></aop:after>-->
          <aop:around method="round" pointcut-ref="mp"></aop:around>
          <!-- 必须使用throwing声明异常参数名 -->
          <aop:after-throwing method="myThrow" pointcut-ref="mp" throwing="ex"> 
          </aop:after-throwing>
          </aop:aspect>
       </aop:config>
</beans>

注意:

after和after-returning,after无论切点是否出现异常都执行的后置通知,after-returning只有在切点正 常执行完成,才会触发的通知。

3. 在通知中获取切入点参数的写法 

package com.tong.advice;
import org.aspectj.lang.ProceedingJoinPoint;
/*
如果希望获取切入点方法的参数,推荐把通知的参和切入点的参数写成一致。
*/
public class MyAdvice2 {
       public void mybefore(int id1, boolean bool1){
             System.out.println("前置通知"+id1+","+bool1);
       }

       public void myafter(int id1, boolean bool1){
             System.out.println("后置:"+id1+","+bool1);
       }

       public void myafter2(int id1, boolean bool1){
             System.out.println("后置2:"+id1+","+bool1);
       }
       
       public Object myaround(ProceedingJoinPoint pjp,int id1, boolean bool1)
throws Throwable {
             System.out.println("环绕前置");
             Object result = pjp.proceed();
             System.out.println("环绕后置");
             return result;
       }
      public void mythrow(Exception e,int id1, boolean bool1){
           System.out.println("异常通知:"+e.getMessage()+",id:"+id1+",bool1:"+bool1);
     }
}

在配置文件中配置

<?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:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       https://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/aop
       https://www.springframework.org/schema/aop/spring-aop.xsd">

      <context:component-scan base-package="com.tong.mapper,com.tong.service.impl"> 
      </context:component-scan>
      <bean id="myadvice2" class="com.tong.advice.MyAdvice2"></bean>
         <aop:config>
            <!-- ref引用自定义通知类对应bean的id -->
            <aop:aspect ref="myadvice2">
            <!-- args写在execution括号外面 -->
                <!-- args括号内的名称和通知中方法参数名相同-->
                <aop:pointcut id="mypoint" expression="execution(* com.tong.service.impl.PeopleServiceImpl.test(int,boolean)) and args(id1,bool1)"/>
               <aop:before method="mybefore" pointcut-ref="mypoint" arg-names="id1,bool1"></aop:before>
               <aop:after-returning method="myafter" pointcut-ref="mypoint" arg-names="id1,bool1"></aop:after-returning>
               <aop:after method="myafter2" pointcut-ref="mypoint" arg-names="id1,bool1"> 
               </aop:after>
               <!-- names里面取值要和通知中方法参数名完全一致。pjp由Spring帮助自动注入进来 --
>
              <aop:around method="myaround" pointcut-ref="mypoint" argnames="pjp,id1,bool1" ></aop:around>
               <!-- 不要忘记通过throwing属性声明异常参数名称-->
               <aop:after-throwing method="mythrow" pointcut-ref="mypoint" argnames="e,id1,bool1" throwing="e"></aop:after-throwing>
        </aop:aspect>
    </aop:config>
</beans>

4.Schema-based和Aspectj的区别

Schema-based:基于模式的。基于接口实现的。每个通知都需要实现特定的接口类型,才能确定通知的类型。由于类已经实现了接口,所以配置起来相对比较简单。尤其是不需要在配置中指定参数和返回值类型。

AspectJ方式:是基于配置实现的。通过不同的配置标签告诉Spring通知的类型。AspectJ方式对于通知类写起来比较简单。但是在配置文件中参数和返回值需要特殊进行配置。

因为Schame-based是运行时增强,AspectJ是编译时增强。所以当切面比较少时,性能没有太多区别。但是当切面比较多时,最好选择AspectJ方式,因为AspectJ方式要快很多。

Schema-based是Spring框架自己的AOP实现。而AspectJ属于Spring框架对AspectJ的集成支持。

 纯注解方式实现AOP

1.总体配置说明

在Spring 框架中,AOP注解只支持AspectJ方式的注解。通过注解来简化AspectJ方式的XML配置。 我们可以通过XML+注解方式实现AOP,也可以使用纯注解方式实现AOP。

1.1 XML+注解

如果使用XML+注解方式实现AOP,需要编写applicationContext.xml配置文件,开启注解扫描和启用 AOP注解

<?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:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/aop
       https://www.springframework.org/schema/aop/spring-aop.xsd">
      
        <!--配置注解扫描路径:-->
        <context:component-scan base-package="com.tong.annotation">
        </context:component-scan>
            <!--配置AOP注解生效-->
            <aop:aspectj-autoproxy expose-proxy="true"></aop:aspectj-autoproxy>
</beans>

然后在测试类中,使用locations属性加载配置文件

@SpringJUnitConfig(locations = "classpath:applicationContext.xml")
public class AnnotationTest {}

1.2 纯注解方式

如果使用纯注解方式,其实就是使用Java Config方式,把applicationContext.xml替换为配置类

@Configuration
// 等效于<context:component-scan base-package="com.tong.annotation">
@ComponentScan("com.tong.annotation")
// 等效于<aop:aspectj-autoproxy expose-proxy="true"></aop:aspectj-autoproxy>
@EnableAspectJAutoProxy
public class AnnotationConfig {}

然后在测试类中,使用classes属性加载配置类

@SpringJUnitConfig(classes = AnnotationConfig.class)
public class AnnotationTest {}

猜你喜欢

转载自blog.csdn.net/m0_58719994/article/details/132000975