레시피 2-2내용은 별 내용 없어서 생략했다.


 이번 장에서는 POJO 인스턴스에 대한 레퍼런스 자동 연결 기능을 보도록 하자.



1. Autowired


 POJO 필드에 @Autowired 어노테이션을 붙이면 그 필드에 호환되는 빈을 찾아서 연결한다.

 필드 변수를 초기화해주지 않아도 자동으로 호환되는 레퍼런스를 찾아 초기화해준다는 것이다.


 다음 예제는 이전 포스팅에서 다루었던 SequenceGenerator 예제를 변형한 것이다.


Sequence

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
package com.apress.springrecipes.sequence;
 
public class Sequence {
    private final String id;
    private final String prefix;
    private final String suffix;
    
    public Sequence(String id, String prefix, String suffix) {
        this.id = id;
        this.prefix = prefix;
        this.suffix = suffix;
    }
    
    public String getId() {
        return id;
    }
    
    public String getPrefix() {
        return prefix;
    }
    
    public String getSuffix() {
        return suffix;
    }
    
    
}
 
cs


SequenceDao

1
2
3
4
5
6
7
8
package com.apress.springrecipes.sequence;
 
public interface SequenceDao {
    public Sequence getSequence(String sequenceId);
    
    public int getNextValue(String sequenceId);
}
 
cs


SequenceDaoImpl

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
package com.apress.springrecipes.sequence;
 
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
 
import org.springframework.context.annotation.ComponentScan;
import org.springframework.stereotype.Component;
 
@Component("sequenceDao")
public class SequenceDaoImpl implements SequenceDao {
    private final Map<String, Sequence> sequences = new HashMap<>();
    private final Map<String, AtomicInteger> values = new HashMap<>();
    
    public SequenceDaoImpl() {
        sequences.put("IT"new Sequence("IT""30""A"));
        values.put("IT"new AtomicInteger(10000));
    }
    
    public Sequence getSequence(String sequenceId) {
        return sequences.get(sequenceId);
    }
    
    public int getNextValue(String sequenceId) {
        AtomicInteger value = values.get(sequenceId);
        return value.getAndIncrement();
    }
}
 
cs


Main

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.apress.springrecipes.sequence;
 
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
 
public class Main {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext("com.apress.springrecipes.sequence");
        
        SequenceService sequenceService = context.getBean(SequenceService.class);
        
        System.out.println(sequenceService.generate("IT"));
        System.out.println(sequenceService.generate("IT"));
    }
}
 
cs


여기까지는 이전 포스팅과 똑같다.


다음에 설명하는 SequenceService가 이번 포스팅의 핵심 내용이다.


 SequenceService

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
package com.apress.springrecipes.sequence;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
 
@Service
public class SequenceService {
    @Autowired
    private SequenceDao sequenceDao;
    
    public SequenceService() {}
    
    public SequenceService(SequenceDao sequenceDao) {
        this.sequenceDao = sequenceDao;
    }
    
    public void setSequenceDao(SequenceDao sequenceDao) {
        this.sequenceDao = sequenceDao;
    }
    
    public String generate(String sequenceId) {
        Sequence sequence = sequenceDao.getSequence(sequenceId);
        int value = sequenceDao.getNextValue(sequenceId);
        return sequence.getPrefix() + value + sequence.getSuffix();
    }
}
 
cs

 

 SequenceService 클래스는 서비스 클래스로, DAO(Data Access Object)와 연동하여 요청을 처리해주는 클래스다. (이런 구현 방법이 실제로 자주 쓰인다.)

 @Service 어노테이션이 붙어있기 때문에 SequenceService는 스프링 빈으로 등록되고, @Autowired가 있기 때문에 sequenceDao 필드엔 이에 호환되는 SequenceDaoImpl 클래스가 자동으로 연결된다. 그렇기 때문에 Main 클래스에서 sequenceService.sequenceDao 필드를 초기화하는 내용이 없음에도 generate() 메소드를 호출해도 오류가 생기지 않는 것이다.


배열, 맵 연결

 배열형 프로퍼티에 @Autowired를 붙이면 스프링은 매치되는 모든 빈을 찾아서 연결하고, type-safe한 컬렉션에 @Autowired를 붙였을 때도 호환되는 모든 빈을 찾아서 연결된다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class SequenceGenerator{
 
    @Autowired
    private PrefixGenerator[] prefixGenerators;
}
 
 
public class SequenceGenerator{
    @Autowired
    private List<PrefixGenerator> prefixGenerators;
}
 
public class SequenceGenerator{
    @Autowired
    private Map<String, PrefixGenerator> prefixGenerators;
}
 
 
cs


 이와 같은 경우 prefixGenerators에 호환되는 모든 빈들이 연결된다.



2. @Autowired로 POJO 메소드와 생성자 연결


 @Autowired는 POJO Setter 메서드, 생성자에도 적용할 수 있다.

 그렇게 되면 세터 메서드와 생성자의 패러미터로 들어오는 변수에 호환되는 빈이 자동으로 연결된다. (패러미터는 여러개 있어도 가능하다)


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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package com.apress.springrecipes.sequence;
 
import org.springframework.beans.factory.annotation.Autowired;
 
public class SequenceGenerator {
    private PrefixGenerator prefixGenerator;
    private String suffix;
    private int initial;
    private int counter;
 
    public SequenceGenerator() {
    }
 
    public SequenceGenerator(PrefixGenerator prefixGenerator, String suffix, int initial) {
        this.prefixGenerator = prefixGenerator;
        this.suffix = suffix;
        this.initial = initial;
    }
 
    //@Autowired
    public void setPrefixGenerator(PrefixGenerator prefixGenerator) {
        this.prefixGenerator = prefixGenerator;
    }
 
    @Autowired
    public void myOwnCustomInjectionName(PrefixGenerator prefixGenerator) {
        this.prefixGenerator = prefixGenerator;
    }
 
    public void setSuffix(String suffix) {
        this.suffix = suffix;
    }
 
    public void setInitial(int initial) {
        this.initial = initial;
    }
 
    public synchronized String getSequence() {
        StringBuilder builder = new StringBuilder();
        builder.append(prefixGenerator.getPrefix());
        builder.append(initial + counter++);
        builder.append(suffix);
        return builder.toString();
    }
}
 
cs


 위에서 Setter 메소드에 @Autowired 어노테이션을 붙여 prefixGenerator에 매칭되는 빈 오브젝트를 자동으로 찾아서 집어넣는다.


 스프링에서 기본적으로는 @Autowired를 붙인 프로퍼티는 필수 프로퍼티로 취급되어, 매칭되는 빈이 없으면 예외가 발생한다. @Autowired의 required 속성값을 false로 지정하면 선택적 프로퍼티로 취급되어 빈을 못 찾아도 그냥 지나친다.

1
2
3
4
5
6
7
8
public class SequenceGenerator {
    ...
    @Autowired(required = false)
    public void myOwnCustomInjectionName(PrefixGenerator prefixGenerator) {
        this.prefixGenerator = prefixGenerator;
    }
}
 
cs



3. 모호한 자동 연결 명시하기


@Primary

 스프링에서는 @Primary를 붙여 후보 빈을 명시한다. 여러 빈이 자동 연결 대상일 때, 특정한 빈에 우선권을 부여하는 것이다.


@Qualifier

 @Qualifier에 이름을 줘서 후보 빈을 명시할 수도 있다.


4. 여러곳에 분산된 POJO 참조 문제 해결


 어플리케이션 규모가 커지면 모든 POJO 설정을 하나의 자바 구성 클래스에 담기 어렵기 떄문에, 보통 POJO 기능에 따라 여러 자바 구성 클래스로 나눠서 관리한다. 그런데 자바 구성 클래스가 여러개 있으면 서로다른 클래스에 정의된 POJO를 자동으로 연결하거나 참조하기가 쉽지 않다.

 이는 다음의 두가지 방법으로 해결 가능하다.


 방법1. 자바 구성 클래스가 위치한 경로마다 어플리케이션 컨텍스트를 초기화

1
2
3
AnnotationConfigApplication context = new AnnotationConfigApplicationContext(PrefixConfiguration.class
                                                            SequenceGeneratorConfiguration.class)
 
cs



 방법2. @Import로 구성 파일을 나눠서 임포트

1
2
3
4
5
6
@Configuration
@Import(PrefixConfiguration.class)
public class SequenceConfiguration{
    ....
 
}
cs


 

4. 레퍼런스

스프링 5 레시피(4판) - 한빛 미디어


'IT 관련 > 스프링(스프링5 레시피)' 카테고리의 다른 글

스프링 프로젝트 생성  (0) 2020.02.23
Chap 2.1 자바로 POJO 구성하기  (0) 2020.02.23
스프링 시작(Chapter1)  (0) 2020.02.20
블로그 이미지

서기리보이

,