LocalThread 持有 RequestScoped CDI 实例

问题描述

一个不是 EJB 或 CDI 的类(它是一个简单的 java pojo,如示例所示)需要以编程方式查找 RequestScoped bean 的实例。为了防止总是查找同一个实例,我虽然 ThreadLocal

这是一个代码,它有点像这样:

STATIC class aClass {

   public static ThreadLocal<RequestScopedBean> aRcBean;

   public static Integer getDataFromreqBean() {
   if(aRcBean == null) {
      aRcBean = new ....
      aRcBean.set(CDI.current.select.get(RequestScopedBean.class));
   }
   return aRcBean.get() != null ? aRcBean.get().doSomething() : null;
  }
}

我的问题是,并行请求和线程本地对象不会向 GC 释放实例,我会遇到 GC 和内存泄漏问题吗?

它是我们的一个 java ee jax rs 项目。一个 http 帖子被触发,在我们后端的某个地方我有这个星座。我无法更改太多代码。我需要一个解决方案来像我在上面的例子中提供的那样工作。

解决方法

嗯。假设您的应用程序运行在一些 JavaEE/JacartaEE 应用服务器上,例如 Payara。不是吗?

首先,在我看来,您不需要将代理置于线程本地,您可能更愿意考虑在 POJO 中保留本地接口视图或什至没有接口视图字段。因此,您仍然可以使用上下文查找。

例如。您的无状态 bean:

package stack.overflow.ejb.module.control;

import javax.ejb.Stateless;

/**
 *
 * @author s.kadakov
 */
@Stateless
public class SomeBean {
    
    public String getBeanName() {
        return SomeBean.class.getCanonicalName();
    }
    
}

您的 POJO:

package stack.overflow.ejb.module.control;

import java.util.logging.Level;
import java.util.logging.Logger;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

/**
 *
 * @author s.kadakov
 */
public class SomePojo {

    SomeBean someBean = lookupSomeBeanLocal();

    public SomeBean getSomeBean() {
        return someBean;
    }
    
    private SomeBean lookupSomeBeanLocal() {
        try {
            Context c = new InitialContext();
            return (SomeBean) c.lookup("java:global/ejb-module-1.0-SNAPSHOT/SomeBean");
        } catch (NamingException ne) {
            Logger.getLogger(getClass().getName()).log(Level.SEVERE,"exception caught",ne);
            throw new RuntimeException(ne);
        }
    }

}

提示:您可以在应用程序部署时在应用程序服务器日志中获取您 bean 的可移植 JNDI 名称。就我而言,这些是:

Portable JNDI names for EJB SomeBean: [java:global/ejb-module-1.0-SNAPSHOT/SomeBean,java:global/ejb-module-1.0-SNAPSHOT/SomeBean!stack.overflow.ejb.module.control.SomeBean]]]

您的 JAX-RS 资源:

package stack.overflow.ejb.module.resources;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.core.Response;
import stack.overflow.ejb.module.control.SomePojo;

/**
 *
 * @author s.kadakov
 */
@Path("javaee8")
public class JavaEE8Resource {
    
    @GET
    public Response ping(){
        
        SomePojo pojo = new SomePojo();
        
        String beanName = pojo.getSomeBean().getBeanName();
        
        return Response
                .ok(beanName)
                .build();
    }
}

那么,让我们试试:

curl http://localhost:8080/ejb-module/resources/javaee8
stack.overflow.ejb.module.control.SomeBean

我在 Payara 5 Full 上测试了这个片段,希望它在其他 JavaEE/JakartaEE 兼容服务器上也能以同样的方式工作。

希望它对您有用或为您提供一些提示。