일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- ㅔㄴ션
- 메소드명
- 추후정리
- 참조키
- MySQL
- Query
- 네이티브쿼리
- 컨테이너실행
- 메세지수정
- 서브쿼리
- application.yml
- appspec.yml
- 포트
- 테스트메소드
- 2 > /dev/null
- 적용우선순위
- EC2
- 외부키
- AuthenticationEntryPoint
- 테스트
- 예약
- docker명령어
- 커밋메세지수정
- WeNews
- foreignkey
- subquery
- querydsl
- 검색
- appspec
- ubuntu
- Today
- Total
제뉴어리의 모든것
[Section2] [Spring Core] Spring Framework의 핵심 개념 - Component Scan 본문
[Section2] [Spring Core] Spring Framework의 핵심 개념 - Component Scan
제뉴어리맨 2022. 8. 12. 23:48@Component Scan이란?
스프링컨테이너의 구성정보가 없어도 @Component라는 애노테이션이 붙은 클래스들을 찾아서 빈으로 등록하는 스프링의 기능이다.
여기서 말하는 구성정보란,
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AutoAppConfig.class);
에서 AutoAppConfig.class 내부에 빈으로 등록할 객체들을 말하고,
구성정보가 없어도 된다는것은,
AutoAppConfig 클래스의 내부에 빈으로 등록할 객체들이 정의되어 있지 않아도 된다는 의미이다.
@Component Scan의 필요성
- 구성정보 클래스에 등록할 빈들을 적어 놓는것은 한눈에 빈들의 존재를 알 수 있다는 장점도 있지만, 관리하는 빈들이 한없이 많아지게 되면 구성정보 클래스가 너무 비대해지고 그럼에따라 누락의 위험성도 있다.
@Component Scan 사용법
주문 프로그램이란 내용으로 사용법을 설명하겠다.
(각 클래스가 구현한 인터페이스 코드는 생략하겠다)
- 작성 프로그램 할 프로그램 : OrderApp
- OrdeApp 프로그램 내용 :
1. 한 유저를 프로그램에 회원으로 주문 시킨다.
2. 가입한 유저가 음식을 주문했을때, 해당 음식의 할인가격까지 포함하여 주문 내역을 출력한다.
- OrderApp 코드
public class OrderApp {
public static void main(String[] args) {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AutoAppConfig.class);
UserService userService = ac.getBean(UserService.class);
OrderService orderService = ac.getBean(OrderService.class);
Long userId = 0L;
User newUser = new User(userId, "제뉴어리맨", UserGrade.GRADE_2);
userService.signup(newUser); //유저 가입
Order order = orderService.createOrder(userId, "치킨", 40000);
System.out.println(order);
}
}
스프링 컨테이너인 AnnotationConfigApplicationContext의 구성정보인 AutoAppConfig.class
내부를 보면 @Configuration을 사용하여 빈을 등록하는 방법과 확연한 차이를 알 수 있다.
- AutoAppConfig 코드
import org.springframework.context.annotation.ComponentScan;
@ComponentScan // @Component 붙은 클래스들을 모두 빈으로 등록하겠다는 애노테이션
public class AutoAppConfig {
}
- UserService 코드
@Component //Component Scan을 위해 추가됨.
public class UserServiceImpl implements UserService{
private final UserRepository userRepository; //의존성 주입됨
// @Autowired //생성자가 하나뿐일때는 생략이 가능하다. 스프링 컨테이너가 자동으로 해당 생성자를 기본 생성자로 인식하여 호출하여 객체생성과 의존성 주입까지 해준다.
public UserServiceImpl(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public void signup(User user) {
userRepository.saveUser(user);
}
@Override
public User findUser(Long userId) {
return userRepository.findByUserId(userId);
}
}
- UserRepositoryImpl
@Component //Component Scan을 위해 추가됨.
public class UserRepositoryImpl implements UserRepository{
private static Map<Long, User> users = new HashMap<>();
@Override
public void saveUser(User user) {
users.put(user.getId(), user);
}
@Override
public User findByUserId(Long userId) {
return users.get(userId);
}
}
- OrderServiceImpl
@Component //Component Scan을 위해 추가됨.
public class OrderServiceImpl implements OrderService{
DiscountInfo discountInfo;
UserRepository userRepository;
// @Autowired //생성자가 하나일때는 생략 가능하다. 자동으로 해당 생성자를 실행시켜 빈을 생성,등록하고 의존성주입도 해준다.
public OrderServiceImpl(DiscountInfo discountInfo, UserRepository userRepository) {
this.discountInfo = discountInfo;
this.userRepository = userRepository;
}
@Override
public Order createOrder(Long userId, String itemName, int itemPrice) {
User findUser = userRepository.findByUserId(userId);
int discountPrice = discountInfo.discount(findUser, itemPrice);
return new Order(userId,itemName,itemPrice,discountPrice);
}
- RateDiscountInfo
@Component
public class RateDiscountInfo implements DiscountInfo{
private int grade_1_rate = 5;
private int grade_2_rate = 10;
@Override
public int discount(User user, int price) {
if (user.getUserGrade() == UserGrade.GRADE_1) {
return price * grade_1_rate / 100;
}
else if (user.getUserGrade() == UserGrade.GRADE_2) {
return price * grade_2_rate / 100;
}
return 0;
}
}
- 각 자파 파일들의 디렉토리 위치
참고로 OrderApp2가 OrderApp이다.
- 내용 설명
AutoAppConfig.class 에 @ComponentScan이 적용되어 있으므로,
AutoAppConfig의 위치 포함하여 하위 디렉토리들에서 @Component 애노테이션을 스캔하여
@Component 가 붙어있는 클래스들을 모두 스프링 빈으로 등록한다.
참고로,
@Configuration, @Controller, @Repository, @Service, @SpringBootApplication 애노테이션도 내부적으로 보면
@Component를 포함하고 있다. (해당 애노테이션에 커서두고 Ctrl + B 눌러서 애노테이션 내부 확인 가능)
그러므로 위에 애노테이션이 붙어있는 클래스들 또한 모두 빈으로 등록 될 것이다.
그리고 해당 애노테이션이 붙은 클래스들은 모두 의존성 주입 또한 자동으로 해주며,
만약 의존성 주입하려는 객체가 빈으로 존재하지 않거나, 빈이 중복으로 존재하거나, 또는 생성자가 오버로딩 되어 있는데 @Autowired로 의존성 주입할 생성자를 지정하지 않은 경우 컴파일 에러를 발생한다. (그러나 기본 생성자만 오버로딩 된경우는 에러가 발생하지 않는다. 어차피 의존성 주입이 안되는 생성자이기에 무시하고 @Autowired가 붙은 생성자를 기본 생성자로 생각하여 의존성 주입하는듯 하다)
- 괄호안에 상황 예
package com.spring_ex01.cmarket.order;
import com.spring_ex01.cmarket.discount.DiscountInfo;
import com.spring_ex01.cmarket.user.User;
import com.spring_ex01.cmarket.user.UserRepository;
import org.springframework.stereotype.Component;
@Component
public class OrderServiceImpl implements OrderService{
DiscountInfo discountInfo;
UserRepository userRepository;
// @Autowired //생성자가 하나일때는 생략 가능하다. 자동으로 해당 생성자를 실행시켜 빈을 생성,등록하고 의존성주입도 해준다.
public OrderServiceImpl(DiscountInfo discountInfo, UserRepository userRepository) {
this.discountInfo = discountInfo;
this.userRepository = userRepository;
}
public OrderServiceImpl() { //이렇게 기본 생성자가 오버로딩 되어 있음에도 에러가 발생되지 않는다.
}
@Override
public Order createOrder(Long userId, String itemName, int itemPrice) {
User findUser = userRepository.findByUserId(userId);
int discountPrice = discountInfo.discount(findUser, itemPrice);
return new Order(userId,itemName,itemPrice,discountPrice);
}
}
코드를 보면
public OrderServiceImpl() { } 와 같이 기본 생성자가 오버로딩 되어 있음에도 에러가 발생하지 않는다.
테스트 해보고 싶다면 위에 OrderApp 예제에서 OrderServiceImpl 클래스에 기본 생성자만 추가해서 테스트해보면 된다.
컴포넌트 스캔 기본 대상
- @Component : 컴포넌트 스캔에서 사용됩니다.
아래의 애노테이션들은 내부적으로 모두 @Component를 가지고 있다. - @Controller & @RestController : 스프링 MVC 및 REST 전용 컨트롤러에서 사용됩니다.
- @Service : 스프링 비즈니스 로직에서 사용됩니다.
- 특별한 처리를 하지 않는다.
- 개발자들이 핵심 비즈니스 로직이 여기에 있다는비즈니스 계층을 인식하는데 도움이 된다.
- @Repository : 스프링 데이터 접근 계층에서 사용됩니다.
- 스프링 데이터 접근 계층으로 인식하고, 데이터 계층의 예외를 스프링 예외로 변환해준다.
- @Configuration : 스프링 설정 정보에서 사용됩니다.
- 스프링 설정 정보로 인식하고, 스프링 빈이 싱글톤을 유지하도록 추가 처리를 한다.
@ComponentScan의 주요 옵션
- basePackages
컴포넌트스캔을 시작할 디렉토리 위치 지정.
- 사용 예
@Configuration
@ComponentScan(basePackages = {"com.package1.component"})
class AppContextConfig{ ... }
com.package1.component 위치 포함, 하위 디렉토리들을 컴포넌트 스캔의 범위로 지정.
@ComponentScan("com.package1.component") 로 basePackages를 생략 하여도 된다. - 필터
- includeFilters : 컴포넌트 스캔 대상을 추가로 지정합니다.
- excludeFilters : 컴포넌트 스캔에서 제외할 대상을 지정합니다.
- FilterType 옵션
--- ANNOTATION: 기본값, 애너테이션으로 인식해서 동작합니다.
--- ASSIGNABLE_TYPE: 지정한 타입과 자식 타입을 인식해서 동작합니다.
--- ASPECTJ: AspectJ 패턴을 사용합니다.
--- REGEX: 정규 표현식을 나타냅니다.
--- CUSTOM: TypeFilter라는 인터페이스를 구현해서 처리합니다.
더 다양한 옵션은 아래 블로그 참조
'코드스테이츠 > 정리 블로깅' 카테고리의 다른 글
[Section2] [Spring Core] Spring Framework의 핵심 개념 - AOP - 1 (용어) (0) | 2022.08.18 |
---|---|
[Section2] [Spring Core] Spring Framework의 핵심 개념 - 스프링 컨테이너와 빈 (0) | 2022.08.13 |
[Section2] [Spring Core] Spring Framework의 핵심 개념 - Spring DI (0) | 2022.08.12 |
[Section2] [Spring Core] Spring Framework의 핵심 개념 - 자바 기반 컨테이너 설정 (0) | 2022.08.12 |
[Section2] [관계형 데이터베이스] [Spring Core] Spring Framework 기본 2 (0) | 2022.08.10 |