通过反射改变public static final字符串

简介

在开发项目的时候,有些资源是需要放在redis里面,一般是以key-value的形式存放。为了区分,key通常会带有一个前缀。后来,项目多了,而redis用的还是同一个,这时有可能会产生冲突。为了解决这样的冲突,需要在固定的前缀前加上项目名做隔离。由于key的前缀定义在一个公用web配置包里,和具体的web项目名称不在同一个地方,所以直接修改前缀是不可行的。最后用了Java的反射机制在项目运行的时候获取项目名并且改变原有前缀。

代码实现

@Component
public class RedisConstants {

    private static String moduleName;
    //此处需要把字符串改为new String的形式,否则不成功
    public static final String REDIS_USER_CACHE = new String("redis_user_cache");
    // 获取项目名
    @Value("${spring.application.name}")
    private void setModuleName(String m) {
        moduleName = m;
        try {
            setStaticFinal(RedisConstants.class.getField("REDIS_USER_CACHE"),
                    moduleName + RedisConstants.REDIS_USER_CACHE);
        }catch (Exception e){}
    }
    private static void setStaticFinal(Field field, Object newValue) throws Exception {
        field.setAccessible(true);
        Field modifiersField = Field.class.getDeclaredField("modifiers");
        modifiersField.setAccessible(true);
        //去掉final修饰符
        modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
        field.set(null, newValue);
    }
}

setStaticFinal方法

上面的代码中,最为关键的就是setStaticFinal方法。这个方法改变了变量的访问控制全,也就是说把final的修饰符给去掉了,从而可以改变变量的值。

modifiers字段

modifiers字段是Field类中一个比较关键的字段。它主要记录字段上都采用类那些修饰符。可以下面的代码与运行结果看出,采用private final static修饰的变量的modifiers值为26。

public class ReflectTest {
    private final static String name="xxxx";

    public static void main(String[] args) throws Exception {
        Field nameField = ReflectTest.class.getDeclaredField("name");
        int modifiers = nameField.getModifiers();
        System.out.println(modifiers);
        System.out.println(Modifier.toString(modifiers));
        System.out.println(Modifier.toString(7));
    }
}

这里写图片描述

Modifier类

modifiers字段有一个对应的Modifier类,该类的toString方法可以把modifiers字段的值解析为具体的修饰符。从该类的源码可以看出,设计者把modifiers字段(整形变量,32位)的不同位赋予了不同的含义。例如,public对应1,也就是把modifiers字段的二进制中的第一位有无对应起是否用public修饰。上述代码中,把7解释为了public protected private,因为7的二进制为0000 0000 0000 0000 0000 0000 0000 0111。
源码中采用16进制表示的方式,把它转为二进制更为直观。
例如:
  public:0000 0000 0000 0000 0000 0000 0000 0001
  private:0000 0000 0000 0000 0000 0000 0000 0010
  protected:0000 0000 0000 0000 0000 0000 0000 0100

/**
     * The {@code int} value representing the {@code public}
     * modifier.
     */
    public static final int PUBLIC           = 0x00000001;

    /**
     * The {@code int} value representing the {@code private}
     * modifier.
     */
    public static final int PRIVATE          = 0x00000002;

    /**
     * The {@code int} value representing the {@code protected}
     * modifier.
     */
    public static final int PROTECTED        = 0x00000004;

    /**
     * The {@code int} value representing the {@code static}
     * modifier.
     */
    public static final int STATIC           = 0x00000008;

    /**
     * The {@code int} value representing the {@code final}
     * modifier.
     */
    public static final int FINAL            = 0x00000010;

    /**
     * The {@code int} value representing the {@code synchronized}
     * modifier.
     */
    public static final int SYNCHRONIZED     = 0x00000020;

    /**
     * The {@code int} value representing the {@code volatile}
     * modifier.
     */
    public static final int VOLATILE         = 0x00000040;

    /**
     * The {@code int} value representing the {@code transient}
     * modifier.
     */
    public static final int TRANSIENT        = 0x00000080;

    /**
     * The {@code int} value representing the {@code native}
     * modifier.
     */
    public static final int NATIVE           = 0x00000100;

    /**
     * The {@code int} value representing the {@code interface}
     * modifier.
     */
    public static final int INTERFACE        = 0x00000200;

    /**
     * The {@code int} value representing the {@code abstract}
     * modifier.
     */
    public static final int ABSTRACT         = 0x00000400;

    /**
     * The {@code int} value representing the {@code strictfp}
     * modifier.
     */
    public static final int STRICT           = 0x00000800;

final修饰符

这行代码的作用是把field字段的final修饰符给去掉。Modifier.FINAL的值为 0000 0000 0000 0000 0000 0000 0001 0000,取反后1111 1111 1111 1111 1111 1111 1110 1111。把原有的modifiers值和取反后的Modifier.FINAL做与运算,即可不改变其他位,单独修改对应的位。

modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);

小结

在最开始的代码中还存在了一些值得注意的问题。
1、设置静态常量的类一般的使用方法为类名.字段名,也就是说不需要创建该类。在这个应用场景中,需要在Spring注入项目的名称,并且需要运行setStaticFinal方法来改变原有的值,所以需要加入@Component注解。把这个类交给Spring管理,并且属性注入的时候需要采用set方法注入,以便在其中运行setStaticFinal方法。
2、如注释所提及的,需要把常量改为用new String包裹起来的形式,这里涉及JDK的知识就不在此深究了。

猜你喜欢

转载自blog.csdn.net/yanfeng9507/article/details/82109719