jakarta ee - 如何从请求中放入自定义范围/上下文(JobScoped - 自定义 CDI 范围)特定实例以使



>简而言之,我想从 rest 请求中放入配置类的自定义范围特定实例。主要问题是自定义作用域(来自 JBeret https://jberet.gitbooks.io/jberet-user-guide/content/custom_cdi_scopes/index.html 的 JobScoped)在作业启动后符合条件。我知道在启动作业时可以添加属性,但我的配置类聚合了很多配置,而且非常复杂因此,将该文件转换为属性类会非常不舒服。

详情如下:

这是 rest 请求伪代码:

@Path("/job")
public class RunJob {
@POST
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Path("/start")
public String startJob(@FormDataParam("file") InputStream uploadedInputStream) {
    JobOperatorImpl jobOperator = (JobOperatorImpl) BatchRuntime.getJobOperator();
    Configuration config = new Configuration(uploadedInputStream);
    Properties properties = new Properties();
    jobOperator.start(job, properties);
}

我想实现的是在 Job 上下文中注入一些配置文件,如下所示:

public class MyReader implements ItemReader {
@Inject
private Configuration configFile;
}

配置类如下所示:

@JobScoped
public class Configuration {
 // some flags, methods etc
}

我已经阅读了有关实例,提供程序的信息,但不知道如何在我的情况下使用它们。 事实上,我认为不可能使用它们,因为这些工作是由它们的名称标识的,这是动态的 并在运行时已知。


同时,我

发现了与我类似的情况:我是否可以创建一个请求范围的对象并从任何位置访问它,并避免在 JAX-RS 中将其作为参数传递?

但随后会出现缺少上下文的问题。当作业启动时,有 JobScoped 上下文。根据上述解决方案,我将配置注释为请求范围,然后我收到:

org.jboss.weld.context.ContextNotActiveException: WELD-001303: No 作用域类型 javax.enterprise.context.RequestScoped 的活动上下文 在 org.jboss.weld.manager.BeanManagerImpl.getContext(BeanManagerImpl.java:689) 在 org.jboss.weld.bean.ContextualInstanceStrategy$DefaultContextualInstanceStrategy.getIfExists(ContextualInstanceStrategy.java:90) 在 org.jboss.weld.bean.ContextualInstanceStrategy$CachingContextualInstanceStrategy.getIfExists(ContextualInstanceStrategy.java:165) 在 org.jboss.weld.bean.ContextualInstance.getIfExists(ContextualInstance.java:63) 在 org.jboss.weld.bean.proxy.ContextBeanInstance.getInstance(ContextBeanInstance.java:83) 在 org.jboss.weld.bean.proxy.ProxyMethodHandler.getInstance(ProxyMethodHandler.java:125) 配置$Proxy$_$$_WeldClientProxy.toString(未知来源)

我认为这个问题由几个部分组成:

  1. 如何将值注入到批处理作业中?
  2. 如何将基于上下文的值播种到批处理作业?
  3. 如何在批处理作业中输入请求范围?
  4. 如何创建自定义范围?
  5. 如何输入自定义范围?
  6. 如何在自定义范围内设定值种子?

我将尝试回答所有单独的问题,但请记住,我最近才开始使用CDI/Weld,并且没有JBeret的经验。

1. 如何将值注入到批处理作业中?

我添加这个问题的原因是,我认为Configuration可能不需要是一个作用域实体。如果Configuration没有特定于范围的内容,则也可以是@Singleton@Stateless。例如,从配置文件、资源或环境变量中考虑,它们在运行时不会更改。非作用域(或单例作用域)依赖项可以使用常规@Inject字段很好地注入到 batchlet 中,而无需任何@JobScoped注释。

2. 如何将基于上下文的值种子到批处理作业?

那么,如果实际值取决于上下文并且不能以@Singleton方式注入怎么办?根据 JBeret 文档,最好通过 Properties 传递所有配置。然后可以从JobContext中读取这些注释,或者使用@BatchProperty注释注入。这仅适用于可从字符串序列化的预定义类型列表。

@Named
public class MyBatchlet extends AbstractBatchlet {
    @Inject
    @BatchProperty(name = "number")
    int number;
}

3. 如何在批处理作业中输入@RequestScope

我认为你不应该。@RequestScope仅用于请求。如果依赖项依赖于应在请求外部可访问的@RequestScope,请考虑引入自定义作用域。

如果您确实需要以编程方式输入@RequestScope,您可以为其定义自己的上下文并输入该上下文(请参阅下面的第 4 部分)或默认输入上下文,如 Dan Haywood 在这篇博文中所述,他试图进入 Java SE 中的@RequestScope

4. 如何创建自定义范围?

创建自定义范围相当容易。但是,自定义作用域需要作用域上下文的实现。我发现这在文档中有点不清楚。幸运的是,有图书馆显微镜图书馆。对于此示例,您只需要microscoped-core依赖项,它提供了在其自定义作用域中使用的ScopeContext实现。我们也会将该ScopeContext用于我们的简单范围。

首先,我们必须创建 Scope 注释:

@Documented
@Scope
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.TYPE})
public @interface CustomScoped {}

其次,我们必须创建一个扩展:

public class CustomScopedExtension implements Extension, Serializable {
    public void addScope(@Observes final BeforeBeanDiscovery event) {
        event.addScope(CustomScoped, true, false);
    }
    public void registerContext(@Observes final AfterBeanDiscovery event) {
        event.addContext(new ScopeContext<>(CustomScoped.class));
    }
}

请注意,我们使用的是 META-INF/services/javax.enterprise.inject.spi.Extension'ScopeContext from microscoped here. Furthermore, you should register your extension by adding the full classname to

5. 如何输入自定义范围?

现在我们需要输入我们的范围。我们可以通过一些代码来做到这一点,例如,您可以将这些代码放置在 Web Filter或方法拦截器中。该代码使用BeanManager实例,可以通过@Inject获得:

ScopeContext<?> context = (ScopeContext<?>) beanManager.getContext(CustomScoped.class);
context.enter(key);
try {
     // continue computation
} finally {
    context.destroy(key);
}

6. 如何在自定义范围内设定值种子?

我一直在问自己同样的问题,这就是我想出的解决方案。另请参阅我关于如何从自定义 Weld CDI 范围正确种子的问题:焊接 CDI 自定义范围中的种子值。不过,我确实有针对您的问题的解决方法:

@Singleton
public class ConfigurationProducer {
    private final InheritableThreadLocal<Configuration>  threadLocalConfiguration =
    new InheritableThreadLocal<>();
    @Produces
    @ActiveDataSet
    public ConfigurationConfiguration() {
       return threadLocalConfiguration.get()
    }
    public void setConfiguration(Configuration configuration) {
         threadLocalConfiguration.set(configuration);
    }    
}
现在,从

上面写的拦截器开始,您可以注入ConfigurationProducer并使用ConfigurationProducer #setConfiguration(Configuration)来设置当前线程的Configuration。我仍然在这里寻找更好的选择。

批处理规范 (JSR 352) 定义了在作业中传递用户对象的标准方法,方法是调用:

javax.batch.runtime.context.JobContext#setTransientUserData(myObject);

对于简单的情况,这应该就足够了。 您可以定义作业侦听器,将JobContext注入作业侦听器类,并在其startJob()方法中设置瞬态用户数据。 然后,它将可用于整个作业执行,并且可以休息到其他值。对于更复杂的用例,@JobScoped是更好的选择。

首先,

我要再次感谢Jan-Willem Gmelig Meyling,因为你的回答非常有帮助。无论如何,我想使用给定的作用域 JBeret 它是 JobScoped,今天它只能在 TYPE 级别使用。我按照Jan-Willem Gmelig Meyling的建议做了类似的解决方法,但是:

  • 可以使用 JobScoped
  • 无需导入额外的库,一切都在 CDI 中运行

溶液:

1)配置类:

@JobScoped
public class Configuration
{...}

2)在JobListener,奇迹发生了。其他注释是多余的。

让我的代码为自己说话;)

import javax.batch.api.listener.AbstractJobListener;
public class MyJobListener extends AbstractJobListener{
    @Inject
    private Configuration jobScopedConfiguration;

    @Override
    public void beforeJob() throws Exception {
        enrichJobScopedConfigurationWithRequestConfiguration();
        ...
        super.beforeJob();
    }
    private void enrichJobScopedConfigurationWithRequestConfiguration(){
    Configuration requestConfiguration =
                (Configuration) BatchRuntime.getJobOperator().getJobExecution(currentExecutionId).getJobParameters()
                        .get("configuration");
        jobScopedConfiguration.updateWith(requestConfiguration);
    }

现在我可以在作业上下文中的任何 jberet/java 批处理工件中注入我的配置,例如:

public class MyReader implements ItemReader {
@Inject
private Configuration configFile;
}

相关内容

  • 没有找到相关文章

最新更新