1. 程式人生 > >SpringMVC結合Freemarker在頁面呼叫靜態方法優化總結

SpringMVC結合Freemarker在頁面呼叫靜態方法優化總結

概述

由於之前一直使用Struts2+Spring開發專案,整合Freemarker時頁面如果想直接呼叫靜態方法,可以使用<#assign a= stack.findValue(‘@[email protected]’)>的方式獲取靜態方法呼叫的返回值,現在使用SpringMVC+Freemarker來開發專案時,由於慣性思維的緣故,也想在頁面上直接呼叫靜態方法,來獲取靜態方法的返回值。

  • 現有方案
    在網上找了一部分的相關資料,只找到類似的方案:http://www.cnblogs.com/yqweber/p/3992513.html,仔細閱讀這個方案,大致的思路是會在專案resources下新建一個staticClass.properties檔案,在檔案內配置所有需要使用的靜態類,然後新建一個FreemarkerStaticModels類繼承HashMap,用於將所有的靜態類配置封裝成<靜態類名,TemplateHashModel >的形式,然後在Spring配置檔案中的對FreeMarkerViewResolver的attributeMap配置成這個FreemarkerStaticModels物件,完成靜態方法的配置,頁面上可以直接使用靜態類名+靜態方法名的方法來訪問靜態方法了。

  • 該方法的明顯不足
    1、如果每次我需要新建一個靜態類,就需要在配置檔案中新增一個配置,感覺特別麻煩;
    2、如果我在配置檔案中配置的靜態類包名不正確,會導致該靜態方法載入異常;
    3、如果靜態類特別多,這個檔案會新增很多配置,這樣看起來也不是特別舒服,查詢起來也不方便。

新的思路

所以就在原有的基礎上進行了修改,大致思路是考慮掃描相關的靜態類所處的包,然後將所有的靜態類在FreemarkerStaticModels裡面進行封裝,而不去進行手動的配置,這樣大大減少了手動配置的麻煩,同時也簡化了開發,當然也會有一些問題,下面也會進行討論。

具體實現

  • 編寫FreemarkerStaticModels類

    該類是對上面FreemarkerStaticModels類的改造,通過包掃描方式來載入靜態資源,這個類裡面使用了懶載入模式,包掃描,後置處理器等一些常用的操作,具體實現如下:
import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.beans.BeansException;
import
org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.core.io.Resource; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.core.io.support.ResourcePatternResolver; import org.springframework.core.type.classreading.CachingMetadataReaderFactory; import org.springframework.core.type.classreading.MetadataReader; import org.springframework.core.type.classreading.MetadataReaderFactory; import org.springframework.ui.ModelMap; import org.springframework.util.ClassUtils; import org.springframework.util.SystemPropertyUtils; import freemarker.ext.beans.BeansWrapper; import freemarker.template.TemplateHashModel; import freemarker.template.TemplateModelException; public class FreemarkerMap extends ModelMap implements BeanFactoryPostProcessor{ private static final long serialVersionUID = -4675940717727748450L; private List<String> locations; private static final String DEFAULT_RESOURCE_PATTERN = "**/*.class"; private static Logger log = Logger.getLogger(FreemarkerMap.class); public List<String> getLocations() { return locations; } public void setLocations(List<String> locations) { this.locations = locations; } private FreemarkerMap(){} private static volatile FreemarkerMap instance; /** * 懶載入模式 */ public static FreemarkerMap getInstance(){ if(instance == null){ synchronized (FreemarkerMap.class) { if(instance == null){ instance = new FreemarkerMap(); } } } return instance; } /** * 後置處理器重新postProcessBeanFactory方法,載入靜態類的配置 */ public void postProcessBeanFactory( ConfigurableListableBeanFactory beanFactory) throws BeansException { loadCheckClassMethods(locations); } /** * 根據掃描包的配置 * 載入需要檢查的方法 */ private static void loadCheckClassMethods(List<String> scanPackages) { ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver(); MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resourcePatternResolver); for (String basePackage : scanPackages) { if (StringUtils.isBlank(basePackage)) { continue; } String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + ClassUtils.convertClassNameToResourcePath(SystemPropertyUtils.resolvePlaceholders(basePackage)) + "/" + DEFAULT_RESOURCE_PATTERN ; try { Resource[] resources = resourcePatternResolver.getResources(packageSearchPath); for (Resource resource : resources) { loadClassMethod(metadataReaderFactory, resource); } } catch (Exception e) { log.error("初始化SensitiveWordInterceptor失敗", e); } } } /** * 載入資源,判斷裡面的方法 * * @param metadataReaderFactory spring中用來讀取resource為class的工具 * @param resource 這裡的資源就是一個Class * @throws IOException */ private static void loadClassMethod(MetadataReaderFactory metadataReaderFactory, Resource resource) throws IOException { try { if (resource.isReadable()) { MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(resource); if (metadataReader != null) { String className = metadataReader.getClassMetadata().getClassName(); try { tryCacheMethod(className); } catch (ClassNotFoundException e) { log.error("檢查" + className + "是否含有需要資訊失敗", e); } } } } catch (Exception e) { log.error("判斷類中的方法實現需要檢測xxx失敗", e); } } /** * 把action下面的所有method遍歷一次,標記他們是否需要進行xxx驗證 * 如果需要,放入cache中 * * @param fullClassName * @throws TemplateModelException */ private static void tryCacheMethod(String fullClassName) throws ClassNotFoundException, TemplateModelException { Class<?> clz = Class.forName(fullClassName); Method[] methods = clz.getDeclaredMethods(); for (Method method : methods) { int mod = method.getModifiers(); if (Modifier.isStatic(mod)&&Modifier.isPublic(mod)) { BeansWrapper beansWrapper = BeansWrapper.getDefaultInstance(); TemplateHashModel model = beansWrapper.getStaticModels(); log.debug("已載入"+clz.getName()); instance.put(clz.getSimpleName(), (TemplateHashModel)model.get(clz.getName())); break; } } } }

該方法會掃描locations指定路徑包及子包下的所有類,同時也支援模糊包匹配,如果該類下面有靜態方法,則會將該類加入FreemarkerMap中,如果沒有則會跳過,掃描包時會做限制掃描指定路徑下的包,而不會全域性掃描,防止因為專案太大,掃描時會比較慢,導致專案啟動變慢,同時該方法也支援列舉內靜態方法的呼叫。

  • Spring配置FreemarkerMap
    locations指定掃描包的路徑:
    <bean id="freemarkerMap" class="com.test.freemarker.FreemarkerMap" factory-method="getInstance">
        <property name="locations">
            <list>
                <value>com.test.common.*.b</value>
                <value>com.test.util.*.a</value>
            </list>
        </property>
    </bean>
  • 配置FreeMarkerViewResolver
    attributeMap指定freemarkerMap的引用
<!-- 要求檢視使用FreeMarker模板,指定controller層返回的頁面在webapp目錄下進行訪問,且為html頁面-->  
    <bean class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">  
        <property name="prefix">  
            <value>/</value>  
        </property>  
        <property name="suffix">  
            <value>.html</value>
        </property>  
        <!-- 此處需要宣告為utf-8編碼,否則即使頁面是utf-8編碼,中文還是不能正常顯示 -->  
        <property name="contentType" value="text/html;charset=UTF-8"></property> 
        <property name="attributesMap" ref="freemarkerMap"/>   
    </bean>
  • 頁面讀取方式
    頁面直接使用類似於${(StaticUtil.get().name)!”}的讀取方式來獲取就行了,由於get()方法可以返回一個物件,freemarker可以直接.屬性來獲取對應屬性的值,非常方便。

不足與改進

  • 不足
    其實這種實現方案自然不是最優的,肯定也會存在一些不足之處:
    1、如果一個包下只有一個靜態類,要掃描整個包感覺會很笨;
    2、如果專案很龐大,包掃描時很有可能會有遺漏,除非有非常明確的包及分層結構;
    3、如果專案很龐大,包掃描可能也會導致專案啟動變得很慢。

  • 優化
    可以考慮將原始的版本和目前版本進行整合,如果有的包下只有一個靜態類,那麼我把這個靜態類放到配置檔案中進行配置,如果明確某一個包下大部的類都是靜態的,比如列舉包,那麼可以直接將這個包通過包掃描的方式進行載入,整合之後也可以有效的解決兩種方案上的不足。

相關推薦

SpringMVC結合Freemarker頁面呼叫靜態方法優化總結

概述 由於之前一直使用Struts2+Spring開發專案,整合Freemarker時頁面如果想直接呼叫靜態方法,可以使用<#assign a= stack.findValue(‘@[email protected]’)>的方式獲取靜態方

[轉] 兩個靜態html頁面傳值方法總結

問題 因最近嘗試實現客戶端與服務端分離,服務端只提供介面,客戶端用html+js實現,分成兩個獨立的專案部署,因專案是個人專案,客戶端展示不考慮使用像Angular、Vue、Native這種前端框架實現,於是全部使用靜態頁面實現,請求資料用ajax,在開發的過程中,就遇到了頁面之間傳

C# 多執行緒呼叫靜態方法或者靜態例項中的同一個方法-方法內部的變數是執行緒安全的

 C#  多執行緒呼叫靜態方法或者靜態例項中的同一個方法-方法內部的變數是執行緒安全的       using System;using System.Threading;using System.Threading.Tasks;using Sys

static{}(即static塊),會在類被載入的時候執行且僅會被執行一次,一般用來初始化靜態變數和呼叫靜態方法

這裡是一個工具類,因為連線配置資訊只需要執行一次就行所以採用static塊 static塊會最先執行 package com.my.jedis; import java.io.IOException; import java.io.InputStream; import

mybatis mapper.xml檔案裡怎麼呼叫靜態方法

剛用mybatis時,當時想要在mapper.xml檔案裡呼叫靜態方法,比如某個物件的id是由工具類的靜態方法生成的,所以想在insert語句裡呼叫那個工具類的靜態方法,不過當時搜尋了半天沒有解決,最近在另一個專案組無意中看到其他同事寫的mapper檔案發現裡面就有這個用法

為什麼可以通過類名呼叫靜態方法?

JVM載入類: 所有的類都是在對其第一次使用時,動態載入到JVM中的。當程式建立第一個類的靜態成員的引用時,就會載入這個類,進而可以得到該類的型別資訊,而型別資訊可以在程式執行時發現和使用型別

static修飾方法--向上轉型呼叫靜態方法的特別之處(反常規)

比較下面程式碼輸出結果(實際開發中不會遇到,只是在找工作時可能會遇到) public class StaticTest { public static void main(String[]

ognl呼叫靜態方法用法以及注意事項

首先要在struts.xml加上<constant name="struts.ognl.allowStaticMethodAccess" value="true"></constant> 還要注意標籤如果巢狀會不起作用 如果像下面這麼用 actio

SpringMVC結合freemarker模板回顯資料的實現

回顯資料經常可以在修改功能中看到,而SpringMVC結合freemarker會和其他的回顯做法有點不同,看栗子: Controller部分是這樣滴:        @RequestMapping(value = "/update", method = RequestMet

java 呼叫靜態方法和例項(物件)方法的區別

靜態方法和例項(物件)方法的區別主要體現在兩個方面: 區別1: 在外部呼叫靜態方法時,可以使用"類名.方法名"的方式,也可以使用"物件名.方法名"的方式。而例項方法只有後面這種方式。也就是說,呼叫靜態方法可以無需建立物件。 區別2: 靜態方法在訪問本類的成員時

Mockito呼叫靜態方法和void方法

1 mock 靜態方法mockito庫並不能mock靜態方法,需要依賴powermock第一步:給類添加註解// 靜態類優先載入,所以需要提前告訴powermock哪些靜態類需要mock @Contex

springmvc結合freemarker配置url訪問的根路徑

轉自:http://blog.csdn.net/suyu_yuan <bean id="freemarkerConfig"class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigur

靜態方法裡不能使用$this標識呼叫靜態方法

# 概述 static關鍵字宣告一個屬性或方法是和類相關的,而不是和類的某個特定的例項相關,因此,這類屬性或方法也稱為"類屬性"

類例項呼叫靜態方法(Java)

前言 第一次看到在Java中可以通過類例項呼叫靜態方法,當然不推薦這麼做,避免出現意想不到的問題,接下來會講到,但是在C#中通過類例項呼叫靜態方法在編譯時就不會通過,這裡做下記錄。 類例項呼叫靜態方法 首先我們來看一個簡單的例子說明為何不能通過類例項呼叫靜態方法,我們知道未例項化的物件去呼叫對應方法或者欄位時

python中例項方法,例項屬性,類方法,類屬性,靜態方法總結

class Game(object): #歷史最高分 top_score = 0 def __init__(self,player_name): self.name = player_name @staticmethod def show

Freemarker呼叫java靜態方法(也可以呼叫常量,列舉)

Java幫助類 public classFreemarkerUtils { /** * 獲取freemarker可使用的bean * @param clz 型別 * @return */ @Su

Spring MVC使用FreeMarker呼叫java靜態方法

Spring mvc配置檔案如下1.配置freemarker的檢視,如下 Java程式碼   <bean id="freemarkerResolver"           class="org.springframework.web.servlet.view.

Freemarker頁面靜態化技術

view color 分享圖片 doctype nbsp fig ces 取數據 doc 初步理解: 架構優化: 靜態頁面的訪問速度優於從緩存獲取數據的動態頁面的訪問速度; Freemarker: 導包 模板:hello.ftl 1 <!DOCTYPE

【PHP】靜態方法呼叫靜態方法靜態呼叫靜態方法程式碼解讀

static 關鍵字用來修飾屬性、方法,稱這些屬性、方法為靜態屬性、靜態方法。 在類的靜態方法中是不能直接以$this->test()的方式呼叫非靜態方法的。還有框架中靜態的呼叫非靜態方法是怎麼回事??? 。。。 算了,不知道說啥 具體為啥看程式碼註釋: <?ph

靜態方法呼叫spring注入的service類問題

@Component //標註此類是個中立類 public class ListMergeUtil { @Autowired private IWXTopupService wxTopupService; @Autowired private IWXConsume