修改最终公共类Java

时间:2018-02-20 19:51:22

标签: java reflection apple-push-notifications java-9 java-11

我有一个关于修改最终公共课的快速问题。基于一些研究,似乎最终的公共类不能被继承或实现。我的目标是在最终的公共类中更改一个最终的静态变量。

类名: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");

我希望摆脱此authorization中的DISALLOWED_HEADERS_SET字段。有没有办法做到这一点?

我听说反射是修改类的一种方法。这是github的Apress/java-9-revealed,它似乎揭示了类内部的内容

此线程(问题)已被识别为XY问题。我将尝试解释为什么我想要解决上述问题的更多信息。在深入研究驱使我提出这个问题的原因之前,我将介绍目前这个问题所在的现状。

重要的是要了解Clevertap已向Oracle提出此问题。如果您关注Oracle链接,则可以看到此问题已得到确认并更新为Jdk 11。希望Oracle将此固定源代码应用于即将到来的Java 10,但鉴于Jdk 9代表Java 9,这种情况极不可能。这就是说,只有解决方案才能使用open thread in clevertap建议的反射。

现在,我将简要解释一下我所取得的成就并试图找出答案。我一直致力于开发一个框架,用于使用Java语言将推送通知发送到APN。一切都有效,除了一个功能。

[我将在不久的将来通过GitHub分享这个框架,以便那些试图向APN发送通知而不依赖于第三方框架,例如Jetty,Netty或okhttp。]

当我尝试使用令牌作为身份验证方式发送通知时,问题就出现了。我已经通过Apple提供的指令成功创建了令牌跟踪。我所要做的就是使用authorization键和bearer <token>值设置请求标头。但是,当我使用从jdk9.incubator.httpclient模块派生的.setHeader来设置这些值时,它会自动省略此字段。如前所述,authorizationDISALLOWED_HEADERS_SET的一部分,显然不允许这样做。如果用户尝试将“授权”设置为请求标头字段的键值,则会将其删除。如果您有任何建议可以解决此问题。对于面临同样问题的其他人来说,这将是非常有用的。

坏消息人员jdk 9.0.4删除了setSystemHeader方法,因此如果您想使用reflection,则需要使用Jdk 9.0.1

正如之前所承诺的,我创建了java库,用于使用纯Java代码发送通知并将其推送到github。我使用基于jdk10的旧版本来发布我的应用。旧版本仅支持tls连接。现在,基于jdk11的当前版本支持基于tls和令牌的身份验证,用于向APN发送推送通知。

4 个答案:

答案 0 :(得分:2)

只是从你的设定中删除该值吗?像这样:

        Field f = Utils.class.getDeclaredField("DISALLOWED_HEADERS_SET");
        f.setAccessible(true);

        @SuppressWarnings("unchecked")
        Set<String> headers = (Set<String>)f.get(null);        
        headers.remove("authorization"); 

答案 1 :(得分:1)

我不清楚你要通过继承实现什么,因为这个值是静态的。 但是,如果您可以重新编译应用程序,是否考虑过对象组合?这可以是继承的替代,并且通常用于在最终类或“乘法”继承的情况下模拟多态行为。 如果你可以“注入”新类而不是原始类(这是最终的),你可以编写一个包含final类实例的新类,并将其方法委托给它们。无需创建相同的最终静态变量。

答案 2 :(得分:-1)

如果你wish to bank upon reflection,这可能会有所帮助 -

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 the module content

--patch-module <module>=<file>(<pathsep><file>)*

对于jdk.incubator.http.internal.common.Utils类,但建议不要超过测试或调试目的。

答案 3 :(得分:-1)

以下代码允许您更改私有静态字段的值(非最终):

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描述的方式进行。