[フレーム]
Last Updated: February 25, 2016
·
844
· edmondo1984

How to avoid Scala plugin 0.19.297 for Idea 12.1.4 Spring framework trap

The new Scala plugin introduces a regression on compilation of Java classes which could make you lose days to solve. If you use Spring Framework constructor injection, you will likely hit this problem

The background

If you use Spring Framework constructor injection, you might be using without knowing what was happening behind the scene.

Since parameters name gets erased during compilation, Spring uses this information to create the link between the constructor-arg name in your context and the constructor arguments. For such a mechanism to work, the correct debug information about vars should be produced by the compiler.

This is how the class would appear if this information is produced and you disassembly with javap -c-v


public class MyClass extends java.lang.Object
 SourceFile: "MyClass.java"
 minor version: 0
 major version: 49
 Constant pool:
const #1 = Method #6.#24; // java/lang/Object."<init>":()V
const #2 = Field #5.#25; // MyClass.myEnum:LMyEnum;
const #3 = Field #5.#26; // MyClass.ok:Z
const #4 = Field #5.#27; // MyClass.good:Z
const #5 = class #28; // MyClass
const #6 = class #29; // java/lang/Object
const #7 = Asciz myEnum;
const #8 = Asciz LMyEnum;;
const #9 = Asciz ok;
const #10 = Asciz Z;
const #11 = Asciz good;
const #12 = Asciz <init>;
const #13 = Asciz (LMyEnum;ZZ)V;
const #14 = Asciz Code;
const #15 = Asciz LineNumberTable;
const #16 = Asciz LocalVariableTable;
const #17 = Asciz this;
const #18 = Asciz LMyClass;;
const #19 = Asciz isOk;
const #20 = Asciz ()Z;
const #21 = Asciz isGood;
const #22 = Asciz SourceFile;
const #23 = Asciz MyClass.java;
const #24 = NameAndType #12:#30;// "<init>":()V
const #25 = NameAndType #7:#8;// myEnum:LMyEnum;
const #26 = NameAndType #9:#10;// ok:Z
const #27 = NameAndType #11:#10;// good:Z
const #28 = Asciz MyClass;
const #29 = Asciz java/lang/Object;
const #30 = Asciz ()V;

{
public MyClass(MyEnum, boolean, boolean);
 Code:
 Stack=2, Locals=4, Args_size=4
 0: aload_0
 1: invokespecial #1; //Method java/lang/Object."<init>":()V
 4: aload_0
 5: aload_1
 6: putfield #2; //Field myEnum:LMyEnum;
 9: aload_0
 10: iload_2
 11: putfield #3; //Field ok:Z
 14: aload_0
 15: iload_3
 16: putfield #4; //Field good:Z
 19: return
 LineNumberTable:
 line 12: 0
 line 13: 4
 line 14: 9
 line 15: 14
 line 16: 19

 LocalVariableTable:
 Start Length Slot Name Signature
 0 20 0 this LMyClass;
 0 20 1 myEnum LMyEnum;
 0 20 2 ok Z
 0 20 3 good Z


public boolean isOk();
 Code:
 Stack=1, Locals=1, Args_size=1
 0: aload_0
 1: getfield #3; //Field ok:Z
 4: ireturn
 LineNumberTable:
 line 19: 0

 LocalVariableTable:
 Start Length Slot Name Signature
 0 5 0 this LMyClass;


public boolean isGood();
 Code:
 Stack=1, Locals=1, Args_size=1
 0: aload_0
 1: getfield #4; //Field good:Z
 4: ireturn
 LineNumberTable:
 line 23: 0

 LocalVariableTable:
 Start Length Slot Name Signature
 0 5 0 this LMyClass;


}

The problem

If you create a simple Java Maven project, everything works fine in Idea 12.1.4. However, if you add Scala and use the last plugin version, LocalVariableTable is not generated for Java classes.

As a result, your application which was working, is not working any more and during the instantiation of beans, Spring will throw an idiot exception with the useless message:

Ambiguous constructor argument types - did you specify the correct bean references as constructor arguments?

The solution

Relying on class reading is not the only way to perform constructor injection in Spring: there is an alternative solution, an annotation named ConstructorProperties which you can use in the following way:


@ConstructorProperties({"currency","startCurrency","refreshQuotesAtStartup"})
 public CurrencyStartupConfiguration(Currency currency, boolean startCurrency, boolean refreshQuotesAtStartup) {
 this.currency = currency;
 this.startCurrency = startCurrency;
 this.refreshQuotesAtStartup = refreshQuotesAtStartup;
 }

AltStyle によって変換されたページ (->オリジナル) /