简体   繁体   English

修改最终公共类Java中的最终静态变量

[英]Modifying final static variable within final public class Java

I have a quick question regarding modifying a final public class. 我有一个关于修改最终公共课的快速问题。 Based on some researches, it seems like final public class cannot be inherited or implemented. 基于一些研究,似乎最终的公共类不能被继承或实现。 My goal is to change one final static variable within this final public class. 我的目标是在最终的公共类中更改一个最终的静态变量。

class name is : public final class Utils 类名是 :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");

I want to get rid of authorization field from this DISALLOWED_HEADERS_SET . 我想从这个DISALLOWED_HEADERS_SET删除authorization字段。 Is there any ways to doing this? 有没有办法做到这一点?

I heard reflection is one way to modify classes. 我听说反射是修改类的一种方法。 This is a Apress/java-9-revealed to a github that seems to reveal what's inside of the class 这是一个Apress / java-9-向github 透露 ,似乎揭示了类内部的内容


This thread (question) has been identified as XY problem. 该线程(问题)已被识别为XY问题。 I will try to explain with more information on why I want a solution for the above problem. 我将尝试解释为什么我想要解决上述问题的更多信息。 Before digging into the reason that drove me to ask this question, I will cover the current situation of where this problem is at as of now. 在深入研究驱使我提出这个问题的原因之前,我将介绍目前这个问题所在的现状。

It is important to understand that this problem has been already posed by Clevertap to Oracle . 重要的是要理解Clevertap已经对Oracle提出了这个问题。 If you follow Oracle link, you can see that this problem has been acknowledged and updated to Jdk 11 . 如果您使用Oracle链接,您可以看到此问题已得到确认并更新到Jdk 11 Hopefully, Oracle applies this fixed source code to the coming Java 10 , but it is highly unlikely given the fact that Jdk 9 represents Java 9 . 希望Oracle将这个固定的源代码应用于即将推出的Java 10 ,但鉴于Jdk 9代表Java 9这一事实,这是极不可能的。 This being said, only solution there is to use reflection which the open thread in clevertap suggests. 就是说 ,只有解决方案是使用反射,这是clevertap中开放线程所暗示的。

Now, I will briefly explain what I have achieved and am trying to figure out. 现在,我将简要解释一下我所取得的成就并试图找出答案。 I have been working on a framework that I have been developing for sending Push Notification to APNs using Java language. 我一直致力于开发一个框架,用于使用Java语言将推送通知发送到APN。 Everything works except one functionality. 一切都有效,除了一个功能。

[ I will share this framework through GitHub in the near future for those trying to send notification to APNs without depending on third party frameworks, such as Jetty, Netty, or okhttp. [ 我将在不久的将来通过GitHub分享这个框架,以便那些试图向APN发送通知而不依赖于第三方框架,例如Jetty,Netty或okhttp。 ] ]

The problem rises when I try to use token as a way of authentication to send notification. 当我尝试使用令牌作为身份验证方式发送通知时,问题就出现了。 I have successfully created token following through the instruction provided by Apple . 我已经通过Apple提供的指令成功创建了令牌跟踪。 All I have to do is to set request header with authorization key and bearer <token> value. 我所要做的就是使用authorization密钥和bearer <token>值设置请求头。 However, when I use .setHeader derived from jdk9.incubator.httpclient module to set these values, it automatically omits this field. 然而,当我使用.setHeader源自jdk9.incubator.httpclient模块设置这些值,它会自动忽略这一领域。 As aforementioned, authorization is part of DISALLOWED_HEADERS_SET and it is clearly not allowed. 如前所述, authorizationDISALLOWED_HEADERS_SET一部分,显然不允许。 If a user attempts to set "authorization" as a key value for the request header field, it is deleted. 如果用户尝试将“授权”设置为请求标头字段的键值,则会将其删除。 If you have any suggestions to work around this problem. 如果您有任何建议可以解决此问题。 It will be awesome and helpful for others facing the same problem. 对于面临同样问题的其他人来说,这将是非常有用的。


Bad news folks... jdk 9.0.4 removed setSystemHeader method, so if you want to use reflection , you need to use Jdk 9.0.1 坏消息的人... jdk 9.0.4删除了setSystemHeader方法,所以如果要使用reflection ,则需要使用Jdk 9.0.1


As promised before, I created java library for sending notification using pure java code and pushed it to the github . 正如之前所承诺的,我创建了java库,用于使用纯Java代码发送通知并将其推送到github I used older version that was based on jdk10 for my published app. 我使用基于jdk10旧版本为我发布的应用程序。 Older version only supported tls connection. 旧版本仅支持tls连接。 Now the current version based on jdk11 is supporting both tls and token based authentication for sending push notification to APNs. 现在,基于jdk11的当前版本支持基于tls和令牌的身份验证,用于向APN发送推送通知。

Will just removing that value from the set work for you? 只是从你的工作中删除那个值吗? Something like this: 像这样的东西:

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

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

There is no clear for me what are you going achieve by inheritance since this value is static. 我不清楚你要通过继承实现什么,因为这个值是静态的。 But if you possible to recompile your application, have you considered of object composition? 但是,如果您可以重新编译应用程序,是否考虑过对象组合? This can be an alternate to inheritance and is often used to simulate polymorphic behavior in case of final class or 'multiply' inheritance. 这可以是继承的替代,并且通常用于在最终类或“乘法”继承的情况下模拟多态行为。 If you can 'inject' the new class instead of original one (which is final), you can compose a new class that contains instance of final class and delegates its methods to them. 如果你可以“注入”新类而不是原始类(这是最终的),你可以编写一个包含final类实例的新类,并将其方法委托给它们。 There is no need to create same final static variable. 无需创建相同的最终静态变量。

If you wish to bank upon reflection , this might help - 如果您希望反思 ,这可能会有所帮助 -

Class reqClz = request.getClass();

Method setHeaderMethod = reqClz.getDeclaredMethod("setSystemHeader", String.class, String.class);
setHeaderMethod.setAccessible(true);

setHeaderMethod.invoke(request, "authorization", "<token>");

for which you might have to open the incubator module's package with the use of VM arg:- 您可能需要使用VM arg打开孵化器模块的包: -

--add-opens jdk.incubator.httpclient/jdk.incubator.http=<your-package x.y.z>

OR 要么

Was also thinking that, another way here possibly could be patch the module content 也在想,这里的另一种方式可能是修补模块内容

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

for the jdk.incubator.http.internal.common.Utils class but as recommended not more than testing or debugging purpose. 对于jdk.incubator.http.internal.common.Utils类,但建议不要超过测试或调试目的。

The following code allows you to change the value of private static fields (non-final): 以下代码允许您更改私有静态字段的值(非最终):

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);
        }
    }

}

If you change the field to final this will be prevented, so to answer your question: It's not possible to do what you currently try to do using reflection. 如果您将字段更改为final字段将被阻止,那么要回答您的问题:您无法使用反射执行当前尝试执行的操作。 There is a way, though by using JNI, but you don't want to go that way. 有一种方法,虽然使用JNI,但你不想这样做。 Whatever you try to accomplish you should try to find a different solution, eg by checking the class that is using this header if it can be configured with an alternate set of disallowed header values or go the way @nullpointer described. 无论您尝试完成什么,都应该尝试找到不同的解决方案,例如,如果可以使用备用的一组不允许的标头值配置或使用@nullpointer描述的方式,则检查正在使用此标头的类。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM