DI

It refers to a specific implementation of Spring’s IOC container that is used to reduce dependencies between objects.

A reminder of IOC is that the IOC container, not the developer, manages the instance from creation to destruction. This IOC container is injected through DI.

Java bean

Java objects created and (destroyed eventually) managed by spring IOC container is called bean.

Analogy of DI

To go with your analogy of your components being in a treasure chest: A system (lets say a treasure polisher) without dependency injection has to have the ability to pick an item from the treasure chest itself. It has to have some knowledge the dependency’s nature in order to pick the correct treasure to polish depending on the current context. Thus coupling.

In a DI scenario, your treasure polisher does not need to know about the existence of the treasure chest at all. All it needs to know is that at some point (preferably at creation) the polisher will be provided (injected) with an object which implements ITreasure:

3 ways of DI

There are 3 types: field injection, setter injection, and constructor injection but constructor is highly recommended.

Field injection

It cannot modify field objects. Also, to use this, we have to rely on DI framework (e.g. Spring)

@Autowired 
private UserRepository userRepository

Dependency Injection through constructor

Advantages: 1) Constructor injection injects required beans at the time of object creation with a constructor. Therefore, the circular reference can be solved. But it can still occur. When it does occur, best solution is to 순환참조가 되지 않도록 설계. Last resort is putting @Lazy annotation, though it is not recommended at all by Spring.

2) Fields can be declared final. So can make them immutable

3) You can run tests more easily without using a DI container

Various methods like setter method are used in DI. But DI through constructor is most recommended.

    public class MemberService {

        private final MemberRepository memberRepository;

        @Autowired
        public MemberService(MemberRepository memberRepository) {
            this.memberRepository = memberRepository;
        }

And then when you want to DI:

    public WebConfig(){
        MemberService memberService = new MemberService(bitchMemberRepository);
    }

Better way

But a better way is remembering that @Autowired can be omitted if there is only one constructor and using Lombok’s @AllArgsConstructor:

    @AllArgsConstructor
    public class MemberService {

        private final MemberRepository memberRepository;

Best way

Best way is using @RequiredArgsConstructor, which creates constructor only for @final or @NonNull field. When there is just 1 constructor, @Autowired is automatically added to that constructor for DI:

    @RequiredArgsConstructor
    public class MemberService {

        private final MemberRepository memberRepository;

Another example of DI:

    @Repository
    @RequiredArgsConstructor
    public class MemberRepository {

    //    @PersistenceContext
        private final EntityManager em;

which is same as:

    @Repository
    public class MemberRepository {

        @Autowired @PersistenceContext
        private final EntityManager em;

        public MemberRepository(EntityManager em){
            this.em = em;
        }