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