最近新开一个项目,一部分页面,大部分属于API,新开当然严格要求下Restful规范,要求技术栈是Java,对比了一下目前的几个框架,觉得 Jersey 和 Spring MVC 都不错,但是 Jersey支持 JSR311 规范,所以就没随主流使用 Spring MVC 了。
关于框架怎么搭,看下官方文档其实很快的,这里记录一搭建时遇到的一些问题。
技术栈说明
首先,使用 Maven 做为依赖管理工具,另外,除子很的的 Restful API 之外,我的项目里面还有一部分 Web 页面。所以多引入了 jersey-mvc-jsp
扩展,使用 jersey-media-json-jackson
返回 json 风格的数据。
另外,使用 Jetty 做为开发用的服务器,感觉比 Tomcat 实在方便太多。直接在 Maven 里面配置即可。
最后,我的前端页面使用了 Sass 进行 CSS 编译,使用 gulp、webpack、babel 进行 Js 编译与自动化。
静态文件Lock
这个是 Jetty 的问题,由于我有一部分静态页面要开发,所以必须不能被 Lock ,不然我的 js,css 文件都无法生成。
如果是用Jetty8的话,需要把connector切换到bio模式,在pom.xml里面加入以下配置即可。
<configuration>
<connectors>
<connector implementation="org.eclipse.jetty.server.bio.SocketConnector">
<port>80</port>
</connector>
</connectors>
</configuration>
但Jetty9不支持BIO了,没有办法通过connector参数解决,我的解决如下:在web.xml上面设置org.eclipse.jetty.servlet.Default.useFileMappedBuffer
为false,覆盖掉jetty默认的值,虽然有污染代码,但对功能没什么影响,因为Jetty只是开发用,生产环境不是Jettty,如果生产环境也是Jetty的话,最好搞清楚再加。
<!-- web.xml -->
<context-param>
<param-name>org.eclipse.jetty.servlet.Default.useFileMappedBuffer</param-name>
<param-value>false</param-value>
</context-param>
WebSocket
如果使用Jersey全局拦截的话,ws请求大半情况是404,你的 @ServerEndpoint
永远找不到。但我又不是太想用Jersey自带的Server-Sent Events (SSE)
功能,感觉太不主流,也不想用Atmosphere
这样的插件,感觉加重代码复杂度增加。目前我的解决方案如下:
- 按原生的WebSocket先写着,代码不贴了,这里路径是
@ServerEndpoint("/_")
- 自定义Fitter
import org.glassfish.jersey.servlet.ServletContainer;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 开放WebSocket请求
*/
public class XDFilter extends ServletContainer {
@Override
public void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
if(request.getRequestURI().endsWith("/_")){
chain.doFilter(request,response);
}else{
super.doFilter(request, response, chain);
}
}
}
- 把web.xml里面先前的
<filter>
<filter-name>xdnote</filter-name>
<!-- 先前的jersey提供的filter注释掉,用我自己的filter-->
<!--<filter-class>org.glassfish.jersey.servlet.ServletContainer</filter-class>-->
<filter-class>com.xdnote.fiter.XDFilter</filter-class>
</filter>
- OK了,使用JavaScript里面的
new WebSocket(ws://localhost/_)
试一试,应该是可以成功了。 - 注意以下几点
- 虽然Jetty7就支持WebSocket,但以Maven插件形式的Jetty需要Jetty9才支持WebSocket
- 我的解决方案(最终War包)在Tomcat7 上 不行,在Tomcat8 上可以
配置
我是一个有洁癖的人,肯定是不想像官方那样写一个启动类的,如果配置能弄好就不手写一个class了,最后我的Fitter配置如下。
<filter>
<filter-name>xdnote</filter-name>
<!-- 说明:如果不使用websocket可以直接用jersey的Filter--->
<!--<filter-class>org.glassfish.jersey.servlet.ServletContainer</filter-class>-->
<filter-class>com.xdnote.fiter.XDFilter</filter-class>
<init-param>
<param-name>jersey.config.server.mvc.templateBasePath.jsp</param-name>
<param-value>/WEB-INF/jsp</param-value>
</init-param>
<!-- 自动扫描包 这样我就不用写配置类了,自动扫描我所有的控制器-->
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>com.xdnote</param-value>
</init-param>
<init-param>
<param-name>jersey.config.server.provider.scanning.recursive</param-name>
<param-value>true</param-value>
</init-param>
<!-- 自动注册配置类 :这里自己写了一个走向通用错误页面的provider -->
<init-param>
<param-name>jersey.config.server.provider.classnames</param-name>
<param-value>
org.glassfish.jersey.server.mvc.jsp.JspMvcFeature,
org.glassfish.jersey.media.multipart.MultiPartFeature,
org.glassfish.jersey.server.spring.scope.RequestContextFilter,
com.xdnote.provider.AppExceptionMapper
</param-value>
</init-param>
<!-- 禁用WADL -->
<init-param>
<param-name>jersey.config.server.wadl.disableWadl</param-name>
<param-value>true</param-value>
</init-param>
<!-- 静态文件正则表达式 -->
<init-param>
<param-name>jersey.config.servlet.filter.staticContentRegex</param-name>
<param-value>/(((images|css|js|fonts)/.*)|(favicon\.ico))</param-value>
</init-param>
<!-- MIME Type -->
<init-param>
<param-name>jersey.config.server.mediaTypeMappings</param-name>
<param-value>json:application/json,js:application/javascript</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>xdnote</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Spring 不能使用最新的 4.x 版本
首先看是不是必须,如果没有强迫症什么都要用最新的版本的话,Jersey 默认会引入3.x 系列的 Spring Core 做为核心依赖。
网上很多使用 Maven excude include 的方法,我也试过,可以,但会有一些莫名奇妙的 Bug,所以这里不推荐使用。
其它
Jersey的坑还有很多的,有一些在配置里面解决了,还有一些坑应该都比较简单,Google一下就OK了。