创建和注入每个请求范围的变量

时间:2018-02-02 15:05:29

标签: java java-ee dependency-injection scope

我希望在java EE中的请求的整个生命周期中都有一个变量。 例如,它可以用于日志记录功能,以便我可以按请求过滤所有日志条目。

我想要了解的关键部分是它必须相对容易在现有的应用程序中实现,因此如果可能的话,某种依赖注入会获得与特定请求相关的变量。

我尝试过注入一个@RequestScoped变量,但它不起作用,因为它只限于容器。我需要能够将相同的对象注入不同的容器。这是可能吗?

编辑:我想要的是:

@RequestScoped
public class RequestVariables {
    public String id;
}
@Stateless
public class Logger {
    @Inject
    private RequestVariables requestVariables;
    public void log(String message) {
        System.out.println(requestVariables.id + ":" + message);
    }   
}

@Stateless
public class Service {
    @Inject
    private Logger logger;
    @Inject
    private RequestVariables requestVariables;
    public void save(String data) {
        logger.log("Save");
        session.save(data + requestVariables.id); //Maybe add request parameter to save aswell
    }
}

public class API {
    @Inject
    private Service service;
    @Inject
    private Logger logger;
    @Inject
    private RequestVariables requestVariables;
    @Path("/1")
    @GET
    public Response get(@QueryParam("data") String data) {
        requestVariables.id = UUID.randomUUID().toString()
        service.save(data);
        logger.log("Get");
        return Response.status(204).build();
    }
}

目前这是我的实验:

@RequestScoped
public class RequestScope {
    private int test = 0;

    public RequestScope(int test) {
        this.test = test;
    }
    public RequestScope(){}

    public int getTest() {
        return test;
    }

    public void setTest(int test) {
        this.test = test;
    }
}
@Provider
public class RequestScopeFilter implements ContainerRequestFilter {
    @Inject
    private javax.inject.Provider<RequestScope> requestScopeProvider;

    @Context
    private HttpServletRequest request;

    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {
        requestScopeProvider.get().setTest(42);
        request.setAttribute("test", "superTest");
    }
}

@Stateless
@TransactionManagement(TransactionManagementType.BEAN)
@TransactionAttribute(value=TransactionAttributeType.NOT_SUPPORTED)
public class Service {

    @Context
    private HttpServletRequest httpServletRequest;

    @Inject
    private Provider<RequestScope> requestScopeProvider;

    public void test() {
        RequestScope scope = requestScopeProvider.get();
        String test = (String)httpServletRequest.getAttribute("test");
    }    
}

所以当我从我的服务中获取范围时,它是一个新的对象,test设置为0,然后它会抛出一个NPE,因为httpServletRequest为空

3 个答案:

答案 0 :(得分:1)

选项#1

实现拦截器并将请求ID设置为HttpServletRequest属性:

@AroundInvoke
public Object setRequestId(InvocationContext ic) throws Exception {
    HttpServletRequest request = [..] // getHttpServletRequest(ic);
    request.setAttribute("request-id", UUID.randomUUID().toString());
    return ic.proceed();
}

然后在您需要的任何地方使用HttpServletRequest

@Context
private HttpServletRequest httpRequest;

选项#2

如果只想按唯一ID过滤日志,可以配置Logger来打印线程名称:[%t]

示例:Log4j PatternLayout

选项#3

使用自定义java bean封装请求数据(查询参数,请求ID等),并将此bean传递给您的应用程序服务。

public class API {

    @Inject
    private Service service;

    @Path("/1")
    @GET
    public Response get(MyCustomRequestBean data) {
        service.doSomejob(data);
        return Response.status(204).build();
    }
}

ParamConverter

中设置请求ID和查询参数

Jax-RS ParamConverter - ParamConverterProvider method return type mismatch

答案 1 :(得分:0)

您可以在服务中注入提供者:

@Inject
Provider<RequestVariables> vars

然后调用get()来获取实例。如果您尝试在请求范围上下文之外的线程中获取(),您将获得异常。然而,我会尝试以不允许这种情况发生的方式构建

答案 2 :(得分:0)

我发现的解决方案是使用ThreadLocal变量。它似乎相当脏,但它的工作原理是每个请求都在它自己的线程上执行(据我所知)。所以这就是我得到的:

UITableview

如果需要的话,我也可以轻松地交换let height : CGFloat = CGFloat((self.transactionArr.count*60)+70) let width = self.view.frame.size.width*2 self.tblWidth.constant = self.view.frame.size.width self.view.updateConstraintsIfNeeded() self.tableView.contentSize = CGSize(width: width, height: height) 以返回更具体的内容。 我可以从几乎任何地方获取变量,假设请求没有启动不同的线程