天天看點

寫一個自己的視圖解析器-MyView

用過 原生jsp 的解析器?用過 Thymeleaf ?用過 Freemark ?随心所欲,好,我們寫一個自己的視圖解析器吧

/**
*	首先實作 ViewResolver
*	實作 Ordered 優先加載我們自己的随心所欲視圖解析器
*/
// 聲明成元件,讓掃描器掃到,大功告成
@Component 
public class MyView implements ViewResolver, Ordered {

	// 用來緩存頁面,這樣就不用每次都讀模版檔案,減少IO
	// key是視圖路徑,value是html文本内容
	// 當然你這裡可以用redis、memcached 等等,随心所欲
    Map<String, String> cache = new HashMap<>();
	// 路徑字首 這裡是放在 resources 下,也就是打包之後的classpath下,當然你也可以放在任何盤符之下
    private String prefix = "static/pages";
    // 模版檔案字尾
    private String suffix = ".html";
    // 是否要緩存模版,開發環境當然 不
    private boolean cached = false;

    @Override
    public int getOrder() {
    	// 最先加載
        return 1;
    }

	/**
	* view:controller return過來的視圖位址
	* locale:國際化環境
	*/
    @Override
    public View resolveViewName(String view, Locale locale) {
    	// 直接實作接口,傳回視圖
        return new View() {
			// 最重要的方法,渲染頁面
            @Override
            public void render(Map<String, ?> map, HttpServletRequest request, HttpServletResponse response) {
                String html = getHtml(view);
                // 設定響應頭
                response.setContentType("text/html;charset=utf-8");
                try {
                // 寫資料到浏覽器
                    IOUtils.write(html, response.getOutputStream(), "UTF-8");
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

			// 設定可以處理的request格式
            @Override
            public String getContentType() {
                return "text/html";
            }
        };
    }

	// 實作的主要方法
    private String getHtml(String path) {
    	// 緩存中取
        String s = cache.get(path);
        if (null == s) {
            try {
            	// 擷取模版檔案
                ClassPathResource resource = new ClassPathResource(prefix + path + suffix);
                // 讀取為字元串
                s = IOUtils.toString(resource.getInputStream(), "UTF-8");
            } catch (FileNotFoundException e) {
                throw new NullPointerException(String.format("視圖[%s]不存在", path));
            } catch (IOException e) {
                e.printStackTrace();
            }
            // 處理包含标簽
            s = processInclude(s);

            if (cached) {
                cache.put(path, s);
            }
        }
        return s;
    }

	/**
	* jstl有 include标簽,我們的随心所欲當然也不能少,至于其他标簽,自己實作吧
	*/
    private String processInclude(String s) {
    	// 這則比對,并且分組
    	// 例: 
    	// <p>123</p>
    	// <include   src="test.html" />
    	// <p>123</p>
        Matcher matcher = Pattern.compile("\\<include\\s+src\\=[\"|\'](.+?)[\"|\']\\s+\\/\\>").matcher(s);

        while (matcher.find()) {
            String source = matcher.group();
            String path = matcher.group(1);
            String html = getHtml(path);
            s = s.replace(source, html);
        }

        return s;
    }

}