zoukankan      html  css  js  c++  java
  • spring aop原理和实现

    一、aop是什么

    1.AOP面向方面编程基于IoC,是对OOP的有益补充;

    2.AOP利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了 多个类的公共行为封装到一个可重用模块,并将其名为“Aspect”,即方面。所谓“方面”,简单地说,就是将那些与业务无关,却为业务模块所共同调用的 逻辑或责任封装起来,比如日志记录,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。

    3.AOP代表的是一个横向的关 系,将“对象”比作一个空心的圆柱体,其中封装的是对象的属性和行为;则面向方面编程的方法,就是将这个圆柱体以切面形式剖开,选择性的提供业务逻辑。而 剖开的切面,也就是所谓的“方面”了。然后它又以巧夺天功的妙手将这些剖开的切面复原,不留痕迹,但完成了效果。

    4.实现AOP的技术,主要分为两大类:一是采用动态代理技术,利用截取消息的方式,对该消息进行装饰,以取代原有对象行为的执行;二是采用静态织入的方式,引入特定的语法创建“方面”,从而使得编译器可以在编译期间织入有关“方面”的代码。

    二、aop的原理

    1.采用动态代理,对被代理对象和特定处理进行修饰和封装,得到代理对象,从使得被代理对象中不需切入任何的代码

    2.采用静态织入,如AspectJ,使用其特定的语法创建切面,在编译期间将切面织入代码中。

    三、aop的基本概念

    切面(ASPECT):横切关注点被模块化的特殊对象。即,它是一个类。

    通知(Advice):切面必须要完成的工作。即,它是类中的一个方法。

    目标(Target):被通知对象。

    代理(Proxy):向目标对象应用通知之后创建的对象。

    切入点(PointCut):切面通知执行的“地点”的定义。

    连接点(JointPoint):与切入点匹配的执行点。

    四、aspectJ的织入方式及其原理概要

    ApectJ采用的就是静态织入的方式。ApectJ主要采用的是编译期织入,在这个期间使用AspectJ的acj编译器(类似javac)把aspect类编译成class字节码后,在java目标类编译时织入,即先编译aspect类再编译目标类。

    五、spring aop的实现

    ApplicationContext.xml 加入

    <aop:aspectj-autoproxy/>

    创建切面处理类

    @Aspect
    @Component 
    public class AspectHandler { 
        
      @Pointcut("execution(* com.marving.service.BaseServ+.*(..))") 
      private void doMethod() { 
      } 
      
        /** 
       * This is the method which I would like to execute before a selected method 
       * execution. 
       */
      @Before("doMethod()") 
      public void beforeAdvice() { 
        System.out.println("before method invoked."); 
      } 
      
      /** 
       * This is the method which I would like to execute after a selected method 
       * execution. 
       */
      @After("doMethod()") 
      public void afterAdvice() { 
        System.out.println("after method invoked."); 
      } 
      
      // 配置controller环绕通知,使用在方法aspect()上注册的切入点 
      @Around("doMethod()") 
      public Object around(ProceedingJoinPoint pjp) throws Throwable{ 
        Object result = null; 
        String methodName = pjp.getSignature().getName(); 
        try { 
          System.out.println("The method [" + methodName + "] begins with " + Arrays.asList(pjp.getArgs())); 
          result = pjp.proceed(); 
        } catch (Throwable e) { 
          System.out.println("The method [" + methodName + "] occurs expection : " + e); 
          throw new RuntimeException(e); 
        } 
        System.out.println("The method [" + methodName + "] ends"); 
        return result; 
      } 
    } 

    使用@Pointcut注解进行定义,应用到通知函数afterDemo()时直接传递切点表达式的函数名称myPointcut()即可,比较简单,下面接着介绍切点指示符。

    1.切入点指示符

    类型签名表达式

    within(<type name>)
    //匹配com.mobanker.dao包及其子包中所有类中的所有方法
    @Pointcut("within(com.mobanker.dao..*)")
    // 匹配UserDaoImpl类中所有方法
    @Pointcut("within(com.mobanker.dao.UserDaoImpl)")
    // 匹配UserDaoImpl类及其子类中所有方法
    @Pointcut("within(com.mobanker.dao.UserDaoImpl+)")
    // 匹配所有实现UserDao接口的类的所有方法
    @Pointcut("within(com.mobanker.dao.UserDao+)")

    方法签名表达式

    //scope :方法作用域,如public,private,protect
    // returnt-type:方法返回值类型
    // fully-qualified-class-name:方法所在类的完全限定名称
    // parameters 方法参数
    execution(<scope><return-type><fully-qualified-class-name>.*(parameters))
    //匹配UserDaoImpl类中的所有方法
    @Pointcut("execution(* com.mobanker.dao.UserDaoImpl.*(..))")
    //匹配UserDaoImpl类中的所有公共的方法
    @Pointcut("execution(public * com.mobanker.dao.UserDaoImpl.*(..))")
    // 匹配UserDaoImpl类中的所有公共方法并且返回值为int类型
    @Pointcut("execution(public int com.mobanker.dao.UserDaoImpl.*(..))")
    // 匹配UserDaoImpl类中第一个参数为int类型的所有公共的方法
    @Pointcut("execution(public * com.mobanker.dao.UserDaoImpl.*(int , ..))")

    其它指示符

    bean:Spring AOP扩展的,AspectJ没有对于指示符,用于匹配特定名称的Bean对象的执行方法;

    //匹配名称中带有后缀Service的Bean。
    @Pointcut("bean(*Service)")
    private void myPointcut1(){}

    this :用于匹配当前AOP代理对象类型的执行方法;请注意是AOP代理对象的类型匹配,这样就可能包括引入接口也类型匹配

    //匹配了任意实现了UserDao接口的代理对象的方法进行过滤
    @Pointcut("this(com.mobanker.spring.springAop.dao.UserDao)")
    private void myPointcut2(){}

    target :用于匹配当前目标对象类型的执行方法;

    //匹配了任意实现了UserDao接口的目标对象的方法进行过滤
    @Pointcut("target(com.mobanker.spring.springAop.dao.UserDao)")
    private void myPointcut3(){}

    @within:用于匹配所以持有指定注解类型内的方法;请注意与within是有区别的, within是用于匹配指定类型内的方法执行;

    //匹配使用了MarkerAnnotation注解的类(注意是类)
    @Pointcut("@within(com.mobanker.spring.annotation.MarkerAnnotation)")
    private void myPointcut4(){}

    @annotation(com.mobanker.spring.MarkerMethodAnnotation) : 根据所应用的注解进行方法过滤

    //匹配使用了MarkerAnnotation注解的方法(注意是方法)
    @Pointcut("@annotation(com.mobanker.spring.annotation.MarkerAnnotation)")
    private void myPointcut5(){}

    2.5种通知函数

    前置通知@Before

      前置通知通过@Before注解进行标注,并可直接传入切点表达式的值,该通知在目标函数执行前执行,注意JoinPoint,是Spring提供的静态变量,通过joinPoint 参数,可以获取目标对象的信息,如类名称,方法参数,方法名称等,,该参数是可选的。

    /**
     * 前置通知
     * @param joinPoint 该参数可以获取目标对象的信息,如类名称,方法参数,方法名称等
     */
    @Before("execution(* com.mobanker.spring.springAop.dao.UserDao.addUser(..))")
    public void before(JoinPoint joinPoint){
        System.out.println("我是前置通知");
    }

    后置通知@AfterReturning
      通过@AfterReturning注解进行标注,该函数在目标函数执行完成后执行,并可以获取到目标函数最终的返回值returnVal,当目标函数没有返回值时,returnVal将返回null,必须通过returning = “returnVal”注明参数的名称而且必须与通知函数的参数名称相同。请注意,在任何通知中这些参数都是可选的,需要使用时直接填写即可,不需要使用时,可以完成不用声明出来。如下

    /**
    * 后置通知
    * returnVal,切点方法执行后的返回值
    */
    @AfterReturning(value="execution(* com.mobanker.spring.springAop.dao.UserDao.*User(..))",returning = "returnVal")
    public void AfterReturning(JoinPoint joinPoint,Object returnVal){
       System.out.println("我是后置通知...returnVal+"+returnVal);
    }

    异常通知 @AfterThrowing

      该通知只有在异常时才会被触发,并由throwing来声明一个接收异常信息的变量,同样异常通知也用于Joinpoint参数,需要时加上即可,如下:

    /**
    * 抛出通知
    * @param e 抛出异常的信息
    */
    @AfterThrowing(value="execution(* com.mobanker.spring.springAop.dao.UserDao.addUser(..))",throwing = "e")
    public void afterThrowable(Throwable e){
      System.out.println("出现异常:msg="+e.getMessage());
    }

    最终通知 @After

      该通知有点类似于finally代码块,只要应用了无论什么情况下都会执行。

    /**
     * 无论什么情况下都会执行的方法
     * joinPoint 参数
     */
    @After("execution(* com.mobanker.spring.springAop.dao.UserDao.*User(..))")
    public void after(JoinPoint joinPoint) {
        System.out.println("最终通知....");
    }

    环绕通知@Around

      环绕通知既可以在目标方法前执行也可在目标方法之后执行,更重要的是环绕通知可以控制目标方法是否指向执行,但即使如此,我们应该尽量以最简单的方式满足需求,在仅需在目标方法前执行时,应该采用前置通知而非环绕通知。案例代码如下第一个参数必须是ProceedingJoinPoint,通过该对象的proceed()方法来执行目标函数,proceed()的返回值就是环绕通知的返回值。同样的,ProceedingJoinPoint对象也是可以获取目标对象的信息,如类名称,方法参数,方法名称等等。

    @Around("execution(* com.mobanker.spring.springAop.dao.UserDao.*User(..))")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("我是环绕通知前....");
        //执行目标函数
        Object obj= (Object) joinPoint.proceed();
        System.out.println("我是环绕通知后....");
        return obj;
    }

     图片和内容:https://blog.csdn.net/javazejian/article/details/56267036

     

     

  • 相关阅读:
    Row size too large. The maximum row size for the used table type 解决
    pandas Series介绍
    Scala核心编程_第08章 面向对象编程(中高级部分)
    mysql增删改字段,重命名替换字段
    python报错ValueError: cannot reindex from a duplicate axis
    pandas DataFrame 数据筛选
    Scala核心编程_第07章 面向对象编程(中级部分)
    Scala核心编程_第06章 面向对象编程(基础部分)
    《The Rise and Fall of Scala》scala兴衰读后感
    信贷业务(Ali)
  • 原文地址:https://www.cnblogs.com/JavaZhangXu/p/10109642.html
Copyright © 2011-2022 走看看