5.1 Spring5源码--Spring AOP源码分析一

目标:

1.什么是AOP, 什么是AspectJ,spring

2. 什么是Spring AOP数据库

3. Spring AOP注解版实现原理编程

4. Spring AOP切面原理解析安全


 一. 认识AOP

1.1 什么是AOP

aop是面向切面编程,相比传统oop,aop可以在方法的前置,中置,后置中插入逻辑代码,对于项目中大量逻辑重复的代码,使用aop能很好的收口逻辑,将逻辑独立于业务代码以外,一处编写,多处使用。ide

AOP是Object Oriented Programming(OOP)的补充.函数

OOP可以很好地解决对象的数据和封装的问题,却不能很好的解决Aspect("方面")分离的问题。下面举例具体说明。oop

好比,咱们有一个Bank(银行)类。Bank有两个方法,save(存钱)和withdraw(取钱)。gradle

类和方法的定义以下:ui

package com.lxl.www.aop;

public class Bank {

  /** * 存钱 */
  public Float save(Account account, float money) {
    // 增长account帐户的钱数,返回帐户里当前的钱数
    return null;
  }

  /** * 取钱 */
  public Float withdraw(Account account, float money) {
    // 减小account帐户的钱数,返回取出的钱数
    return null;
  }
};

 

这两个方法涉及到用户的帐户资金等重要信息,必需要很是当心,因此编写完上面的商业逻辑以后,项目负责人又提出了新的要求--给Bank类的每一个重要方法加上安全认证特性。spa

因而, 咱们在两个方法上增长安全代码

改后的类和方法以下:

public class Bank {

  /**
   * 存钱 */
  public Float save(Account account, float money) {
    // 验证account是否为合法用户 // 增长account帐户的钱数,返回帐户里当前的钱数
    return null;
  }

  /**
   * 取钱 */
  public Float withdraw(Account account, float money) {
    // 验证account是否为合法用户 // 减小account帐户的钱数,返回取出的钱数
    return null;
  }
};

 

这两个方法都须要操做数据库,为了保持数据完整性,项目负责人又提出了新的要求--给Bank类的每一个操做数据库的方法加上事务控制。

因而,咱们不得不分别在上面的两个方法中加入安全认证的代码。

类和方法的定义以下:

package com.lxl.www.aop;

public class Bank {

  /**
   * 存钱 */
  public Float save(Account account, float money) {
    // 验证account是否为合法用户 // begin Transaction // 增长account帐户的钱数,返回帐户里当前的钱数 // end Transaction
    return null;
  }

  /**
   * 取钱 */
  public Float withdraw(Account account, float money) {
    // 验证account是否为合法用户 // begin Transaction // 减小account帐户的钱数,返回取出的钱数 // end Transaction 
    return null;
  }
};

 

咱们看到,这些与商业逻辑无关的重复代码遍及在整个程序中。实际的工程项目中涉及到的类和函数,远远不止两个。如何解决这种问题?

AOP就是为了解决这种问题而出现的。在不修改代码的状况下达到加强的效果

1.2 AOP的相关概念

  • 切面(Aspect): 封装通用业务逻辑的组件,即咱们想要插入的代码内容. 在spring AOP中, 切面能够使用通用类基于模式的方式, 或者在普通类中标注@Aspect注解来实现
  • 链接点(Join point): 链接点是在应用执行过程当中可以插入切面的点。简单理解, 能够理解为须要加强的方法.
  • 通知(Advice): 用于指定具体产生做用的位置,是方法以前或以后等等
    • 前置通知(before) - 在目标方法被调用以前调用通知功能
    • 后置通知(after) - 在目标方法完成以后调用通知(不论程序是否出现异常),此时不会关心方法的输出是什么
    • 返回通知(after-returning) - 在目标方法成功执行以后调用通知
    • 异常通知(after-throwing) - 在目标方法抛出异常后调用通知
    • 环绕通知(around) - 通知包裹了被通知的方法,在被通知的方法调用以前和调用以后执行自定义的行为
  • 目标对象(target): 目标对象是指要被加强的对象, 即包含主业务逻辑的类对象
  • 切点(PointCut): 指定哪些Bean组件的哪些方法使用切面组件. 例如:当执行某个特定名称的方法时.咱们定义一个切点(execution com.lxl.www.aop.*.*(..)) . 切点表达式如何和链接点匹配是AOP的核心. spring默认使用AspectJ切点语义.
  • 织入(Weaving): 将通知切入链接点过程叫作织入
  • 引入(Introductions): 能够将其它接口或者实现动态引入到targetClass中

对照上图, 来对应每个区域,看看其具体含义

  

那么在Spring中使用AOP就意味着你须要哪些东西呢?咱们来举个例子, 就实现上面银行的例子.
  • 首先有一个bank银行类
    package com.lxl.www.aop.bank;
    
    public interface Bank {
    
      /**
       * 存钱
       */
      Float save(Account account, float money) ;
    
      /**
       * 取钱
       */
      Float withdraw(Account account, float money);
    };

     

     

  • 有一个银行类的实现方法. 这里面save, withdraw就是链接点. 最终会将各类通知插入到链接点中
    package com.lxl.www.aop.bank;
    
    import org.springframework.stereotype.Service;
    
    /**
     * 工商银行
     *
     *
     * DATE 2020/12/6.
     *
     * @author lxl.
     */
    @Service
    public class IcbcBank implements Bank{
        @Override
        public Float save(Account account, float money) {
    // 主业务逻辑: 增长account帐户的钱数,返回帐户里当前的钱数 System.out.println(account.getName() + "帐户存入" + money); return null; } @Override public Float withdraw(Account account, float money) {
    // 主业务逻辑: 减小account帐户的钱数,返回取出的钱数 System.out.println(account.getName() + "帐户取出" + money); return null; } }

     

     

  • 接下来, 要有一个切面, 切面是一个类. 切面类里面定义了切点, 通知, 引用
    package com.lxl.www.aop.bank;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.After;
    import org.aspectj.lang.annotation.AfterReturning;
    import org.aspectj.lang.annotation.AfterThrowing;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.DeclareParents;
    import org.aspectj.lang.annotation.Pointcut;
    import org.springframework.core.annotation.Order;
    import org.springframework.stereotype.Component;
    
    /**
     * 切面
     */
    @Aspect // 标记这是一个切面
    @Order
    @Component // 将其放到ioc容器管理
    public class BankLogAspect {
    
      /**
       * 引入
       *
       * 这段话能够理解为, 为com.lxl.www.aop.bank.IcbcBank 引入了一个接口 EnhanceFunctionOfBank,
       * 同时, 引入了默认的实现类 IcbcEnhanceFunctionOfBank
       */
      @DeclareParents(value = "com.lxl.www.aop.bank.IcbcBank",    // 引入的目标类. 也就是须要引入动态实现的类
              defaultImpl = IcbcEnhanceFunctionOfBank.class) // 引入的接口的默认实现
      public static EnhanceFunctionOfBank enhanceFunctionOfBank; // 引入的接口
    
    
      /**
       * 定义一个切点 */
      @Pointcut("execution(* com.lxl.www.aop.bank.IcbcBank.*(..))")
      public void pointCut() {}
    
      /**
       * 定义一个前置通知
       * @param joinPoint
       */
      @Before(value = "pointCut()")
      public void beforeAdvice(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("执行目标方法"+methodName+"的前置通知");
      }
    
      /**
       * 定义了一个后置通知 */
      @After(value = "pointCut()")
      public void afterAdvice(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("执行目标方法"+methodName+"的后置通知");
      }
    
      /**
       * 定义了一个返回通知 */
      @AfterReturning(value = "pointCut()", returning = "result")
      public void returningAdvice(JoinPoint joinPoint, Object result) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("执行目标方法"+methodName+"的返回通知");
      }
    
      /**
       * 定义了一个异常通知 */
      @AfterThrowing(value = "pointCut()")
      public void throwingAdvice(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("执行目标方法"+methodName+"的异常通知");
      }
    
    }

    那么这里的目标对象是谁呢? 就是咱们的IcbcBank类. 这里须要注意的是引入: 引入的概念是将一个接口动态的让另外一个类实现了. 这样实现了接口的类, 就能够动态的拥有接口实现类的功能.

  • 银行的额外功能. 也就是银行除了能够存钱, 取钱. 还有一个额外的功能. 好比理财. 不是每一个银行都有的. 
    package com.lxl.www.aop.bank;
    
    /**
     * 加强的功能 */
    public interface EnhanceFunctionOfBank {
    
        void Financialanagement(Account account);
    }

     

  • 具体银行额外功能的实现类
    package com.lxl.www.aop.bank;
    
    import org.springframework.stereotype.Service;
    
    /**
     * Description
     */
    @Service
    public class IcbcEnhanceFunctionOfBank implements EnhanceFunctionOfBank {
        /**
         * 理财功能
         * @param account
         */
        @Override
        public void Financialanagement(Account account) {
            System.out.println(account.getName() +"的帐户 增长 理财功能");
        }
    
    }

    这个功能咱们能够经过引入,动态增长到IcbcBank类中, 本来IcbcBank只有存钱和取钱的功能. 这样, 就能够增长理财功能了. 这就是引入.

  • 总体配置类
    package com.lxl.www.aop.bank;
    
    import org.springframework.beans.factory.annotation.Configurable;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.EnableAspectJAutoProxy;
    
    @Configurable
    // 使用注解的方式引入AOP @EnableAspectJAutoProxy
    @ComponentScan("com.lxl.www.aop.bank")
    public class BankMainConfig {
    
    }

     

    使用aop,须要引入AOP, 这里使用的注解的方式引入的.

  • main入口方法
    package com.lxl.www.aop.bank;
    
    import com.lxl.www.aop.Calculate;
    import com.lxl.www.aop.MainConfig;
    import com.lxl.www.aop.ProgramCalculate;
    
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    
    public class BankMainClass {
      public static void main(String[] args) {
    
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(BankMainConfig.class);
    
        Account account = new Account("张三");
    
        Bank bank = (Bank) ctx.getBean("icbcBank");
        bank.save(account, 100);
    
        System.out.println();
        EnhanceFunctionOfBank enhanceFunctionOfBank = (EnhanceFunctionOfBank) ctx.getBean("icbcBank");
        enhanceFunctionOfBank.Financialanagement(account);
      }
    }

    如上, 运行结果:

         

  • 须要注意的地方: 是.gradle配置文件. 一般, 咱们在引入AspectJ的jar包的时候, 会引入到父类项目的build.gradle中. 以下所示

     

    最后咱们还须要引入到指定的项目中 

       

 

以上就是对整个AOP的理解. 接下来, 分析AOP的源码.

详见第二篇文章

 

 

 

 

 

 

 

 

 

 

 

as

相关文章
相关标签/搜索