[스프링 IoC 컨테이너와 빈]_02 ApplicationContext와 다양한 빈 설정 방법
ApplicationContext와 다양한 빈 설정 방법
스프링 IoC 컨테이너의 역할
● 빈 인스턴스 생성
● 의존 관계 설정
● 빈 제공
AppcliationContext
● ClassPathXmlApplicationContext (XML)
● AnnotationConfigApplicationContext (Java)
빈 설정
● 빈 명세서
● 빈에 대한 정의를 담고 있다.
○ 이름
○ 클래스
○ 스코프
○ 생성자 아규먼트 (constructor)
○ 프로퍼트 (setter)
○ ..
컴포넌트 스캔
● 설정 방법
○ XML 설정에서는 context:component-scan
○ 자바 설정에서 @ComponentScan
● 특정 패키지 이하의 모든 클래스 중에 @Component 애노테이션을 사용한 클래스를 빈으로 자동으로 등록 해 줌.
1. xml 파일의 bean 태그로 직접 빈 설정
고전적인 방식으로 현재 거의 쓰지 않는 방식이다.
-
프로젝트 생성
[Spring Initializr] > [Dependencies] :
web
선택Spring Boot 프로젝트로 생성해서
spring-boot-starter-web
의존성만 추가하면, 우리가 학습하는데 필요한 대부분의 의존성이 추가된다.이 3개가 우리가 주로 사용하는 모듈들이다.
Maven: org.springframework:spring-beans:5.2.7.RELEASE Maven: org.springframework:spring-context:5.2.7.RELEASE Maven: org.springframework:spring-core:5.2.7.RELEASE
-
BookService와 BookRepository 클래스 생성
-
Application Context
를 이용해서 BookService와 BookRepository를 빈으로 등록 -
main 클래스의
@SpringBootApplication
삭제, main 메서드 안의 run 도 삭제 -
고전적인 방식으로 스프링 빈 설정 파일을 만들어보자
스프링 IoC 컨테이너는 빈 설정파일이 있어야 한다.
[resources] > New : [XML Configuration File] > [Spring Config] >
application.xml
파일 생성 -
BookService와 BookRepository를 빈으로 직접 등록
<bean id="bookService" class="dev.solar.springapplicationcontext.BookService"/> <bean id="bookRepository" class="dev.solar.springapplicationcontext.BookRepository"/>
<bean>
태그로 빈을 생성id 네이밍 컨벤션 : 소문자로 시작하는 카멜케이스
class : bean의 타입을 지정
scope : 스코프를 지정
* prototype : 매번 객체를 생성 * request : request 당 객체를 생성 * session : session 당 객체를 생성 * singleton : 하나의 객체만 생성 (default)
autowire : 모드를 지정
* default : 기본값 (뒤에서 자세히 설명) * byType * byName * constructor * no
지금은 빈을 등록만한 상태이기 때문에 BookService에서 BookRepository를 주입받지 못한다.
따라서 빈설정 파일에서 주입시켜줘야 한다.
-
BookService에 BookRepository를 주입
<property>
태그 사용name : BookRepository의 setter에서 이름을 가져옴
ref : 다른 빈을 참조하도록
해당 빈의 id
를 값으로 설정<bean id="bookService" class="dev.solar.springapplicationcontext.BookService"/> <property name="bookRepository" ref="bookRepository" /> </bean>
-
앞서 만든 빈을 사용하는 ApplicationContext를 만들어서 사용
ClassPathXmlApplicationContext
로 ApplicationContext를 생성public class SpringapplicationcontextApplication { private static final Logger log = LoggerFactory.getLogger(SpringapplicationcontextApplication.class); public static void main(String[] args) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application.xml"); String[] beanDefinitionNames = context.getBeanDefinitionNames(); log.debug("생성된 빈의 이름 : {}", Arrays.toString(beanDefinitionNames)); } }
출력결과
- 빈으로 등록되어있는 빈들의 이름을 확인
getBean()
으로 빈을 가져올 수 있다. Object 타입으로 반환되므로 형변환 필요BookService bookService = (BookService) context.getBean("bookService");//Type cast
null이 아니면 의존성 주입이 잘 된 것이므로 출력해서 확인해보자
log.debug("의존성 주입이 되었는지 확인 : {}",bookService.bookRepository != null);//null이 아닌지 확인 -> true : 빈주입 성공
단점
-
일일이 빈으로 등록하고 주입을 해줘야 한다.
그래서 등장한 것이
context:component-scan
이다.
2. xml 파일의 Context:component-scan 으로 빈 등록
-
application.xml 파일에 context:component-scan 태그로 빈 스캔
base-package : 스캐닝 대상이 될 패키지명 등록 (메인 패키지명 입력)
<context:component-scan base-package="dev.solar.springapplicationcontext"/>
application.xml 파일
<?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 https://www.springframework.org/schema/context/spring-context.xsd"> <context:component-scan base-package="dev.solar.springapplicationcontext"/> </beans>
-
어노테이션을 이용해서 빈 등록
기본적으로
@Component
어노테이션을 사용해서 빈으로 등록할 수 있다.뿐만 아니라
@Component
어노테이션을 상속받은@Service
,@Repository
등의 어노테이션으로도 빈등록이 가능@Service public class BookService { ... }
@Repository public class BookRepository {}
이렇게만 하면 빈이 등록만 되고, 의존성 주입은 되지 않는다.
-
빈 주입
@Autowired
또는@Inject
어노테이션으로 빈을 주입받을 수 있다.@Inject
어노테이션은 별도의 의존성이 추가로 필요하므로@Autowired
로 주입받자@Service public class BookService { @Autowired BookRepository bookRepository; public void setBookRepository(BookRepository bookRepository) { this.bookRepository = bookRepository; } }
다시 실행해보면 빈이 생성되고, 주입도 성공한 것을 확인할 수 있다.
application.xml 파일을 읽어들이긴 하지만 xml에 들어있는 component-scan
기능을 사용해서 설정한 패키지 하위에 존재하는 어노테이션들을 스캐닝하여 클래스들을 빈으로 생성해준다.
3. 자바 코드를 이용한 빈 설정
-
자바 설정 파일임을 알려주는
@Configuration
어노테이션을 사용하여 빈 설정파일을 생성메소드명 : bean의 id
반환타입 : bean의 타입
-
BookService에 직접 BookRepository 의존성을 주입할 수 있다.
- setter을 이용해서 필요한 빈을 주입
(1) 의존성 주입에 필요한 인스턴스는 메서드로 호출해서 가져와 넘김
@Configuration public class ApplicationConfig { @Bean public BookRepository bookRepository() { return new BookRepository(); } @Bean public BookService bookService() { BookService bookService = new BookService(); bookService.setBookRepository(bookRepository()); //직접 의존성 주입 return bookService; } }
(2) 메서드 파라미터로 넘겨받아서 의존성 주입
@Bean public BookService bookService(BookRepository bookRepository) { BookService bookService = new BookService(); bookService.setBookRepository(bookRepository); return bookService; }
(3)
@Autowired
어노테이션으로 의존성 주입빈으로 등록만 하면 @Autowired 어노테이션 적용이 가능하다.
직접 의존성 주입을 하던 코드 삭제
// ApplicationConfig.java @Bean public BookService bookService(BookRepository bookRepository) { return new BookService(); }
@Autowired
어노테이션 추가public class BookService { @Autowired BookRepository bookRepository; }
- 생성자를 통한 빈 주입
@Configuration public class ApplicationConfig { @Bean public BookRepository bookRepository() { return new BookRepository(); } @Bean public BookService bookService(BookRepository bookRepository) { return new BookService(bookRepository); } }
public class BookService { BookRepository bookRepository; public BookService(BookRepository bookRepository) { this.bookRepository = bookRepository; } }
-
앞서 만든 빈을 사용하는 ApplicationContext를 만들어서 사용
AnnotationConfigApplicationContext
로 ApplicationContext를 생성지정한 클래스를 빈설정 파일로 사용한다.
ApplicationContext context = new AnnotationConfigApplicationContext(ApplicationContext.class);
빈설정에서
@Bean
으로 빈 정의를 읽어서 빈으로 등록하고 정의해준 대로 의존성 주입을 한다.public class SpringapplicationcontextApplication { private static final Logger log = LoggerFactory.getLogger(SpringapplicationcontextApplication.class); public static void main(String[] args) { ApplicationContext context = new AnnotationConfigApplicationContext(ApplicationContext.class); String[] beanDefinitionNames = context.getBeanDefinitionNames(); log.debug("생성된 빈의 이름 : {}", Arrays.toString(beanDefinitionNames)); BookService bookService = (BookService) context.getBean("bookService");//Type cast log.debug("의존성 주입이 되었는지 확인 : {}",bookService.bookRepository != null);//null이 아닌지 확인 -> true : 빈주입 성공 } }
단점
하지만 여전히 @Bean
어노테이션으로 일일이 지정하여 만드는 불편함이 있다.
xml에서 component scanning을 했던 것처럼 자바코드로도 스캐닝을 할 수 있다.
4. ComponentScan 어노테이션을 이용한 빈 설정
@ComponentScan(basePackageClasses = SpringapplicationcontextApplication.class)
-
basePackages = “path/of/package/class”
문자열로 입력할 수 있다. (IDE가 좋으면 자동완성을 지원하긴 하지만 Type Safety1하지 않다.)
-
basePackageClasses = 클래스명
좀 더 Type Safety한 방법
이 어플리케이션(클래스)이 위치한 곳부터 Component Scanning을 진행한다.
특정 어노테이션이 설정된 클래스들을 스캐닝해서 빈으로 알아서 등록해준다.
@Configuration
@ComponentScan(basePackageClasses = SpringapplicationcontextApplication.class)
public class ApplicationConfig {
}
@Repository
public class BookRepository {}
@Service
public class BookService {...}
이 방식이 스프링부트를 이용해 빈을 설정하는 방식에 가장 가까운 방법이다.
물론 스프링부트 프로젝트에서 ApplicationContext
를 직접 만들어서 사용하고 있진 않다. 이것 또한 스프링이 알아서 생성해준다.
5. 스프링 부트에서의 빈 설정
@SpringBootApplication
을 붙여주면 스프링이 ApplicationContext를 알아서 생성해준다.
@SpringBootApplication
public class SpringapplicationcontextApplication {
public static void main(String[] args) {
}
}
@SpringBootApplication을 확인해보면 이미 @ComponentScan
어노테이션을 상속받고 있고, (@SpringBootConfiguration → ) @Configuration
이 붙어있는 것이다.
사실상 위 코드의 클래스가 빈 설정파일이 되는 것이다. (따라서 별도로 만들었던 ApplicationConfig.java 파일은 불필요하다.)
-
어떠한 오퍼레이션(또는 연산)도 정의되지 않은 결과를 내놓지 않는것, 즉, 예측불가능한 결과를 내지 않는것을 뜻한다. ↩