목차
어노테이션을 사용한 빈 설정 방법
스프링 프레임워크에서 빈을 설정하는 방법으로 XML 기반 설정, 자바 기반 설정 외에 자바 어노테이션(Annotaion, @) 기반 설정 방법이 있습니다. 자바에서 어노테이션은 코드 사이에 주석처럼 쓰이며 특별한 의미, 기능을 수행하도록 하는 기술입니다. 즉, 프로그램에 추가적인 정보를 제공해주는 메타 데이터라고 볼 수 있습니다. 어노테이션 기반 설정 방법은 @Component 같은 마커 어노테이션(Marker Annotation)이 부여된 클래스를 탐색해서(Component Scan) DI 컨테이너에 빈을 자동으로 등록하는 방법입니다.
Component Scan 설정
지정한 패키지의 하위 클래스를 탐색한 후 DI 컨테이너에 객체를 생성하여 등록합니다.
방식 | 예시 |
XML 방식 | <context:component-scan base-package="패키지명"></context:component-scan> |
Annotation 방식 | @ComponentScan(basePackages = {"패키지명"}) |
XML 방식을 사용하기 위해서는 XML 파일의 namespace context를 체크한 후, 다음의 코드를 추가해 줍니다.
<context:component-scan base-package="패키지명"></context:component-scan>
<context:component-scan> 태그는 지정된 패키지내 클래스를 검색하여 자동으로 빈을 등록합니다. 여기에는 <context:annotation-config> 태그의 기능이 포함되는데, 이는 XML 파일에 이미 등록되어 있는 빈을 어노테이션 기반으로 설정하기 위해 어노테이션을 스캔하고 활성화하는 기능을 수행합니다.
Component Scan 대상 어노테이션
지정된 패키지내 모든 클래스들 중 다음의 특정 어노테이션이 선언된 클래스의 빈을 자동으로 생성 및 등록하게 됩니다. @Service, @Repository, @Controller 어노테이션은 모두 @Component의 특수한 형태로서 보다 구체적인 용도를 위해 사용합니다. 이 때문에 @Component 어노테이션으로 모든 컴포넌트 클래스를 지정할 수 있지만, 용도에 맞게 각 역할을 명시적으로 구분하여 사용하는 것이 좋습니다.
어노테이션 | 설명 |
@Component | 스프링에서 관리되는 모든 컴포넌트에 대한 일반 스테레오 타입(generic stereotype)이다. |
@Service | 비즈니스 로직과 관련된 코드가 있는 Service 클래스에 사용한다. |
@Repository | DB에 접근하는 코드가 있는 DAO 또는 Repository 클래스에 사용한다. @Repository를 사용하면 persistence layer의 예외를 자동으로 스프링의 예외로 translate해 주는 장점이 있다. |
@Controller | Spring MVC 패턴에서 Controller 역할을 한다고 명시하기 위해 사용한다. |
@Configuration | 설정 정보를 작성하는 Configuration 클래스에 사용하며 @Bean 어노테이션을 사용하여 빈을 정의하는 메소드가 포함될 수 있다. |
@Component 사용 예시
applicationContext.xml
자동으로 스캔 대상이 되도록 할 패키지 경로를 지정해 줍니다. 해당 패키지 안에 있는 모든 클래스들 중 @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: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="com.dto"></context:component-scan>
</beans>
Product.java
package com.dto;
public interface Product {
}
Store.java
클래스 선언 부분에 @Component를 설정해줌으로써 스프링 컨테이너는 해당 클래스를 bean으로 생성하고 관리하게 됩니다. @Component(value="이름")으로 Bean의 이름을 지정할 수 있고, 지정하지 않으면 컨테이너가 자동으로 클래스 이름의 첫글자를 소문자로 변경하여 이름을 설정해 줍니다.
package com.dto;
import org.springframework.stereotype.Component;
@Component
public class Store {
private Product product;
//Getters and Setters
}
TestMain.java
public class TestMain {
public static void main(String[] args) {
ApplicationContext ctx = new GenericXmlApplicationContext("classpath:applicationContext.xml");
Store store = ctx.getBean("store", Store.class);
System.out.println(store);
//com.dto.Store@45b9a632
}
}
자동으로 생성 및 등록되어 있는 빈을 가져온 것을 확인할 수 있습니다.
어노테이션을 사용한 의존성 주입 방법
XML 설정 파일이 아닌 어노테이션을 이용해 의존성 주입을 설정할 수 있습니다.
@Required
- setter 메소드에 붙여 반드시 주입해야 하는 필수 프로퍼티로 설정합니다.
- Spring 5.1 버전 부터 Deprecated 되었기 때문에, 반드시 주입해야 할 프로퍼티는 생성자 주입을 이용합니다.
- 스프링 5.1이상을 사용하거나 자바 파일로 bean을 등록했을 경우 무시됩니다.
Store.java
@Component
public class Store {
private Product product;
@Required
public void setProduct(Product product) {
this.product = product;
}
}
setter 메소드에 @Required를 사용하였으나 필요한 프로퍼티를 주입하지 않은 경우, BeanInitializationException: Property 'product' is required for bean 'store' 라는 에러 메시지가 출력됩니다.
@Autowired
- 객체 타입을 통해 빈 객체를 자동으로 주입합니다.
- 필드, 생성자, setter 메소드에 붙일 수 있습니다.
- 필드, setter 메소드에 붙여서 사용할 경우 반드시 기본 생성자가 정의되어 있어야 합니다.
- 필드에 붙이면 setter 메소드를 통해 주입되며 setter 메소드가 없을 경우 컴파일 과정에서 자동으로 추가됩니다.
- 기본적으로 필수 프로퍼티로 설정되지만, @Autowired(required = false)로 필수 프로퍼티 해제가 가능합니다.
- 동일한 타입 빈이 여러 개인 경우 이름을 기준으로 빈을 주입합니다.
Pencil.java
@Component
public class Pencil implements Product{
}
Store.java
@Component
public class Store {
@Autowired
private Product product;
@Override
public String toString() {
return "Store [product=" + product + "]";
}
}
TestMain.java
public class TestMain {
public static void main(String[] args) {
ApplicationContext ctx = new GenericXmlApplicationContext("classpath:applicationContext.xml");
Store store = ctx.getBean("store", Store.class);
System.out.println(store);
//Store [product=com.dto.Pencil@309e345f]
}
}
위의 예제에서 Pencil.java 클래스를 추가하여 Store 클래스에 자동 주입되도록 설정했습니다. 다른 설정을 하지 않았는데도 Product 인터페이스를 구현하고 있는 Pencil 타입 빈이 Store 빈에 자동으로 주입된 것을 확인할 수 있습니다.
@Qualifier
- @Autowired와 함께 사용합니다.
- @Autowired를 통한 자동 주입 시 같은 타입의 빈이 여러 개 등록되어 있으면 예외가 발생합니다. 이때 @Qualifier를 사용하면 특정 빈을 주입하도록 설정할 수 있습니다.
Desk.java
@Component
public class Desk implements Product {
}
위의 예제에서 Product 인터페이스를 구현하는 Desk 클래스를 선언하고 다시 메인을 실행해 보면, NoUniqueBeanDefinitionException: No qualifying bean of type 'com.dto.Product' available: expected single matching bean but found 2: desk,pencil 라는 에러 메시지가 출력되는 것을 확인할 수 있습니다. 이는 @Autowired 어노테이션을 사용해 Product 타입으로 자동 주입이 되도록 설정하였는데, 동일한 Product 타입의 빈이 여러 개 있기 때문에 발생하는 문제입니다.
이때 다음과 같이 @Autowired와 함께 @Qualifier 어노테이션을 사용하여 정확히 어떤 bean을 사용할지 빈의 id를 지정하여 특정 의존 객체를 주입할 수 있도록 할 수 있습니다.
Store.java
@Component
public class Store {
@Autowired
@Qualifier(value = "desk")
private Product product;
@Override
public String toString() {
return "Store [product=" + product + "]";
}
}
TestMain.java
public class TestMain {
public static void main(String[] args) {
ApplicationContext ctx = new GenericXmlApplicationContext("classpath:applicationContext.xml");
Store store = ctx.getBean("store", Store.class);
System.out.println(store);
//Store [product=com.dto.Desk@25d250c6]
}
}
"desk"라는 id의 빈을 주입하도록 설정했기 때문에, Pencil 빈이 아닌 Desk 빈이 자동 주입된 것을 확인할 수 있습니다.
@Resource
- 필드명과 동일한 이름의 id를 가진 빈을 주입합니다.
- 필드명과 빈의 id가 다르면 @Resource(name="id")으로 주입할 빈을 지정할 수 있습니다.
- @Autowired + @Qualifier와 유사한 기능을 수행합니다.
- 이름으로 빈을 찾지 못한 경우 타입을 기준으로 빈을 주입합니다.
Store.java
@Component
public class Store {
//@Resource
@Resource(name = "desk")
private Product product;
@Override
public String toString() {
return "Store [product=" + product + "]";
}
}
@Resource 어노테이션을 사용하면 필드명과 동일한 id를 가진 빈을 주입합니다. 그런데 여기에서는 product라는 이름을 가진 빈이 등록되어 있지 않은데 Product 타입을 가진 빈도 하나가 아닌 여러 개이기 때문에, NoUniqueBeanDefinitionException: No qualifying bean of type 'com.dto.Product' available: expected single matching bean but found 2: desk,pencil 라는 에러가 발생합니다. 따라서 @Resource 어노테이션 사용시 name 속성을 이용해 빈의 id를 지정하여 해당 id를 가진 빈을 자동 주입하게 설정합니다.
TestMain.java
public class TestMain {
public static void main(String[] args) {
ApplicationContext ctx = new GenericXmlApplicationContext("classpath:applicationContext.xml");
Store store = ctx.getBean("store", Store.class);
System.out.println(store);
//Store [product=com.dto.Desk@1fe20588]
}
}
"desk"라는 id의 빈을 주입하도록 설정했기 때문에, desk 빈이 주입된 것을 확인할 수 있습니다.
@Value
- 특정 값을 주입할 때 사용합니다.
- 대표적인 용도는 자바코드 외부의 리소스나 환경정보 설정값을 사용하는 경우입니다.
Store.java
@Component
public class Store {
@Value(value = "Emart")
private String storeName;
@Override
public String toString() {
return "Store [storeName=" + storeName + "]";
}
}
TestMain.java
public class TestMain {
public static void main(String[] args) {
ApplicationContext ctx = new GenericXmlApplicationContext("classpath:applicationContext.xml");
Store store = ctx.getBean("store", Store.class);
System.out.println(store);
//Store [storeName=Emart]
}
}
참고
- https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-stereotype-annotations
- https://atoz-develop.tistory.com/entry/Spring-%EC%95%A0%EB%85%B8%ED%85%8C%EC%9D%B4%EC%85%98%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EB%B9%88-%EC%84%A4%EC%A0%95-%EB%B0%A9%EB%B2%95-%EC%A0%95%EB%A6%AC
- https://wpunch2000.tistory.com/18
- https://velog.io/@gillog/Spring-Annotation-%EC%A0%95%EB%A6%AC#qualifierid123
- https://velog.io/@_koiil/Spring-Component%EC%99%80-Repository-Service-Controller
'Spring' 카테고리의 다른 글
[Spring] DB 연동 - JDBC (0) | 2022.08.25 |
---|---|
[Spring] SpEL 사용법 (0) | 2022.08.22 |
[Spring] XML 기반 설정 의존 객체 자동 주입 (autowire) (0) | 2022.08.17 |
[Spring] 의존성 주입(Dependency Injection) - (1) XML 기반 설정 (0) | 2022.08.14 |
[Spring] 스프링 프레임워크 개요 (0) | 2022.08.13 |
댓글