실습 코드 참조
https://github.com/JaeKyungHeo/SpringFramework
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 어노테이션을 이용해서 객체를 자동 생성
댓글