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

rtb*_*tbf 7 jax-rs java-ee cdi java-batch jberet

简而言之,我想在rest请求中放入自定义范围的Configuration类的特定实例.主要问题是自定义范围(JobScoped来自JBeret https://jberet.gitbooks.io/jberet-user-guide/content/custom_cdi_scopes/index.html)在工作开始后是合格的.我知道有可能在启动作业时添加属性,但我的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);
}
Run Code Online (Sandbox Code Playgroud)

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

public class MyReader implements ItemReader {

@Inject
private Configuration configFile;
}
Run Code Online (Sandbox Code Playgroud)

配置类如下所示:

@JobScoped
public class Configuration {
 // some flags, methods etc
}
Run Code Online (Sandbox Code Playgroud)

我已经阅读了Instance,Provider但在我的情况下不知道如何使用它们.事实上,我认为使用它们是不可能的,因为作业是通过名称来识别的,这些名称是动态的并且在运行时已知.


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

但后来发生缺少上下文的问题.Job启动时有JobScoped上下文.根据上面的解决方案,我注释了Configuration作为RequestScoped,然后我收到了:

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

Jan*_*ing 5

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

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

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

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

我添加这个问题的原因是因为我认为Configuration可能不需要是一个范围实体.如果Configuration没有具体的范围,它可能是@Singleton@Stateless也是.例如,从配置文件,资源或环境变量中考虑,这些变量在运行时不会改变.可以使用常规@Inject字段将非范围(或单例范围)依赖项注入到小批量中,而无需任何@JobScoped注释.

2.如何将基于上下文的值植入批处理作业?

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

@Named
public class MyBatchlet extends AbstractBatchlet {

    @Inject
    @BatchProperty(name = "number")
    int number;

}
Run Code Online (Sandbox Code Playgroud)

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 {}
Run Code Online (Sandbox Code Playgroud)

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

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

}
Run Code Online (Sandbox Code Playgroud)

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

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

现在我们需要进入我们的范围.我们可以使用一些代码来执行此操作,您可以将其放置在Web Filter或方法拦截器中.代码使用一个BeanManager实例,可以通过以下方式获得@Inject:

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

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

}
Run Code Online (Sandbox Code Playgroud)

现在从您上面编写的拦截器中,您可以注入ConfigurationProducer并使用它ConfigurationProducer #setConfiguration(Configuration)来设置Configuration当前线程.我仍然在寻找更好的选择.