精华帖 (1) :: 良好帖 (1) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2010-12-20
最后修改:2010-12-20
一直没太注意两个问题: 1、JSTL/EL官方上无法方便、直接的访问静态变量。 比如,我们定义了一个Constants类: public class Constants implements Serializable { public static final String CONSTANT_A= "ABC"; ... } 我们并不能直接这样使用: <c:out value="${Constants.CONSTANT_A}"/> 原因很简单: 1)这个Constants必须出现在某个scope,比如requestScope; 2)这个CONSTANT_A必须有一个getter方法,EL支持bean和map的规范 怎么办? 2、JSTL/EL如何才能简单的使用CodeTable(存储于DB的应用系统变量)? 应用系统变量几乎是无可避免的,好处大家都懂的。 一般人的思维肯定是: 1)提供一个service,拥有若干方法,比如getCodesByType(String codeType); 2)用的时候get出来,然后put到request上面,最后在JSP中用JSTL/EL来取出 最后的用法,以Spring MVC的tag为例,一般是: <form:select path="gender"> <form:option value="-" label="--Please Select"/> <form:options items="${CodeTable.Gender}" itemValue="codeValue" itemLabel="codeName"/> </form:select> 此时表示要获取Gender的CodeTable,并以codeValue为值,codeName为Label 是否存在更简单有效的方法? 我目前正在整合一些信息并加以模式化,试图提供一个简洁有效的办法来达成目标,也希望大家参与讨论,提供“一站式”的解决方案 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2010-12-20
自己封一个标签,或者封一个fn,这是最简单的办法。
|
|
返回顶楼 | |
发表时间:2010-12-20
标签?不行吧,有了custom的标签,如何用EL?
自己封装fn,我也觉得不好,或者说你在这方面有啥更具体的建议? |
|
返回顶楼 | |
发表时间:2010-12-20
我目前的思路是:
1、对于Constants,我将自动在启动时导出为applicationScope的map; 2、对于Code Table,我将自动在启动时加载到一系列的map,以CodeType为key 那之后就可以用“标准”的EL语法透明的访问Constants和CodeTable了。 但在这里有一个问题,那就是对于Code Table而言,一旦“自动在启动时加载到一系列的map”,会不会导致这些Code Table是non-reloadable的——我想这是难以让人接受的——总不能改一下系统变量就要reload一次application吧? |
|
返回顶楼 | |
发表时间:2010-12-28
基于上述思路,针对Constants的解决方案如下:
... public class ApplicationScopeLoader extends WebApplicationObjectSupport { private static final Logger logger = LoggerFactory.getLogger(ApplicationScopeLoader.class); /** constant classes which can be injected by spring */ private Map<String, String> constantClass; protected void initApplicationContext() { try { //build constants and put it to the application scope this.buildConstants(); } catch (Exception e) { e.printStackTrace(); } } /** * build constants map * * @return constants map * @throws ClassNotFoundException */ private void buildConstants() throws ClassNotFoundException{ Map<String, Object> result = new HashMap<String, Object>(); for(Map.Entry<String, String> map: constantClass.entrySet()){ //the constant group name String k = map.getKey(); //the class String v = map.getValue(); Class<?> clazz = this.getClass().getClassLoader().loadClass(v); Field[] fields = clazz.getDeclaredFields(); for (int i = 0; i < fields.length; i++) { Field field = fields[i]; int modifier = field.getModifiers(); //must be final & public if (Modifier.isFinal(modifier) && !Modifier.isPrivate(modifier)) try { String name = field.getName(); Object value = field.get(this); logger.debug("building constants{}: name={},value={}", new Object[]{k, name,value}); result.put(name, value); } catch (IllegalAccessException e) { } } logger.info("put constant [{}] to application scope.", k); this.getServletContext().setAttribute(k, result); } } public Map<String, String> getConstantClass() { return constantClass; } public void setConstantClass(Map<String, String> constantClass) { this.constantClass = constantClass; } } 配置如下,可以配置多个需要EL引用访问的类: <bean name="applicationScopeLoader" class="xxx.utils.ApplicationScopeLoader" lazy-init="false"> <property name="constantClass"> <map> <entry key="Constants" value="xxx.framework.Constants"/> <!-- <entry key ="Constants2" value="another.constants.class"/> --> </map> </property> </bean> 用法如下: ${Constants.PAGE_SIZE_DEFAULT} 可以看到,此时可以简单的用标准的EL实现静态变量的访问。 |
|
返回顶楼 | |
发表时间:2010-12-28
而针对code table的解决方案,其实有不少技巧:
1、暴露到application scope的不是简单的数据,而是引用[color=red][/color]。这一点非常重要,也确保了我们的code table是reloadable的——而reload的策略,完全取决于业务的需要,比如我们用ibatis,可以简单的配置cache-model就行了,此时在DAO层cache,如要在service层,要专门考虑采用cache框架的API; 2、单独定义一个需要真正业务实现的接口,这样就变得更具通用性了。 public class ApplicationScopeLoader extends WebApplicationObjectSupport { ... /** code table service */ private final CodeTableLoaderService codeTableLoaderService = ApplicationContextManager .getBean("codeTableLoaderService",CodeTableLoaderService.class); private final static String CODE_TABLE_CONTEXT_NAME = "CodeTable"; protected void initApplicationContext() { try { //build constants and put it to the application scope this.buildConstants(); //build code table and put it to the application scope this.buildCodeTables(); } catch (Exception e) { e.printStackTrace(); } } /** * build code tables * each code table will be group by the type * for example, if there is a code table type 'gender' * then will be put to */ private void buildCodeTables(){ String[] codeTypes = codeTableLoaderService.loadAllCodeTypes(); for(String codeType: codeTypes){ this.buildOneCodeTableByCodeType(codeType); } } /** * extract given codes to a sub array of codes by code type * @param codes * @return */ private void buildOneCodeTableByCodeType(String codeType){ Map<Object,Object> codeMap = new HashMap<Object,Object>() { private static final long serialVersionUID = -1759893921358235848L; /** * get from the service, not raw data for avoiding non-refresh issue */ public Object get(Object key) { return codeTableLoaderService.getCodesByType(String.valueOf(key)); } public boolean containsKey(Object key) { return true; } }; logger.debug("put code table of [{}] to application scope.", codeType); this.getServletContext().setAttribute(CODE_TABLE_CONTEXT_NAME, codeMap); } } 接口定义: /** * It's a code table loading service * for exporting code tables to application scope * * but we don't put the dummy data to application scope directly * for avoiding non-refreshable issue * * @author bright_zheng * */ public interface CodeTableLoaderService { /** * load all code types from db/cache * * @return an array including all code types */ public String[] loadAllCodeTypes(); /** * get all code objects/beans filter by code type * * @param codeType * @return responding code objects/beans */ public Object[] getCodesByType(String codeType); } 为此,解决方案提供完毕。 当然了,关于Constants和Code table是整合考虑的方案,我分开了描述,只是为了阐述更有针对性而已 大家整合两部分的代码即可 欢迎讨论 |
|
返回顶楼 | |
发表时间:2010-12-28
itstarting 写道 我目前的思路是:
1、对于Constants,我将自动在启动时导出为applicationScope的map; 2、对于Code Table,我将自动在启动时加载到一系列的map,以CodeType为key 那之后就可以用“标准”的EL语法透明的访问Constants和CodeTable了。 但在这里有一个问题,那就是对于Code Table而言,一旦“自动在启动时加载到一系列的map”,会不会导致这些Code Table是non-reloadable的——我想这是难以让人接受的——总不能改一下系统变量就要reload一次application吧? 该系统变量时 单独对 Map 里某个 key 作更新就好了,为何要reload. |
|
返回顶楼 | |
发表时间:2010-12-28
itstarting 写道 而针对code table的解决方案,其实有不少技巧:
1、暴露到application scope的不是简单的数据,而是引用[color=red][/color]。这一点非常重要,也确保了我们的code table是reloadable的——而reload的策略,完全取决于业务的需要,比如我们用ibatis,可以简单的配置cache-model就行了,此时在DAO层cache,如要在service层,要专门考虑采用cache框架的API; 2、单独定义一个需要真正业务实现的接口,这样就变得更具通用性了。 public class ApplicationScopeLoader extends WebApplicationObjectSupport { ... /** code table service */ private final CodeTableLoaderService codeTableLoaderService = ApplicationContextManager .getBean("codeTableLoaderService",CodeTableLoaderService.class); private final static String CODE_TABLE_CONTEXT_NAME = "CodeTable"; protected void initApplicationContext() { try { //build constants and put it to the application scope this.buildConstants(); //build code table and put it to the application scope this.buildCodeTables(); } catch (Exception e) { e.printStackTrace(); } } /** * build code tables * each code table will be group by the type * for example, if there is a code table type 'gender' * then will be put to */ private void buildCodeTables(){ String[] codeTypes = codeTableLoaderService.loadAllCodeTypes(); for(String codeType: codeTypes){ this.buildOneCodeTableByCodeType(codeType); } } /** * extract given codes to a sub array of codes by code type * @param codes * @return */ private void buildOneCodeTableByCodeType(String codeType){ Map<Object,Object> codeMap = new HashMap<Object,Object>() { private static final long serialVersionUID = -1759893921358235848L; /** * get from the service, not raw data for avoiding non-refresh issue */ public Object get(Object key) { return codeTableLoaderService.getCodesByType(String.valueOf(key)); } public boolean containsKey(Object key) { return true; } }; logger.debug("put code table of [{}] to application scope.", codeType); this.getServletContext().setAttribute(CODE_TABLE_CONTEXT_NAME, codeMap); } } 接口定义: /** * It's a code table loading service * for exporting code tables to application scope * * but we don't put the dummy data to application scope directly * for avoiding non-refreshable issue * * @author bright_zheng * */ public interface CodeTableLoaderService { /** * load all code types from db/cache * * @return an array including all code types */ public String[] loadAllCodeTypes(); /** * get all code objects/beans filter by code type * * @param codeType * @return responding code objects/beans */ public Object[] getCodesByType(String codeType); } 为此,解决方案提供完毕。 当然了,关于Constants和Code table是整合考虑的方案,我分开了描述,只是为了阐述更有针对性而已 大家整合两部分的代码即可 欢迎讨论 补充用法: 1、在HTLM元素中使用: --这里用了Spring的TAG,当然谁的TAG并不是关键 <form:select path="hotel.country"> <form:option value="-" label="--Please Select"/> <form:options items="${CodeTable.Gender}" itemValue="codeValue" itemLabel="codeName"/> </form:select> 这里表示要以Code Type为Gender的作为下拉框元素 2、用以打印: <c:forEach var="ct" items="${CodeTable.Gender}"> -->codeName: ${ct.codeName}, -->codeValue: ${ct.codeValue}, -->codeDesc: ${ct.codeDesc}<br> </c:forEach> 对了,我这里为了方便,封装了个简单的CodeTable POJO,一并贴出来: public class CodeTable implements Serializable { private String codeType; private String codeName; private String codeValue; private String codeDesc; //getter/setter omitted ... } |
|
返回顶楼 | |
发表时间:2010-12-28
xieke 写道 itstarting 写道 我目前的思路是:
1、对于Constants,我将自动在启动时导出为applicationScope的map; 2、对于Code Table,我将自动在启动时加载到一系列的map,以CodeType为key 那之后就可以用“标准”的EL语法透明的访问Constants和CodeTable了。 但在这里有一个问题,那就是对于Code Table而言,一旦“自动在启动时加载到一系列的map”,会不会导致这些Code Table是non-reloadable的——我想这是难以让人接受的——总不能改一下系统变量就要reload一次application吧? 该系统变量时 单独对 Map 里某个 key 作更新就好了,为何要reload. 你这种思路是:更改后马上更新,即时生效。 也是不错的思路 但需要remove掉原先application scope中,当前在改的key的map,然后再reload进来,感觉有点麻烦——会不会对原先的code table管理部分造成不必要的困扰? |
|
返回顶楼 | |
发表时间:2011-01-04
最后修改:2011-01-04
既然是增强EL表达式的解释能力,添加一个ELResolver会更加自然。
大致思路是,在应用启动时(例如你上面的 initApplicationContext()方法中,或者一个load-on-startup的servlet的init方法里)用 JspFactory.getJspApplicationContext(javax.servlet.ServletContext).addELResolver(ELResorver resolver) 方法加入一个ELResolver的实现。(这个方法不能在应用已经处理过请求之后使用,否则会抛IllegalStateException) 这个ELResolver会被加入到JSP规范的默认的ImplicitObjectELResolver之后,其他规范ELResolver之前(例如BeanELResolver与ScopedAttributeELResolver)。也就是说,优先级低于隐含对象,高于scope属性。 在ELResolver中,是知道了EL表达式的实际内容(例如"Constants"字符串),再去求值。这时你可以用forName或者ClassLoader拿到Constants类(拿不到就不处理,留给后续的ELResolver处理,如果后续的Resolver也无法处理,容器就报错),这就没有reload的需要了。 详情可参考 http://download.oracle.com/docs/cd/E17802_01/products/products/jsp/2.1/docs/jsp-2_1-pfd2/javax/servlet/jsp/JspApplicationContext.html |
|
返回顶楼 | |