Last Updated: February 25, 2016
·
792
· 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;
    }