저번 글에 빈이랑 스프링 컨테이너라는 말이 나와서그런데 이것도 정리해보면 좋겠다~ 싶어서 정리해보자!
스프링 컨테이너는 자바 객체의 생명주기를 관리하며, 생성된 자바 객체들에게 기능을 제공하는 역할을한다. 여기서 말하는 자바의 객체란 빈(Bean)이다.?
빈(Bean)을 등록하는 방법을 알기전에 누가 빈을 관리하나? 결론부터 말하자면 스프링은 IoC 컨테이너로 빈을 관리한다.
Don't call us, we'll call you.
Inversion of Control(IoC)는 직역하자면 제어의 역전이다. 이게 뭔지 간단하게 설명해보자면, 우리는 스프링 프레임워크를 쓸 때 Controller, Service같은 객체들의 동작은 직접 구현은 하지만 얘네가 언제쓰일지는 상관하지않는다. 그럼 객체들이 언제 생성되고 어떻게 쓰일 지는 누가 담당할까? 그게 바로 스프링 컨테이너다. 즉, 개발자가 코드의 흐름과 제어를 관리하는 대신에 스프링 컨테이너가 제어의 권한을 가진다!
이전시간에 정리한 DI도 결국은 IoC의 한가지 예시라고 볼 수 있겠다.
이제 본격적으로 빈을 등록하는 방법에 대해 알아보자
<!-- applicationContext.xml -->
<beans xmlns="http://www.springframework.org/schema/beans"
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.xsd">
<!-- UserService 빈 등록 -->
<bean id="userService" class="com.example.UserService">
<!-- UserRepository 의존성 주입 -->
<property name="userRepository" ref="userRepository"/>
</bean>
<!-- UserRepository 빈 등록 -->
<bean id="userRepository" class="com.example.UserRepository"/>
</beans>
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyApp {
public static void main(String[] args) {
// 스프링 컨테이너 생성 및 설정 파일 로드
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
// 빈 가져오기
UserService userService = context.getBean("userService", UserService.class);
UserRepository userRepository = context.getBean("userRepository", UserRepository.class);
// 빈 사용
userService.setUserRepository(userRepository);
userService.doSomething();
}
}
위의 방법이 초기의 방법이다..! 새삼 편해진게 느껴지는군.. 근데 아직 위의 방법도 자주 쓰인다네 😅
이를 해결해주는 스프링의 component scan 기능이 추가된 뒤로는 좀 더 간편해졌다
<!-- applicationContext.xml -->
<context:component-scan base-package="lsy.code.sample" />
component Scan으로 어노테이션이 등록된 패키지안에 bean 클래스를 만들어서 자동으로 빈이 등록될수있게됨
package lsy.code.sample;
@Component
public class ProductService {
}
@Component
public class ProductController {
private final ProductService productService;
@Autowired
public ProductController(ProductService productService) {
this.productService = productService;
}
}
여기서 @Component
어노테이션은 자동으로 빈이 등록될 수 있게하는 어노테이션이고 위처럼 다 @Component
를 사용해도되지만 Entity, Service, Controller를 명시적으로 구분할수있도록 @Entity
, @Service
, @Controller
를 사용하면 좋다~
@Service
public class ProductService {
// ProductService의 비즈니스 로직
}
@Controller
public class ProductController {
private final ProductService productService;
@Autowired
public ProductController(ProductService productService) {
this.productService = productService;
}
// Controller의 요청 처리 메서드
}
요렇게말이죠
근데 아직도 좀 불편한 감이 없지않죠?
@Configuration
public class AppConfig {
@Bean
public MyService myService() {
return new MyService();
}
@Bean
public MyController myController(@Autowired MyService myService) {
return new MyController(myService);
}
}
@Configuration
어노테이션을 사용한 config파일에서 @Bean
을 사용해 빈으로 등록할수있다. 이걸 활용하려면?
@Controller
public class MyController {
private final MyService myService;
@Autowired
public MyController(MyService myService) {
this.myService = myService;
}
// Controller의 메서드 구현
}
이렇게 쓸수있다
지금까지 방법들은 모두 xml이나 config파일에서 일일이 등록해야하는 문제가 있다. 이걸 해결하기위해 component Scan을 사용할 수 있다!
@Configuration
@ComponentScan(basePackages = "com.example")
public class AppConfig {
}
@Configuration
@ComponentScan(baseClasses = "exampleApplication.class")
public class AppConfig {
}
@ComponentScan
의 속성인 basePackages로 최상단 패키지를 설정하던지 baseClasses으로 최상단 클래스를 설정하여 모든 bean을 스캔하고 등록해줄수있다!
그런데? 이것도 귀찮다??
@SpringBootApplication
활용@SpringBootApplication
public class exampleApplication {
public static void main(String[] args) {
SpringApplication.run(exampleApplication.class, args);
}
}
@SpringBootApplication
어노테이션은 @ComponentScan
+ @Configuration
이 담겨져있기때문에 따로 config파일이나 컴포넌트스캔이 필요하지않는다!
아마 대부분 위의 방법을 쓰지않을까? 싶을정도로 날로먹을수있다 ㅎㅎ;