Spring:从final方法访问返回null的字段

时间:2017-06-06 09:20:06

标签: java spring

所以我在项目中遇到了一个非常奇怪的错误。我有以下两个班级

@Component
@Scope("prototype")
public abstract class World {
    String name;

    public World(String name) {
          this.name = name;
    }

    final void print() {
          System.out.println(name);
    }
}

@Component
@Scope("prototype")
public class Hello extends World {

    public Hello(String name) {
          super(name);
    }
}

当我使用context.getBean(Hello.class, "foo")实例化一个对象时,我得到一个Hello类bean。但是当我hello.print()时,它会打印null

删除最终修饰符解决了问题。这里发生了什么?春天对最终方法有任何限制吗?

2 个答案:

答案 0 :(得分:5)

我的假设是您使用 CGLIB代理。您可以通过

查看
<resources>
  <style name="CalculatorButton" >
    <item name="android:layout_width" >0dp</item>
    <item name="android:layout_weight" >33</item>
    <item name="android:layout_height" >fill_parent</item>
    <item name="android:background">@drawable/CalculatorButtonBackground</item>
  </style>

  <style name ="CalculatorRow">
    <item name="android:layout_width">match_parent</item>
    <item name="android:layout_weight">25</item>
    <item name="android:layout_height">0dip</item>
    <item name="android:weightSum">99</item>
    <item name="android:gravity">fill_vertical</item>
    <item name="android:divider">@drawable/CalculatorDivider</item>
    <item name="android:dividerPadding">0dp</item>
    <item name="android:showDividers">middle</item>
  </style>

  <style name ="CalculatorTable">
    <item name="android:layout_width">match_parent</item>
    <item name="android:layout_height">match_parent</item>
    <item name="android:weightSum">100</item>
    <item name="android:divider">@drawable/CalculatorDivider</item>
    <item name="android:dividerPadding">0dp</item>
    <item name="android:showDividers">middle</item>
    <item name="android:background">@drawable/CalculatorTableBackground</item>
    <item name="android:layout_margin">1dp</item>
  </style>
</resources>

在这种情况下,Spring要求CGLIB创建Hello的子类。该子类获取Hello类的真实实例。这允许覆盖所有方法,然后最终调用目标,这是它的外观:

context.getBean(Hello.class, "foo").getClass()

因为您将方法标记为class Proxy extends Hello { Hello target; void print() { target.print(); } } Java不允许覆盖。代理本身(虽然是Hello的子类)没有初始化final字段 - 它是null。

因此,不会调用您的真实name方法 - 仅调用Hello#print()并且Proxy#print()为空,这会导致在控制台中显示null。

答案 1 :(得分:0)

因此,我提出的解决方法是简单地将依赖于自动装配的方法实现移动到不会被CGLIB代理的私有方法。 这将产生一个给定的例子:

@Component
@Scope("prototype")
public abstract class World {
    String name;

    public World(String name) {
          this.name = name;
    }

    final void print() {
          doPrint();
    }

    private void doPrint() {
          //name is populated here
          System.out.println(name);
    }
}

@Component
@Scope("prototype")
public class Hello extends World {

    public Hello(String name) {
          super(name);
    }
}