본문 바로가기
개발하자/Spring

Dependency Injection(의존성 주입) - 어노테이션

by ulqaef 2020. 3. 26.
728x90

XML Configure 파일에 <bean> 등록하지 않고 자동으로 생성하려면 <context:component-scan /> 엘리먼트를 정의해주어야 한다. 이 엘리먼트를 추가하면 스프링 컨테이너는 classpath에 있는 클래스들을 스캔하면서 @Component가 설정된 클래스들을 자동으로 객체 생성한다.

 

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">

	<!-- Root Context: defines shared resources visible to all other web components -->    	
	
	<context:component-scan base-package="com.spring.example" />
</beans>

 

위의 소스를 보면 <context:component-scan>엘리먼트의 base-package속성을 "com.spring.example"으로 지정해주었는데 이 의미는 "com.spring.example"로 패키지로 시작하는 모든 패키지를 스캔하라는 의미이다.

 

@Component("pet")
public class Pet {
    public Pet() {
    /* default construct */
    }
}

Pet클래스의 기본생성자를 생성해야 스프링컨테이너가 Pet클래스를 메모리에 생성을 할 수 있고 스프링컨테이너가 생성한 Pet객체를 요청하기 위해 "pet"이라는 식별자도 등록해주어야 한다. 만약 식별자를 등록하지 않는다면 클래스이름의 첫글자를 소문자로 바꿔서 사용하면 된다.

 

[Pet.class]

package com.spring.example;

import org.springframework.stereotype.Component;

@Component("pet")
public class Pet {
    
    public Pet() {
        System.out.println("Pet 객체가 생성되었습니다.");
    }
    
    
    /*  중략  */
    
}

 

[MainExample.class]

package com.spring.example;

import org.springframework.context.support.GenericXmlApplicationContext;

public class MainExample {

    public static void main(String[] args) {
        GenericXmlApplicationContext context = new GenericXmlApplicationContext("classpath:META-INF/spring/root-context.xml");
        Pet pet = (Pet)context.getBean("pet");
        
        context.close();
   
    }
}

 

 

스프링에서는 어노테이션(@)방식으로 의존성 주입을 지원하고 있다.

@Autowired

@Inject

@Qualifer

@Resource

 

이렇게 네 가지가 있다.

 

 

1. @Autowired

@Autowired는 생성자, 메서드, 멤버변수에 모두 사용할 수 있다. 어디에 사용하든 상관없지만 보통은 멤버변수에 선언하여 사용한다. 

 

스프링 컨테이너는 멤버변수 위에 붙은 @Autowired를 확인하는 순간 해당 변수의 타입을 체크한다. 그리고 그 객체가 메모리에 존재하는지 확인한 다음 그 객체를 주입하게 된다.

 

 

1-1. 필드(멤버변수)주입 예제

package com.spring.example;

public interface PetService {
    
    public List<PetVO> getPetList() throws Exception;   
}
package com.spring.example;

@Service
public class PetServiceImpl implements PetService {
    
    @Autowired
    private PetDAO petDAO;
    
    @Override
    public List<PetVO> getPetList() throws Exception {
    
    /*  section of implementation of getting list of pets  */
    
    }
}

 

1-2 메서드 주입 예제

package com.spring.example;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class PetServiceImpl implements PetService {
    
    private PetVO petVO;
    
    @Autowired
    public void getPetList(PetVO petVO) {
        this.petVO = petVO;        
    }  
}

 

1-3 생성자 주입 예제

package com.spring.example;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class PetServiceImpl {
    
    private PetVO petVO;
    
    @Autowired
    public PetServiceImpl(PetVO petVO) {
        this.petVO = petVO;
    }
   
}

 

2. @Qualifier

스프링에서 Bean을 생성할때 <context:annotation-config/>을 설정해주면 굳이 <bean>태그 안에 <property>나 <construct-arg>를 추가하지 않아도 @Autowired가 적용된 필드,메서드,생성자에 대해서 자동으로 주입을 한다.

 

그런데 만약 동일한 타입을 가진 bean객체가 두 개 이상이라면 문제가 발생한다.

같은 타입의 두 bean객체가 모두 메모리에 생성되어 있는 상황이면 스프링컨테이너는 어떤 객체를 할당할지 판단할 수 없기 때문에 에러가 발생하게 된다.

 

[AppleSpeaker.class]

package com.spring.example;

import org.springframework.stereotype.Component;

@Component("apple")
public class AppleSpeaker implements Speaker {
    
    public AppleSpeaker() {
        System.out.println("AppleSpeaker객체 생성됨");
    }
}

[SonySpeaker.class]

package com.spring.example;

import org.springframework.stereotype.Component;

@Component("sony")
public class SonySpeaker implements Speaker {
    
    public SonySpeaker() {
        System.out.println("SonySpeaker객체 생성됨");
    }
}

[SamsungTV.class]

package com.spring.example;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

@Component("tv")
public class SamsungTV {
    
    @Autowired
    private Speaker speaker;

    public SamsungTV() {
        System.out.println("SamsungTV 객체 생성됨");
    }
}

 

[에러발생]

 

SamsungTV클래스에서 Speaker 타입의 변수를 @Autowired설정했는데 SonySpeaker와 AppleSpeaker객체 모두 Speake타입이기 때문에 스프링컨테이너가 어떤 객체를 주입해주어야 할지 모르기 때문에 에러가 나는 것이다.

 

[ @Qualifier("apple") 추가 ]

package com.spring.example;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

@Component("tv")
public class SamsungTV {
    
    @Autowired
    @Qualifier("apple")
    private Speaker speaker;

    public SamsungTV() {
        System.out.println("SamsungTV 객체 생성됨");
    }
}

 

해결방법 : @Autowired설정한 멤버변수에 @Qualifier("식별자")를 설정해주면 해당 식별자를 가지고 있는 객체를 주입해주게 된다.

 

 

3. @Resource 

@Autowired는 변수의 타입을 기준으로 객체를 검색하여 의존성 주입을 처리하지만 @Resource는 객체의 이름을 이용하여 의존성 주입을 처리한다.

 

@Resource(name=" ... ")

name 속성을 이용하여 스프링 컨테이너가 해당 이름으로 생성된 객체를 검색하여 의존성 주입을 처리한다.

 

package com.spring.example;

import javax.annotation.Resource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component("tv")
public class SamsungTV {
    
    @Autowired
    @Resource(name="apple")
    private Speaker speaker;

    public SamsungTV() {
        System.out.println("SamsungTV 객체 생성됨");
    }
}

 

728x90
반응형

'개발하자 > Spring' 카테고리의 다른 글

DI(Dependency Injection)이란?  (0) 2020.03.26
SPRING bean life cycle  (0) 2020.03.25
Controller에서 json으로 객체 변환 불가 에러.  (0) 2020.03.19

댓글


`