Spring开发者的Quarkus实战
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

2.3.1 两种框架的依赖注入(DI)和AOP比较

本节先简单介绍CDI规范和DI规范、AOP的基本概念,然后分析这两种框架的不同应用。

1 CDI规范和DI规范

CDI(Contexts and Dependency Injection,上下文依赖注入)规范即JSR 299规范,是从Java EE 6开始引入的。其本身基于Java依赖注入(JSR 330),引入了@Inject、@Named等。而JSR 330仅用于DI并已实现。CDI规范添加了各种EE内容,如@RequestScoped、拦截器/装饰器、生产者、事件,以及与JSF、EJB等集成的基础。已经将EJB等Java EE组件重新定义为基于CDI的规范。CDI规范除了具有DI功能之外,其关键部分还在于CDI规范对Bean上下文的管理以及对Bean生命周期的管理以及这些上下文中的依赖关系。

DI(Dependency Injection,依赖注入)规范即JSR 330规范,具有在任何应用程序上进行Bean发现和Bean连接过程的功能。DI规范不仅可以使用在应用程序中,还可以应用在单元测试和模拟中。现有有很多DI框架,包括Guice、Weld、Spring、EJB 3.x和CDI本身。Weld和Spring还扩展了DI方案,并建立了自己的DI框架。

2 面向切面(AOP)概述

AOP(Aspect Oriented Programming,面向切面编程)是一种在OOP(Oriented Object Programming,面向对象编程)基础之上的一种更高级的设计思想。AOP侧重于切面组件,切面组件可以理解成封装了通用功能的组件,切面组件可以通过配置方式灵活地切入某一批目标对象方法上。AOP用于处理系统中分布于各个模块的横切注入点,比如事务管理、日志、缓存等。

AOP的实现方式有很多,主要分为构建时AOP实现和运行时AOP实现。

构建时AOP实现可以分为构建时静态AOP实现和构建时动态AOP实现。

(1)构建时静态AOP实现

构建时静态AOP实现是指在定义阶段定义好切面和指定的Bean注入点。当Java文件编译为类时,会在指定的Bean上增加切面代码。当Bean运行时,就可以直接调用切面代码。构建时静态AOP实现也可以称为编译时增强。其特征是在定义时必须指定注入点的Bean。AspectJ框架是构建时静态AOP实现的代表。

(2)构建时动态AOP实现

构建时动态AOP实现指在定义阶段定义好切面和注入点注解。当Java文件编译为类时,会使用动态生成字节码技术,对有注解的Bean增加子类并继承Bean,在Bean子类创建一个基于反射的动态代理,增加指向切面的代码。当Bean运行时,实际上就是Bean的子类在运行,就可以通过动态代理调用切面代码。其特征是在定义时要求Bean不能是final类(final类不支持继承)。cglib动态代理是构建时动态AOP实现的代表。cglib动态代理需要asm开源包。cglib动态代理不但可以对Bean实现动态代理,还可以对接口实现动态代理。

运行时AOP实现分为两类,一类是JDK动态代理AOP实现,另一类是JDK运行时拦截AOP实现。

(1)JDK动态代理AOP实现

JDK动态代理AOP实现是在定义阶段定义好切面和注入点注解。当应用程序初始化进行Bean装载时,针对Bean上的注入点注解创建动态代理,即利用反射机制生成一个实现代理接口的匿名类。当在运行时调用Bean时,在调用具体方法前调用动态代理的InvokeHandler来处理,实现切面功能。Spring AOP是JDK动态代理AOP实现的典型应用。Spring采用的是DI规范,在Bean工厂初始化装载Bean时实现Bean的动态代理注入。但Spring AOP只能对Bean进行AOP实现,接口则不行。所以,当只有接口而无具体实现的Bean时,Spring会切换到cglib动态代理实现模式。

(2)JDK运行时拦截AOP实现

JDK运行时拦截AOP实现在定义阶段定义好切面和注入点注解。当应用程序运行期间,通过拦截器(Interceptor)把上下文(Context)中带有注入点注解的实例化Bean装入切面组件来执行,然后返回到上下文(Context)中。Quarkus是JDK运行时拦截AOP实现的代理。

3 Spring Boot的控制反转(IoC)和面向切面(AOP)

Spring Boot是以Spring为核心的,Spring的核心是控制反转(IoC)和面向切面(AOP)。Spring以IoC、AOP为主要思想。在Spring框架中通过配置创建类对象,由Spring在运行阶段实例化、组装对象。AOP的思想是在执行某些代码前执行另外的代码,使程序更灵活、扩展性更好,可以随便地添加、删除某些功能。Servlet中的Filter便是一种AOP思想的实现。

4 Quarkus中的依赖注入(CDI)和AOP编程

Quarkus框架的CDI方案基于CDI规范。但Quarkus框架只实现了CDI规范的一部分功能,并不是完全符合TCK的CDI规范实现。在CDI规范中有一个Beans管理容器。首先加载的是核心的Beans,其次加载外部的Beans,但是外部的Beans需要被识别,因此需要进行转换。Quarkus Extensions程序就是把外部的Beans转换为Beans管理可以识别的Beans。

Quarkus支持的通用CDI规范如下。

1)程序模型。包括:①由Java类实现的托管Bean;②@PostConstruct和@PreDestroy生命周期回调;③Producer方法和fields、disposers;④Qualifiers;⑤Alternatives;⑥Stereotypes。

2)依赖注入和查找。包括:①字段、构造函数和初始化器/设置器注入;②类型安全分辨率;③通过编程查找javax.enterprise.inject.Instance;④客户端代理;⑤注入点元数据。

3)范围和上下文。包括:①@Dependent、@ApplicationScoped、@Singleton、@RequestScoped和@SessionScoped;②自定义范围和上下文。

4)拦截器。包括:①业务方法拦截器,如@AroundInvoke;②生命周期事件回调的拦截器,如@PostConstruct、@PreDestroy、@AroundConstruct。

5)事件和观察者方法。包括异步事件和事务观察者方法。

Quarkus不支持通用CDI规范的功能如下。

1)不支持@ConversationScoped。

2)不支持Decorators模式。

3)不支持便携式扩展(Portable Extensions)。

4)BeanManager仅实现以下方法:getBeans、createCreationalContext、getReference、getInjectable-Reference、resolve、getContext、fireEvent、getEvent和createInstance。

5)不支持专门化(Specialization)。

6)beans.xml描述符内容被忽略。

7)不支持钝化(Passivation)和钝化作用域(Passivating Scopes)。

8)超类上的拦截器方法尚未实现。

9)不支持@Interceptors注解。

当然,Quarkus还提供了大量的对CDI规范的增强功能,这里就不一一列出了。