JavaSpring缓存会破坏反射吗?

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

我最近正在使用spring引导和集成缓存。在我的测试中,我使用了一些反射

这里有一个例子:

@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非常感谢-这澄清了我的怀疑,通过您的更新,我甚至可以在测试中激活缓存。再次感谢-谢谢。