Search code examples
javaspringautowiredspring-ioc

Magic behind @ComponentScan and @Component


I'm still learning spring dependency injection in depth.

My first class is a configuration class, where i declare to the container to load and manage the beans declared in the annotated methods.

package ioc.question_004;

import ioc.commun.Person;
import ioc.commun.Profession;
import org.springframework.context.annotation.*;

@Configuration
public class MyConfiguration {

    @Bean
    public Profession profession(){
        return Profession.builder()
                     .name("professor")
                     .description("professor in the university")
                     .build();
    }

    @Bean
    public Person person(){
        return Person.builder()
                     .name("Bagna")
                     .age(52)
                     .profession(profession())
                     .build();
    }

}

My second class is a daoRepository, it looks likes:

package ioc.question_008.dao;

import ioc.commun.Person;
import lombok.*;
import org.springframework.stereotype.Repository;

import java.util.ArrayList;
import java.util.List;

@Repository
@Builder
@Setter
@Getter
@EqualsAndHashCode
@ToString
public class MyDaoRepository implements dao {

    List<Person> personList = new ArrayList<>();

    @Override
    public boolean save( Person person ){
        return this.personList.add(person);
    }

    @Override
    public boolean delete( Person person ){
        return  this.personList.remove(person);
    }

}

My running code is as follows:

@Configuration
@Import(MyConfiguration.class)
@ComponentScan(basePackageClasses = {MyDaoRepository.class} )
public class DependencyInjection {
    public static void main( String[] args ) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(DependencyInjection.class);
        dao myDaoRepository = (MyDaoRepository) context.getBean("myDaoRepository");
        System.out.println(myDaoRepository);
    }

}

Magically MyDaoRepository contains the @Bean person declared in the configuration class MyConfiguration:

MyDaoRepository(personList=[Person(name=Bagna, age=52, profession=Profession(name=professor, description=professor in the university))])

I thnik that the container injected this object automatically even if i didn't ask for that. May be some @Autowired annotation is added by the compiler. I'm not sure.

Could you please explain to me how i can ask the spring container to not inject the beans even if they exists in the container(the bean person for example), unless i ask the to do the injection by myself with the @Autowired annotation.


Solution

  • The reason is the combination of spring's autowiring of collections and lombok's Builder

    First, the @Builder adds an all args constructor MyDaoRepository(List<Person> list).

    Second, spring automatically detects your factory methods @Bean public Person person().

    Now, since the constructor expects a collection, spring accumulates all Person-beans it can find into a List and injects it into the constructor, setting the list accordingly.

    I think (but it is currently untested but documented here), that adding @Autowired(required = false) to your List<Person> persons property is what you want in this case, as that marks it as optional and spring will not inject it. (EDIT: I was mistaken, you still need the no-args-constructor for this to work, but without any @Autowired annotation the list would not get injected that way anyway. The required=false simply prevents the exception when no proper Person bean is found.)

    EDIT: As Mr. Roddy, the Roddy of the Frozen Peas, pointed out, it would also be possible to add a different constructor for spring to create your bean, e.g. by annotating your repository with @NoArgsConstructor