• 一起学Spring之注解和Schema方式实现AOP


    概述

    在上一篇,我们了解了通过实现接口和XML配置的方式来实现AOP,在实现注解方式AOP之前,先了解一下AspectJ。AspectJ是一个面向切面的框架,它扩展了Java语言,定义了AOP语法,能够在编译时实现代码的注入。Spring通过集成ApsectJ实现了以注解方式定义通知类,大大减少了配置文件的工作量。本文主要讲解通过注解方式和Schema方式实现AOP编程,仅供学习分享使用,如有不足之处,还请指正。

    实现AOP的三种方式:

    1. 通过实现相应接口,并配置Xml进行实现,可以参考上一篇。
    2. 通过注解方式实现。
    3. 通过Schema的方式实现。

    通过注解方式实现AOP

    @Aspect 放在类的上面,表示这个类在Spring容器中是一个切点注入类。

    @Component("logAnnotation") 表示此类是一个Bean,在Spring Ioc容器里面进行注入。

    步骤如下:

    1. 定义一个类,并在类上面增加@Aspect注解,表名此类为切面通知类。

    如下所示:

     1 package com.hex.second;
     2 
     3 import org.aspectj.lang.JoinPoint;
     4 import org.aspectj.lang.ProceedingJoinPoint;
     5 import org.aspectj.lang.annotation.AfterReturning;
     6 import org.aspectj.lang.annotation.AfterThrowing;
     7 import org.aspectj.lang.annotation.Around;
     8 import org.aspectj.lang.annotation.Aspect;
     9 import org.aspectj.lang.annotation.Before;
    10 import org.springframework.stereotype.Component;
    11 
    12 /**
    13  * @Aspect:声明类为一个通知类
    14  * @Component("logAnnotation"):通过注解方法生成一个Bean,但是需要配置注解的支持
    15  * 通过注解的方式声明通知
    16  * @author Administrator
    17  *
    18  */
    19 @Component("logAnnotation")
    20 @Aspect
    21 public class LogAspectAnnotation {
    22 
    23 }

    2. 前置通知函数

    @Before("execution(public void com.hex.second.StudentServiceImpl.addStudent(com.hex.second.Student))") 表示前置通知,用两个参数,默认为value值,指示切入点,告诉Spring在哪些地方进行注入。

    JoinPoint 可以获取切入点的所有内容,包括目标对象,函数名称,参数,返回值等等。如下所示:

    1 /**
    2  * 前置通知,@Before的参数为目标通知类的表达式
    3  * JoinPoint 用来获取目标函数的参数及对象等信息
    4  */
    5 @Before("execution(public void com.hex.second.StudentServiceImpl.addStudent(com.hex.second.Student))")
    6 public void MyBefore(JoinPoint jp){
    7     System.out.println("我是注解方式的前置通知");
    8     System.out.println("method="+jp.getSignature().getName()+",args数量="+jp.getArgs().length+",target="+jp.getTarget());
    9 }

    3. 后置通知

    @AfterReturning(pointcut = "execution(public void com.hex.second.StudentServiceImpl.addStudent(com.hex.second.Student))",returning="returningValue") 表示后置通知,其中value和poingcut都表示切入点,功能一样。returning表示定义目标函数的返回值。如下所示:

     1 /**
     2      * 功能:后置通知
     3      * 注解形式实现AOP通知时,参数不能随便写,否则和目标函数对应不上,会报错
     4      * @param jp :切入点目标对象
     5      * @param returningValue 返回值
     6      */
     7     @AfterReturning(pointcut = "execution(public void com.hex.second.StudentServiceImpl.addStudent(com.hex.second.Student))",returning="returningValue")
     8     public void MyAfterReturning(JoinPoint jp,Object returningValue){
     9         System.out.println("我是注解方式的后置通知");
    10         System.out.println("返回值是:"+returningValue);
    11     }

    4. 异常通知

    @AfterThrowing(pointcut="execution(public void com.hex.second.StudentServiceImpl.addStudent(com.hex.second.Student))",throwing="e") 表示异常通知,其中trowing表示将抛出异常绑定到参数中。当切入函数抛出异常时将会触发,如下所示:

    1 /**
    2      * 异常通知
    3      */
    4     @AfterThrowing(pointcut="execution(public void com.hex.second.StudentServiceImpl.addStudent(com.hex.second.Student))",throwing="e")
    5     public void MyAfterThrow(JoinPoint jp,Throwable e){
    6         System.out.println("我是注解方式的异常通知");
    7     }

    5. 环绕通知

    @Around(value="execution(public void com.hex.second.StudentServiceImpl.addStudent(com.hex.second.Student))") 环绕通知功能最全面,可以实现其他几种,其中参数使用ProceedingJoinPoint,是JoinPoint的子类。

     1 /**
     2      * 环绕通知
     3      * @param jp 才用的是JoinPoint的子类
     4      */
     5     @Around(value="execution(public void com.hex.second.StudentServiceImpl.addStudent(com.hex.second.Student))")
     6     public void MyAround(ProceedingJoinPoint jp){
     7         Object obj = null;
     8         try {
     9 
    10             // 前置通知
    11             System.out.println("注解环绕实现前置通知。。。");
    12             System.out.println("环绕通知:target="+jp.getTarget()+",method="+jp.getSignature().getName()+",args="+jp.getArgs().length);
    13             // 控制目标方法的执行 obj表示目标方法的返回值,表示执行addStudent(student)方法
    14             //此方法控制目标方法的执行,如果不写此方法,则目标方法不会执行,此方法前的是前置通知,此方法后的是后置通知
    15             obj = jp.proceed();
    16             // 后置通知
    17             System.out.println("注解环绕实现后置通知。。。");
    18         } catch (Throwable e) {
    19             // 异常通知
    20             System.out.println("注解环绕实现异常通知。。。");
    21         }finally{
    22             //最终通知
    23             System.out.println("注解环绕实现最终通知。。。");
    24         }
    25     }

    6. 最终通知

    @After("execution(public void com.hex.second.StudentServiceImpl.addStudent(com.hex.second.Student))") 表示最终通知,不管是否抛出异常,都会得到执行,类似于finally。如下所示:

    1 /**
    2      * 最终通知,@After的参数为目标通知类的表达式
    3      * JoinPoint 用来获取目标函数的参数及对象等信息
    4      */
    5     @After("execution(public void com.hex.second.StudentServiceImpl.addStudent(com.hex.second.Student))")
    6     public void MyAfter(JoinPoint jp){
    7         System.out.println("我是注解方式最终通知");
    8         System.out.println("method="+jp.getSignature().getName()+",args数量="+jp.getArgs().length+",target="+jp.getTarget());
    9     }

    7. 除了上述注解之外,还需要在Sping容器中,配置对注解的支持和AOP的自动扫描。

    如下所示:

     1 <?xml version="1.0" encoding="UTF-8"?>
     2 <beans xmlns="http://www.springframework.org/schema/beans"
     3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     4 xmlns:p="http://www.springframework.org/schema/p"
     5 xmlns:aop="http://www.springframework.org/schema/aop"
     6 xmlns:context="http://www.springframework.org/schema/context"
     7 xsi:schemaLocation="http://www.springframework.org/schema/beans
     8  http://www.springframework.org/schema/beans/spring-beans.xsd
     9  http://www.springframework.org/schema/aop
    10  http://www.springframework.org/schema/aop/spring-aop.xsd
    11  http://www.springframework.org/schema/context
    12  http://www.springframework.org/schema/context/spring-context.xsd">
    13      
    14      <!-- 服务类 -->
    15      <bean id="studentService" class="com.hex.second.StudentServiceImpl">
    16          
    17      </bean>
    18      <!-- 将addStudent和通知进行关联 -->
    19      <!-- 配置对注解方式AOP的支持 -->
    20      <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    21      <!-- 配置对注解的扫描 -->
    22      <context:component-scan base-package="com.hex.second"></context:component-scan>
    23 </beans>

    通过Schema方式实现AOP

    通过Schema方式实现步骤如下:

    1. 定义一个普通的类,分别实现各种功能的通知,参数和注解方式的一致。

    如下所示:

     1 package com.hex.second;
     2 
     3 import org.aspectj.lang.JoinPoint;
     4 import org.aspectj.lang.ProceedingJoinPoint;
     5 
     6 /**
     7  * 通过Schema配置的方式实现通知
     8  * @author Administrator
     9  *
    10  */
    11 public class LogAspectSchema {
    12     /**
    13      * 前置通知,@Before的参数为目标通知类的表达式
    14      * JoinPoint 用来获取目标函数的参数及对象等信息
    15      */
    16     public void MyBefore(JoinPoint jp){
    17         System.out.println("我是Schema方式的前置通知");
    18         System.out.println("method="+jp.getSignature().getName()+",args数量="+jp.getArgs().length+",target="+jp.getTarget());
    19     }
    20     
    21     /**
    22      * 功能:后置通知
    23      * Schema形式实现AOP通知时,参数不能随便写,否则和目标函数对应不上,会报错
    24      * @param jp :切入点目标对象
    25      * @param returningValue 返回值
    26      */
    27     public void MyAfterReturning(JoinPoint jp,Object returningValue){
    28         System.out.println("我是Schema方式的后置通知");
    29         System.out.println("返回值是:"+returningValue);
    30     }
    31     
    32     /**
    33      * 异常通知
    34      */
    35     public void MyAfterThrow(JoinPoint jp ,Throwable ex){
    36         System.out.println("我是Schema方式的异常通知");
    37         System.out.println("ex:"+ex.getMessage());
    38     }
    39     
    40     /**
    41      * 环绕通知
    42      * @param jp 才用的是JoinPoint的子类
    43      */
    44     public void MyAround(ProceedingJoinPoint jp){
    45         Object obj = null;
    46         try {
    47 
    48             // 前置通知
    49             System.out.println("Schema环绕实现前置通知。。。");
    50             System.out.println("Schema环绕通知:target="+jp.getTarget()+",method="+jp.getThis()+",args="+jp.getArgs().length);
    51             // 控制目标方法的执行 obj表示目标方法的返回值,表示执行addStudent(student)方法
    52             //此方法控制目标方法的执行,如果不写此方法,则目标方法不会执行,此方法前的是前置通知,此方法后的是后置通知
    53             obj = jp.proceed();
    54             // 后置通知
    55             System.out.println("Schema环绕实现后置通知。。。");
    56         } catch (Throwable e) {
    57             // 异常通知
    58             System.out.println("Schema环绕实现异常通知。。。");
    59         }finally{
    60             //最终通知
    61             System.out.println("Schema环绕实现最终通知。。。");
    62         }
    63     }
    64     
    65     /**
    66      * 最终通知
    67      * @param jp
    68      */
    69     public void MyAfter(JoinPoint jp){
    70         System.out.println("我是Schema方式的最终通知");
    71     }
    72 }
    View Code

    2. 在Spring容器中配置

    首先将通知类注入到Spring IOC容器中,然后配置<aop:config>将业务类和切面类关联起来。如下所示:

     1 <?xml version="1.0" encoding="UTF-8"?>
     2 <beans xmlns="http://www.springframework.org/schema/beans"
     3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     4 xmlns:p="http://www.springframework.org/schema/p"
     5 xmlns:aop="http://www.springframework.org/schema/aop"
     6 xmlns:context="http://www.springframework.org/schema/context"
     7 xsi:schemaLocation="http://www.springframework.org/schema/beans
     8  http://www.springframework.org/schema/beans/spring-beans.xsd
     9  http://www.springframework.org/schema/aop
    10  http://www.springframework.org/schema/aop/spring-aop.xsd
    11  http://www.springframework.org/schema/context
    12  http://www.springframework.org/schema/context/spring-context.xsd">
    13      
    14      <!-- 服务类 -->
    15      <bean id="studentService" class="com.hex.second.StudentServiceImpl">
    16          
    17      </bean>
    18      <bean id="logSchema" class="com.hex.second.LogAspectSchema"></bean>
    19      <aop:config>
    20          <!-- 配置切入点 -->
    21          <aop:pointcut expression="execution(public void com.hex.second.StudentServiceImpl.deleteStudent(int)) or execution(public void com.hex.second.StudentServiceImpl.addStudent(com.hex.second.Student))" id="pc"/>
    22          <aop:aspect ref="logSchema">
    23              <!-- 通过Schema实现的通知 -->
    24              <aop:before method="MyBefore" pointcut-ref="pc"/>
    25              <aop:after-returning method="MyAfterReturning" pointcut-ref="pc" returning="returningValue"/>
    26              <aop:after-throwing method="MyAfterThrow" pointcut-ref="pc" throwing="ex" />
    27              <aop:around method="MyAround" pointcut-ref="pc"/>
    28              <aop:after method="MyAfter"  pointcut-ref="pc"/>
    29          </aop:aspect>
    30      </aop:config>
    31 </beans>
    View Code

    备注

    假如你不够快乐
    也不要把眉头深锁
    人生本来短暂
    为什么 还要栽培苦涩

    打开尘封的门窗
    让阳光雨露洒遍每个角落
    走向生命的原野
    让风儿熨平前额

    博大可以稀释忧愁
    深色能够覆盖浅色

  • 相关阅读:
    C#获取指定日期为一年中的第几周
    Javascript arguments详解
    select2 插件
    [转]oracle存储过程中update不成功的一个原因
    [转]Oracle存储过程给变量赋值的方法
    [转]cron表达式详解
    [转]ssh中如何实现定时任务(spring对quartz的支持)
    [转]Oracle存储过程总结
    [转]Oracle 树操作(select…start with…connect by…prior)
    [转]oracle在删除表表空间用户时,如何释放磁盘空间
  • 原文地址:https://www.cnblogs.com/hsiang/p/11747309.html
Copyright © 2020-2023  润新知