본문 바로가기

FrameWork/Spring 3.0

[Spring 3.0] CH04-1. 어노테이션 기반 설정


전자정부 프레임웍을 사용하기에 앞서 전자정부 프레임웍이 Spring 기반으로 되어 있으나, 어노테이션을 많이 사용한다고 하여 어노테이션에 대해 간략하게 정리를 해보려고 한다.
어노테이션은 Spring 2.0 & java 5 부터 제공하고 있다.



1. @Required 어노테이션을 이용한 필수 프로퍼티 검사

# Camera.java 파일
import org.springframework.beans.factory.annotation.Required;

public class Camera {

private int number;

public Camera() {
 }

 
 @Required
 public void setNumber(int number) {
  this.number = number;
 }

}
<< 스샷 >>


#방식1 ApplicationContext.xml
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://www.springframework.org/schema/beans  
       http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-3.0.xsd">

<bean
  class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor" />
 <bean id="camera1" class="madvirus.spring.chap04.homecontrol.Camera">
 
 <property name="number" value="1" />
 </bean>
</beans>

@Required 어노테이션을 추가했다고 해서 해당프로퍼티를 인식하지 않기 때문에 ApplicationContext.xml 파일에 프로퍼티를 위와 같이 명시해 주어야 한다.

applicationContext.xml 에는 2가지 방식이 있는데 하나느 위 처럼
1. CommonAnnotationBeanPostProccessor 클래스를 bean 으로 명시해 주는 방식이 있고 또 하나는
2. <context:annotation-config> 태그를 사용하는 방식이 있다.

#방식 2 ApplicationContext.xml

<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://www.springframework.org/schema/beans  
       http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-3.0.xsd
">

<context:annotation-config />

<beans>



<context:annotation-config /> 태그는 BeanPostProcessor 를 함께 등록해 주므로

- RequiredAnnotationBeanPostProcessor : @Required 어노테이션 처리
- AytowiredAnnotationBeanPostProcessor : @Autowired 어노테이션 처리
- CommonAnnotationBeanPostProcessor : @Resource, @PostConstruct, @PreDestroy 어노테이션 처리
- ConfigurationClassPostProcessor : @Configuration 어노테이션 처리

특별히 특정 기능을 사용하지 않아야 하는 경우가 아니라면
<context:annotation-config> 태그를 사용하는 것이 설정파일을 단순하게 만들어 준다.




2. @Autowired 어노테이션을 이용한 자동설정

: @Autowired 어노테이션은 의존관계를 자동으로 설정할 때 사용 한다.
(Spring2.5 추가된 기능으로 의존하는 객체를 삽입)
생성자, 필드, 메서드 세곳에 적용이 가능하다.

#Viewer.java (Interface 생성)

public interface Viewer {
void add(Camera camera1);
 void draw();
}


#MonitorViewer.java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component("viewer")
public class MonitorViewer implements Viewer {

 @Autowired
 private DisplayStrategy displayStrategy;

 public void setDisplayStrategy(DisplayStrategy displayStrategy) {
  this.displayStrategy = displayStrategy;
 }

 @Override
 public void draw() {
  System.out.println("DisplayType "
    + displayStrategy.getDisplayType().name() + "로 카메라 이미지 출력");
 }

 @Override
 public void add(Camera camera) {
  System.out.println("MonitorViewer에 " + camera + " 영상 추가");
 }

}


<<스샷 Viewer.java 와 MonitorViwer.java>>

Viewer.java


MonitorViewer.java


# 방식1 ApplicationContext.xml

<bean
  class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor" />

# 방식2 ApplicationContext.xml

<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://www.springframework.org/schema/beans  
       http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-3.0.xsd
">

<context:annotation-config />

<beans>

위와 마찬가지로 2가지 방식이 사용되나 <context:annotation-config/> 를 쓰는것이 간편하다.

제네릭이 적용된 컬렉션 타입을 사용하는 경우 java.util.List 타입이나 java.util.Set  타입을 이용해서 특정 타입의 빈 객체 목록을 전달 받을 수 있다.



2-1. @Autowired 어노테이션 적용 프로퍼티의 필수 여부 지정

: 타입을 이용해서 자동적으로 프로퍼티 값을 설정하기 때문에, 해당 타입의 빈 객체가 존재하지 않거나 또는 빈 객체가 두 개 이상 존재할 경우 스프링은 @Autowired 어노테이션이 적용된 빈 객체를 생성할 때 예외를 발생 시킨다.

프로퍼티를 반드시 설정할 필요가 없는경우도 있는데 이럴땐 @Autowired 어노테이션에 required  속성의 값을 false 로 지정해 주면 된다.

#HomeController.java
import java.util.List;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;

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

@Component("homeController")
public class HomeController {

 private AlarmDevice alarmDevice;
 private Viewer viewer;

 @Resource(name = "camera1")
 private Camera camera1;

 @Resource(name = "camera2")
 private Camera camera2;

 @Resource(name = "camera3")
 private Camera camera3;

 private Camera camera4;

 private List<InfraredRaySensor> sensors;

 @Autowired
 @Qualifier("main") //@Qualifier 어노테이션을 사용 - bean 객체의 수식어를 값으로 가짐
 private Recorder recorder;

 private DisplayStrategy displayStrategy;

 @Autowired //메서드에 Autowired 적용
 
public void prepare(AlarmDevice alarmDevice, Viewer viewer) {
  this.alarmDevice = alarmDevice;
  this.viewer = viewer;
 }

 @Autowired(required = false) //required속성값을 false
 @Qualifier("intrusionDetection")
 public void setSensors(List<InfraredRaySensor> sensors) {
  this.sensors = sensors;
  for (InfraredRaySensor sensor : sensors) {
   System.out.println("센서 등록: " + sensor);
  }
 }

 public void setCamera1(Camera camera1) {
  this.camera1 = camera1;
 }

 public void setCamera2(Camera camera2) {
  this.camera2 = camera2;
 }

 public void setCamera3(Camera camera3) {
  this.camera3 = camera3;
 }

 @Resource(name = "camera4")
 public void setCamera4(Camera camera4) {
  this.camera4 = camera4;
 }

 public void setRecorder(Recorder recorder) {
  this.recorder = recorder;
 }

 @Resource(name = "displayStrategy")
 public void setDisplayStrategy(DisplayStrategy displayStrategy) {
  this.displayStrategy = displayStrategy;
 }

 @PostConstruct
 public void init() {
  viewer.add(camera1);
  viewer.add(camera2);
  viewer.add(camera3);
  viewer.add(camera4);
 }

 @PreDestroy
 public void close() {
}

 public void checkSensorAndAlarm() {
  for (InfraredRaySensor sensor : sensors) {
   if (sensor.isObjectFounded()) {
    alarmDevice.alarm(sensor.getName());
   }
  }
 }

 public void showCameraImage() {
  viewer.draw();
 }
}

required 속성값을 flase 로 지정할 경우 해당 타입의 빈 객체가 존재하지 않더라도 스프링은 예외를 발생하지 않는다.
@Autowired 어노테이션의 required 속성의 기본값은 true 이다.


여러개의 생성자에 @Autowired 어노테이션을 적용할 때에는
한 개의 생성자
에 적용된 @Autowired 어노테이션만 required 속성값이 true  여야 하며
나머지 생성자에 적용되는 @Autowired 어노테이션의 required 속성값은 false 여야 한다.



2-2. @Qualifier 어노테이션을 이용한 자동설정 제한

@Autowired 어노테이션과 함께 사용되며 @Qualifier 어노테이션은 자동 연결될 빈 객체의 수식어를 값으로 갖는다.

위의 HomeController 소스를 보면 주석을 달아 놓았다

#HomeController.java
public class HomeController {

.... 
 @Autowired
 @Qualifier("main")
 private Recorder recorder;
....
}


=> recorder 멤버필드에 Recorder 타입의 빈 객체를 자동 연결하는데, 수식어가 "main" 인 빈 객체를 연결한다는 것을 의미한다.

빈 객체의 수식어는 설정파일에서 <qualifier> 태그를 이용하여 설정 할 수 있다.

#ApplicationContext.xml
<bean id="recorder" class="madvirus.spring.chap04.homecontrol.Recorder">
  <qualifier value="main" />
 </bean>

<qualifier> 태그의 value 속성의 값을 @Qualifier 어노테이션의 값으로 사용 하게 된다.



3. @Resource 어노테이션을 사용한 프로퍼티 설정

: @ Resource 어노테이션은 자바 6 버전 및 JEE 5 버전에 추가된 어노테이션으로서
어플리케이션에서 필요로 하는 자원을 자동 연결할 때 사용 한다.

마찬가지로 HomeController.java 파일의 한 부분을 보면

#HomeController.java
public class HomeController {
.......
@Resource(name = "camera2")
 private Camera camera2;

 @Resource(name = "camera3")
 private Camera camera3;
......
}

name 속성에 자동으로 연결할 빈 객체의 이름을 입력하면 된다.

#방식 1 ApplicationContext.xml

 <bean
  class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor" />


#방식 2 ApplciationContext.xml

<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://www.springframework.org/schema/beans  
       http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-3.0.xsd
">
 
<context:annotation-config />

<beans>


만약 @Resource 어노테이션에서 지정한 빈 객체가 존재하지 않으면,
스프링은 컨테이너를 초기화 하는 과정에서 NoSuchBeanDefinitionException 예외를 발생 시킨다.



4. @PostConstruct 어노테이션 & @PreDestory 어노테이션과 라이프 사이클

: 라이프 사이클의 초기화 및 제거 과정을 제공한다.

@PostConstruct : 의존하는 객체를 설정한 이후에 초기화 작업을 수행할 메서드에 적용
@PreDestory : 컨테이너에서 객체를 제거하기 전에 호출 될 메서드에 적용


스프링 설정 파일에서 init-method 속성과 destroy-method 솔석을 이용하여 명시한 메서드와 동일한 시점에 실행된다.

위와 마찬가지로 HomeController.java 파일을 보면

#HomeController.java
public class HomeController {

 @PostConstruct
 public void init() {
  viewer.add(camera1);
  viewer.add(camera2);
  viewer.add(camera3);
  viewer.add(camera4);
 }

 @PreDestroy
 public void close() {
 }

}

#방식1 ApplicationContext.xml
 <bean
  class="org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor" />


#방식2 ApplicationContext.xml
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://www.springframework.org/schema/beans  
       http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-3.0.xsd
">


<context:annotation-config />

<beans>



다시한번 요약 하자면,

<context:annotation-config /> 태그는 BeanPostProcessor 를 함께 등록해 주므로

- RequiredAnnotationBeanPostProcessor : @Required 어노테이션 처리
- AytowiredAnnotationBeanPostProcessor : @Autowired 어노테이션 처리
- CommonAnnotationBeanPostProcessor : @Resource, @PostConstruct, @PreDestroy 어노테이션 처리
- ConfigurationClassPostProcessor : @Configuration 어노테이션 처리

특별히 특정 기능을 사용하지 않아야 하는 경우가 아니라면
<context:annotation-config> 태그를 사용하는 것이 설정파일을 단순하게 만들어 준다.