SpringMVC结合Freemarker在页面调用静态方法优化总结
来源:互联网 发布:极限挑战电影 知乎 编辑:程序博客网 时间:2024/05/08 08:38
概述
由于之前一直使用Struts2+Spring开发项目,整合Freemarker时页面如果想直接调用静态方法,可以使用<#assign a= stack.findValue(‘@com.test.package.class@method’)>的方式获取静态方法调用的返回值,现在使用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在页面调用静态方法优化总结
- SpringMVC + FreeMarker 页面静态化
- SpringMVC+Freemarker生成静态页面
- freemarker调用静态方法
- springMVC freemarker实现页面静态化
- springMVC、freemarker页面半自动静态化
- SpringMVC的freemarker支持(页面静态话)
- FreeMarker调用JAVA静态方法
- Freemarker 调用Java静态方法
- Freemarker 调用Java静态方法
- Freemarker 调用Java静态方法
- freemarker调用java静态方法以及枚举
- freemarker之调用JAVA静态方法和静态属性
- 工作总结----freemarker调用静态方法(静态属性)
- 性能优化:用FreeMarker实现页面静态化
- Freemarker调用java静态方法(也可以调用常量,枚举)
- freemarker页面静态化
- Freemarker生成静态页面
- CodeForces
- 安装SQL Server 2005提示IIS未安装或者未启用的解决方法
- tomcat内存溢出解决方法
- 启动Tomcat6.x时manager does not exist or is not a readable directory
- 重新安装windows后,恢复ubuntu正常引导
- SpringMVC结合Freemarker在页面调用静态方法优化总结
- 树莓派Android Things物联网开发:创建一个Things项目
- 写给未来的你——老婆
- PHP中global的用法
- linx下的ssh配置
- grails中axis2插件问题
- MySQL必知必会读书笔记26-30章
- CentOS6.1网络服务无法正常启动
- edhat/Centos第二块网卡添加/绑定IP