JavaSpring缓存会破坏反射吗?
我最近正在使用spring引导和集成缓存。在我的测试中,我使用了一些反射 这里有一个例子:JavaSpring缓存会破坏反射吗?,java,spring,caching,reflection,Java,Spring,Caching,Reflection,我最近正在使用spring引导和集成缓存。在我的测试中,我使用了一些反射 这里有一个例子: @Service public class MyService { private boolean fieldOfMyService = false; public void printFieldOfMyService() { System.out.println("fieldOfMyService:" + fieldOfMyService); } @
@Service
public class MyService {
private boolean fieldOfMyService = false;
public void printFieldOfMyService() {
System.out.println("fieldOfMyService:" + fieldOfMyService);
}
@Cacheable("noOpMethod")
public void noOpMethod() {
}
}
@RunWith(SpringRunner.class)
@SpringBootTest(classes = { MyApplication.class })
public class MyServiceTest {
@Autowired
private MyService myService;
@Test
public void test() throws Exception {
myService.printFieldOfMyService();
boolean fieldOfMyService = (boolean) FieldUtils.readField(myService, "fieldOfMyService", true);
System.out.println("fieldOfMyService via reflection before change:" + fieldOfMyService);
FieldUtils.writeField(myService, "fieldOfMyService", true, true);
boolean fieldOfMyServiceAfter = (boolean) FieldUtils.readField(myService, "fieldOfMyService", true);
System.out.println("fieldOfMyService via reflection after change:" + fieldOfMyServiceAfter);
myService.printFieldOfMyService();
}
}
这就是测试:
@Service
public class MyService {
private boolean fieldOfMyService = false;
public void printFieldOfMyService() {
System.out.println("fieldOfMyService:" + fieldOfMyService);
}
@Cacheable("noOpMethod")
public void noOpMethod() {
}
}
@RunWith(SpringRunner.class)
@SpringBootTest(classes = { MyApplication.class })
public class MyServiceTest {
@Autowired
private MyService myService;
@Test
public void test() throws Exception {
myService.printFieldOfMyService();
boolean fieldOfMyService = (boolean) FieldUtils.readField(myService, "fieldOfMyService", true);
System.out.println("fieldOfMyService via reflection before change:" + fieldOfMyService);
FieldUtils.writeField(myService, "fieldOfMyService", true, true);
boolean fieldOfMyServiceAfter = (boolean) FieldUtils.readField(myService, "fieldOfMyService", true);
System.out.println("fieldOfMyService via reflection after change:" + fieldOfMyServiceAfter);
myService.printFieldOfMyService();
}
}
正如您所看到的,它非常简单:
有一个私有字段MyService
fieldOfMyService
- 测试通过反射将其从
更改为false
true
- 没有缓存,一切都正常。。这是输出:
春季@EnableCaching
- 然后你会发现:
提前感谢您的帮助:-)行为上的差异是由于Spring框架所做的代理。当bean需要任何特殊处理时(在本例中为缓存),将在运行时为bean创建一个代理。Spring中有两种代理技术-。有关更多详细信息,请阅读共享的文档链接 在示例代码共享中,CGLIB代理发挥了作用(提示:
MyService
没有实现接口)。由CGLIB创建的运行时子类将具有原始类的所有字段,但根据数据类型(此处false
)将保持null/default。此外,对代理的方法调用被委托给原始实例方法
下面对代码进行的更改将提供有关其工作原理的更多详细信息
更改1:同时打印对象.getClass()
@Service
public class MyService {
private Boolean fieldOfMyService = false;
public void printFieldOfMyService() {
System.out.println("fieldOfMyService:" + this.getClass()+" : "+fieldOfMyService);
}
@Cacheable("noOpMethod")
public void noOpMethod() {
}
}
测试班
@SpringBootTest(classes = { MyApplication.class })
public class MyServiceTest {
@Autowired
private MyService myService;
@Test
public void test() throws Exception {
myService.printFieldOfMyService();
boolean fieldOfMyService = (boolean) FieldUtils.readField(myService, "fieldOfMyService", true);
System.out.println(
"fieldOfMyService via reflection before change:" + myService.getClass() + " " + fieldOfMyService);
FieldUtils.writeField(myService, "fieldOfMyService", true, true);
boolean fieldOfMyServiceAfter = (boolean) FieldUtils.readField(myService, "fieldOfMyService", true);
System.out.println(
"fieldOfMyService via reflection after change:" + myService.getClass() + " " + fieldOfMyServiceAfter);
myService.printFieldOfMyService();
}
}
使用@EnableCaching
可打印此代码
fieldOfMyService:class rg.so.qn.MyService : false
fieldOfMyService via reflection before change:class rg.so.qn.MyService$$EnhancerBySpringCGLIB$$dfa75fca false
fieldOfMyService via reflection after change:class rg.so.qn.MyService$$EnhancerBySpringCGLIB$$dfa75fca true
fieldOfMyService:class rg.so.qn.MyService : false
fieldOfMyService:class rg.so.qn.MyService : false
fieldOfMyService via reflection before change:class rg.so.qn.MyService false
fieldOfMyService via reflection after change:class rg.so.qn.MyService true
fieldOfMyService:class rg.so.qn.MyService : true
这里,通过反射更新的值是针对运行时子类实例的
如果没有@EnableCaching
,此代码将打印出来
fieldOfMyService:class rg.so.qn.MyService : false
fieldOfMyService via reflection before change:class rg.so.qn.MyService$$EnhancerBySpringCGLIB$$dfa75fca false
fieldOfMyService via reflection after change:class rg.so.qn.MyService$$EnhancerBySpringCGLIB$$dfa75fca true
fieldOfMyService:class rg.so.qn.MyService : false
fieldOfMyService:class rg.so.qn.MyService : false
fieldOfMyService via reflection before change:class rg.so.qn.MyService false
fieldOfMyService via reflection after change:class rg.so.qn.MyService true
fieldOfMyService:class rg.so.qn.MyService : true
这里,通过反射更新的值是针对实际实例的
更改2:将MyService.fieldOfMyService的数据类型修改为Boolean
@Service
public class MyService {
private Boolean fieldOfMyService = false;
public void printFieldOfMyService() {
System.out.println("fieldOfMyService:" + this.getClass()+" : "+fieldOfMyService);
}
@Cacheable("noOpMethod")
public void noOpMethod() {
}
}
使用@EnableCaching
此代码将在myservicest.test()
,boolean fieldOfMyService=(boolean)FieldUtils.readField(myService,“fieldOfMyService”,true)产生NPE代码>因为运行时代理将字段初始化为null
如果没有@EnableCaching
,此代码将按预期运行
希望这有帮助
注意:可以避免使用@ExtendWith
和@RunWith
进行元注释
-------------------------------更新---------------------------------------
也想过分享这个问题的答案“有解决方案吗?”
或者使用setter方法更新字段的状态
或
如果反射是唯一选项,则获取实际实例并使用反射更新字段
下面的代码假设缓存是通过@EnableCaching
启用的,并且强制转换可以在不进行任何检查的情况下工作。请做必要的修改,使它万无一失
@Test
public void test() throws Exception {
myService.printFieldOfMyService();
MyService actualInstance = (MyService)((Advised) myService).getTargetSource().getTarget();
boolean fieldOfMyService = (boolean) FieldUtils.readField(actualInstance, "fieldOfMyService", true);
System.out.println(
"fieldOfMyService via reflection before change:" + myService.getClass() + " " + fieldOfMyService);
FieldUtils.writeField(actualInstance, "fieldOfMyService", true, true);
boolean fieldOfMyServiceAfter = (boolean) FieldUtils.readField(actualInstance, "fieldOfMyService", true);
System.out.println(
"fieldOfMyService via reflection after change:" + myService.getClass() + " " + fieldOfMyServiceAfter);
myService.printFieldOfMyService();
}
您还需要进一步澄清吗?是的@R.G非常感谢-这澄清了我的怀疑,通过您的更新,我甚至可以在测试中激活缓存。再次感谢-谢谢。