Expression Language 3.0表达式语言规范最终版从2013-4-29发布到现在已经非常久的时间了;目前如Tomcat 8、Jetty 9、GlasshFish 4已经支持EL 3.0。新特性包括:如字符串拼接操作符、赋值、分号操作符、对象方法调用、Lambda表达式、静态字段/方法调用、构造器调用、Java8集合操作。目前Glassfish 4/Jetty实现最好,对大多数新特性都支持、Tomcat8对如Lambda、静态字段/方法调用、构造器调用目前不支持。
点击下载EL 3.0规范。另外可以访问规范官网下载javadoc等:https://jcp.org/en/jsr/detail?id=341
初始数据
<%@page pageEncoding="utf-8" trimDirectiveWhitespaces="true"%> <%@page import="java.util.*"%> <%@page import="com.User"%> <% User user = new User(); user.setId(1); user.setName("zhangsan"); pageContext.setAttribute("user", user); List<String> list = new ArrayList<String>(); list.add("1"); list.add("2"); pageContext.setAttribute("list", list); %>
1、字符串拼接
${a="1"}<br/> ${b="2"}<br/> ${a=a+=b}<br/> ${a}<br/>
a+=b 连接a和b,并把结果返回;注意,此处不等价于a=a+b。
2、赋值操作符
${i = 1}<br/> ${i}<br/> <%=pageContext.getAttribute("i")%><br/> ${set={1,2,3,3}}<br/> ${list=[1,2,3,3]}<br/> ${list[3]}<br/> ${map = {"k1" : "123", "k2": "234"}}<br/> ${map['k1']}<br/> <br/>
如${i=1} 会把1赋值给i,i默认存储在page作用域;可以使用pageContext.getAttribute("i")获取。另外可以创建Set、List、Map类型的对象。
3、分号操作符
${a=1;123}<br/> <br/>
最后一个分号后的表达式是整个表达式的值。
4、调用对象方法
${user.setId(123)} ${user.getId()}<br/> <br/>
5、Lambda表达式
${(()->9)()}<br/> ${((x,y)->x+y)(1,2)}<br/> ${fact = n -> n==0? 1: n*fact(n-1); fact(5)}<br/> <br/>
6、静态字段/方法调用、构造器调用
<% pageContext.getELContext().getImportHandler().importClass("java.util.UUID"); pageContext.getELContext().getImportHandler().importClass("java.util.Locale"); pageContext.getELContext().getImportHandler().importPackage("java.io"); pageContext.getELContext().getImportHandler().importStatic("java.util.UUID.randomUUID"); pageContext.getELContext().getImportHandler().importStatic("java.lang.Math.PI"); pageContext.getELContext().getImportHandler().importClass("com.User"); %> ${Integer.valueOf("123")}<br/> ${UUID.randomUUID()}<br/> ${Locale.CHINESE}<br/> ${File("D:\\aa.txt").exists()}<br/> ${randomUUID()}<br/> ${PI}<br/> ${User.i}<br/> ${User.hello()}<br/> ${Boolean(true)}<br/> ${User()}<br/> <% javax.el.ELProcessor elp = new javax.el.ELProcessor(); elp.getELManager().importStatic("com.User.i"); out.print(elp.eval("i")); %> <br/> <br/>
使用前必须通过importHandler导入相应的包/类/静态字段/方法。导入包后,可以直接使用如${UUID.randomUUID()}访问该包下的类的静态字段/方法;也可以只导入某个类,然后也可以使用如${UUID.randomUUID()}访问该类的静态字段/方法;另外可以直接导入静态字段/方法,然后直接使用如${PI} / ${randomUUID()}访问静态字段/方法。 也可以使用如${Boolean(true)}、${User()}、${File("D:\\aa.txt").exists()}访问构造器创建对象。此处默认导入"java.lang"包下的内容。
6、集合对象操作
${list.stream().filter(i -> i != "1").toList()} <br/>
非常类似于Java 8中的集合操作类库,更多语法请参考EL 3.0规范。
tomcat 8目前对Lambda表达式、静态字段/方法调用、构造器调用暂不支持。Jetty 9/ GlasshFish对静态字段/方法访问还是有点小bug的,可以通过修改如下代码修复:
问题1、类加载问题
首先如tomcat类加载器模型如下:
Bootstrap
|
System
|
Common
/ \
Webapp1 Webapp2 ...
tomcat类加载器模型请参考http://tomcat.apache.org/tomcat-8.0-doc/class-loader-howto.html。
另外类加载器加载类时使用委托模型,即先从父加载,父没有再自己加载。此处javax.el.jar是由common类加载器加载;如我们的el webapp,是由自己的类加载器加载。
在[5、静态字段/方法调用、构造器调用]我们使用pageContext.getELContext().getImportHandler().importClass("java.util.UUID")注册类;然后呢当我们访问如${UUID.randomUUID()}时会首先调用ImportHandler.getClassFor("UUID")得到类:
private Class<?> getClassFor(String className) { if (!notAClass.contains(className)) { try { return Class.forName(className, false, getClass().getClassLoader()); } catch (ClassNotFoundException ex) { notAClass.add(className); } } return null; }
首先尝试common类加载器加载,其委托给System类加载,其又委托给Bootstrap类加载加载,此时会加载到java.util.UUID类;没有什么问题;当我们使用pageContext.getELContext().getImportHandler().importClass("com.User");注册el webapp的类时;此处要注意的是pageContext.getELContext().getImportHandler()该类是common类加载加载的;因此加载User时是使用该common类加载器加载,此时会加载不到el webapp中的User类,因为无法向后代加载器加载。因此会有问题;不过我们可以使用:
return Class.forName(className, false, Thread.currentThread().getContextClassLoader());
即使用当前线程类加载器加载User,此时就能加载到User了。
请到https://svn.java.net/svn/uel~svn/trunk/签出代码进行修改。另外可以直接下载附件中已经修改好的javax.el.jar进行测试。
问题2、静态字段解析问题
经过测试,无法正确解析静态导入静态字段解析;如pageContext.getELContext().getImportHandler().importStatic("java.lang.Math.PI");,无法使用${PI}获取其值。这是JSP中提供的ScopedAttributeELResolver解析器的问题,该解析器的作用是从作用域中查找属性值;其不管解析到还是解析不到都会调用context.setPropertyResolved(true)告知属性已经解析出来了,此处将中断后续的静态字段解析。
public Object getValue(ELContext context, Object base, Object property) { if (context == null) { throw new NullPointerException(); } if (base == null) { context.setPropertyResolved(true); if (property instanceof String) { String attribute = (String) property; PageContext ctxt = (PageContext) context.getContext(JspContext.class); Object value = ctxt.findAttribute(attribute); // To support reference of static fields for imported class in // EL 3.0, if a scoped attribute returns null, this attribute // is further checked to see if it is the name of an imported // class. If so, an ELClass instance is returned. // Note: the JSP spec needs to be updated for this behavior. Note // also that this behavior is not backward compatible with JSP 2.2 // and a runtime switch may be needed to force backward // compatility. if (value == null) { // check to see if the property is an imported class if (context.getImportHandler() != null) { Class<?> c = context.getImportHandler().resolveClass(attribute); if (c != null) { value = new ELClass(c); // A possible optimization is to set the ELClass // instance in an attribute map. } } } return value; } } return null; }
修改为
public Object getValue(ELContext context, Object base, Object property) { if (context == null) { throw new NullPointerException(); } if (base == null) { if (property instanceof String) { String attribute = (String) property; PageContext ctxt = (PageContext) context.getContext(JspContext.class); Object value = ctxt.findAttribute(attribute); // To support reference of static fields for imported class in // EL 3.0, if a scoped attribute returns null, this attribute // is further checked to see if it is the name of an imported // class. If so, an ELClass instance is returned. // Note: the JSP spec needs to be updated for this behavior. Note // also that this behavior is not backward compatible with JSP 2.2 // and a runtime switch may be needed to force backward // compatility. if (value == null) { // check to see if the property is an imported class if (context.getImportHandler() != null) { Class<?> c = context.getImportHandler().resolveClass(attribute); if (c != null) { value = new ELClass(c); // A possible optimization is to set the ELClass // instance in an attribute map. } } } if(value != null) { context.setPropertyResolved(true); } return value; } } return null; }
修改后,即解析到时才调用 context.setPropertyResolved(true)告知已经解析到属性值,后续不再解析了。
请到https://svn.java.net/svn/jsp~svn/trunk/api签出代码进行修改。另外可以直接下载附件中已经修改好的javax.servlet.jsp-api.jar进行测试。
glassfish4:需要把这两个jar复制到:glassfish4\glassfish\modules下,把其内部的覆盖掉;
jetty9:需要把这两个jar复制到:jetty-distribution-9.1.2.v20140210\lib\jsp下,把javax.el-3.0.0.jar和javax.servlet.jsp-api-2.3.1.jar删掉,并添加新的jar;
tomcat8:通过替换jar包还是解决不了问题,没有仔细研究,希望未来官方修复吧。
其实从如上可以看出,调用静态字段/方法、调用构造器比较麻烦;如果需要可以写一个taglib消除java脚本的调用。
另外也可以在普通java环境使用如下代码直接进行表达式计算:
javax.el.ELProcessor elp = new javax.el.ELProcessor(); elp.getELManager().importStatic("com.User.i"); out.print(elp.eval("i"));
从目前来看,以后可能用的最多的就是:赋值操作符、调用对象方法;其他的可能会用的比较少。
另外EL 3.0拥有OGNL的大多数功能,但是EL在JSP中大部分时间都是编译期翻译,所以相对于OGNL更安全,不会像OGNL那样漏洞百出。
相关推荐
Spring 3.0 是 Spring 框架的一个重要版本,引入了许多新特性,极大地提升了开发效率和灵活性。以下是对这些新特性的详细说明: 一、模块化与构建系统的改进 在 Spring 3.0 中,模块的组织变得更加精细化,不再像...
3. **表达式语言(SpEL)增强**:Spring Expression Language在3.0版本中得到了显著增强,支持更复杂的表达式和方法调用,使得在配置中动态计算和访问对象属性变得更为简单。例如,`#{T(java.util.Arrays).asList('a...
**Spring 3.0新特性详解** Spring框架作为Java企业级应用开发的基石,自发布以来不断进化,其中Spring 3.0版本是其一个重要里程碑,引入了许多创新特性和改进,提升了开发效率和应用程序的可维护性。在这个版本中,...
**Spring 3.0 新特性详解** Spring框架作为Java企业级应用开发的主流选择,其3.0版本的发布带来了许多显著的改进和创新。这个官方PPT资料深入剖析了这些新特性,使得开发者能够更好地利用Spring提升应用程序的效率...
4. **表达式语言(SpEL)**:Spring Expression Language是Spring 3.0引入的一种强大的表达式语言,用于在运行时查询和操作对象图。它可以用于属性访问、方法调用、条件和逻辑运算以及算术运算。 5. **AOP增强**:...
C#3.0作为C#语言的一个重要版本,引入了一系列新的语言特性,这些新特性不仅增强了开发者的生产力,还提高了代码的可读性和可维护性。本篇将详细介绍C#3.0中的主要新特性及其应用场景。 #### 自动实现属性 ...
Spring 3.0是Spring框架的一个重要版本,它在2009年发布,为Java开发者带来了许多新特性和改进。这个"spring 3.0 jar spring最新开发包"包含了该版本的所有核心组件和库,使得开发者能够进行高效且灵活的Java应用...
9. **Spring Expression Language (SpEL)**:Spring 3.0引入了强大的表达式语言SpEL,用于运行时评估对象图中的表达式,常用于Bean的初始化、AOP等方面。 10. **WebSocket支持**:虽然Spring 3.0本身并未直接包含...
3. **Expression Language (SpEL)**:Spring Expression Language是Spring 3.0引入的一个强大特性,允许在运行时查询和操作对象图。SpEL用于在配置文件中定义表达式,如在AOP切点表达式或数据绑定中。 4. **...
Spring 3.0是Spring框架的一个重要版本,它引入了许多新特性和改进,极大地扩展了其功能和灵活性。本文将详细解析Spring 3.0 API的主要特性,帮助开发者更好地理解和利用这一强大的Java企业级开发工具。 一、核心...
Spring Expression Language (SpEL) 在3.0版本中得到了增强,它允许在配置文件中使用更强大的表达式进行条件判断和运算。 #### 更好的注解支持 Spring 3.0加强了对注解的支持,包括@PostConstruct、@PreDestroy等...
Spring 3.0是Spring框架的一个重要版本,它引入了许多新特性和改进,极大地提升了开发效率和灵活性。这个"spring3.0的最新官方例子源码spring-samples"压缩包包含的是Spring官方提供的示例代码,旨在帮助开发者理解...
六、Spring Expression Language (SpEL) SpEL是Spring 3.0引入的一种强大的表达式语言,用于在运行时查询和操作对象图。它在Bean定义中的属性值、AOP的切入点表达式以及动态语言支持等方面都有广泛应用。 七、...
Spring 3.0是Spring框架的一个重要版本,它在2009年发布,为Java开发者带来了许多新特性和改进。这个版本标志着Spring框架在企业级应用开发中的成熟,提供了更强大、更灵活的解决方案。 1. **依赖注入增强**: ...
7. **SpEL(Spring Expression Language)**:Spring 3.0引入的表达式语言,用于在运行时查询和操作对象图,如在AOP中动态设置切入点表达式。 8. **RESTful支持**:Spring 3.0增强了对RESTful Web服务的支持,可以...
6. **Spring Expression Language (SpEL)**:Spring 3.0 引入了强大的表达式语言SpEL,用于在运行时查询和操作对象图,增强了bean的动态属性设置和方法调用。 7. **支持泛型元数据**:Spring 3.0 开始支持基于泛型...