7

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

详情如下:

这是休息请求伪代码:

@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
}

我读过关于 Instance, Provider 但不知道如何在我的情况下使用它们。事实上,我认为使用它们是不可能的,因为这些作业是由它们的名称标识的,它是动态的并且在运行时是已知的。


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

但是随后会出现缺少上下文的问题。当 Job 启动时,会有 JobScoped 上下文。根据上述解决方案,我将 Configuration 注释为 RequestScoped,然后我收到:

org.jboss.weld.context.ContextNotActiveException: WELD-001303: org.jboss.weld.manager.BeanManagerImpl.getContext(BeanManagerImpl.java:689) 的范围类型 javax.enterprise.context.RequestScoped 没有活动上下文.weld.bean.ContextualInstanceStrategy$DefaultContextualInstanceStrategy.getIfExists(ContextualInstanceStrategy.java:90) at org.jboss.weld.bean.ContextualInstanceStrategy$CachingContextualInstanceStrategy.getIfExists(ContextualInstanceStrategy.java:165) at 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(未知来源)

4

3 回答 3

8

我认为这个问题包括几个部分:

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

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

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

我添加此问题的原因是因为我认为Configuration可能不需要成为范围实体。如果Configuration没有特定于范围的内容,则可能是@Singleton或也可能是@Stateless。例如,从不会在运行时更改的配置文件、资源或环境变量中考虑。可以使用常规字段将非范围(或单例范围)依赖项很好地注入到批处理中@Inject,而无需任何@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 在这篇博文中所述,他试图进入@RequestScope在 Java SE 中。

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));
    }

}

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

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

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

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

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

我一直在问自己同样的问题,这就是我想出的解决方案。另请参阅我关于如何从自定义 Weld CDI 范围正确播种的问题:Weld 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当前线程。我仍然在这里寻找更好的选择。

于 2016-07-18T12:52:16.030 回答
1

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

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

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

于 2016-09-26T20:20:28.307 回答
0

首先,我要再次感谢 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;
}
于 2016-09-30T11:19:14.813 回答