@PostConstruct 调用 Stateful EJB 调用而不是 Stateless 为什么?

@PostConstruct called on Stateful EJB invocation but not Stateless Why?

这个问题是我几周前 post 编辑的另一个问题的分支: log4j2 logging of code in EJB jar on JBoss EAP 7。在那个 post 中,我提出了一个问题,并最终提出了一个解决方案,用于记录 EJB 上作为 EJB jar(不是 EAR)部署到 JBoss EAP 7 服务器上并从另一个 servlet 调用的初始化。这些 EJB 通过本地接口调用。

我在 link 中提出的解决方案非常适合我尝试使用的第一个 EJB,一个有状态的 EJB。 @PostConstruct-annotated 方法被调用并初始化日志记录上下文,一切正常。

该解决方案在我尝试使用的第二个 EJB 上失败。这个 EJB 是无状态的。 @PostConstruct 方法永远不会被调用,并且第一次尝试记录会把事情搞砸,因为记录器是空的。我在这两个 bean 之间看到的唯一区别是第二个是无状态的,而第一个是有状态的。作为实验,我将第二个设为有状态。一旦我这样做了,@PostConstruct 被调用,日志记录被初始化,一切基本上都很好。

根据 Oracle JavaEE6 tutorial,@PostConstruct 方法应该在无状态 bean 实例化时调用。那么,为什么有状态会话 Bean 实例化调用 @PostConstruct 而无状态会话 Bean 实例化不调用,我该怎么办?

谢谢。

更新:添加源代码。

ejb-jar.xml

<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         version="3.2"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/ejb-jar_3_2.xsd">
    <module-name>DealerLocatorBean</module-name>
    <enterprise-beans>
        <session>
            <ejb-name>DealerLocatorBean</ejb-name>
            <home>com.whatever.ServiceLogicHome</home>
            <remote>com.whatever.ServiceLogic</remote>
            <local-home>com.whatever.ServiceLogicLocalHome</local-home>
            <local>com.whatever.ServiceLogicLocal</local>
            <ejb-class>com.whatever.ejbs.DealerLocatorBean</ejb-class>
            <session-type>Stateless</session-type>
            <!-- <session-type>Stateful</session-type> No problem if this is made stateful-->
            <transaction-type>Bean</transaction-type>
        </session>
    </enterprise-beans>
</ejb-jar>

DealerLocatorBean.java:

public class DealerLocatorBean implements SessionBean
{

    private static final String LOGGER_CONFIG = "/path/to/log4j2.xml";
    private static final String LOGGER_CONTEXT_NAME = "VTDLLOC-EJB";
    private static LoggerContext logctx;
    private static Logger logger = null;

    public DealerLocatorBean() {
        System.out.println("calling DealerLocatorBean() constructor");
    }
    @PostConstruct
    @TransactionAttribute(value=TransactionAttributeType.NOT_SUPPORTED)
    private void postConstruct() {
        System.out.println("DealerLocatorBean.postConstruct()");
        logctx = Configurator.initialize(LOGGER_CONTEXT_NAME, LOGGER_CONFIG);
        logger = logctx.getLogger(getClass().getName());
        logger.log(Level.INFO, ("postConstruct() in DealerLocatorBean called"));
        logger.log(Level.INFO, ("******END OF THE postConstruct() CALL******"));
    }

    @PreDestroy
    @TransactionAttribute(value=TransactionAttributeType.NOT_SUPPORTED)
    private void preDestroy() {
        logger.log(Level.INFO, ("preDestroy() in DealerLocatorBean called.  Shutting down logging."));
        Configurator.shutdown(logctx);
    }

如果 bean 被部署为有状态的(在 ejb-jar.xml 中),@postConstruct 在部署后第一次使用 bean 时被调用并且一切正常。 DealerLocatorBean.postConstruct() 出现在输出中,所有后续日志记录都有效。

如果 bean 被部署为无状态的(在 ejb-jar.xml 中),@postConstruct 永远不会被调用。 DealerLocatorBean.postConstruct() 未出现在输出中。日志记录未初始化,一旦代码尝试通过记录器记录内容,就会出现 NullPointerExceptions。

您是否看到此 bean 已部署到日志中? SLSB 中也不允许最终静态。我建议更改 private static LoggerContext logctx;到 private static final LoggerContext logctx= Configurator.initialize(LOGGER_CONTEXT_NAME, LOGGER_CONFIG);如果可能的话,对于记录器也是如此,并从@PostConstruct 中删除相应的语句,或者只删除 logctx 和记录器声明中的静态关键字。