[Spring] 어노테이션 (@, annotation)
Annotation이란 사전적 의미로 주석이라는 뜻
"xml은 설정이 과도하다."
하나의 클래스 내부에
여러개의 객체 멤버변수
+
bean(등록된 객체)의 수가 多
=> 개발자들은, JAVA 코드와 함께 의존관계를 파악하기를 선호
그냥 "JAVA 코드에 xml 설정을 추가"해보자!
그래서 탄생한게 어노테이션 (@)
어노테이션 사용법
1. 초기설정을 위해서 Naemspaces에서 context 추가
Source로 돌아오면 하기 코드가 추가됨
xmlns:context="http://www.springframework.org/schema/context"
2. 컴포넌트 스캔 설정
어노테이션을 컨테이너가 사전에 스캔할 예정
그러므로 스캔 범위를 지정해줘야한다!
<context:component-scan> 태그를 사용하고
base-package는 필수속성으로 참조해야할 패키지명을 기입
<?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.2.xsd">
<context:component-scan base-package="test"/>
</beans>
3. @Component 사용
@Component
- 개발자가 직접 작성한 Class를 Bean으로 등록하기 위한 어노테이션
- @Bean과 다르게 @Component는 name이 아닌 value를 이용해 Bean의 이름을 지정
IPhone에서 상단에 @Component를 입력하는데 <bean> 등록없이 자동으로 객체가 생성 됨
@Component("ip") == <bean id="ip" class="test.IPhone"> or new IPhone(); 과 동일
@Component("ip")로 Lookup을 해줌
package test;
import org.springframework.stereotype.Component;
@Component("ip") // <bean id="ip" class="test.IPhone"> or new IPhone(); 과 동일
public class IPhone implements Phone{
private Watch watch; //객체 맴버변수 이기 때문에 의존관계 -< 의존성 주입(DI)이 필요한 상태
private int number;
//생성자
public IPhone() {
System.out.println("아이폰 객체 생성완료");
}
// 생성자 인젝션
public IPhone(Watch watch) { //외부에서 받아온걸로 생성자 new
System.out.println("아이폰 객체 생성 완료22");
this.watch=watch;
}
public IPhone(Watch watch, int number) { //외부에서 받아온걸로 생성자 new
System.out.println("아이폰 객체 생성 완료33");
this.watch=watch;
this.number=number;
}
@Override
public void powerOn() {
System.out.println("아이폰 전원 ON : " + this.number);
}
@Override
public void powerOff() {
System.out.println("아이폰 전원 OFF");
}
@Override
public void volumeUp() {
//System.out.println("아이폰 소리 ++");
watch.volumeUp();
}
@Override
public void volumeDown() {
//System.out.println("아이폰 소리 --");
watch.volumeDown();
}
}
클라이언트에서 IPhone poweron, volumeup 실행
package test;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
public class Client {
public static void main(String[] args) {
//Spring 컨테이너를 동작시킬 수 있도록 코드작성
AbstractApplicationContext factory=new GenericXmlApplicationContext("applicationContext.xml");
Phone phone=(Phone)factory.getBean("ip");
phone.powerOn();
phone.volumeUp();
factory.close();
}
}
volumeup의 메서드를 수행하기 위해서는 watch 객체가 초기화(new)를 해주어야하는데
그렇지않아서 오류 발생 이를 해결하기 위해서는 의존성 주입이 필요! (@Autowired)
3. 의존성 주입 (DI)
3-1) @Autowired
- 멤버변수 뿐아니라 메서드, 생성자에서도 사용가능
- 해당 멤버변수의 "타입"을 체크
- 메모리를 확인해서 같은 타입을 주입함
package test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component("ip") // <bean id="ip" class="test.IPhone"> or new IPhone(); 과 동일
public class IPhone implements Phone{
@Autowired // 의존성 주입
private Watch watch; //객체 맴버변수 이기 때문에 의존관계 -< 의존성 주입(DI)이 필요한 상태
private int number;
//생성자
public IPhone() {
System.out.println("아이폰 객체 생성완료");
}
// 생성자 인젝션
public IPhone(Watch watch) { //외부에서 받아온걸로 생성자 new
System.out.println("아이폰 객체 생성 완료22");
this.watch=watch;
}
public IPhone(Watch watch, int number) { //외부에서 받아온걸로 생성자 new
System.out.println("아이폰 객체 생성 완료33");
this.watch=watch;
this.number=number;
}
@Override
public void powerOn() {
System.out.println("아이폰 전원 ON : " + this.number);
}
@Override
public void powerOff() {
System.out.println("아이폰 전원 OFF");
}
@Override
public void volumeUp() {
//System.out.println("아이폰 소리 ++");
watch.volumeUp();
}
@Override
public void volumeDown() {
//System.out.println("아이폰 소리 --");
watch.volumeDown();
}
}
@Autowired 만 추가하고 다시 실행해보면 하기와 같이 오류가 또 발생
이를 해결하기 위해서는 객체화를 해주면 되는데
첫번째 방법 : 설정 파일에서 사용하려는 watch를 <bean> 해줌
<context:component-scan base-package="test"/>
<!-- 필수 속성으로 base-package에서 어딜 스캔할지 결정 -->
<bean id="aw" class="test.AppleWatch"></bean>
</beans>
두번째 방법 : 사용하고자 하는 watch의 클래스 상단에 @Component를 기입
package test;
import org.springframework.stereotype.Component;
@Component
public class AppleWatch implements Watch{
public AppleWatch() {
System.out.println("애플워치 객체생성완료");
}
public void volumeUp() {
System.out.println("애플워치 ++");
}
public void volumeDown() {
System.out.println("애플워치 --");
}
}
3-2) @Qualifier
만약 여러개의 watch의 객체를 초기화 하려고 한다면 오류가 발생되는데
Autowired 모호성의 문제 때문!
이를 해결하기 위해서 이름으로 지정하는 @Qualifier 사용
GalaxyWatch, AppleWatch에 @Component 추가한 후
package test;
import org.springframework.stereotype.Component;
@Component("aw")
public class AppleWatch implements Watch{
public AppleWatch() {
System.out.println("애플워치 객체생성완료");
}
public void volumeUp() {
System.out.println("애플워치 ++");
}
public void volumeDown() {
System.out.println("애플워치 --");
}
}
package test;
import org.springframework.stereotype.Component;
@Component("gw")
public class GalaxyWatch implements Watch{
public GalaxyWatch(){
System.out.println("갤럭시워치 객체생성완료");
}
@Override
public void volumeUp() {
System.out.println("갤럭시워치 ++");
// TODO Auto-generated method stub
}
@Override
public void volumeDown() {
System.out.println("갤럭시워치 --");
// TODO Auto-generated method stub
}
}
IPhone 클래스에서 @Qualifier("참조할 객체명") 기입
저는 GalaxyWatch를 불러내기 위해서 "gw"
package test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
@Component("ip") // <bean id="ip" class="test.IPhone"> or new IPhone(); 과 동일
public class IPhone implements Phone{
@Autowired // 의존성 주입
@Qualifier("gw")
private Watch watch; //객체 맴버변수 이기 때문에 의존관계 -< 의존성 주입(DI)이 필요한 상태
private int number;
//생성자
public IPhone() {
System.out.println("아이폰 객체 생성완료");
}
// 생성자 인젝션
public IPhone(Watch watch) { //외부에서 받아온걸로 생성자 new
System.out.println("아이폰 객체 생성 완료22");
this.watch=watch;
}
public IPhone(Watch watch, int number) { //외부에서 받아온걸로 생성자 new
System.out.println("아이폰 객체 생성 완료33");
this.watch=watch;
this.number=number;
}
@Override
public void powerOn() {
System.out.println("아이폰 전원 ON : " + this.number);
}
@Override
public void powerOff() {
System.out.println("아이폰 전원 OFF");
}
@Override
public void volumeUp() {
//System.out.println("아이폰 소리 ++");
watch.volumeUp();
}
@Override
public void volumeDown() {
//System.out.println("아이폰 소리 --");
watch.volumeDown();
}
}
하기와 같이 오류없이 아이폰, 갤럭시워치가 정상 출력
@Autowired의 대상이 @Component로 되어있는것이
때로는 불리할때도 있기 때문에
@ 와 xml을 같이 사용!!
위에서 지정해 두었던 @Qualifier 삭제하고 xml 파일에서 의존성 주입 객체를 지정해주면 됨!!
@Autowired가 IPhone에 선언된 watch를 조회할텐데 <bean>으로 AppleWatch에 있는 객체가 초기화(new) 되기 때문에
AppleWatch, GalaxyWatch 클래스 객체에는 @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: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.2.xsd">
<context:component-scan base-package="test"/>
<bean class="test.AppleWatch"></bean>
</beans>