Sharing application context between JavaSE and Web

2020-07-26 13:39发布

问题:

I have a Java SE application running an embedded Jetty web server hosting a RESTful web service. I want to use Spring in both the Java SE part and in the web application running in Jetty. Some beans, e.g. a dao and the entity manager, needs to be shared between the two parts. When I start the application I get duplicate versions of both the beans and the entity manager.

WARN EntityManagerFactoryRegistry.addEntityManagerFactory:80 - HHH000436: Entity manager factory name (persistenceUnit) is already registered.  If entity manager will be clustered or passivated, specify a unique value for property 'hibernate.ejb.entitymanager_factory_name'

In the Java SE part the first thing I do is to create the application context.

ApplicationContext context = new ClassPathXmlApplicationContext("classpath:server-context.xml","classpath:application-context.xml");

In the web application Spring is configured in web.xml:

<servlet>
    <servlet-name>Jersey REST Service</servlet-name>
    <servlet-class>com.sun.jersey.spi.spring.container.servlet.SpringServlet</servlet-class>
    <init-param>
        <param-name>com.sun.jersey.spi.container.ResourceFilters</param-name>
        <param-value>example.restful.server.filter.SecurityFilterFactory</param-value>
    </init-param>
    <init-param>
        <param-name>com.sun.jersey.config.property.resourceConfigClass</param-name>
        <param-value>com.sun.jersey.api.core.PackagesResourceConfig</param-value>
    </init-param>
    <init-param>
        <param-name>com.sun.jersey.config.property.packages</param-name>
        <param-value>example.restful.rest</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:application-context.xml</param-value>
</context-param>
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

How can I make the web application use the application context already created instead of duplicating all the beans?

回答1:

I think you can create your own ContextLoaderListener and overriding createWebApplicationContext to return the ApplicationContext of your JavaSE app (assuming you have some kind of ApplicationContextHolder to keep a static access to it).

It seems easy, but you need to return a WebApplicationContext and not a ClassPathXmlApplicationContext to do this, I think you can do something like this:

public class MyContextLoaderListener extends ContextLoaderListener{
    protected WebApplicationContext createWebApplicationContext(ServletContext sc,
                                                        ApplicationContext parent){
        ApplicationContext javaSEAppContext = AppContextHolder.getAppContext();
        GenericWebApplicationContext context = new GenericWebApplicationContext(servletContext);
        context.setParent(javaSEAppContext);
        return context;
    }
}

And of course adapt your web.xml

    <!-- <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:application-context.xml</param-value>
    </context-param> -->
    <listener>
        <listener-class>com.company.MyContextLoaderListener</listener-class>
    </listener>


回答2:

Another approach would be to register your bean as a JNDI and then lookup for the JNDI to get a handle of the bean. Unfortunately as far as I am aware there is a MBeanExporter to register Mbeans but there isn't anything to bind a bean to the server's JNDI tree.

However developing a customer JNDI Exporter would not be very difficult as spring has an hook to JNDI - JndiTemplate.So all you would need to do is create a class which implements the DisposableBean and with attributes like jndiName and jndiBean and use the below to bind the bean to the jndi name.

JndiTemplate.bind()

The spring xml should read something like this

<bean id="testBean" class="com.test.testBean"/>

<bean class="com.framework.JndiExporter">
   <property name="jndiBean" ref="testBean" />
   <property name="jndiName" value="com.test.testBean"/>
</bean>

Once this is registered, you can use the usual way to lookup the jndiTree for this jndiName.

Hope this helps to find a cleaner approach !



标签: java spring