项目中需要内嵌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的实例:ClassPathXmlApplicationContext
和AnnotationConfigWebApplicationContext
,如果你在项目中有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属性未注入");
}
}