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