简介
首先来说一下什么是jersey,他是实现了restful风格的其中一个框架,当然除了jersey还有其他的,例如
- apache axis2
- apache CXF
- spring mvc 也算上一个,但不是标准的
不过在平时在开发web下,目前流行的还是以spring mvc为主,但现在也有一些后起之秀,就例如
- JFinal
- Spring boot
以笔者了解呢目前jersey用得还是蛮多的,最主要是体现在对外接口上面。
不说那么多了,送上看看怎么在jersey用freemarker
配置
maven
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>javax.el</groupId>
<artifactId>javax.el-api</artifactId>
<version>2.2.4</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
<version>2.23.1</version>
</dependency>
<dependency>
<groupId>org.glassfish.web</groupId>
<artifactId>javax.el</artifactId>
<version>2.2.4</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.ext</groupId>
<artifactId>jersey-mvc-mustache</artifactId>
<version>2.23.1</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.ext</groupId>
<artifactId>jersey-mvc-freemarker</artifactId>
<version>2.23.1</version>
</dependency>
简单说一下这个配置,最重要的还是最后那两个,是jersey做的扩展,当然,他肯定有依赖freemarker的
web.xml
<servlet>
<servlet-name>restful</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>
com.example
</param-value>
</init-param>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>
com.example.MyApplication
</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>restful</servlet-name>
<url-pattern>/restful/*</url-pattern>
</servlet-mapping>
最重要的是配置那个扫描包(jersey.config.server.provider.packages
),和spring下的scan package一样的道理 另外还要让jersey识别我们的配置,那么还要写一个key为javax.ws.rs.Application
的配置
java 配置
public class MyApplication extends ResourceConfig {
public MyApplication() {
/*模板*/
Map<String, Object> pro = new HashMap<String, Object>(1);
//模板编码
pro.put("jersey.config.server.mvc.encoding.freemarker", "UTF-8");
//指定模板基础路径
pro.put("jersey.config.server.mvc.templateBasePath.freemarker", "WEB-INF/freemarker");
addProperties(pro).
register(FreemarkerMvcFeature.class);
}
}
这里主要注册一个让他支持freemarker的Feature,当然jersey支持了很多通过注册可以做的事情,包括Oauth、Filter、BeanValidata、Security、ContainerRequestFilter等等
Control
@Path("/views")
public class ViewResource {
@GET
@Path("example")
@Produces("text/html")
public Viewable exampleView() {
Map<String, String> data = new HashMap<String, String>();
data.put("text", "this is the ViewResource test text");
//这里需要说明一下,前面有"/"和没有是很大区别的
//没有的话,jersey会在base路径下,加上当前类的路径在加上这个exmaple
return new Viewable("/example", data);
}
}
最后这个java呢,其实就是配置一个控制器,最后通过访问/restful/views/example
就可以定位到这个控制器,最后找模板,这个配置的模板是在,/WEB-INF/freemarker/example.ftl
的
源码分析
FreemarkerMvcFeature.java
直接看源码config方法
public boolean configure(final FeatureContext context) {
final Configuration config = context.getConfiguration();
if (!config.isRegistered(FreemarkerViewProcessor.class)) {
// Template Processor.
context.register(FreemarkerViewProcessor.class);
// MvcFeature.
if (!config.isRegistered(MvcFeature.class)) {
context.register(MvcFeature.class);
}
return true;
}
return false;
}
我们平时要jersey支持web服务需要注册MvcFeature
,那么,现在看这里相当于FreemarkerMvcFeature
就依赖这个MvcFeature
,如果没有注册,他会帮我们注册,jersey这个点做得还是不错的 我们继续往下看看FreemarkerViewProcessor
是干嘛的
FreemarkerViewProcessor.java
@Override
protected Template resolve(final String templateReference, final Reader reader) throws Exception {
return factory.getConfiguration().getTemplate(templateReference);
}
@Override
public void writeTo(final Template template, final Viewable viewable, final MediaType mediaType,
final MultivaluedMap<String, Object> httpHeaders, final OutputStream out) throws IOException {
try {
Object model = viewable.getModel();
if (!(model instanceof Map)) {
model = new HashMap<String, Object>() {{
put("model", viewable.getModel());
}};
}
Charset encoding = setContentType(mediaType, httpHeaders);
template.process(model, new OutputStreamWriter(out, encoding));
} catch (TemplateException te) {
throw new ContainerException(te);
}
}
从resolve看出,其实这里算一个入口 ,他从freemarker的configuration中获取模板,然后直接从wirteTo进行写出,这里有个东西需要注意, 他把所有从Viewable中的数据如果是非Map,放到一个以model为key的Map当中,所以啊,我们开的时候,如果不是一个Map,需要从model中拿数据
另外,看看这个factory是如何产生的
this.factory = getTemplateObjectFactory(serviceLocator, FreemarkerConfigurationFactory.class,
new Value<FreemarkerConfigurationFactory>() {
@Override
public FreemarkerConfigurationFactory get() {
Configuration configuration =
getTemplateObjectFactory(serviceLocator, Configuration.class,
Values.<Configuration>empty());
if (configuration == null) {
return new FreemarkerDefaultConfigurationFactory(servletContext);
} else {
return new FreemarkerSuppliedConfigurationFactory(configuration);
}
}
});
哎呀,这个非常好啊,从这里创建这个Configuration,那么先不说getTemplateObjectFactory这个是干嘛,先说,下面FreemarkerDefaultConfigurationFactory
,这个就是采用web的加载器,其中包括模板加载器:WebappTemplateLoader
从web上下文加载、ClassTemplateLoader
累加载器加载,FileTemplateLoader
文件根目录加载 _FreemarkerSuppliedConfigurationFactory_这个就不说了,他其实就是直接返回上面拿到的configuration好了,我们下面分析一下getTemplateObjectFactory
getTemplateObjectFactory 自定义加载
先看源码
protected <F> F getTemplateObjectFactory(final ServiceLocator serviceLocator,
final Class<F> type, final Value<F> defaultValue) {
//这个值为 jersey.config.server.mvc.factory.freemarker
final Object objectFactoryProperty =
config.getProperty(MvcFeature.TEMPLATE_OBJECT_FACTORY + suffix);
if (objectFactoryProperty != null) {
//如果是Configuration类型,直接转成对象并且返回
if (type.isAssignableFrom(objectFactoryProperty.getClass())) {
return type.cast(objectFactoryProperty);
} else {
//如果是String类型,直接new 出来并且进行返回
Class<?> factoryClass = null;
if (objectFactoryProperty instanceof String) {
factoryClass = ReflectionHelper
.classForNamePA((String) objectFactoryProperty).run();
} else if (objectFactoryProperty instanceof Class<?>) {
factoryClass = (Class<?>) objectFactoryProperty;
}
if (factoryClass != null) {
if (type.isAssignableFrom(factoryClass)) {
return type.cast(serviceLocator.create(factoryClass));
} else {
LOGGER.log(Level.CONFIG,
LocalizationMessages.WRONG_TEMPLATE_OBJECT_FACTORY(factoryClass, type));
}
}
}
}
//否则返回空
return defaultValue.get();
}
这下子明白了,jersey的配置工厂都可以自定义,细节的东西还是考虑的很周全的,那么如果需要_扩展_Freemarker
配置,如TemplateLoader
、自定义指令
等等的这些玩意我们都可以弄了,那么具体怎么弄呢,是程序员的,还是喜欢看代码比文字的要多点的
//在jersey的Application中可以配置
//这个obj啊,做的事情可以是,一个类名,一个Configuration对象,一个Configuration的class
//这个就是Freemarker的配置
pro.put("jersey.config.server.mvc.factory.freemarker", obj);
//把你添加的信息告诉jersey
addProperties(pro);
那么最后完成我们的工作,其中包括怎么配置出jersey怎么输出freemarker模板,并且简单的分析了一下他的源码,以及怎么自定义配置freemarker 如有说得不好多多指教