Jersey採用模板Freemarker輸出

簡介

首先來講一下什麼是jersey,他是實現了restful風格的其中一個框架,固然除了jersey還有其餘的,例如html

不過在平時在開發web下,目前流行的仍是以spring mvc爲主,但如今也有一些後起之秀,就例如java

  • JFinal
  • Spring boot

以筆者瞭解呢目前jersey用得仍是蠻多的,最主要是體如今對外接口上面。程序員

不說那麼多了,送上看看怎麼在jersey用freemarkerweb


配置

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的spring


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的配置apache


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等等api


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.ftlrestful


源碼分析

FreemarkerMvcFeature.java

直接看源碼config方法mvc

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是幹嗎的app


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 若有說得很差多多指教

相關文章
相關標籤/搜索