
이 포스트는 백기선님의 예제로 배우는 스프링 입문을 듣고 정리한 내용입니다.
Bean을 등록하는 방법에는 크게 두가지가 있다.
스프링은 특정 범위 전체를 컴포넌트 스캔하여 @Component 어노테이션이 붙은 클래스의 인스턴스를 만들어 Bean으로 등록한다.
@Component말고도 컴포넌트 스캔의 대상이 되는 어노테이션은 여러개가 있는데 예를 들어 아래의 어노테이션들이 대표적이다.
@Repository@Service@Controller@Configuration등등..
이것들이 컴포넌트 스캔의 대상이 되는 이유는, 내부 구현을 보면 이 어노테이션들도 @Component이 붙어있기 때문이다.
Controller.class (@Controller 파일)
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package org.springframework.stereotype;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component // ⭐️여기에 Controller 어노테이션이 붙어있다!
public @interface Controller {
@AliasFor(
annotation = Component.class
)
String value() default "";
}
그러면 컴포넌트 스캔을 하는 범위는 어떻게 되는 것일까?
바로 @ComponentScan 어노테이션이 붙은 class 파일부터 하위 패키지를 모두 스캔한다.
스프링 부트 프로젝트를 만들면 ~~Application.java라는 파일이 생기고 해당 파일의 클래스 위에는 @SpringBootApplication라는 어노테이션이 붙어 있다.
이 어노테이션 파일을 열어보면
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
) // ⭐️ @ComponentScan 어노테이션
public @interface SpringBootApplication {
@AliasFor(
annotation = EnableAutoConfiguration.class
)
Class<?>[] exclude() default {};
// .. 생략
}
@SpringBootApplicatin 어노테이션에도 @ComponentScan 어노테이션이 붙어있는 것을 확인할 수 있다.
따라서 스프링부트 프로젝트를 만들면 컴포넌트 스캔의 범위는~~Application.java의 하위 패키지 전체가 된다.
또, @Component 어노테이션이 붙지 않아도, Repository와 같은 특정한 인터페이스를 구현하는 클래스인 경우 빈으로 등록된다.
이는 xml 설정 파일 또는 자바 설정 파일을 수정하여 등록하는 방법인데, 요즘은 자바 설정 파일을 더 많이 쓴다.
이 글에서는 자바 설정 파일로 등록하는 방법에 대해서만 알아보려고 한다.
우선 Bean으로 등록해줄 샘플 Controller Class를 하나 만들었다.
SampleController.java
package org.springframework.samples.petclinic.sample;
import org.springframework.stereotype.Controller;
@Controller
public class SampleController {
}
그리고 자바 설정 파일을 만들어줬다.
SampleConfig.java
package org.springframework.samples.petclinic.sample;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SampleConfig {
@Bean
public SampleController sampleController() {
return new SampleController();
}
}
이렇게 @Bean 어노테이션을 달아주고, 해당 클래스의 인스턴스를 리턴하는 메소드를 만들어주면 빈으로 등록된다. 따라서 기존 SampleController 위에 있는 @Controller 어노테이션을 떼더라도 빈으로 잘 등록이 된다.
package org.springframework.samples.petclinic.sample;
import org.springframework.stereotype.Controller;
public class SampleController { // @Controller 지움
}
앞선 포스트에서 봤던 applicationContext.getBean()을 통해 직접 꺼내쓰는 방법 말고도, @Autowired 와 같은 어노테이션을 통해 의존성을 주입하여 빈을 사용하는 방법도 존재한다.
아래에서는 @Autowired를 통한 의존성 주입 방법을 알아보고자 한다.
@Autowired로 의존성을 주입하는 방법에는 3가지가 있다.
생성자 주입은 생성자 메소드 위에 @Autowired 어노테이션을 달아서 의존성 주입을 하는 방식이다.
private final OwnerRepository owners;
@Autowired
public OwnerController(OwnerRepository clinicService) {
this.owners = clinicService;
}
그런데 스프링 4.3부터 @Autowired를 붙이지 않고도 자동으로 생성자 주입을 할 수 있는 기능이 추가되었다.
아래 두 조건을 만족한다면 @Autowired 어노테이션 없이도 생성자 주입이 가능하다.
필드 주입은 생성자 주입을 할 인스턴스 변수 위에 @Autowired를 붙여 의존성 주입을 하는 방법이다.
private OwnerRepository owners;
@Autowired
private OwnerRepository owners;
세번째 방법은 setter 주입으로, setter 메소드 위에 @Autowired 어노테이션을 붙여서 구현할 수 있다.
private OwnerRepository owners;
@Autowired
public void setOwners(OwnerRepository owners) {
this.owners = owners;
}