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

    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. 分享网上搜到的Oracle中对判定条件where 1=1的正解

    今天在网上找到了Oracle中对判定条件where 1=1的正解,粘贴出来和大家分享下 1=1 是永恒成立的,意思无条件的,也就是说在SQL语句里有没有这个1=1都可以. 这个1=1常用于应用程序根据 ...

  2. mybaits错误解决:There is no getter for property named 'parentId ' in class 'java.lang.String'

    在使用mybaitis传参数的时候,如果仅传入一个类型为String的参数,那么在 xml文件中应该使用_parameter来代替参数名. 比如mapper中如下方法,只有一个String值 publ ...

  3. 如何得到iPhone手机的UUID

    背景 测试ad-hoc打包方式打出来的包,必须在证书里面配置手机的uuid才能安装. 这样就需要获取iPhone手机的uuid来进行证书配置 一般来说iPhone手机可以安装通过Apple发布的软件. ...

  4. jquery html5 file 上传图片显示图片

    jquery js 的代码:不同浏览器下的路径 //建立一個可存取到該file的url function getObjectURL(file) {     var url = null ;     i ...

  5. Everything 使用记录

    背景:在windows环境下,使用系统自带的搜索框经常出现搜索不到指定文件的问题,在网上无意发现了这款软件,真的很好用! 1 文件列表 建立文件列表主要是为了以后可以在指定的目录内查找自己想要的文件, ...

  6. Java 9 揭秘(17. Reactive Streams)

    Tips 做一个终身学习的人. 在本章中,主要介绍以下内容: 什么是流(stream) 响应式流(Reactive Streams)的倡议是什么,以及规范和Java API 响应式流在JDK 中的AP ...

  7. RequireJs加载Codemirror,配合AngularJS的坑

    requireJS加载codemirror,并且配合angularJs一起使用的时候,高亮显示代码编辑器.要注意以下几点: 1:普通Js加载CodeMirror  代码如下: <!DOCTYPE ...

  8. 连续子序列最大和的O(NlogN)算法

    对于一个数组,例如:int[] a = {4,-3,5,-2,-1,2,6,-2}找出一个连续子序列,对于任意的i和j,使得a[i]+a[i+1]+a[i+2]+.......+a[j]他的和是所有子 ...

  9. python 中 list 的各项操作

    最近在学习 python 语言.大致学习了 python 的基础语法.觉得 python 在数据处理中的地位和它的 list 操作密不可分. 特学习了相关的基础操作并在这里做下笔记. ''' Pyth ...

  10. 如何开发自己的搜索帝国之Elasticsearch

    搜索引擎是什么? 搜索引擎是指根据一定的策略.运用特定的计算机程序从互联网上搜集信息,在对信息进行组织和处理后,为用户提供检索服务,将用户检索相关的信息展示给用户的系统.搜索引擎包括全文索引.目录索引 ...