内嵌Jetty集成SpringMVC提供HTTP服务

项目中需要内嵌Jetty提供HTTP服务,并且需要基于SpringMVC实现MVC模式和Restful接口。实现方案如下所示。

需要的jar包

本方案需要的核心jar包如下(其他jar包自行补充):

jackson-annotations-2.9.5.jar
jackson-core-2.9.5.jar
jackson-databind-2.9.5.jar
jetty-all-9.4.10.v20180503-uber.jar
spring-aop-4.3.16.RELEASE.jar
spring-aspects-4.3.16.RELEASE.jar
spring-beans-4.3.16.RELEASE.jar
spring-context-4.3.16.RELEASE.jar
spring-context-support-4.3.16.RELEASE.jar
spring-core-4.3.16.RELEASE.jar
spring-expression-4.3.16.RELEASE.jar
spring-web-4.3.16.RELEASE.jar
spring-webmvc-4.3.16.RELEASE.jar
spring-websocket-4.3.16.RELEASE.jar

基于Java的配置

由于使用的是spring4.x版本,所以直接使用Java配置,代码如下:

1.MVC配置

@EnableWebMvc
@Configuration
public class MvcConfiguration implements WebMvcConfigure{
    ...
    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> list){
        list.add(new MappingJackson2HttpMessageConverter());
    }
}

2.应用配置

@Configuration
@ComponentScan("com.xx.xx")
@Import({MvcConfiguration.class})
public class HttpServerConfiguration{

}

启动函数

启动函数作为启动Jetty服务的入口,主要用于配置Handler,将SpringMVC的DispatcherServlet加入到handler中,拦截所有请求。代码示例如下所示:

public class JettyServer{
    private static final int DEFAULT_PORT = 8080;
    private static final String CONTEXT_PATH = "/";
    private static final string MAPPING_URL = "/*";

    private WebApplicationContext webApplicationContext(){
        AnnotationConfigWebApplicationContext configWebApplicationContext = new AnnotationConfigWebApplicationContext();
        configWebApplicationContext.register(HttpServerConfiguration.class);
        return configWebApplicationContext;
    }

    private ServletContextHandler servletContextHandler(WebApplicationContext context){
        ServletContextHandler handler = new ServletContextHandler();
        handler.setContextPath(CONTEXT_PATH);
        handler.addServlet(new ServletHolder(new DispatcherServlet(context)),MAPPING_URL);
        handler.addEventListener(new ContextLoaderListener(context));
        return handler;
    }

    public void run() throws Exception {
        Server server = new Server();
        ServerConnector connector = new ServerConnector(server);
        connector.setPort(DEFAULT_PORT);
        server.setConnectors(new Connector[]{connector});
        server.setHandler(servletContextHandler(webApplicationContext()));
        server.start();
        server.join();
    }
}

配置启动函数

通过在spring-context.xml中配置JettyServer,实现在应用启动时启动Http服务。

<bean id="jettyServer" class="com.xx.xx.JettyServer" init-method="run" />

如果要自定义HTTP服务的端口、连接池大小、上下文路径、URL映射等,可以在JettyServer中添加对应的属性并设置get/set方法,然后在这里通过<property>进行配置。

最后,在主函数里启动Spring即可。

public class Launcher{
    public static void main(String[] args){
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:/spring-context.xml");
    }
}

ApplicationContext冲突问题

通过上面的配置可以看到,应用会生成两个ApplicationContext的实例:ClassPathXmlApplicationContextAnnotationConfigWebApplicationContext,如果你在项目中有SpringContextHolder类,用于获取ClassPathXmlApplicationContext,此时要注意,AnnotationConfigWebApplicationContext可能会覆盖掉你想要的ClassPathXmlApplicationContext的实例。再注入的时候就需要判断下,代码示例如下:

@Service
@Lazy(false)
public class SpringContextHolder implements ApplicationContextAware,DisposableBean{
     private static final Logger LOGGER = LoggerFactory.getLogger(SpringContextHolder.class);
     private static ApplicationContext applicationContext = null;
     public static ApplicationContext getApplicationContext(){
         assertContextInjected();
         return applicationContext;
     }

     public static <T> T getBean(Class<T> requiredType,String name){
         assertContextInjected();
         return (T)applicationContext.getBean(name);
     }

     public static <T> T getBean(Class<T> requiredType){
         assertContextInjected();
         return applicationContext.getBean(requiredType)
     }

     public static void clearHolder(){
         applicationContext = null;
     }

     @Override
     public void setApplicationContext(ApplicationContext applicationContext){
         if(applicationContext != null && applicationContext instanceof ClassPathXmlApplicationContext){
             SpringContextHolder.setApplicationContext2(applicationContext);
         } else if(SpringContextHolder.applicationContext!=null&& applicationContext instanceof ClassPathXmlApplicationContext){
             LOGGER.info("SpringContextHolder中的ApplicationContext被覆盖,原有ApplicationContext为:"+SpringContextHolder.applicationContext);
             SpringContextHolder.setApplicationContext2(applicationContext);
         }
     }

     public static void setApplicationContext2(ApplicationContext applicationContext){
         SpringContextHolder.applicationContext = applicationContext;
     }

     @Override
     public void destroy() throws Exception{
         SpringContextHolder.clearHolder();
     }

     private static void assertContextInjected(){
         Validate.validState(applicationContext!=null,"applicationContext属性未注入");
     }
}

猜你喜欢

转载自blog.csdn.net/twypx/article/details/80461817