[英]Modifying final static variable within final public class Java
我有一个关于修改最终公共课的快速问题。 基于一些研究,似乎最终的公共类不能被继承或实现。 我的目标是在最终的公共类中更改一个最终的静态变量。
类名是 :public final class Utils
private static final Set<String> DISALLOWED_HEADERS_SET = Set.of(
"authorization", "connection", "cookie", "content-length",
"date", "expect", "from", "host", "origin", "proxy-authorization",
"referer", "user-agent", "upgrade", "via", "warning");
我想从这个DISALLOWED_HEADERS_SET
删除authorization
字段。 有没有办法做到这一点?
我听说反射是修改类的一种方法。 这是一个Apress / java-9-向github 透露 ,似乎揭示了类内部的内容
该线程(问题)已被识别为XY问题。 我将尝试解释为什么我想要解决上述问题的更多信息。 在深入研究驱使我提出这个问题的原因之前,我将介绍目前这个问题所在的现状。
重要的是要理解Clevertap已经对Oracle提出了这个问题。 如果您使用Oracle链接,您可以看到此问题已得到确认并更新到Jdk 11
。 希望Oracle将这个固定的源代码应用于即将推出的Java 10
,但鉴于Jdk 9
代表Java 9
这一事实,这是极不可能的。 这就是说 ,只有解决方案是使用反射,这是clevertap中的开放线程所暗示的。
现在,我将简要解释一下我所取得的成就并试图找出答案。 我一直致力于开发一个框架,用于使用Java语言将推送通知发送到APN。 一切都有效,除了一个功能。
[ 我将在不久的将来通过GitHub分享这个框架,以便那些试图向APN发送通知而不依赖于第三方框架,例如Jetty,Netty或okhttp。 ]
当我尝试使用令牌作为身份验证方式发送通知时,问题就出现了。 我已经通过Apple提供的指令成功创建了令牌跟踪。 我所要做的就是使用authorization
密钥和bearer <token>
值设置请求头。 然而,当我使用.setHeader
源自jdk9.incubator.httpclient模块设置这些值,它会自动忽略这一领域。 如前所述, authorization
是DISALLOWED_HEADERS_SET
一部分,显然不允许。 如果用户尝试将“授权”设置为请求标头字段的键值,则会将其删除。 如果您有任何建议可以解决此问题。 对于面临同样问题的其他人来说,这将是非常有用的。
坏消息的人... jdk 9.0.4
删除了setSystemHeader
方法,所以如果要使用reflection
,则需要使用Jdk 9.0.1
正如之前所承诺的,我创建了java库,用于使用纯Java代码发送通知并将其推送到github 。 我使用基于jdk10
旧版本为我发布的应用程序。 旧版本仅支持tls连接。 现在,基于jdk11
的当前版本支持基于tls和令牌的身份验证,用于向APN发送推送通知。
只是从你的工作中删除那个值吗? 像这样的东西:
Field f = Utils.class.getDeclaredField("DISALLOWED_HEADERS_SET");
f.setAccessible(true);
@SuppressWarnings("unchecked")
Set<String> headers = (Set<String>)f.get(null);
headers.remove("authorization");
我不清楚你要通过继承实现什么,因为这个值是静态的。 但是,如果您可以重新编译应用程序,是否考虑过对象组合? 这可以是继承的替代,并且通常用于在最终类或“乘法”继承的情况下模拟多态行为。 如果你可以“注入”新类而不是原始类(这是最终的),你可以编写一个包含final类实例的新类,并将其方法委托给它们。 无需创建相同的最终静态变量。
如果您希望反思 ,这可能会有所帮助 -
Class reqClz = request.getClass();
Method setHeaderMethod = reqClz.getDeclaredMethod("setSystemHeader", String.class, String.class);
setHeaderMethod.setAccessible(true);
setHeaderMethod.invoke(request, "authorization", "<token>");
您可能需要使用VM arg打开孵化器模块的包: -
--add-opens jdk.incubator.httpclient/jdk.incubator.http=<your-package x.y.z>
要么
也在想,这里的另一种方式可能是修补模块内容
--patch-module <module>=<file>(<pathsep><file>)*
对于jdk.incubator.http.internal.common.Utils
类,但建议不要超过测试或调试目的。
以下代码允许您更改私有静态字段的值(非最终):
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
public class SetFinalField {
@SuppressWarnings("unchecked")
public final static void main(String[] args) throws Exception {
System.out.println("elements before: " + SetTarget.DISALLOWED_HEADERS_SET);
Field f = SetTarget.class.getDeclaredField("DISALLOWED_HEADERS_SET");
f.setAccessible(true);
ArrayList<String> l = new ArrayList<String>();
l.addAll((Set<String>) f.get(null));
l.remove("authorization");
HashSet<String> hs = new HashSet<>();
hs.addAll(l);
f.set(null, Collections.unmodifiableSet(hs));
System.out.println("elements after: " + SetTarget.DISALLOWED_HEADERS_SET);
}
public final static class SetTarget {
private static Set<String> DISALLOWED_HEADERS_SET;
static {
HashSet<String> l = new HashSet<>();
Collections.addAll(l, new String[]{"authorization", "connection", "cookie", "content-length",
"date", "expect", "from", "host", "origin", "proxy-authorization",
"referer", "user-agent", "upgrade", "via", "warning"});
DISALLOWED_HEADERS_SET = Collections.unmodifiableSet(l);
}
}
}
如果您将字段更改为final
字段将被阻止,那么要回答您的问题:您无法使用反射执行当前尝试执行的操作。 有一种方法,虽然使用JNI,但你不想这样做。 无论您尝试完成什么,都应该尝试找到不同的解决方案,例如,如果可以使用备用的一组不允许的标头值配置或使用@nullpointer描述的方式,则检查正在使用此标头的类。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.