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

    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. informatica 学习总结

    问:什么是BI? 答:BI是商务智能,它包含的应用系统和技术较宽泛,通过收集,存储,分析和提供对数据的访问,来帮助企业用户做出更好的商务决策. BI应用包括决策支持,查询和报表,联机分析处理OLAP, ...

  2. jquery-ajax实现文件上传异常处理web.multipart.MultipartException

    异常如下: org.springframework.web.multipart.MultipartException: The current request is not a multipart r ...

  3. HTML5的应用缓存

    HTML5:提供一种应用缓存机制,使得基于web的应用程序可以离线运行.开发者可以使用  Application Cache (AppCache)  接口设定浏览器缓存的数据并使得数据离线有效. 在处 ...

  4. ES6函数扩展

    前面的话 函数是所有编程语言的重要组成部分,在ES6出现前,JS的函数语法一直没有太大的变化,从而遗留了很多问题和的做法,导致实现一些基本的功能经常要编写很多代码.ES6大力度地更新了函数特性,在ES ...

  5. NLP —— 图模型(一)隐马尔可夫模型(Hidden Markov model,HMM)

    本文简单整理了以下内容: (一)贝叶斯网(Bayesian networks,有向图模型)简单回顾 (二)隐马尔可夫模型(Hidden Markov model,HMM) 写着写着还是写成了很规整的样 ...

  6. useradd新建用户和权限分配

    场景:在搭建Ftp服务器时候,需要新建ftp用户,其实新建的ftp用户和Linux中root新建的用户一样,只是需要了解新建用户时候的相关规则. 1 解决新建用户缺少配置文件 1.1 新建用户 指定目 ...

  7. (转)mq经验总结-转

    场景:学习mq相关的知识,发现这是一篇总结性很强的文章,转过来学习学习! 1 mq经验总结 首先了解什么是mq?mq的作用是什么? mq是通讯中间件.他的作用是省去开发人员开发通讯工具的时间,节省开发 ...

  8. (转)CentOS6.5下Redis安装与配置

    场景:项目开发中需要用到redis,之前自己对于缓存这块一直不是很理解,所以一直有从头做起的想法. 本文详细介绍redis单机单实例安装与配置,服务及开机自启动.如有不对的地方,欢迎大家拍砖o(∩_∩ ...

  9. AtCoder Beginner Contest 068

    A - ABCxxx 题意: 给出n,输出“ABCn”就可以了,纯水题. B - Break Number 题意: 给出n,找出从1到n的闭区间内能够被2整除最多次的数. 思路: 直接模拟. 代码: ...

  10. Redis-误操作尝试恢复

    如果不小心使用了flushall把全部数据清楚怎么办 127.0.0.1:6379> set site www.google.com OK 127.0.0.1:6379> set addr ...