一、面向切面的编程需求的产生

    1. 代码混乱:越来越多的非业务需求(日志和验证等)加入后,原有的业务方法急剧膨胀。每个方法在处理核心逻辑的同时还必须兼顾其他多个关注点。
    2. 代码分散: 以日志需求为例,只是为了满足这个单一需求,就不得不在多个模块(方法)里多次重复相同的日志代码。如果日志需求发生变化,必须修改所有模块。

  二、实现面向切面的编程

    1. 将需要实现AOP的类注入到Spring容器中,例如:

       package com.neuedu.aop;
      
       import org.springframework.stereotype.Component;
      
       @Component
      public class RawCaculator implements MathCaculator{ @Override
      public int add(int i, int j) {
      int rs=i+j;
      System.out.println(i+"+"+j+"="+rs);
      return rs;
      } @Override
      public int sub(int i, int j) {
      int rs=i-j;
      System.out.println(i+"-"+j+"="+rs);
      return rs;
      } @Override
      public int mul(int i, int j) {
      int rs=i*j;
      System.out.println(i+"*"+j+"="+rs);
      return rs;
      } @Override
      public int div(int i, int j) {
      int rs=i/j;
      System.out.println(i+"/"+j+"="+rs);
      return rs;
      } }

      要实现AOP的计算方法类

    2. 实现切面类
      1. 使用前置通知、后置通知、返回通知、异常通知实现切面类,注入到Spring容器中

         package com.neuedu.aop;
        
         import static org.hamcrest.CoreMatchers.nullValue;
        
         import java.util.Arrays;
        import java.util.List; 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.springframework.core.annotation.Order;
        import org.springframework.stereotype.Component; @Component
        //@Aspect表明当前类是一个切面类
        @Aspect
        //@Order表示切面执行顺序,value值越小优先级越高
        @Order(value=50)
        public class CaculatorAspect {
        @Before(value = "execution(public int com.neuedu.aop.RawCaculator.*(int, int))")
        public void showBeginLog(JoinPoint point){
        //System.out.println("【日志】【前置通知】");
        //获得参数列表:
        Object[] args = point.getArgs();
        List<Object> asList = Arrays.asList(args);
        //获得方法名:
        Signature signature = point.getSignature();
        String name = signature.getName();
        System.out.println("【日志】【前置通知】 目标方法名为:"+name+"参数为:"+asList);
        }
        @AfterThrowing(value = "execution(public int com.neuedu.aop.RawCaculator.*(..))" ,throwing="ex")
        public void showThrowing(JoinPoint point,Exception ex){
        //System.out.println("【日志】【异常通知】");
        System.out.println("【日志】【异常通知】 异常信息:"+ex);
        }
        @After(value = "execution(public int com.neuedu.aop.RawCaculator.*(..))")
        public void showAfter(){
        System.out.println("【日志】【后置通知】");
        }
        @AfterReturning(value = "execution(* * .*(..))",returning="result")
        public void showAfterReturning(JoinPoint point,Object result){
        //System.out.println("【日志】【返回通知】");
        System.out.println("【日志】【返回通知】 目标方法的返回值为"+result);
        }
        }

           使用黄色背景标注的代码是声明的通知,其中包括通知类型切入点表达式,以下就是对通知类型的介绍,切入点表达式在最后

            @Before  前置通知:在方法执行之前执行的通知

            @After 后置通知:后置通知是在连接点完成之后执行的,即连接点返回结果或者抛出异常的时候

            @AfterReturning   返回通知:

          • 无论连接点是正常返回还是抛出异常,后置通知都会执行。如果只想在连接点返回的时候记录日志,应使用返回通知代替后置通知。
          • 在返回通知中访问连接点的返回值:
            • 在返回通知中,只要将returning属性添加到@AfterReturning注解中,就可以访问连接点的返回值。该属性的值即为用来传入返回值的参数名称
            • 必须在通知方法的签名中添加一个同名参数。在运行时Spring AOP会通过这个参数传递返回值
            • 原始的切点表达式需要出现在pointcut属性中 

            @AfterThrowing 异常通知:只在连接点抛出异常时才执行异常通知.:

        • 将throwing属性添加到@AfterThrowing注解中,也可以访问连接点抛出的异常。Throwable是所有错误和异常类的顶级父类,所以在异常通知方法可以捕获到任何错误和异常。

            • 如果只对某种特殊的异常类型感兴趣,可以将参数声明为其他异常的参数类型。然后通知就只在抛出这个类型及其子类的异常时才被执行

           

          2.使用环绕通知实现切面类,注入到Spring容器中

 1   package com.neuedu.aop;

   import java.util.Arrays;
  import java.util.List;   import org.aspectj.lang.ProceedingJoinPoint;
  import org.aspectj.lang.Signature;
  import org.aspectj.lang.annotation.Around;
  import org.aspectj.lang.annotation.Aspect;
  import org.springframework.core.annotation.Order;
  import org.springframework.stereotype.Component;   @Component
  //@Aspect表明当前类是一个切面类
  @Aspect
  @Order(value=40)
    public class secondAspect {
   @Around(value = "execution(* * .*(..))")
   public Object Around(ProceedingJoinPoint point){
   Object result=null;
   Object[] args = point.getArgs();
   List<Object> asList = Arrays.asList(args);
   Signature signature = point.getSignature();
   String name = signature.getName();
   try{
   System.out.println("【事物日志】【前置通知】 目标方法名为:"+name+"参数为:"+asList);
   try {
   result=point.proceed(args);
      } finally{
      System.out.println("【事物日志】【后置通知】");
      }
   System.out.println("【事物日志】【返回通知】 目标方法的返回值为"+result);
   }catch (Throwable e) {
   System.out.println("【事物日志】【异常通知】 异常信息:"+e);
   }
   return result;
   }
38   }

            @Around  环绕通知:

          • 环绕通知是所有通知类型中功能最为强大的,能够全面地控制连接点,甚至可以控制是否执行连接点。
          • 对于环绕通知来说,连接点的参数类型必须是ProceedingJoinPoint。它是 JoinPoint的子接口,允许控制何时执行,是否执行连接点。

          • 在环绕通知中需要明确调用ProceedingJoinPoint的proceed()方法来执行被代理的方法。如果忘记这样做就会导致通知被执行了,但目标方法没有被执行。

          • 注意:环绕通知的方法需要返回目标方法执行之后的结果,即调用 joinPoint.proceed();的返回值,否则会出现空指针异常。

     

       3.实现测试类

package junit.test;

import static org.junit.Assert.*;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext; import com.neuedu.aop.MathCaculator;
import com.neuedu.aop.RawCaculator; public class TestCaculator { @Test
public void test() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
//MathCaculator bean = ioc.getBean(RawCaculator.class); 不可用 (代表类 类型不一样)
//rawCaculator即使使用注解时代表类的id;又是xml配置文件里的id
MathCaculator bean = (MathCaculator) ioc.getBean("rawCaculator");
bean.add(10, 5);
System.out.println();
bean.sub(14, 5);
System.out.println();
bean.mul(8, 7);
System.out.println();
bean.div(10, 0);
} }

测试类

  

  三、关于切入点表达式

    1.作用:

      通过表达式的方式定位一个或多个具体的连接点。

    2.语法细节:

      1)切入点表达式的语法格式:execution([权限修饰符] [返回值类型] [简单类名/全类名] [方法名]([参数列表]))

      2)举例说明:

          

表达式

execution(* com.atguigu.spring.ArithmeticCalculator.*(..))

含义

ArithmeticCalculator接口中声明的所有方法。

第一个“*”代表任意修饰符及任意返回值。

第二个“*”代表任意方法。

“..”匹配任意数量、任意类型的参数。

若目标类、接口与该切面类在同一个包中可以省略包名。

表达式

execution(public * ArithmeticCalculator.*(..))

含义

ArithmeticCalculator接口的所有公有方法

表达式

execution(public double ArithmeticCalculator.*(..))

含义

ArithmeticCalculator接口中返回double类型数值的方法

表达式

execution(public double ArithmeticCalculator.*(double, ..))

含义

第一个参数为double类型的方法。

“..” 匹配任意数量、任意类型的参数。

表达式

execution(public double ArithmeticCalculator.*(double, double))

含义

参数类型为double,double类型的方法

    3.重用切入点:

      1)

      • 在编写AspectJ切面时,可以直接在通知注解中书写切入点表达式。但同一个切点表达式可能会在多个通知中重复出现

      • 在Aspect切面中,可以通过@Pointcut注解将一个切入点声明成简单的方法。切入点的方法体通常是空的,因为将切入点定义与应用程序逻辑混在一起是不合理的。 
      • 切入点方法的访问控制符同时也控制着这个切入点的可见性。如果切入点要在多个切面中共用,最好将它们集中在一个公共的类中。在这种情况下,它们必须被声明为public。在引入这个切入点时,必须将类名也包括在内。如果类没有与这个切面放在同一个包中,还必须包含包名。  

      2)示例代码:

 1   @Component
2   //@Aspect表明当前类是一个切面类
  @Aspect
4   //@Order表示切面执行顺序,value值越小优先级越高
5   @Order(value=50)
6   public class CaculatorAspect {
   //重用切入点
   @Pointcut("execution(* * .*(..))")
   private void LoggingOperation(){
  
   }
   @Before(value = "LoggingOperation()")
   public void showBeginLog(JoinPoint point){
   //获得参数列表:
   Object[] args = point.getArgs();
   List<Object> asList = Arrays.asList(args);
   //获得方法名:
   Signature signature = point.getSignature();
   String name = signature.getName();
   System.out.println("【日志】【前置通知】 目标方法名为:"+name+"参数为:"+asList);
   }
   @AfterThrowing(value = "LoggingOperation()" ,throwing="ex")
   public void showThrowing(JoinPoint point,Exception ex){
   System.out.println("【日志】【异常通知】 异常信息:"+ex);
   }
   @After(value = "LoggingOperation()")
   public void showAfter(){
   System.out.println("【日志】【后置通知】");
29   }
   @AfterReturning(value = "LoggingOperation()",returning="result")
   public void showAfterReturning(JoinPoint point,Object result){
   System.out.println("【日志】【返回通知】 目标方法的返回值为"+result);
   }
   }

 

AOP 面向切面的编程的更多相关文章

  1. Guice 学习(八)AOP (面向切面的编程)

    Guice的AOP还是非常弱的.眼下只支持方法级别上的,另外灵活性也不是非常高. 看例如以下演示样例: Guice支持AOP的条件是: 类必须是public或者package (default) 类不 ...

  2. AOP 面向切面编程, Attribute在项目中的应用

    一.AOP(面向切面编程)简介 在我们平时的开发中,我们一般都是面对对象编程,面向对象的特点是继承.多态和封装,我们的业务逻辑代码主要是写在这一个个的类中,但我们在实现业务的同时,难免也到多个重复的操 ...

  3. AOP面向切面编程的四种实现

     一.AOP(面向切面编程)的四种实现分别为最原始的经典AOP.代理工厂bean(ProxyFacteryBean)和默认自动代理DefaultAdvisorAutoProxyCreator以及Bea ...

  4. Javascript aop(面向切面编程)之around(环绕)

    Aop又叫面向切面编程,其中“通知”是切面的具体实现,分为before(前置通知).after(后置通知).around(环绕通知),用过spring的同学肯定对它非常熟悉,而在js中,AOP是一个被 ...

  5. Method Swizzling和AOP(面向切面编程)实践

    Method Swizzling和AOP(面向切面编程)实践 参考: http://www.cocoachina.com/ios/20150120/10959.html 上一篇介绍了 Objectiv ...

  6. [转] AOP面向切面编程

    AOP面向切面编程 AOP(Aspect-Oriented Programming,面向切面的编程),它是可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术. ...

  7. AOP 面向切面编程、拦截器

    AOP(Aspect-Oriented Programming,面向切面的编程),它是可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术.它是一种新的方法论, ...

  8. C# AOP 面向切面编程之 调用拦截

    有时候我们需要在代码中对方法调用进行拦截,并修改参数和返回值,这种操作叫做AOP(面向切面编程) 不过需要注意的是,AOP的效率很慢,在需要高效率场合慎用. 以下是C#的AOP方法: 首先建立一个控制 ...

  9. 【原创】Android AOP面向切面编程AspectJ

    一.背景: 在项目开发中,对 App 客户端重构后,发现用于统计用户行为的友盟统计代码和用户行为日志记录代码分散在各业务模块中,比如在视频模块,要想实现对用户对监控点的实时预览和远程回放行为进行统计, ...

随机推荐

  1. 关于locals()、globals()以及作用域的一些感悟

    示例代码 i=6def foo(): print locals() print globals() i=66 print locals() print globals()class demo: i=6 ...

  2. String to Integer (atoi) - 字符串转为整形,atoi 函数(Java )

    String to Integer (atoi) Implement atoi to convert a string to an integer. [函数说明]atoi() 函数会扫描 str 字符 ...

  3. C#获取当前时间详解

    [转]C#获取当前日期时间(转)  http://blog.163.com/ljq086@126/blog/static/549639712010112921658843/ 我们可以通过使用DataT ...

  4. 第2章 rsync(二):inotify+rsync详细说明和sersync

    本文目录: inotify+rsync 1.1 安装inotify-tools 1.2 inotifywait命令以及事件分析 1.3 inotify应该装在哪里 1.4 inotify+rsync示 ...

  5. maven-编译速度优化

    故障描述: 公司搭建了一个新jenkins持续集成环境,jenkins构建job时间越来越长. 原因分析: 系统CPU限制:判断依据,构建中查看日志 tail -f /var/log/messages ...

  6. tensorflow softsign函数应用

    1.softsign函数 图像 2.tensorflow softsign应用 import tensorflow as tf input=tf.constant([0,-1,2,-30,30],dt ...

  7. ural 1989 subplindromes

    https://vjudge.net/problem/URAL-1989 题意: 先给出一个字符串,对于这个字符串,有两种操作,一种是询问从下标x到y的串是不是回文串,另一种是将下标为pos的字符改为 ...

  8. 实例化bean

    从bean.xml中<bean>标签内容可以看出bean其实是一个管理对象的东西,我们只需要修改xml配置文件,就可以改变对象之间的依赖关系,不需要去修改任何源代码.我觉得学习好sprin ...

  9. 服务器运行环境部署(PHP)

    1.使用的是腾讯云服务器,系统为centOS: 2.LAMP环境配置查看:https://cloud.tencent.com/document/product/213/8043: 3.mysql-se ...

  10. 9. Palindrome Number 回文 my second leetcode 20170807

    Determine whether an integer is a palindrome. Do this without extra space. click to show spoilers. S ...