본문 바로가기
👨‍🏫Study/Spring Study

[Spring] 5장 어노테이션 기반 설정 - 어노테이션 설정(Component) 및 의존성 주입 설정(Autowired, Qualifier)

by 코푸는 개발자 2022. 4. 18.
728x90
실습 코드 참조

https://github.com/JaeKyungHeo/SpringFramework

 

GitHub - JaeKyungHeo/SpringFramework: Spring Framework 실습 코드 정리

Spring Framework 실습 코드 정리. Contribute to JaeKyungHeo/SpringFramework development by creating an account on GitHub.

github.com

 

5.1절 어노테이션 설정 기초

 

0. 어노테이션 개념

 

1) 어노테이션이란?

- @를 이용하여 주석이나 자바코드에 특별한 의미를 부여하는 기능

- 컴파일러가 특정 오류를 억제하도록 지시하는 것처럼 프로그램 코드의 일부가 아닌, 프로그램에 관한 데이터를 제공

- 코드에 대한 정보를 추가하는 방법   ex) @Component, @Resource, @Controller, @Autowired

 

2) 어노테이션 등장 배경

- 모든 객체를 설정파일에 등록하게 되면 설정파일이 복잡해짐

- 따라서 전체 시스템에 영향을 주는 객체들만 설정파일로 관리하고 나머지 객체들은 어노테이션으로 관리

 

1. Context 네임스페이스 추가

 

1) applicationContext.xml Namespaces 추가

- 어노테이션 설정을 추가하려면 스프링 설정 파일의 루트 엘리먼트인 <beans>에 Context 관련 네임스페이스를 등록

- 방법 : [Namespaces] 탭에 'context' 항목만 체크하면 끝

 

2) 컴포넌트 스캔(component-scan) 설정

- <bean>에 등록하지 않아도 자동으로 생성되서 좋음

- 다만, 각 클래스 선언부에 @Component는 적어주어야함

- base-pakage 속성에 polymorphism은 polymorphism 패키지 안에 있는 모든 클래스들이 스캔 대상이 되는 것을 의미

<?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:p="http://www.springframework.org/schema/p"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
	
	<!-- context:component-scan : base-package에 있는 클래스 중 @Component 어노테이션 설정되어있는 객체를 찾아서
					자동으로 객체를 생성 -->
	<context:component-scan base-package="polymorphism"></context:component-scan>
</beans>

 

3) @Component 설정

- 여기까지 하면 이제 스프링 설정 파일에 클래스들을 <bean>으로 일일이 설정해줄 필요가 없어짐

- 이제 객체 생성에는 문제가 없지만 클라이언트에서 요청할 수 있도록 해주는 설정이 필요함

- 클라이언트의 요청을 위해서는 id나 name 속성 설정이 필요함

- id나 name 속성을 지정하지 않았다면 컨테이너가 자동으로 이름을 설정해줌

- 규칙 : 클래스 이름의 첫 글자를 소문자로 변경하기 (카멜표기법)

package polymorphism;

import org.springframework.stereotype.Component;

//@Component
//<bean class="polymorphism.LgTV"></bean>
@Component("tv")
//<bean id="tv" class="polymorphism.LgTV"></bean>
public class LgTV implements TV {
	public void powerOn() {
		System.out.println("LgTV---전원 켠다.");
	}
	
	public void powerOff() {
		System.out.println("LgTV---전원 끈다.");
	}
	
	public void volumeUp() {
		System.out.println("LgTV---소리 올린다.");
	}
	
	public void volumeDown() {
		System.out.println("LgTV---소리 내린다.");
	}
}

- 클래스 선언 부분에 @Component를 설정해줌으로써 스프링 컨테이너는 해당 클래스를 bean으로 생성하고 관리함

 

4) TVUser 실행 결과

 

 

5.2절 의존성 주입 설정

 

1. 의존성 주입 어노테이션 종류

 

- @Autowired와 @Qualifier만 스프링에서 제공함

 

2. @Autowired ( _018_BoardWeb_Annotation_Autowired )

 

1) @Autowired 특징

- 생성자나 메소드, 멤버변수 위에 모두 사용 가능 (대부분은 멤버변수 위에 선언)

- 스프링컨테이너는 멤버변수 위에 붙은 @Autowired를 확인하는 순간 해당 변수의 타입을 체크함

- 그 후 그 타입의 객체가 메모리에 존재하는지를 확인하고 있다면 그 객체를 변수에 주입함

- @Autowired가 붙은 객체가 메모리에 없다면 컨테이너는 NoSuchBeanDefinitionException을 발생시킴

 

1) @Autowired 설정

- LgTV 클래스에 Speaker 멤버변수 추가하고 @Autowired 설정

- volumeUp()과 volumeDown() 메소드 수정

- 이렇게 하면 생성자 인젝션이나 Setter 메소드는 필요없음

package polymorphism;

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

//@Component//==> id나 name 미지정 시 lgTV 자동으로 이름이 지정됨
//<bean class="polymorphism.LgTV></bean>
@Component("tv")
//<bean id="tv" class="polymorphism.LgTV></bean>
public class LgTV implements TV {
	//@Autowired 어노테이션 사용 시 생성자, setter 메소드를 이용한 의존성 주입이 이뤄지지 않음
	@Autowired
	private Speaker speaker;
	
	public void powerOn() {
		System.out.println("LgTV---전원 켠다.");
	}
	
	public void powerOff() {
		System.out.println("LgTV---전원 끈다.");
	}
	
	public void volumeUp() {
		speaker.volumeUp();
	}
	
	public void volumeDown() {
		speaker.volumeDown();
	}
}

 

2) SonySpeaker 객체 생성

- SonySpeaker 객체가 메모리에 없으면 에러가 발생함

- SonySpeaker  객체 생성하는 방법 (1. xml에 bean 추가, 2. @Component("sony")로 Annotaion 설정)

package polymorphism;

import org.springframework.stereotype.Component;

@Component("sony")
public class SonySpeaker implements Speaker {
	public SonySpeaker() {
		System.out.println("====> 소니 스피커 객체 생성");
	}
	public void volumeUp() {
		System.out.println("소니 스피커---소리 울린다.");
	}
	public void volumeDown() {
		System.out.println("소니 스피커---소리 내린다.");
	}
}

 

3) 실행 결과

- SonySpeaker 객체가 메모리에 생성만 되면 @Autowired에 의해 컨테이너가 SonySpeaker 객체를 speaker 변수에 자동으로 할당함

 

3. Qualifier ( _019_BoardWeb_Annotation_Qualifier )

 

1) 특징

- 의존성 주입 대상이 되는 Speaker 타입의 객체가 두 개 이상일 때 문제 발생

- SonySpeaker와 AppleSpeaker 객체가 모두 생성되어있다면 컨테이너는 어떤 객체를 할당할 지 스스로 판단할 수 없음 

- 이러한 상황에는 에러가 발생

 

2) Error 확인해보기

- 우선 AppleSpeaker 클래스에도 @Component 선언하기

package polymorphism;

import org.springframework.stereotype.Component;

@Component("apple")
public class AppleSpeaker implements Speaker {
	public AppleSpeaker() {
		System.out.println("====> 애플 스피커 객체 생성");
	}
	public void volumeUp() {
		System.out.println("애플 스피커---소리 울린다.");
	}
	public void volumeDown() {
		System.out.println("애플 스피커---소리 내린다.");
	}

}

 

- 에러 발생

 

- 다이어그램을 통해 @Autowired 대상이 되는 Speaker 타입의 객체가 두 개임을 확인

- 어떤 객체를 의존성 주입할지 모르기 때문에 발생한 예외임

 

3) 해결책 -> @Qualifier

- @Qualifier 어노테이션을 통해 의존성 주입될 객체의 아이디나 이름을 지정할 것

- apple이나 sony 중 하나로 지정하면 간단하게 처리됨 

package polymorphism;

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

//@Component//==> id나 name 미지정 시 lgTV 자동으로 이름이 지정됨
//<bean class="polymorphism.LgTV></bean>
@Component("tv")
//<bean id="tv" class="polymorphism.LgTV></bean>
public class LgTV implements TV {
	//@Autowired 어노테이션 사용 시 생성자, setter 메소드를 이용한 의존성 주입이 이뤄지지 않음
	//@Qualifier : 동일한 타입의 객체가 두 개 이상 생성되어 있을 때 명확하게 지정해서 의존서 주입할 때 사용
	@Autowired //같이 설정해주어야함 
	@Qualifier("apple")
	private Speaker speaker;
	
	public void powerOn() {
		System.out.println("LgTV---전원 켠다.");
	}
	
	public void powerOff() {
		System.out.println("LgTV---전원 끈다.");
	}
	
	public void volumeUp() {
		speaker.volumeUp();
	}
	
	public void volumeDown() {
		speaker.volumeDown();
	}
}

 

4) 실행 결과

 

4. @Resource ( _020_BoardWeb_Annotation_Resource )

 

1) 특징

- @Autowired는 변수의 '타입'을 기준으로 객체를 검색함

- @Resource는 객체의 '타입'과 '이름'을 한 번에 이용하여 의존성 주입을 처리함 (name 속성 사용)

- 스프링에서는 지원하지 않지만 javax.annotation.Resource를 import하면 사용이 가능함

 

2) LgTV 클래스에 @Resource 설정

- apple로 선언된 AppleSpeaker 객체가 LgTV에 있는 speaker 변수에 할당하는 설정

@Resource(name="apple")
private Speaker speaker;

 

3) 실행 결과

 

5. 어노테이션과 XML 설정 병행하여 사용하기

 

1) XML 방식과 어노테이션 방식 특징

- 이제까지 XML 설정 방식과 어노테이션 설정 방식에 대해 모두 배움

- XML 방식의 장점 : 코드를 수정하지 않고 XML 파일의 설정만 변경하면 되서 유지보수가 편함

- XML 방식의 단점 : bean 객체를 사용하게되면 설정 파일이 너무 길어지게 되서 부담스러움

 

- 어노테이션 방식의 장점 : XML 설정에 대한 부담이 없고 의존관계 정보가 자바소스에 들어있어서 사용이 편함

- 어노테이션 방식의 단점 : Speaker를 교체하기 위해서는 자바소스를 변경해야하는 번거로움

- 결론 :  XML 방식과 어노테이션 방식의 장점만 조합하여 사용 

 

2) 두 방식 조합해서 사용하기 실습 ( _021_BoardWeb_Annotation_Xml )

① 어노테이션 설정

- LgTV 클래스에 @Resource 어노테이션 지우고 @Autowired만 남기기

@Component("tv")
public class LgTV implements TV { ... }

 

- AppleSpeaker와 SonySpeaker 클래스의 @Component 설정 지우기 (객체가 자동 생성되는 것을 차단)

@Autowired
private Speaker speaker;

 

② XML 설정

- @component가 사라졌기 때문에 compnent-scan을 지나면서 객체가 자동으로 생성되지 않음

- applicationContext.xml 파일에 두 스피커 중 하나만 bean 등록

- 이후 speaker 객체 교체시 <bean> 객체의 class 속성만 AppleSpeaker에서 SonySpeaker로 변경해주면 됨

<context:component-scan base-package="polymorphism"></context:component-scan>
<bean class="polymorphism.AppleSpeaker"></bean>

 

3) 동작 원리

① LgTV 클래스에서 @Component를 이용하여 LgTV 클래스 객체 생성 - 어노테이션 사용

② @Autowired에 의해 bean객체로 생성된 AppleSpeaker 객체가 LgTV로 의존성 주입됨 - 어노테이션, XML 사용 

 

* 즉, 변경되지 않는 객체는 어노테이션으로, 변경될 가능성이 있는 객체는 XML 설정으로 사용한다!

 

4) 실행 결과

- 자바 소스를 수정하지 않아도 speaker 객체 교체가 가능함

 

 

5) 라이브러리 형태의 클래스

- 라이브러리로 제공되는 클래스는 반드시 XML 설정 방식만 사용이 가능함

- 예를 들어 commons-dbcp-1.4.jar 파일에 들어있는 BasicDataSource 클래스에는 어노테이션 추가할 수 없음

 

 

5.3절 추가 어노테이션

 

- 추가 어노테이션을 사용해야하는 이유 :  전부 다 @Component로 처리하면 어떤 클래스가 어떤 역할을 수행하는지 파악이 어렵기 때문에 

 

1. 프로젝트 구성과 역할

 

1) 시스템 구조

2) Presentation Layer와 Business Layer의 역할

① Presentation Layer : MVC모델에서 View와 Controller를 맡음

② Business Layer : MVC모델에서 Model을 처리함

 

2. 추가 어노테이션 종류

 

- 세 가지 어노테이션 모두 @Component 어노테이션을 상속받아 만들어짐 (Component-scan을 하면 찾기 가능)

- Business Layer에서 ServiceImpl과 DAO는 @Service 어노테이션과 @Repository 어노테이션을 사용함

- Presentation Layer에서 Controller는 @Controller 어노테이션을 이용해서 객체를 자동 생성

728x90

댓글