1.上一篇文章,我介绍了OGNL表达式语言,本来想在那篇文章上加上一个使用OGNL访问静态方法和静态属性的例子,但是昨天晚上我写代码时,报了一个警告,获取得到静态属性的值,却获取不到静态方法的返回值,所以今天特别的写篇文章来介绍一下。
2.这里我先附上这个例子,来了解如何使用OGNL表达式访问静态方法和属性。
(1).其中访问静态方法或者静态属性的语法分别为:@类的路径@方法名,@类的路径@属性名,语法中类的路径,即包名+类名。
(2).对于OGNL来说,java.lang.Math是其默认的类,如果调用java.lang.Math的静态方法时,无需指定类的名字,比如:@@min(4,10),就比较两者谁比较小,输出较小的那个数。
注:其中第一点也可以理解为调用一个类的静态方法和静态属性,其中第二点也可以理解为调用JDK类中的静态方法。
(3).项目结构图如下:
(4).首先新建一个Struts2项目,项目名为StaticTest,新建一个类,放在com.gk包下,类名为StaticTest,完整代码如下:
package com.gk; public class StaticTest{ public static String str="static property"; public static String s(){ return "static method"; } }
打开index.jsp页面,编码格式改为utf-8,加入<%@ taglib uri="/struts-tags" prefix="s" %>,就可以使用struts的标签,具体代码如下:
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%> <%@ taglib uri="/struts-tags" prefix="s" %> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>My JSP 'index.jsp' starting page</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="This is my page"> <!-- <link rel="stylesheet" type="text/css" href="styles.css"> --> </head> <body> <ol> <li>访问静态方法:<s:property value="@com.gk.StaticTest@s()"/></li> <li>访问静态属性:<s:property value="@com.gk.StaticTest@str"/></li> <li>访问java.lang.Math类的静态方法:<s:property value="@@max(2,5)"/></li> <s:debug></s:debug> </ol> </body> </html>
一定要在struts.xml配置文件中配置允许使用OGNL访问静态方法,否则不能使用OGNL访问静态方法,必须在struts.xml文件中加入这行代码:
<constant name="struts.ognl.allowStaticMethodAccess" value="true"></constant>
其中完整struts.xml文件如下:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <constant name="struts.enable.DynamicMethodInvocation" value="true"></constant> <constant name="struts.devMode" value="true"></constant> <constant name="struts.ognl.allowStaticMethodAccess" value="true"></constant> </struts>
(5).接着部署该项目到Tomcat服务器上,开启Tomcat服务器,运行后如下:
为何取不到静态方法的返回值呢?很奇怪!
3.发现访问静态方法,获取不出它的返回值,然后看一下控制台,有警告信息,但是不是错误,警告信息如下图所示:
这个警告信息使我搞了我半天都没解决,控制台输出的信息是com.opensymphony.xwork2.ognl.SecurityMemberAccess warn个类警告,警告内容是Target class [class com.gk.StaticTest] or declaring class of member type [public static java.lang.String com.gk.StaticTest.s()] are excluded! ,意思是目标类StaicTest类或者声明类的成员类型[public static java.lang.String com.gk.StaticTest.s()]是被排除,被赶出了,表明意思是这样,我就百度了一下,发现有小部分的同学都会报这错误,但是都没解决,所以我就去查看一下底层代码,看能看出个所以然么?
(1).这个警告是com.opensymphony.xwork2.ognl.SecurityMemberAccess warn,所以我就找到这个SecurityMemberAccess类,它是放在com.opensymphony.xwork2.ognl包下的,而这个包是放在xwork-core-2.3.20.jar 包下的,所以我们必须导入这个包的源代码,才能查看,这个导入我在之前的文章说过,这里再用图的方式显示一次:
首先找到这个jar包:
选中这个jar包,然后鼠标右键单击,选择Properties属性,找到你下载的那个搭建环境所需要的包,然后找到xwork-core相关的文件,即E:/struts-2.3.20/struts-2.3.20/src/xwork-core/src/main/java,复制到下图的红色框框的地方,点击箭头指向的两个按钮,首先Apply应用按钮,再点击Ok确定按钮:
这样我们就可以打开这个jar包下的.class文件,都是我们熟悉的Java代码。
(2).打开com.opensymphony.xwork2.ognl包下的SecurityMemberAccess类,即如下图所示:
双击打开后,代码如下:
/* * Copyright 2002-2006,2009 The Apache Software Foundation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.opensymphony.xwork2.ognl; import com.opensymphony.xwork2.util.logging.Logger; import com.opensymphony.xwork2.util.logging.LoggerFactory; import ognl.DefaultMemberAccess; import java.lang.reflect.Member; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Collections; import java.util.Map; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Allows access decisions to be made on the basis of whether a member is static or not. * Also blocks or allows access to properties. */ public class SecurityMemberAccess extends DefaultMemberAccess { private static final Logger LOG = LoggerFactory.getLogger(SecurityMemberAccess.class); private final boolean allowStaticMethodAccess; private Set<Pattern> excludeProperties = Collections.emptySet(); private Set<Pattern> acceptProperties = Collections.emptySet(); private Set<Class<?>> excludedClasses = Collections.emptySet(); private Set<Pattern> excludedPackageNamePatterns = Collections.emptySet(); public SecurityMemberAccess(boolean method) { super(false); allowStaticMethodAccess = method; } public boolean getAllowStaticMethodAccess() { return allowStaticMethodAccess; } @Override public boolean isAccessible(Map context, Object target, Member member, String propertyName) { if (checkEnumAccess(target, member)) { if (LOG.isTraceEnabled()) { LOG.trace("Allowing access to enum #0", target); } return true; } if (isPackageExcluded(target.getClass().getPackage(), member.getDeclaringClass().getPackage())) { if (LOG.isWarnEnabled()) { LOG.warn("Package of target [#0] or package of member [#1] are excluded!", target, member); } return false; } if (isClassExcluded(target.getClass(), member.getDeclaringClass())) { if (LOG.isWarnEnabled()) { LOG.warn("Target class [#0] or declaring class of member type [#1] are excluded!", target, member); } return false; } boolean allow = true; if (!checkStaticMethodAccess(member)) { if (LOG.isTraceEnabled()) { LOG.warn("Access to static [#0] is blocked!", member); } allow = false; } //failed static test if (!allow) return false; // Now check for standard scope rules return super.isAccessible(context, target, member, propertyName) && isAcceptableProperty(propertyName); } protected boolean checkStaticMethodAccess(Member member) { int modifiers = member.getModifiers(); if (Modifier.isStatic(modifiers)) { return allowStaticMethodAccess; } else { return true; } } protected boolean checkEnumAccess(Object target, Member member) { if (target instanceof Class) { Class clazz = (Class) target; if (Enum.class.isAssignableFrom(clazz) && member.getName().equals("values")) return true; } return false; } protected boolean isPackageExcluded(Package targetPackage, Package memberPackage) { for (Pattern pattern : excludedPackageNamePatterns) { if (pattern.matcher(targetPackage.getName()).matches() || pattern.matcher(memberPackage.getName()).matches()) { return true; } } return false; } protected boolean isClassExcluded(Class<?> targetClass, Class<?> declaringClass) { if (targetClass == Object.class || declaringClass == Object.class) { return true; } for (Class<?> excludedClass : excludedClasses) { if (targetClass.isAssignableFrom(excludedClass) || declaringClass.isAssignableFrom(excludedClass)) { return true; } } return false; } protected boolean isAcceptableProperty(String name) { return name == null || ((!isExcluded(name)) && isAccepted(name)); } protected boolean isAccepted(String paramName) { if (!this.acceptProperties.isEmpty()) { for (Pattern pattern : acceptProperties) { Matcher matcher = pattern.matcher(paramName); if (matcher.matches()) { return true; } } //no match, but acceptedParams is not empty return false; } //empty acceptedParams return true; } protected boolean isExcluded(String paramName) { if (!this.excludeProperties.isEmpty()) { for (Pattern pattern : excludeProperties) { Matcher matcher = pattern.matcher(paramName); if (matcher.matches()) { return true; } } } return false; } public void setExcludeProperties(Set<Pattern> excludeProperties) { this.excludeProperties = excludeProperties; } public void setAcceptProperties(Set<Pattern> acceptedProperties) { this.acceptProperties = acceptedProperties; } public void setExcludedClasses(Set<Class<?>> excludedClasses) { this.excludedClasses = excludedClasses; } public void setExcludedPackageNamePatterns(Set<Pattern> excludedPackageNamePatterns) { this.excludedPackageNamePatterns = excludedPackageNamePatterns; } }
代码也不多,我们只看重要的,可以通过大纲看代码:
大纲中有变量,常量,和方法,一个个点击来看,其中我找到了报错的那一行,但是看了也不知道怎么解决,郁闷!如下图所示:
它就是报这行警告,这行警告是通过判断isClassExcluded这个方法来决定的:
这个方法判断了目标类和超类是否相等或者声明类和超类是否相同,相等的话返回真,这就不多做解释了,然后我们报错的那行的那幅图里传进来的两个类,一个是目标类,一个是声明类,而这个声明类是通过成员来获得的,这又不知道为何了,所以看底层代码又看不懂,所以只能求助我热心的群友了。
注:这里我为何要介绍看底层代码呢,因为这样有利于我们理解和学习代码为什么这样写,又为什么报这警告。由于水平有限,所以我也郁闷了!
(3).我就去请教一下我的群友,我那个群是安卓群,发了一下牢骚,群友就问你遇到什么问题了,说一下,看我懂不,我就跟他们说了这个问题,过了一会儿一位群友说我去Struts的官网查了下,说最新版本的struts2不支持OGNL访问静态方法了,估计是因为安全问题,访问静态方法怕不安全,想想我用的还真是最新版本,我说难怪老师要我们用较稳定的版本,别用最新的,果然,我把上面这个项目的核心代码复制到低版本的项目中,就可以了,真是一波三折啊!
4.以上那个例子的代码是没错的,是因为版本问题,才导致只可以使用OGNL访问静态属性,而访问不了静态方法,所以我使用较低版本jar包,我把上面这个项目的一些代码复制,调整一下,改变一些名字等等,我用的是下图的版本:
一切搞定之后,运行效果如下:
这样就可以使用OGNL访问静态方法,并获取得到它的值。
5.总结:对于使用OGNL表达式语言来访问静态方法,这个比较少用,如果在使用OGNL访问静态方法,但是获取不到值,并控制台输出如上面我所介绍的一些警告信息,有可能是你版本是最新的原因,代码是没错误的,所以建议大家使用较稳定版本,使用较稳定版本后,通过上面这个例子就可以使用OGNL访问静态方法并获取其静态方法的返回值。
6.以上内容仅供大家学习参考,写的不好,请见谅,如有问题,请指出,如文章有错误,请指出,谢谢!