前篇文章 Impala 设置查询选项 介绍常用设置查询选项的方法,本文介绍实现动态设置查询选项用到的技术和过程。
AOP 概念
在我们开始之前,让我们对术语和核心概念做一个回顾:
- Aspect:切面,由切点和增强相结合而成,定义增强应用到哪些切点上。即一个横跨多个核心逻辑的功能,或者称之为系统关注点。
- Joinpoint:连接点,这是程序执行过程中的一个特殊点,例如方法执行,构造函数调用或字段分配。
- Pointcut:切入点,一个匹配连接点的正则表达式。 每当任何连接点匹配一个切入点时,就执行与该切入点相关联的指定增强。
- Advice:增强,指特定连接点上执行的动作,实际中想要添加的功能,如日志、权限校验。有5种:
@Before @After @AfterReturning @AfterThrowing @Around
。 - Weaving:织入,即对方法的增强,将切面的代码织入(应用)到目标函数的过程。
Spring AOP 和 AspectJ
下表总结了 Spring AOP 和 AspectJ 之间的关键区别:
Spring AOP | AspectJ |
---|---|
在纯 Java 中实现 | 使用 Java 编程语言的扩展实现 |
不需要单独的编译过程 | 除非设置 LTW,否则需要 AspectJ 编译器 (ajc) |
只能使用运行时织入 | 运行时织入不可用。支持编译时、编译后和加载时织入 |
功能不强-仅支持方法级编织 | 更强大 - 可以编织字段、方法、构造函数、静态初始值设定项、最终类/方法等…。 |
只能在由 Spring 容器管理的 bean 上实现 | 可以在所有域对象上实现 |
仅支持方法执行切入点 | 支持所有切入点 |
代理是由目标对象创建的, 并且切面应用在这些代理上 | 在执行应用程序之前 (在运行时) 前, 各方面直接在代码中进行织入 |
比 AspectJ 慢多了 | 更好的性能 |
易于学习和应用 | 相对于 Spring AOP 来说更复杂 |
选择正确的框架
选择那个框架很大程度上取决于我们的要求:
- 框架: 如果应用程序没有使用 Spring 框架, 那么我们就别无选择, 只能放弃使用 Spring AOP 的想法, 因为它无法管理任何超出 Spring 容器范围的东西。但是, 如果我们的应用程序是完全使用 Spring 框架创建的, 那么我们可以使用 Spring AOP, 因为它是简单的学习和应用。
- 灵活性: 由于有限的 joinpoint 支持, Spring AOP 不是一个完整的 AOP 解决方案, 但它解决了程序员面临的最常见的问题。尽管如果我们想深入挖掘和开发 AOP 以达到其最大能力, 并希望得到广泛的可用 joinpoints 的支持, 那么最好选择 AspectJ。
- 性能: 如果我们使用的是有限的切面, 那么就会有细微的性能差异。但有时, 应用程序有成千上万个切面的情况。我们不想在这样的情况下使用运行时编织, 所以最好选择 AspectJ。AspectJ 已知的速度比 Spring AOP 快8到35倍。
- 两者的最佳之处: 这两个框架都是完全兼容的。我们总是可以利用 Spring AOP;只要有可能, 仍然可以在不支持前者的地方使用 AspectJ 获得支持。
基于我们的需求:对 Impala jdbc 驱动包的内容做增强。不受 Spring 管理,AspectJ 支持这种方式,并提供了 Compile-time weaving,Post-compile weaving ,Load-time weaving 三种织入方式,从开发部署便捷性上这里选择 Post-compile weaving 方式。
测试环境
Spring Boot: 2.1.4.RELEASE
Impala JDBC: 2.6.17.1020
mpala: 3.4
实现步骤:
步骤中省略了数据源的配置过程。驱动可以开启日志,有利于我们跟踪分析请求。
jdbc:impala://localhost:21050;LogLevel=6;LogPath=/tmp/impala
1. 添加 aspects 支持
首先,我们通过 Maven 引入Spring 对 aspects 的支持:
<!-- aspects -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj.version}</version>
<scope>compile</scope>
</dependency>
上述依赖会自动引入 AspectJ,使用 AspectJ 实现 AOP 比较方便。
2. 定义切面类
然后,我们定义一个HS2ClientAspect
:
package com.cloudera.example.aspects;
import com.cloudera.example.helper.HS2ClientHelper;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Arrays;
/**
* HS2ClientWrapper 类的切面。
* <p>
*/
@Aspect
public class HS2ClientAspect {
private final Logger log = LoggerFactory.getLogger(this.getClass());
/**
* 切入点,一个匹配连接点的正则表达式。
*/
@Pointcut("execution(public * com.cloudera.impala.hivecommon.api.HS2ClientWrapper" +
".ExecuteStatement(..))")
public void hs2Pointcut() {
// Method is empty as this is just a Pointcut, the implementations are in the advices.
}
/**
* 前置增强,目标方法执行前之前执行。
*
* @param joinPoint 连接点
*/
@Before("hs2Pointcut()")
public void optimizeQueryOption(JoinPoint joinPoint) {
if (log.isDebugEnabled()) {
log.debug("Enter: {}.{}() with argument[s] = {}",
joinPoint.getSignature().