java spring缓存会破坏反射吗?

2024-04-22

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

这是一个例子:

@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 to true通过反射

problem

  • 没有缓存,一切正常。这是输出:
fieldOfMyService:false
fieldOfMyService via reflection before change:false
fieldOfMyService via reflection after change:true
fieldOfMyService:true

现在我通过以下方式激活缓存:

  • @EnableCaching在春天
  • 然后你会得到这个:
fieldOfMyService:false
fieldOfMyService via reflection before change:false
fieldOfMyService via reflection after change:true
fieldOfMyService:false                      <<<<< !!!

长话短说:

  • 当缓存被激活时,服务似乎不受通过反射所做的更改的影响

有趣的是,只有当相应的服务实际上通过至少一个使用缓存时才会发生这种情况@Caching带注释的方法。如果该服务没有这样的服务:

@Service
public class MyService {

    private boolean fieldOfMyService = false;

    public void printFieldOfMyService() {
        System.out.println("fieldOfMyService:" + fieldOfMyService);
    }

}

..然后它仍然有效。

我想这与激活缓存时添加的层有关。但为什么?有解决办法吗?

在此先感谢您的帮助 :-)


行为上的差异是由 Spring 框架完成的代理造成的。当某个 bean 需要进行任何特殊处理(在本例中为缓存)时,就会在运行时创建该 bean 的代理。 Spring中有两种类型的代理技术 -基于 JDK 和 CGLIB 的代理 https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#aop-pfb-proxy-types。请阅读共享的文档链接以了解更多详细信息。

在共享的示例代码中,CGLIB 代理发挥了作用(线索:MyService不实现接口)。 CGLIB 创建的运行时子类将具有原始类的所有字段,但根据数据类型将保留 null/默认值(此处false)。此外,对代理的方法调用也被委托给原始实例方法。

对代码进行更改后将提供有关其工作原理的更多详细信息。

Change 1:打印object.getClass() as well

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

    }

}

With @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

这里通过反射更新的值是针对运行时子类实例的

Without @EnableCaching这段代码会打印

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

这里通过反射更新的值是针对实际实例的。

Change 2:修改数据类型MyService.fieldOfMyService to Boolean

@Service
public class MyService {

    private Boolean fieldOfMyService = false;

    public void printFieldOfMyService() {
        System.out.println("fieldOfMyService:" + this.getClass()+" : "+fieldOfMyService);
    }

    @Cacheable("noOpMethod")
    public void noOpMethod() {
    }

}

With @EnableCaching此代码将导致 NPEMyServiceTest.test() , boolean fieldOfMyService = (boolean) FieldUtils.readField(myService, "fieldOfMyService", true);因为运行时代理将该字段初始化为 null。

Without @EnableCaching这段代码将按预期运行。

希望这可以帮助。

Note : @SpringBootTest https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/test/context/SpringBootTest.html元注释为@ExtendWith and @RunWith是可以避免的。

-------------------------------------------Update---------------------------------------

也想分享这个问题的答案“有解决办法吗?”

要么使用 setter 方法来更新字段的状态

Or

如果反射是唯一的选择,则获取实际实例并使用反射来更新字段。

以下代码假设通过以下方式启用缓存@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();

}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

java spring缓存会破坏反射吗? 的相关文章

随机推荐

  • 为什么 C++ 编译器对许多大括号的处理方式不同?

    在下面的 C 20 程序中 我错误地添加了一对额外的弯曲大括号 in B A include
  • 如何在样式组件中访问 Material-ui 的主题

    我将 CRA 与 Material ui 和 Styled Components 类型的样式一起使用 在构建 CSS 时 我想访问 Material ui 的默认主题 package json 的一部分 dependencies react
  • 两点层之间的距离矩阵

    我有两个数组 其中包含不同大小的点坐标 shapely geometry Point Eg Point X Y Point X Y Point X Y Point X Y 我想用距离函数创建这两个数组的 叉积 距离函数来自shapely g
  • RETROFIT 如何解析此响应

    我正在尝试解析这个 json 响应雅虎YQL http chartapi finance yahoo com instrument 1 0 goog chartdata type quote range 1d json使用 Retrofit
  • 如何使用 Chromium 发送帖子和标题数据?

    我正在尝试将一些代码从 TWebBrowser 转换为 Chromium 但无法弄清楚如何使用 HTTP 请求发送帖子和标头数据 下面是我正在尝试实现的 TWebBrowser 功能 var VHeader PostData OleVari
  • 如何为产品类别设置 SQL 表结构?

    我对 SQL 还很陌生 目前正在尝试用 PHP 和 MySql 制作一个简单的产品页面 现在我有一个products有我的产品的表id name price and stock 编辑 样本products tables id name pr
  • 不同命名空间中 k8s 的入口配置

    我需要在 azure k8s 上配置 Ingress Nginx 我的问题是是否可以在一个命名空间等中配置 ingress ingress nginx 和其他命名空间中的一些服务 例如 资源 我的文件看起来像这样 ingress nginx
  • 在 Heroku 上运行 pdf2htmlEX

    我正在尝试奔跑pdf2htmlEX https github com coolwanglu pdf2htmlEX在赫罗库上 起初 我想到在具有与 Heroku 相同堆栈的虚拟机上编译 pdf2htmlEX 然后将二进制文件包含在 git 存
  • 如何从 PHP 字符串中获取 64 位整数哈希值?

    我需要 64 位字符串整数哈希值来实现哈希映射之类的功能 在我看来 没有可以返回 64 位整数的原生 PHP 哈希功能 我认为可以获取 sha1 哈希值的第一部分并将其转换为整数 然而 这不会带来最好的性能 而且转换似乎很棘手 当然 如果不
  • VBA 6.0 和 VBA 7.0 有什么区别?

    我注意到 Office 2010 附带了 Visual Basic for Applications 7 0 但是我似乎找不到太多关于所做更改的文档 有没有人有更改摘要或描述差异的任何资源 VBA6 和 VBA7 之间并没有太多变化 引入
  • 使用 JSoup 时选择具有多个类的元素

    我正在解析网站上的一些表格 特别是我试图按类名提取以下单元格 Elements e d select span class bld lrg red for Element element e System out println eleme
  • 如何捕捉 contentEditable 粘贴事件?

    我有一个很棒的可编辑文本区域wysihat http github com josh wysihat 和内容可编辑 我确实需要一种方法来拦截粘贴事件以阻止它们 或者在允许插入之前处理它们的 DOM 人们可以将整个网页粘贴到可编辑区域 这有点
  • Dependency Walker 未显示所有依赖的 Dll

    我有一个 fortran dll 我想知道它所依赖的程序集再分配目的 http software intel com en us forums showthread php t 73161 我发现的一件事是依赖项步行器没有显示所有依赖项 即
  • 复制上个月的值并插入到新行中

    这是我当前表的示例 1 表名称 TotalSales Name Year Month Sales Alfred 2011 1 100 我想要做的是创建一个像这样的表 添加一个新行 上个月的销售额 2 表名称 TotalSales Name
  • PHPstorm PHPunit 代码共同覆盖

    当我在 PHPstorm 中运行测试套件时 所有代码覆盖率都为 0 我知道这不是真的 当我从命令行运行 PHPunit 时 记录的 HTML 输出文档显示我实际上确实有覆盖率并列出所有详细信息 在 PHPstorm 中 我将 PHP 解释器
  • 从 xml 节点获取行号 - java

    我已经解析了一个 XML 文件并获得了一个我感兴趣的节点 现在如何在源 XML 文件中找到该节点出现的行号 编辑 目前我正在使用 SAXParser 来解析我的 XML 不过 我会对使用任何解析器的解决方案感到满意 除了节点之外 我还有节点
  • Solr距离过滤

    我正在尝试使用 Solr 进行距离范围搜索 我知道在5公里范围内进行搜索过滤很容易 q fq geofilt pt 45 15 93 85 sfield store d 5 我所追求的是如果我正在寻找一系列的说法 如何做同样的事情5至10公
  • 如何在 Django 管理中显示 ManyToMany 关系的 raw_id 值?

    我有一个应用程序在ForeignKeyField 和ManyToManyField 上使用raw id 管理员在编辑框右侧显示外键的值 不幸的是 它不适用于ManyToMany 我检查了代码 我认为这是正常行为 但是我想知道是否有人有一个简
  • 如何在不同的扬声器上播放声音?

    我的设备有 2 个外部扬声器 我如何分别测试它们 一种功能仅在左侧播放声音 一种功能仅在右侧播放声音 谷歌搜索没有成功 也许我使用了错误的术语 也许使用 WIN32 API 设置余额 如果您谈论的是立体声扬声器系统设置中的 2 个扬声器 那
  • java spring缓存会破坏反射吗?

    我最近正在使用 spring boot 和集成缓存 在我的测试中 我使用了一点反射 这是一个例子 Service public class MyService private boolean fieldOfMyService false p