How to make embedded Jetty use the AppContext it&#

2019-08-07 08:23发布

问题:

I have a standalone java app that now runs an embedded Jetty server to expose a RESTful API for HTTP. It does make heavy use of Spring beans for everything from Hibernate to Jetty. I have Jetty configured with a DispatcherServlet ( the thought being that adding a non-REST API in the future will be as simple as making the new Controller and mapping it correctly for the dispatcher).

My app has a class with a main method that creates a ClassPathXmlApplicationContext from my appContext.xml to start everything up.

ApplicationContext ac= new ClassPathXmlApplicationContext(new String[] { "appContext.xml" });

I don't know how to make beans defined in the context config file for the DispatcherServlet have access to beans defined in the appContext.xml where jetty is defined. My Jetty definition looks like this:

<bean id="JettyServer" class="org.eclipse.jetty.server.Server" init-method="start" destroy-method="stop">

    <constructor-arg>
            <bean id="threadPool" class="org.eclipse.jetty.util.thread.QueuedThreadPool">
                <property name="minThreads" value="2"/>
                <property name="maxThreads" value="10"/>
            </bean>
    </constructor-arg>

    <property name="connectors">
        <list>
            <bean id="Connector" class="org.eclipse.jetty.server.ServerConnector">
                <constructor-arg ref="JettyServer"/>
                <property name="port" value="8090"/>
            </bean>
        </list>
    </property>

    <property name="handler">
        <bean id="handlers" class="org.eclipse.jetty.server.handler.HandlerCollection">
            <property name="handlers">
                <list>
                    <bean class="org.eclipse.jetty.servlet.ServletContextHandler">
                        <property name="contextPath" value="/"/>
                        <property name="servletHandler">
                            <bean class="org.eclipse.jetty.servlet.ServletHandler">
                                <property name="servlets">
                                    <list>
                                        <bean class="org.eclipse.jetty.servlet.ServletHolder">
                                            <property name="name" value="DefaultServlet"/>
                                            <property name="servlet">
                                                <bean class="org.springframework.web.servlet.DispatcherServlet"/>
                                            </property>
                                            <property name="initParameters">
                                                <map>
                                                    <entry key="contextConfigLocation" value="classpath:./DefaultServlet.xml" />
                                                </map>
                                            </property>
                                        </bean>
                                    </list>
                                </property>
                                <property name="servletMappings">
                                    <list>
                                        <bean class="org.eclipse.jetty.servlet.ServletMapping">
                                            <property name="pathSpecs">
                                                <list><value>/</value></list>
                                            </property>
                                            <property name="servletName" value="DefaultServlet"/>
                                        </bean>
                                    </list>
                                </property>
                            </bean>
                        </property>
                    </bean>
                    <bean class="org.eclipse.jetty.server.handler.RequestLogHandler">
                        <property name="requestLog">
                            <bean class="org.eclipse.jetty.server.NCSARequestLog">
                                <constructor-arg value="/opt/impulse/logs/jetty-yyyy_mm_dd.log"/>
                                <property name="extended" value="false" />
                            </bean>
                        </property>
                    </bean>
                </list>
            </property>
        </bean>
    </property>

  </bean>

Then in DefaultServlet.xml I try to defined a bean with a property references a bean defined in appContext.xml, which is what breaks.

<bean id="restApiController" class="com.mycompany.myapp.api.controllers.RESTfulController">
    <property name="someBean" ref="someBean"/>
</bean>

回答1:

You are bootstrapping Jetty with applicationContext.xml, which in turn sets up jetty with your servlet configuration. Inside it you are configuring your servlet with the contextConfigLocation parameter pointing to the servlet application context. It will still run as a webapp, even if you embed it. So you need to provide your servlet with the config to your other beans as well. I suggest that you extract the jetty setup into it's own file, and then the rest of your beans in a different file. You then supply the other context file in the contextConfigLocation.

Edit

If you really need to share the application context between jetty and your servlet, maybe you can use some of the information in this blog. It seems to be possible, but it looks like you have to wire up the parent/child relationship between the contexts manually.



回答2:

For me, what worked is setting of ResourceConfig. With DispatcherServlet server was not even able to serve Rest call. So I used ServletContainer. Now Rest call worked but not able to access beans loaded in ApplicationContext. There ResourceConfig registration helped. Below was my configuration that I came up after long R & D. I had Jetty version 9.2.11.v20150529 and Spring 4.1.2.RELEASE

<bean class="org.eclipse.jetty.servlet.ServletHolder">
    <property name="name" value="DefaultServlet"/>
       <property name="servlet">              
             <bean id="servletContainer" class="org.glassfish.jersey.servlet.ServletContainer">
                <constructor-arg>
                    <ref bean="config" />
                </constructor-arg>
            </bean>                                            
         </property>
      </bean>
<bean id="config" class="org.glassfish.jersey.server.ResourceConfig" />

Basically I set ResourceConfig under ServletContainer. Then in application, I fetched all beans loaded in my applicationContext and register with this Resource config like below

ResourceConfig restConfig = (ResourceConfig)webContext.getBean("config");

String[] beans = context.getBeanDefinitionNames();

for(String bean : beans)
  restConfig.registerInstances(context.getBean(bean));

Well, webContext here is WebAppContext which is required instead of ServletContaxtHandler. So instead of below lines as mentioned in question

 <bean class="org.eclipse.jetty.servlet.ServletContextHandler">
 <property name="contextPath" value="/"/>

I have

    <!-- To work with Spring , we need WebAppContext instead of ServletContext -->
    <!-- <bean id="servletContextHandler" class="org.eclipse.jetty.servlet.ServletContextHandler"> -->
    <constructor-arg name="webApp" value="./target/classes/" />
    <constructor-arg name="contextPath" value="/" />