Struts2中使用OGNL表达式语言访问静态方法和静态属性以及我遇到的问题和解决方法

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.以上内容仅供大家学习参考,写的不好,请见谅,如有问题,请指出,如文章有错误,请指出,谢谢!

.

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。