일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- java
- Sub Bytes
- Reading HttpServletRequest Multiple Times
- Open Close Principal
- Jndi DataSource
- mTLS
- Srping MVC
- HandlerMethodArgumentResolver
- Tomcat DBCP
- mapstruct
- 바이트 절삭
- tomcat jndi
- requestheaderdto
- 상호 인증
- AfterMapping
- Unchecked Exception
- Request Body 여러 번 사용
- 데이터 압축
- WildFly
- try - with - resources
- Checked Exception
- graphql
- 이중정렬
- Graphql Client
- Socket is closed
- Java Graphql
- 개방 폐쇄 원칙
- Java Singleton
- Java Rest
- NoUniqueBeanDefinitionException
- Today
- Total
Developer Sang Guy
Spring @Autowired를 사용한 스프링 빈 주입 본문
스프링 빈 컨테이너에 등록 된 Bean을 자동으로 주입해주는 @Autowired에 대해 알아보자
@Autowired란 스프링 빈 컨테이너에 존재하는 빈을 타입에 맞게 자동으로 주입해주는 것을 의미한다.
스프링이 Bean 등록을 하기 위해 각 객체들을 스캔하는 과정에서 자동으로 실행 된다.
주의 : Bean 등록을 하지 않는 객체에서는 @Autowired를 사용 할 수 없다.
@Autowired를 사용하는 방식은 주로 아래와 같다.
1. 생성자 주입
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
@Service
public class Trade {
private Coin coin;
@Autowired
public Trade(Coin coin) {
this.coin = coin;
}
public User buyCoin(User user, String coinName, Integer quantity) {
return coin.buy(user, coinName, quantity);
}
public User sellCoin(User user, String coinName, Integer quantity) {
return coin.sell(user, coinName, quantity);
}
}
|
cs |
2. 수정자 주입(setter 주입)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
@Service
public class Trade {
private Coin coin;
@Autowired
public void setCoin(Coin coin) {
this.coin = coin;
}
public User buyCoin(User user, String coinName, Integer quantity) {
return coin.buy(user, coinName, quantity);
}
public User sellCoin(User user, String coinName, Integer quantity) {
return coin.sell(user, coinName, quantity);
}
}
|
cs |
3. 필드 주입
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
@Service
public class Trade {
@Autowired
private Coin coin;
public User buyCoin(User user, String coinName, Integer quantity) {
return coin.buy(user, coinName, quantity);
}
public User sellCoin(User user, String coinName, Integer quantity) {
return coin.sell(user, coinName, quantity);
}
}
|
cs |
4. 일반 메서드 주입
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
@Service
public class Trade {
private Coin coin;
@Autowired
public void init(Coin coin) {
this.coin = coin;
}
public User buyCoin(User user, String coinName, Integer quantity) {
return coin.buy(user, coinName, quantity);
}
public User sellCoin(User user, String coinName, Integer quantity) {
return coin.sell(user, coinName, quantity);
}
}
|
cs |
각 주입 방법에는 모두 장단점이 있으며 개인적으로 정답은 없다고 생각한다.
모두 목적은 같지만 방법이 다를 뿐이니 각 상황에 알맞게 사용하자
그래서 어떤게 좋은건데? 어떤 일이 생기는 건데?
@Autowired로 주입을 받는 객체의 타입이 스프링 빈에 등록되어 있다면 자동으로 해당 객체가 주입이 된다.
말은 어려우니 바로 소스로 알아보자
아래 Trade 객체는 @Service를 통해 스프링 빈에 등록을 할 것이며 @Autowired 생성자 주입 방법을 통해 Coin Type의 객체(BitCoin.class)를 주입 받을 것이다.
주의 : 주입 될 객체 역시 스프링 빈으로 등록되어 있어야 한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
@Service
public class Trade {
private Coin coin;
@Autowired
public Trade(Coin coin) {
this.coin = coin;
}
public User buyCoin(User user, String coinName, Integer quantity) {
return coin.buy(user, coinName, quantity);
}
public User sellCoin(User user, String coinName, Integer quantity) {
return coin.sell(user, coinName, quantity);
}
}
|
cs |
Coin.interface
1
2
3
4
5
6
|
public interface Coin {
User buy(User user, String coinName, Integer quantity);
User sell(User user, String coinName, Integer quantity);
}
|
cs |
Coin.interface를 상속받는 BitCoin.class
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
@Service
public class BitCoin implements Coin {
@Override
public User buy(User user, String coinName, Integer quantity) {
Map<String, Integer> pocket = user.getPocket();
pocket.put(coinName, quantity);
return new User(user.getName(), pocket);
}
@Override
public User sell(User user, String coinName, Integer quantity) {
Map<String, Integer> pocket = user.getPocket();
pocket.put(coinName, pocket.get(coinName) - quantity);
return new User("UserA", pocket);
}
}
|
cs |
실행 :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
@Test
public void tradeCoinTest() {
// PreAutowiredApplication를 실행하여 컴포넌트 스캔 실행
ApplicationContext ac = new AnnotationConfigApplicationContext(PreAutowiredApplication.class);
// 유저 객체 생성
User user = new User("UserA", new HashMap<String, Integer>());
/* BitCoin 거래 */
Trade trade = ac.getBean(Trade.class);
trade.buyCoin(user, "bitCoin", 155);
System.out.println(user.getPocket().get("bitCoin"));
assertThat(user.getPocket().get("bitCoin")).isEqualTo(155);
trade.sellCoin(user, "bitCoin", 20);
System.out.println(user.getPocket().get("bitCoin"));
assertThat(user.getPocket().get("bitCoin")).isEqualTo(135);
}
|
cs |
결과 :
잔여 bitCoin : 155
잔여 bitCoin : 135
위와 같이 Trade.class의 생성자에 @Autowired를 사용하여 빈 주입을 해주기만 하면 따로 BitCoin.class를 주입 해 줄 필요가 없다.
ex) 기본 자바의 경우 Trade trade = new Trade(new BitCoin())와 같은 방법으로 주입해야 함
Coin Type의 빈이 여러개라면?? BitCoin 말고 다른 코인도 사용 하겠어!
위 설명했던 내용 중 아래와 같은 내용이 있었는데..
@Autowired 생성자 주입 방법을 통해 Coin Type의 객체(BitCoin.class)를 주입 받을 것이다.
만약에 Coin Type의 객체가 BitCoin.class 1개가 아닌 여러 개의 경우에는 어떻게 될까?
DogeCoin.class를 새로 생성하여 테스트를 진행해보자
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
@Service
public class dogeCoin implements Coin {
@Override
public User buy(User user, String coinName, Integer quantity) {
Map<String, Integer> pocket = user.getPocket();
pocket.put(coinName, quantity);
return new User(user.getName(), pocket);
}
@Override
public User sell(User user, String coinName, Integer quantity) {
Map<String, Integer> pocket = user.getPocket();
pocket.put(coinName, pocket.get(coinName) - quantity);
return new User("UserA", pocket);
}
}
|
cs |
실행 :
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
|
@Test
public void tradeCoinTest() {
// PreAutowiredApplication를 실행하여 컴포넌트 스캔 실행
ApplicationContext ac = new AnnotationConfigApplicationContext(PreAutowiredApplication.class);
// 유저 객체 생성
User user = new User("UserA", new HashMap<String, Integer>());
/* BitCoin 거래 */
Trade trade = ac.getBean(Trade.class);
trade.buyCoin(user, "bitCoin", 155);
System.out.println("잔여 bitCoin : " + user.getPocket().get("bitCoin"));
assertThat(user.getPocket().get("bitCoin")).isEqualTo(155);
trade.sellCoin(user, "bitCoin", 20);
System.out.println("잔여 bitCoin : " + user.getPocket().get("bitCoin"));
assertThat(user.getPocket().get("bitCoin")).isEqualTo(135);
/* DogeCoin 거래 */
trade.buyCoin(user, "dogeCoin", 547519);
System.out.println("잔여 dogeCoin : " + user.getPocket().get("dogeCoin"));
assertThat(user.getPocket().get("dogeCoin")).isEqualTo(547519);
trade.sellCoin(user, "dogeCoin", 5000);
System.out.println("잔여 dogeCoin : " + user.getPocket().get("dogeCoin"));
assertThat(user.getPocket().get("dogeCoin")).isEqualTo(542519);
}
|
cs |
결과 :
1
|
18:21:15.556 [main] WARN org.springframework.context.annotation.AnnotationConfigApplicationContext - Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'trade': Unsatisfied dependency expressed through method 'init' parameter 0; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'me.pre.demo.service.Coin' available: expected single matching bean but found 2: bitCoin,dogeCoin
|
cs |
에러 내용 중 아래와 같은 내용이 있다.
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'me.pre.demo.service.Coin' available: expected single matching bean but found 2: bitCoin,dogeCoin
대충 보면 요구하는 건 단일 주입인데 스프링 빈에는 Coin Type으로 매칭되는 빈이 BitCoin, dogeCoin 두 개가 있다는 내용이다.
현재 Trade.class 에서 생성자 주입으로 Coin Type의 객체를 주입받고 있는데 이는 1개의 객체밖에 주입받을 수 없는 상태라 해당 에러가 발생한 것이다.
이와 같이 Coin Type의 객체는 여러개를 사용해야하는 상황이며 다 수의 빈을 주입받아 사용해야 할 경우에는 Trade.class를 아래와 같이 수정하여 사용 할 수 있다.
다 수의 빈을 주입받아 사용하는 Trade.class
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
|
@Service
public class TradeAllCoin {
private final Map<String, Coin> coinMap;
@Autowired
public TradeAllCoin(Map<String, Coin> coinMap) {
this.coinMap = coinMap;
}
public User buyCoin(User user, String coinName, Integer quantity) {
Coin coin = coinMap.get(coinName);
return coin.buy(user, coinName, quantity);
}
public User sellCoin(User user, String coinName, Integer quantity) {
Coin coin = coinMap.get(coinName);
return coin.sell(user, coinName, quantity);
}
}
|
cs |
Map 또는 List 타입으로 빈을 주입받으면 여러 개의 빈을 가질 수 있다.
실행 :
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
|
@Test
public void tradeAllCoinTest() {
ApplicationContext ac = new AnnotationConfigApplicationContext(PreAutowiredApplication.class);
User user = new User("UserA", new HashMap<String, Integer>());
TradeAllCoin tradeAllCoin = ac.getBean(TradeAllCoin.class);
/* BitCoin 거래 */
tradeAllCoin.buyCoin(user, "bitCoin", 155);
System.out.println("잔여 bitCoin : " + user.getPocket().get("bitCoin"));
assertThat(user.getPocket().get("bitCoin")).isEqualTo(155);
tradeAllCoin.sellCoin(user, "bitCoin", 20);
System.out.println("잔여 bitCoin : " + user.getPocket().get("bitCoin"));
assertThat(user.getPocket().get("bitCoin")).isEqualTo(135);
/* DogeCoin 거래 */
tradeAllCoin.buyCoin(user, "dogeCoin", 547519);
System.out.println("잔여 dogeCoin : " + user.getPocket().get("dogeCoin"));
assertThat(user.getPocket().get("dogeCoin")).isEqualTo(547519);
tradeAllCoin.sellCoin(user, "dogeCoin", 5000);
System.out.println("잔여 dogeCoin : " + user.getPocket().get("dogeCoin"));
assertThat(user.getPocket().get("dogeCoin")).isEqualTo(542519);
}
|
cs |
결과 :
1
2
3
4
5
|
coinMap : {bitCoin=me.pre.demo.service.BitCoin@67fe380b, dogeCoin=me.pre.demo.service.dogeCoin@4a325eb9}
잔여 bitCoin : 155
잔여 bitCoin : 135
잔여 dogeCoin : 547519
잔여 dogeCoin : 542519
|
cs |
위와 같이 Map으로 빈을 주입받는 방법 외에 아래 두 개의 에노테이션을 사용해 사용 할 빈을 컨트롤 하는 방법도 있다.
@Qualifier, @Primary
하지만 해당 방법은 다 수의 빈 사용하는 방법이 아닌 다 수의 빈 중 사용 할 빈을 컨트롤 하는 방법이므로 위 내용에서는 다루지 않았다.
'Spring' 카테고리의 다른 글
HttpServletRequest Body 여러 번 읽기 (0) | 2022.12.28 |
---|---|
[Spring] RequestMapping 기능 (produces, consumes ) (0) | 2022.10.07 |
[Spring Boot] DB 연결 (0) | 2022.09.28 |
[Spring] Aop, Logback 활용 (0) | 2022.04.28 |
스프링 WebMvcConfigurer (0) | 2021.08.08 |