记一次线上事故:
原本代码是这样的,线上application.yml中istest=true
@Value("${istest}")
private boolean istest=false;
IDEA提示将该字段用final修饰,结果导致读取到该字段为false。
原因是@Value注解的字段是在bean实例化之后,通过反射赋值的,而反射能顺利修改final字段(无报错提示),但由于字符串和基本类型的final字段会经过JVM内联优化,对变量的访问被替换成常量值的访问,导致获取不到修改后的值。即反射能修改final,但普通的方式无法获取到修改后的值。
final String s = "abc";
final int i = 3;
但引用类型final除外,普通方式能获取到反射修改后的结果
final String s1 = new String("abc");
final Object o = new Object();
完整例子
public class FinalReflectEdit {
final String s = "abc";
final String s1 = new String("abc");
final int i = 3;
final Object o = new Object();
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
FinalReflectEdit finalReflectEdit = new FinalReflectEdit();
Field s = FinalReflectEdit.class.getDeclaredField("s");
Field i = FinalReflectEdit.class.getDeclaredField("i");
Field o = FinalReflectEdit.class.getDeclaredField("o");
Field s1 = FinalReflectEdit.class.getDeclaredField("s1");
System.out.printf("s:%s,i:%s,o:%s,s1:%s%n", finalReflectEdit.s, finalReflectEdit.i, finalReflectEdit.o, finalReflectEdit.s1);
s.setAccessible(true);
i.setAccessible(true);
o.setAccessible(true);
s1.setAccessible(true);
s.set(finalReflectEdit, "cde");
i.set(finalReflectEdit, 4);
o.set(finalReflectEdit, new Object());
s1.set(finalReflectEdit, "cde");
System.out.printf("s:%s,i:%s,o:%s,s1:%s%n", finalReflectEdit.s, finalReflectEdit.i, finalReflectEdit.o, finalReflectEdit.s1);
System.out.printf("s:%s,i:%s,o:%s%n", s.get(finalReflectEdit), i.get(finalReflectEdit), o.get(finalReflectEdit));
}
}
s:abc,i:3,o:java.lang.Object@6aceb1a5,s1:abc
s:abc,i:3,o:java.lang.Object@67117f44,s1:cde
s:cde,i:4,o:java.lang.Object@67117f44