Java 防止调用实例的实例方法
我认识到这可能是一个问题,所以我将解释上下文,以防有比我尝试做的更好的解决方案 我试图添加从JSP中的EL表达式中访问类的静态方法的功能 到目前为止,我的解决方案是实例化指定的类并将其放在页面(或指定的)范围内。因为您可以从实例访问静态方法,所以它似乎是最简单、最直接的解决方案。为此,我编写了一个标记类,Java 防止调用实例的实例方法,java,reflection,Java,Reflection,我认识到这可能是一个问题,所以我将解释上下文,以防有比我尝试做的更好的解决方案 我试图添加从JSP中的EL表达式中访问类的静态方法的功能 到目前为止,我的解决方案是实例化指定的类并将其放在页面(或指定的)范围内。因为您可以从实例访问静态方法,所以它似乎是最简单、最直接的解决方案。为此,我编写了一个标记类,StaticMethodAccessTag,并将其作为常量:staticMethodAccess添加到一个tld中 这个解决方案的问题是,也可以调用非静态方法,这意味着可能出现问题和滥用 有没有
StaticMethodAccessTag
,并将其作为常量:staticMethodAccess
添加到一个tld中
这个解决方案的问题是,也可以调用非静态方法,这意味着可能出现问题和滥用
有没有一种方法可以使用反射来实例化一个类的实例,同时防止(例如通过抛出异常)调用任何非静态方法
或者更好的是,有没有一种方法可以通过EL“调用”任何任意命名和参数化的方法,并让一个java方法处理所有这些调用?类似于${MyStaticUtil.foo(bar1,bar2)}
和${MyStaticUtil.baz(bar1)}
这两种方法都将进入StaticMethodAccessTag.ProcessArbiaryMethod(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;
}
}
import java.lang.reflect.Constructor;
导入javax.servlet.jsp.JspException;
导入javax.servlet.jsp.tagext.SimpleTagSupport;
导入org.apache.log4j.Logger;
公共类StaticMethodAccessTag扩展了SimpleTagSupport{
私有静态记录器log=Logger.getLogger(StaticMethodAccessTag.class);
私有字符串varName;
私有字符串类名;
私有字符串范围;
私有对象[]args;
@凌驾
public void doTag()抛出JSPEException{
试一试{
如果(范围==null | |“”.equals(范围)){
scope=“page”;
}
int requestedScope=TagUtils.getScope(范围);
Class declaringClass=Class.forName(this.className);
对象实例=null;
类[]argTypes=null;
if(args==null | | args.length==0){
//无参数;使用默认构造函数
instance=declaringClass.newInstance();
}否则{
//如果提供了args,请找到匹配的构造函数并使用它
argTypes=新类[args.length];
对于(int i=0;i<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>
<%@ 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 --%>
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);
}
public class MyStaticUtil implements InvocationHandler {
/* (non-Javadoc)
* @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// it's just a lot easier if all method names in StaticMethodAccessTag are unique..
String methodName = method.getName();
Method methodToCall = null;
for( Method realMethod : StaticMethodAccessTagInterface.class.getMethods() ) {
if( realMethod.getName().equals(methodName) ) {
methodToCall = realMethod;
break;
}
}
// check if the method has actually been found or not, otherwise possible NPE's..
return methodToCall.invoke(null, args);
}
private final static MyStaticUtil _instance = new MyStaticUtil();
public static Object getInstance() {
Class [] proxyInterfaces = {
StaticMethodAccessTagInterface.class
};
return Proxy.newProxyInstance(
MyStaticUtil.class.getClassLoader(),
proxyInterfaces,
_instance);
}
}