zoukankan      html  css  js  c++  java
  • 02-Spring(2)

    1. 动态代理

    1.1 代理模式的原理

    使用一个代理将原本对象包装起来,然后用该代理对象”取代”原始对象。任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原始对象上。

    1.2 动态代理的方式

    • 基于接口实现动态代理: JDK 动态代理(代理对象和和被代理对象实现相同的接口)
    • 基于继承实现动态代理: Cglib、Javassist 动态代理

    2. AOP

    2.1 概述

    AOP(Aspect-Oriented Programming) 是一种新的方法论,是对传统 OOP(Object-Oriented Programming) 的补充。

    面向对象 → 纵向继承机制
    面向切面 → 横向抽取机制

    AOP 编程操作的主要对象是 [切面 (存放公共功能的类)],而切面用于模块化 [横切关注点 (公共功能)]

    在应用 AOP 编程时,仍然需要定义公共功能,但可以明确地定义这个功能应用在哪里,以什么方式应用,并且不必修改受影响的类。这样一来 [横切关注点] 就被模块化到特殊的类里 —— 这样的类我们通常称之为 "切面"。

    AOP 的好处:① 每个事物逻辑位于一个位置,代码不分散,便于维护和升级;② 业务模块更简洁,只包含核心业务代码

    AOP 图解:

    2.2 AOP 术语

    1. 横切关注点:从每个方法中抽取出来的同一类非核心业务
    2. 切面 (Aspect):封装 [横切关注点] 信息的类,每个关注点体现为一个 [通知] 方法
    3. 通知 (Advice):[切面] 必须要完成的各个具体工作;[通知] 封装了 [横切关注点]
    4. 目标 (Target):被通知的对象
    5. 代理 (Proxy):向目标对象应用 [通知] 之后创建的代理对象
    6. 连接点 (Joinpoint):[横切关注点] 在程序代码中的具体体现,对应程序执行的某个特定位置。例如:类某个方法调用前、调用后、方法捕获到异常后等。简单来讲,[横切关注点] 抽取出来的位置就叫 [连接点]。
    7. 切入点(Pointcut):定位 [连接点] 的条件,是一个表达式。每个类的方法中都包含多个 [连接点],所以 [连接点] 是类中客观存在的事物。如果把 [连接点] 看作数据库中的记录,那么 [切入点表达式] 就是查询条件。AOP 可以通过 [切入点表达式] 定位到 [切入点(特定的连接点)]。[切入点表达式] 通过 org.springframework.aop.Pointcut<I> 进行描述,它使用类和方法作为 [连接点] 的查询条件。
    • [横切关注点] 和 [通知] 这俩是一个意思,只是代码所处位置不同而产生的不同的叫法:
      • 横切关注点:从业务方法中抽取出来的同一类非核心业务
      • 通知:把横切关注点封装到一个方法放在切面类中时,称它为“通知”。
    • [切入点] 和 [切入点表达式]
      • [切入点] 侧重于方法中的哪个位置
      • [切入点表达式] 侧重于是哪个方法

    2.3. AspectJ

    AspectJ 是 Java 社区里最完整最流行的 AOP 框架。在 Spring 2.0 以上版本中,可以使用基于 AspectJ 注解或基于 XML 配置的 AOP。AspectJ 是 AOP 思想的具体实现。

    在 Spring 中启用 AspectJ 注解支持:

    1. 导入 jar 包
    2. 引入 aop 名称空间
    3. 配置:当 Spring IOC 容器侦测到 bean 配置文件中的 <aop:aspectj-autoproxy/> 元素时,会自动为与 AspectJ 切面匹配的 bean 创建代理

    用 AspectJ 注解声明切面:

    • 要在 Spring 中声明 AspectJ 切面,只需要在 IOC 容器中将切面声明为 bean 实例
    • 当在 Spring IOC 容器中初始化 AspectJ 切面之后,Spring IOC 容器就会为那些与 AspectJ 切面相匹配的 bean 创建代理
    • 在 AspectJ 注解中,切面只是一个带有 @Aspect 注解的 Java 类,它往往要包含很多 [通知]
    • 通知是标注有某种注解的简单的 Java 方法,AspectJ 支持 5 种类型的通知注解(其必设属性 value 的值为切入点表达式)
      • @Before:前置通知,在方法执行之前执行
      • @After:后置通知,在方法执行之后执行
      • @AfterRunning:返回通知,在方法返回结果之后执行
      • @AfterThrowing:异常通知,在方法抛出异常之后执行
      • @Around:环绕通知,围绕着方法执行

    3. AOP 细节

    3.1 切入点表达式

    [作用] 通过表达式的方式定位一个或多个具体的连接点。
    [语法格式] execution([权限修饰符] [返回值类型] [简单类名/全类名] 方法名)

    举例说明:

    切入点表达式应用到实际的切面类中:

    3.2 JoinPoint

    切入点表达式通常都会是从宏观上定位一组方法,和具体某个通知的注解结合起来就能够确定对应的连接点。那么就一个具体的连接点而言,我们可能会关心这个连接点的一些具体信息,例如:当前连接点所在方法的方法名、当前传入的参数值等等。这些信息都封装在 JoinPoint<I> 的实例对象中。

    @Before("execution(* cn.edu.nuist.spring.aop.*.*(..))")
    // JoinPoint 参数的值是由框架赋予,必须是第一个位置的参数
    public void beforeMethod(JoinPoint joinPoint) {
        // 获取方法的参数
        Object[] args = joinPoint.getArgs();
        // 方法签名
        Signature signature = joinPoint.getSignature();
        String methodName = signature.getName();
        System.out.println("method: " + methodName + ", args: " + Arrays.toString(args));
        System.out.println("方法执行之前");
    }
    

    3.3 必须被容器管理

    无论是切面类,还是目标类,都必须在 Spring 容器的管理下才能有效。所以切面类上不仅要加上 @Aspect,还得加 @Component,使切面和 SpringIOC 容器产生关系,让其作为一个组件。目标类同理。

    3.4 通知

    1. 在具体的连接点上要执行的操作
    2. 一个切面可以包括一个或者多个通知
    3. 通知所使用的注解的值往往是切入点表达式

    3.4.1 前置通知

    • 前置通知是在方法执行之前执行的通知
    • 使用 @Before 注解

    3.4.2 后置通知

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

    3.4.3 返回通知

    • 无论连接点是正常返回还是抛出异常,后置通知都会执行。如果只想在连接点返回的时候记录日志,应使用返回通知代替后置通知。
    • 使用 @AfterReturning 注解,可在返回通知中访问连接点的返回值
      • 在返回通知中,只要将 returning 属性添加到 @AfterReturning 注解中,就可以访问连接点的返回值。该属性的值即为用来传入返回值的参数名称
      • 必须在通知方法的签名中添加一个同名参数。在运行时 Spring AOP 会通过这个参数传递返回值
    • 举例说明
      @AfterReturning(value="execution(* cn.edu.nuist.spring.aop.*.*(..))", returning="result")
      public void afterReturning(JoinPoint joinPoint, Object result) {
          System.out.println("[返回通知] " + joinPoint.getSignature().getName()
              + "方法的返回值为:" + result);
      }
      

    3.4.4 异常通知

    • 异常通知是只在连接点抛出异常时才执行的通知
    • 将 throwing 属性添加到 @AfterThrowing 注解中,也可以访问连接点抛出的异常。Throwable 是所有错误和异常类的顶级父类,所以在异常通知方法可以捕获到任何错误和异常
    • 如果只对某种特殊的异常类型感兴趣,可以将参数声明为其他异常的参数类型。然后通知就只在抛出这个类型及其子类的异常时才被执行
    • 举例说明
      @AfterThrowing(value="execution(* cn.edu.nuist.spring.aop.*.*(..))", throwing = "e")
      public void afterThrowing(NullPointerException e) {
          System.out.println("[异常通知] " + e);
      }
      

    3.4.5 环绕通知

    1. 环绕通知是所有通知类型中功能最为强大的,能够全面地控制连接点,甚至可以控制是否执行连接点
    2. 对于环绕通知来说,连接点的参数类型必须是 ProceedingJoinPoint。它是 JoinPoint 的子接口,允许控制何时执行,是否执行连接点
    3. 在环绕通知中需要明确调用 ProceedingJoinPoint 的 proceed() 来执行被代理的方法。如果忘记这样做就会导致通知被执行了,但目标方法没有被执行
    4. 环绕通知的方法需要返回目标方法执行之后的结果,即调用 joinPoint.proceed() 的返回值,否则会出现 NullPointerException
    @Around("execution(* cn.edu.nuist.spring.aop.*.*(..))")
    public Object aroundMethod(ProceedingJoinPoint pjp) {
        Object result = null;
        try {
            System.out.println("前置通知");
            result = pjp.proceed(); // 执行方法
            System.out.println("返回通知");
            return result;
        } catch (Throwable e) {
            e.printStackTrace();
            System.out.println("异常通知");
        } finally {
            System.out.println("后置通知");
        }
        return null;
    }
    

    3.5 重用切入点

    1. 在编写 AspectJ 切面时,可以直接在通知注解中书写切入点表达式。但同一个切入点表达式可能会在多个通知中重复出现
    2. 在 AspectJ 切面中,可以通过 @Pointcut 注解将一个切入点声明成简单的方法。切入点的方法体通常是空的,因为将切入点定义与应用程序逻辑混在一起是不合理的
    3. 切入点方法的访问控制符同时在控制着这个切入点的可见性。如果切入点要在多个切面中共用,最好将它们集中在一个公共的类中。在这种情况下,它必须被声明为 public。在引入这个切入点时,必须将类名也包括在内。如果类没有与这个切面放在同一个包中,还必须包含包名。
    4. 其他通知可以通过方法名称引入该切入点

    3.6 指定切面的优先级

    1. 在同一个连接点上应用不止一个切面时,除非明确指定,否则它们的优先级是不确定的
    2. 切面的优先级可以通过实现 Ordered<I> 或利用 @Order 注解指定
    3. 实现 Ordered<I>getOrder() 的返回值越小,优先级越高
    4. 若使用 @Order 注解,序号出现在注解中

    4. 以 XML 方式配置切面

    4.1 概述

    除了使用 AspectJ 注解声明切面,Spring 也支持在 bean 配置文件中声明切面。这种声明是通过 AOP 命名空间中的 XML 元素完成的。

    正常情况下,基于注解的声明要优先于基于 XML 的声明。通过 AspectJ 注解,切面可以与 AspectJ 兼容,而基于 XML 的配置则是 Spring 专有的。由于 AspectJ 得到越来越多的 AOP 框架支持,所以,以注解风格编写的切面将会有更多重用的机会。

    4.2 配置细节

    在 bean 配置文件中,所有的 Spring AOP 配置都必须定义在 <aop:config> 元素内部。对于每个切面而言,都要创建一个 <aop:aspect> 元素来为具体的切面实现引用后端 bean 实例。

    切面 bean 必须有一个标识符,供 <aop:aspect> 元素引用。

    4.3 声明切入点

    • 切入点使用 <aop:pointcut> 元素声明
    • 切入点必须定义在 <aop:aspect> 元素下,或者直接定义在 <aop:config> 元素下
      • 定义在 <aop:aspect> 元素下:只对当前切面有效
      • 定义在 <aop:config> 元素下:对所有切面都有效
    • 基于 XML 的 AOP 配置不允许在切入点表达式中用名称引入其他切入点
  • 相关阅读:
    Android Logcat使用技巧
    SqlClient使用存储过程并获取输出参数的指
    内存调试的东西D/dalvikvm( 809 ): GC_CONCURRENT freed
    android 读写sd卡的权限设置
    Android中EditText的使用方法持續更新
    RSL编译方式的FLEX站点出现#2046错误
    从网络读取数据并动态的显示在ListView中
    LEFT JOIN INNER JOIN 效率比较
    使用Installshield2009 在IIS6部署Asp.net mvc 应用程序
    Installshield2009中使用osql.exe执行数据库脚本
  • 原文地址:https://www.cnblogs.com/liujiaqi1101/p/13569617.html
Copyright © 2011-2022 走看看