[Spring] 의존 객체 자동 주입_2

2023. 10. 18. 23:10학부 강의/웹프로그래밍 (Spring)


0. 출처

아직 배우고 있는 중이라 부정확한 정보가 포함되어 있을 수 있습니다!
주의하세요!

 

올인원 스프링 프레임워크 참고.

 

올인원 스프링 프레임워크 : 네이버 도서

네이버 도서 상세정보를 제공합니다.

search.shopping.naver.com

 


 

1. 문제 발생

 

 

[Spring] 의존 객체 자동 주입_1

0. 출처 아직 배우고 있는 중이라 부정확한 정보가 포함되어 있을 수 있습니다! 주의하세요! 올인원 스프링 프레임워크 참고. 올인원 스프링 프레임워크 : 네이버 도서 네이버 도서 상세정보를

ramen4598.tistory.com

 

앞서 의존 객체 자동 주입_1에서 의존 객체를 자동으로 주입해 보았다.

 

앞선 글에 사용한 예제는 아주 단순하기 때문에 아무런 문제 없이 의존 객체를 자동으로 주입할 수 있었다.

 

하지만 조금만 코드가 복잡해지면 의존 객체가 자동으로 주입되지 않을 수 있다.

 

예를 들어 만약 생성자가 여러 개 오버로딩되어 있다면 어떨까?

 


 

가. 수동 주입

    <bean id="registerService" class="ch05_pjt_01.contact.service.ContactRegisterService">
        <constructor-arg ref="contactDao"/>
    </bean>

 

의존 객체를 자동으로 주입하기 이전에는 <constructor-arg>를 가지고서 어떤 생성자를 사용할지 판단할 수 있었다.

 


 

나. 자동 주입

 

하지만 자동으로 주입하는 경우는 조금 다르다.

 

지난 시간에 사용한 예제를 보자.

package ch05_pjt_01.contact.service;

import ch05_pjt_01.contact.ContactSet;
import ch05_pjt_01.contact.dao.ContactDao;

public class ContactRegisterService {

    private ContactDao contactDao;

    public ContactRegisterService(ContactDao contactDao) {
        this.contactDao = contactDao;
    }

    public void register(ContactSet contactSet) {
        String name = contactSet.getName();
        if(verify(name)){
            contactDao.insert(contactSet);
        }else {
            System.out.println("The name has already regiseterd.");
        }
    }

    public boolean verify(String name){
        ContactSet contactSet = contactDao.select(name);
        return contactSet == null ? true : false;
    }

    public void SetWordDao(ContactDao contactDao) {
        this.contactDao = contactDao;
    }
}
<bean id="registerService" class="ch05_pjt_01.contact.service.ContactRegisterService"/>

 

registerService bean은 의존 객체를 자동으로 주입하게 되면서 <constructor-arg ref="contactDao"/>를 생략했다.

 

만약 이 상태에서 또 다른 생성자를 추가하면 어떻게 될까?

public ContactRegisterService(){}

 

이제 어떤 생성자를 사용해서 의존객체를 생성할지 판단할 수 없게 된다.

 

이런 문제를 해결하기 위해서 @Autowired, @Resource, @Inject를 사용한다.

 


 

2. @Autowired

 

가. 생성자에 연결

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

...

public ContactRegisterService(){}

@Autowired
public ContactRegisterService(ContactDao contactDao){
    this.contactDao = contactDao;
}

@Autowired는 실행에 필요한 객체를 데이터 타입을 바탕으로 스스로 추측하여 주입한다.

 

즉, ContactRegisterService 객체가 생성될 때 필요한 ContactDao 객체를 데이터 타입을 바탕으로 판단하여 주입한다.

 

@Autowired는 생성자 외에도 필드와 메서드에도 사용할 수 있다.

 


 

나. 필드에 연결

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

...

@Autowired
private ContactDao contactDao;

public ContactRegisterService(){}

//@Autowired
//public ContactRegisterService(ContactDao contactDao){
//    this.contactDao = contactDao;
//}

디폴트 생성자에 의해서 ContactRegisterService가 생성되고 @Autowired한 contactDao는 자동 주입된다.

 

직관적이기 때문에 가장 많이 사용된다.

 


 

다. 메서드, setter에 연결

 

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

...

//@Autowired
//private ContactDao contactDao;
//public ContactRegisterService(){}

//@Autowired
//public ContactRegisterService(ContactDao contactDao){
//    this.contactDao = contactDao;
//}

@Autowired
public void setContactDao(ContactDao contactDao){
    this.contactDao = contactDao;
}

 


 

라. 주의점

 

  • @Autowired 필드 또는 메서드에 적용할 때는 반드시 디폴트 생성자가 필요한다.
  • @Autowired는 객체 생성에 필요한 객체를 데이터 타입을 바탕으로 스스로 추측하여 주입한다. 이때 같은 데이터 타입을 가진 bean이 여러 개 있는 경우 문제가 된다. 즉, ContactRegisterService 객체가 생성될 때 필요한 ContactDao 객체를 데이터 타입을 바탕으로 판단하는데 ContactDao 타입을 사용하는 contactDao1, contactDao2가 있을 경우 둘 중 어느 것을 주입할 지 판단할 수 없다. (해결법은 아래에)

 


 

3. @Resource

 

@Resource@Autowired와 비슷하다.

 

@Resource를 사용해서 의존 객체를 자동으로 주입받을 수 있다.

 

이때 다른 점은 다음과 같다.

 

  @Autowired @Resource
의존 객체 탐색 방법 데이터 타입 바탕 빈 객체의 이름
적용 대상 생성자, 필드, 메서드 필드, 메서드
annotation 제공 spring java

 

@Autowired와 다르게 빈 객체의 이름을 바탕으로 객체를 탐색해서 주입한다.

 

또한 생성자에 연결할 수 없고 오직 필드와 메서드에만 사용할 수 있다.

 

spring이 아닌 java에서 제공하는 annotation임으로 spring을 사용하지 않는 java 프로젝트에서도 사용할 수 있다.

 

단, spring에서 @Resouce를 사용하려면 pom.xml에 빌드 설정을 추가해야 한다.

 

<!-- pom.xml에 추가-->
<dependency>
    <groupId>javax.annotation</groupId>
    <artifactId>javax.annotaion-api</artifactId>
    <version>1.3.2</version>
</dependency>

 


 

가. 필드에 연결

import javax.annotation.Resource;

@Resouce
private ContactDao contactDao;

appCtx.xml에서 contactDao라는 이름을 가진 bean을 주입한다.

 


 

나. 메서드에 연결

import javax.annotation.Resource;

private ContactDao contactDao;

@Resource
public void setContactDao(ContactDao contactDao){
    this.contactDao = contactDao;
}

 


 

다. 주의점

  • 생성자에 연결할 순 없다.
  • @Resource는 스프링이 아닌 자바 자체의 annotation이다. → Maven 설정 파일 pom.xml에 설정을 추가해야 함.

 


 

4. @Qualifier

 

ContactDao 타입을 사용하는 bean이 여러 개 있다고 가정해 봅시다.

<bean id="contactDao1" class="ch05_pjt_01.contact.dao.ContactDao" />
<bean id="contactDao2" class="ch05_pjt_01.contact.dao.ContactDao" />
<bean id="contactDao3" class="ch05_pjt_01.contact.dao.ContactDao" />

<bean id="registerService" class="ch05_pjt_01.contact.service.ContactRegisterService" />

@Autowired@Resource 모두 @Qualifier를 사용해서 주입할 bean을 명시적으로 지정할 수 있다.

 

이를 통해서 @Autowired@Resource가 가진 문제를 해결할 수 있다.

 


 

가. @Autowired의 경우

 

@Autowired는 객체 생성에 필요한 객체를 데이터 타입을 바탕으로 스스로 추측하여 주입한다.

 

이때 같은 데이터 타입을 가진 bean이 여러 개 있는 경우 문제가 된다.

 

즉, ContactRegisterService 객체가 생성될 때 필요한 ContactDao 객체를 데이터 타입을 바탕으로 판단하는데 ContactDao 타입을 사용하는 contactDao1, contactDao2, contactDao3가 있을 경우 어느 것을 주입할지 판단할 수 없다.

 

이때 @Qualifier를 사용해서 주입할 bean을 명시적으로 지정할 수 있다.

 

이를 통해서 @Autowired가 가진 문제를 해결할 수 있다.

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

@Autowired
@Qualifier("usedDao")
private ContactDao contactDao;

ContactDao 데이터 타입을 사용하면서 Qualifier가 usedDao인 것을 골라서 주입한다.

 

<bean id="contactDao1" class="ch05_pjt_01.contact.dao.ContactDao" >
    <qualifier value="usedDao"/>
<bean/>
<bean id="contactDao2" class="ch05_pjt_01.contact.dao.ContactDao" />
<bean id="contactDao3" class="ch05_pjt_01.contact.dao.ContactDao" />

<bean id="registerService" class="ch05_pjt_01.contact.service.ContactRegisterService" />

contactDao1가 qualifier가 usedDao기 때문에 주입된다.

 


 

나. @Resource의 경우

 

@Resource는 bean의 이름을 바탕으로 객체를 탐색해서 주입한다.

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

@Resource
private ContactDao contactDao;

contactDao1, contactDao2, contactDao3은 있지만 contactDao라는 이름의 bean이 없다.

 

그래서 주입할 객체를 찾을 수 없는 문제가 있다.

 

이때 @Resource@Qualifier를 사용해서 주입할 bean을 명시적으로 지정할 수 있다.

 

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

@Resource
@Qualifier("usedDao")
private ContactDao contactDao;
<bean id="contactDao1" class="ch05_pjt_01.contact.dao.ContactDao" >
    <qualifier value="usedDao"/>
<bean/>
<bean id="contactDao2" class="ch05_pjt_01.contact.dao.ContactDao" />
<bean id="contactDao3" class="ch05_pjt_01.contact.dao.ContactDao" />

<bean id="registerService" class="ch05_pjt_01.contact.service.ContactRegisterService" />

이를 통해서 @Autowired@Resource가 가진 문제를 해결할 수 있다.

 


 

다. @Qualifier를 생략 가능한 경우

 

bean id와 같은 이름을 사용하는 경우 @Qualifier를 생략할 수 있다.

<bean id="contactDao" class="ch05_pjt_01.contact.dao.ContactDao" /> // 수정
<bean id="contactDao2" class="ch05_pjt_01.contact.dao.ContactDao" />
<bean id="contactDao3" class="ch05_pjt_01.contact.dao.ContactDao" />
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;

@Resource 또는 @Autowired
//@Qualifier("usedDao") 생략 가능
private ContactDao contactDao;

 


 

라. 매개변수 개수가 2개 이상인 경우

public class Example{

    @Autowired
    public Example(@Qualifier("hello") FirstBean fBean, @Qualifier("world") SecondBean sBean) {    ... }

 


 

5. @Inject

 

@Autowired와 기능적으로 유사하지만, @Inject@Resource처럼 Java의 javax.inject 패키지에서 제공하는 애노테이션이다.

 

기능적으로 유사하다는 것은 생성자, 필드, 메서드에 사용할 수 있고 데이터 타입을 바탕으로 주입할 객체를 탐색한다는 의미다.

 

주의점! @Inject@Autowired와 같이 필드 또는 메서드에 적용할 때는 반드시 디폴트 생성자가 필요하다.

 

단, @Inject@Autowired와 달리 required 속성을 지원하지 않는다.

 

@Autowired에는 의존 객체 주입 시 필수 여부를 지정할 수 있는 @Autowired(required = false)와 같은 속성이 있다.

 

@Resource처럼 Java의 javax.inject 패키지를 pom.xml에 추가해야 한다.

 

<!-- pom.xml에 추가-->
<dependency>
    <groupId>javax.inject</groupId>
    <artifactId>javax.inject</artifactId>
    <version>1</version>
</dependency>

 


 

가. @Named

 

@Autowired가 같은 데이터 타입을 가진 bean이 여러 개 있는 경우 발생하는 문제를 @Qualifier로 풀었다면 @Inject@Named로 풀 수 있다.

 

<bean id="contactDao1" class="ch05_pjt_01.contact.dao.ContactDao" />
<bean id="contactDao2" class="ch05_pjt_01.contact.dao.ContactDao" />
<bean id="contactDao3" class="ch05_pjt_01.contact.dao.ContactDao" />
@Inject
@Named("contactDao1")
private ContactDao contactDao;

 


6. 정리

 

 

  Autowired Inject Resource
탐색 방법 데이터 타입 데이터 타입 bean id, 이름
범위 생성자, 필드, 메서드 생성자, 필드, 메서드 필드, 메서드
spring? java? spring java java
주입할 bean을 지정하는 법 @Qualifier @Named @Qualifier
기타 required 속성 사용 가능 required 속성 사용 불가능