我知道这可能是XY Problem,所以我会解释上下文,以防有更好的解决方案进一步上行,而不是我想要的。
我正在尝试添加从JSP中的EL表达式中访问类的静态方法的能力。
到目前为止,我的解决方案是实例化指定的类并将其放在页面(或指定的)范围内。由于您可以从实例访问静态方法,因此它似乎是最简单,最直接的解决方案。我为此目的编写了一个标记类StaticMethodAccessTag
,并将其添加到我们的某个标记类constants:staticMethodAccess
。
这个解决方案的问题在于,也可以调用非静态方法,这意味着所有潜在的问题和滥用。
有没有办法使用反射来实例化一个类的实例,同时防止(例如通过抛出异常)任何非静态方法被调用?
或者更好的是,有没有办法去"打电话"任何通过EL任意命名和参数化的方法,并有一个java方法处理所有这些调用?类似于${MyStaticUtil.foo(bar1, bar2)}
和${MyStaticUtil.baz(bar1)}
的内容,其中两者都会进入StaticMethodAccessTag.processArbitraryMethod(String methodName, Object... args)
,然后可以使用反射在MyStaticUtil
上调用相应的静态方法。
或者更好的是,有没有办法在EL中调用静态方法而不需要类的实例来调用它?
StaticMethodAccessTag.java:
import java.lang.reflect.Constructor;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.SimpleTagSupport;
import org.apache.log4j.Logger;
public class StaticMethodAccessTag extends SimpleTagSupport {
private static Logger log = Logger.getLogger(StaticMethodAccessTag.class);
private String varName;
private String className;
private String scope;
private Object[] args;
@Override
public void doTag() throws JspException {
try {
if (scope == null || "".equals(scope)) {
scope = "page";
}
int requestedScope = TagUtils.getScope(scope);
Class<?> declaringClass = Class.forName(this.className);
Object instance = null;
Class<?>[] argTypes = null;
if (args == null || args.length == 0) {
// no args; use default constructor
instance = declaringClass.newInstance();
} else {
// args provided, find the matching constructor and use it
argTypes = new Class<?>[args.length];
for (int i = 0; i < args.length; i++) {
if (args[i] == null) {
// unknown type, assume Object. This can lead to ambiguity when picking a constructor.
argTypes[i] = Object.class;
} else {
argTypes[i] = args[i].getClass();
}
}
// check each constructor
Constructor<?> matchedConstructor = null;
nextConstructor: for (Constructor<?> constructor : declaringClass.getConstructors()) {
Class<?>[] expectedTypes = constructor.getParameterTypes();
// check the provided arguments against the current constructor
if (expectedTypes == null || expectedTypes.length != args.length) {
// mismatch; move on to the next constructor
continue;
}
for (int i = 0; i < argTypes.length; i++) {
if (!argTypes[i].isAssignableFrom(expectedTypes[i])) {
// mismatch; move on to the next constructor
continue nextConstructor;
}
}
// if another match was already found, that means we have ambiguous arguments.
if (matchedConstructor != null) {
outputArgs(argTypes);
throw new Exception("Given the provided arguments, there are multiple eligible constructors for " + declaringClass.getName());
}
matchedConstructor = constructor;
}
if (matchedConstructor != null) {
instance = matchedConstructor.newInstance(args);
}
}
if (instance == null) {
outputArgs(argTypes);
throw new NullPointerException("Failed to find a matching constructor for the provided args for " + this.className + ".");
}
getJspContext().setAttribute(varName, instance, requestedScope);
} catch (Exception e) {
// TODO output jsp name, other helpful information, if possible.
throw new JspException("Exception setting up static method access for " + this.className, e);
}
}
private void outputArgs(Class<?>[] argTypes) {
log.debug("Provided " + args.length + " arguments: ");
for (int i = 0; i < argTypes.length; i++) {
String argStr = "null";
if (args[i] != null) {
argStr = args[i].toString();
}
log.debug("[" + i + "] expected type: " + argTypes[i].getName() + " toString: " + argStr);
}
}
public String getVar() {
return varName;
}
public void setVar(String varName) {
this.varName = varName;
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
public String getScope() {
return scope;
}
public void setScope(String scope) {
this.scope = scope;
}
public Object[] getArgs() {
return args;
}
public void setArgs(Object... args) {
this.args = args;
}
}
constants.tld中的标签定义:
<tag>
<name>staticMethodAccess</name>
<tagclass>foopackage.presentation.resource.tag.StaticMethodAccessTag</tagclass>
<bodycontent>empty</bodycontent>
<attribute>
<name>className</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>var</name>
<required>true</required>
<rtexprvalue>false</rtexprvalue>
</attribute>
<attribute>
<name>scope</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>args</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
JSP中的示例用法:
<%@ taglib prefix="constants" uri="/constants" %>
<%-- debug, delete this block --%>
<constants:staticMethodAccess var="MyJSPUtils" className="foopackage.presentation.resource.tag.MyJSPUtils"/>
<c:forEach items="${MyJSPUtils.args('one', 'two', 'three')}" var="v">
${v};
</c:forEach>
<constants:staticMethodAccess var="type1" className="foopackage.integration.value.Type" args="${jspUtils.args(DBConstants.STATUS_TYPE_ID_ACTIVE)}"/>
<constants:staticMethodAccess var="type2" className="foopackage.integration.value.Type" args="${jspUtils.args(DBConstants.STATUS_TYPE_ID_ACTIVE, 'Active')}"/>
<constants:staticMethodAccess var="type3" className="foopackage.integration.value.Type" args="${jspUtils.args(type1)}"/>
<constants:staticMethodAccess var="type4" className="foopackage.integration.value.Type" args="${jspUtils.args(DBConstants.STATUS_TYPE_ID_ACTIVE, 'Active', 'desc')}"/>
<constants:staticMethodAccess var="type5" className="foopackage.integration.value.Type" args="${jspUtils.args(DBConstants.STATUS_TYPE_ID_ACTIVE, null, 'desc')}"/>
<constants:staticMethodAccess var="type3" className="foopackage.integration.value.Type" args="${jspUtils.args(null)}"/><%-- this line will throw an error because it matches 2 possible constructors --%>
<br/>${type1.id} ${type1.name} ${type1.description}
<br/>${type2.id} ${type2.name} ${type2.description}
<br/>${type3.id} ${type3.name} ${type3.description}
<br/>${type4.id} ${type4.name} ${type4.description}
<br/>${type5.id} ${type5.name} ${type5.description}
<br/>
<%-- end debug block --%>
答案 0 :(得分:0)
我认为您正在寻找Java反射中使用的for (var p in obj) console.log(p + " " + typeof obj[p] + " " + obj[p])
功能。
有没有办法使用反射来实例化一个实例 类,同时防止(例如通过抛出异常)任何一个 被称为非静态方法?
如果您只想调用静态方法,为什么需要实例化该类?例如,您可以执行以下操作。
QMetaObject
或者更好的是,有没有办法“召唤”任意命名和 - 通过EL的参数化方法,并有一个java方法处理所有这些调用?像$ {MyStaticUtil.foo(bar1,bar2)}和 $ {MyStaticUtil.baz(bar1)}两者都会进入 StaticMethodAccessTag.processArbitraryMethod(String methodName, 对象... args)然后可以使用反射来调用 MyStaticUtil上适当的静态方法。
在这种情况下,最佳解决方案是Java中的代理/ modifiers
功能。但是,为了做到这一点,您需要为public Object callOnlyStaticMethods(String name, Class [] parameterTypes, Object [] parameters) throws Exception {
// use getDeclaredMethod and setAccessible if you want to call non-public methods
Method method = this.getClass().getMethod(name, parameterTypes);
if( ! Modifier.isStatic(method.getModifiers()) ) {
throw new UnsupportedOperationException("Only static methods may be called, " + method.getName() + " is not static!");
}
return method.invoke(null, parameters);
}
类创建一个接口 - 然后然后将静态方法移动到该接口。
(好吧,我希望你使用Java 8,因为那时你可以这样做 - 否则,创建调用静态方法的非静态方法?)例如:
InvocationHandler
或者更好的是,有没有办法在EL中调用静态方法 需要一个类的实例来调用它吗?
好问题:看起来已经回答:How to call a static method in JSP/EL?