Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How Dependency Injection by name works in Spring @Bean method parameters

I understand Spring DI and how it works in general.

But what I cannot understand here is in case of @Bean method parameter injection, how spring knows about the parameter name so it could inject beans from its bean's factory based on the parameter's name?

For example, In the following example, the methods fernas1 and fernas2 parameters are being wiped at runtime. however, spring still can inject the correct Abbas bean instance into it.

@SpringBootApplication
public class DemoApplication {

    @Autowired
    private Abbas abbas1;    // this is understandable, hence the field name is available at runtime

    @Autowired
    private Abbas abbas2;   // this is understandable, hence the field name is available at runtime

    public static void main(String[] args) {
        ConfigurableApplicationContext ctx = SpringApplication.run(DemoApplication.class, args);

        Map<String, Fernas> beansOfType = ctx.getBeansOfType(Fernas.class);
        System.out.println(beansOfType);

        Arrays.stream(DemoApplication.class.getMethods())
                .filter(m -> m.getName().startsWith("fernas"))
                .flatMap(m -> Stream.of(m.getParameters()))
                .map(Parameter::getName)
                .forEach(System.out::println);

        System.out.println(ctx.getBean(DemoApplication.class).abbas1);
        System.out.println(ctx.getBean(DemoApplication.class).abbas2);
    }

    class Abbas {
        String name;

        @Override
        public String toString() {
            return name;
        }
    }

    class Fernas {
        Abbas abbas;

        @Override
        public String toString() {
            return abbas.toString();
        }
    }

    @Bean
    public Abbas abbas1() {
        Abbas abbas = new Abbas();
        abbas.name = "abbas1";
        return abbas;
    }

    @Bean
    public Abbas abbas2() {
        Abbas abbas = new Abbas();
        abbas.name = "abbas2";
        return abbas;
    }

    // this is not understandable, hence the parameter name is NOT available at runtime
    @Bean
    public Fernas fernas1(Abbas abbas1) {
        Fernas fernas1 = new Fernas();
        fernas1.abbas = abbas1;
        return fernas1;
    }

    // this is not understandable, hence the parameter name is NOT available at runtime
    @Bean
    public Fernas fernas2(Abbas abbas2) {
        Fernas fernas2 = new Fernas();
        fernas2.abbas = abbas2;
        return fernas2;
    }
}

EDIT: The same problem and solution by @Javier both works on method and constructor parameters.

like image 542
Muhammad Hewedy Avatar asked Feb 04 '18 23:02

Muhammad Hewedy


1 Answers

If parameter name reflection is not available, it uses the information in the class file itself. See DefaultParameterNameDiscoverer

Default implementation of the ParameterNameDiscoverer strategy interface, using the Java 8 standard reflection mechanism (if available), and falling back to the ASM-based LocalVariableTableParameterNameDiscoverer for checking debug information in the class file.

For instance, the LocalVariableTable of DemoApplication.fernas2 is

    Start  Length  Slot  Name   Signature
        0      16     0  this   Lcom/example/demo/DemoApplication;
        0      16     1 abbas2   Lcom/example/demo/DemoApplication$Abbas;
        9       7     2 fernas2   Lcom/example/demo/DemoApplication$Fernas;
like image 197
Javier Avatar answered Oct 05 '22 23:10

Javier