JavaEE CDI 的 Spring Boot 替代品是什么?

What is SpringBoot alternative to JavaEE CDI?

我知道 JakartaEE(以前称为 JavaEE)从 JavaEE 6 开始支持 CDI。

据我所知,SpringBoot 没有 CDI,只有 DI。

SpringBoot 是否支持 CDI(上下文和依赖注入)或提供一些替代方案?

整个 Spring 框架本身实际上是一个替代方案。这里的不同之处在于它在 Spring 中没有那么清晰地分开 - 相反它是 Spring 框架复杂性的一部分。

Spring 框架几乎为 CDI 中的所有功能提供了替代方案。为了论证和完整性,我们可以做一个比较,讨论异同;

托管对象

在 CDI 中,您可以按以下方式定义可注射或托管 class:

@[ManagedBean / Named]
@[ApplicationScoped / Dependant / RequestScoped / Singleton / ViewScoped]
public class MyClass {
    @PostConstruct private void init() { ... }
    @PreDestroy private void destroy() { ... }
}

Springs 等效于相同的定义如下所示:

@[Bean / Controller / Component / Repository / Service]
public class MyClass {
    @PostConstruct private void init() { ... }
    @PreDestroy private void destroy() { ... }
}

每当 class 在 Spring 中被定义为 @Component@Bean 时,它也可以采用一个范围:

@[Bean / Component]
@Scope([
    SCOPE_APPLICATION / SCOPE_PROTOTYPE / SCOPE_REQUEST / SCOPE_SESSION / 
    SCOPE_SINGLETON / "websocket"
])
public class MyClass { ... }

就像 CDI 一样,Spring 也是可扩展的,如果开发人员需要特殊行为,可以使用额外的范围进行扩展。

使用 CDI,我们通过使用 @Inject 注释成员来注入并让框架注入托管对象。在 Spring 中注入托管对象的等效注释是 @Autowired.

信号

在 CDI 中定义信号时,我们会注入一个成员,例如:

@Inject
private Event<Payload> event;

然后我们将通过调用 event.fire(new Payload())event.fireAsync(new Payload()) 向所有听众发送信号。一个事件侦听器(或观察者,因为它们在 CDI 中被称为)然后将通过向托管对象添加这样的方法来定义:

public void onPayload(@Observes Payload event) { ... }

以类似的方式,Spring 应用程序中的信号定义如下:

@Autowired
private ApplicationEventPublisher event;

在 Spring 中,然后通过调用 event.publishEvent(new Payload()) 触发事件。 Spring 中的事件侦听器定义略有不同:

@Component
public class Observer {
    @EventListener public void onPayload(Payload payload) { ... }
}

制作人

在CDI中,我们可以通过定义生产者来创建一个“生产”特定对象的方法。这些本质上就像工厂一样。稍后可以通过使用 @Inject 注入来创建这些对象。默认情况下,生产者是 @Dependant,但他们也可以定义范围。

@RequestScoped
public class LoggerFactory {
    @Produces
    public Logger createLogger() {
        return new Logger();
    }
}

要在 Spring 中做同样的事情,我们会做这样的事情:

@Configuration
public class LoggerFactory {
    @Bean @Scope(SCOPE_REQUEST)
    public Logger createLogger() {
        return new Logger();
    }
}

当然,您可以在 Spring 中使用 @Autowired 来注入这些对象的实例,而不是使用 @Inject

拦截器

拦截器允许我们使用自定义行为“修改”或“装饰”方法。在 CDI 中,我们通常首先定义一个注解。此注解随后用于使用我们的自定义代码装饰方法:

@Target( { METHOD, TYPE } ) @Retention( RUNTIME )
public @interface MyInterceptor { }

然后我们为该拦截器定义一个行为:

@Interceptor @MyInterceptor
public class MyInterceptorHandler {
    @AroundInvoke public Object interception(InvocationContext ctx) throws Exception {
        /* You can do something additional here ... */
        Object result = ctx.proceed();
        /* ... and/or here ... */

        return result;
    }
}

所以在这里我们可以准确选择要插入额外代码的位置以及何时继续调用装饰方法。要装饰拦截方法,您可以这样做:

@ApplicationScoped
public class MyService {
    @MyInterceptor public String operation(String str) {
        return str;
    }
}

在Spring中既相似又不同。 Spring 本身并没有专门这样的东西,而是在框架中添加了对 AspectJ 的支持。所以要在 Spring 中创建一个类似的拦截器,你可以这样做:

@Aspect
public class MyInterceptorHandler {
    @Around("execution("*com.mycompany.project.MyService.*(..))")
    public Object interception(ProceedingJoinPoint jp) throws Throwable {
        /* You can do something additional here ... */
        Object result = jp.proceed();
        /* ... and/or here ... */

        return result;
    }
}

此时我们不需要再做任何事情,因为拦截将应用于 MyService 的所有方法,如 @Around 注释所指定。

结论

所以这个post有点拉长了。但它确实显示了 CDI 和 Spring 之间的一些异同。如果您知道 Spring,使用这个小方法,对于最常见的任务,您可以轻松迁移到 CDI,反之亦然。

Spring是一个single-vendor product. CDI is a multi-vendor standard implemented by several different companies and products (Weld, OpenWebBeans) that has been in Java EE since version 6 and Jakarta EE since version 8. Both Spring and CDI implement the JSR-330 specification,但除此之外完全没有任何关系。