2023. 12. 10. 21:42ㆍspring
제어의 역전(IoC)은 스프링 프레임워크의 심장이다.
IoC 컨테이너는 POJO를 구성하고 관리한다.
스프링 프레임워크의 가장 중요한 의의가 이 POJO로 자바 애플리케이션을 개발하는 것이다.
그러므로 스프링의 주요 기능은 대부분 IoC 컨테이너 안에서 POJO를 구성 및 관리하는 일과 연관돼 있다.
*이 책에서 POJO랑 빈을 같은 의미로 혼용해서 쓴다.
2-1 자바로 POJO 구성하기(빈 등록)
1. POJO 설계
2. @Configuration, @Bean을 붙여 자바 구성 클래스로 만들거나
@Component, @Repository, @Service, @Controller 등을 붙인 자바 컴포넌트를 구성한다.
IoC 컨테이너는 이렇게 애너테이션을 붙인 자바 클래스를 스캐닝하여 애플리케이션의 일부인 것처럼 POJO 인스턴스/빈을 구성한다.
public class SequenceGenerator {
private String prefix;
private String suffix;
private int initial;
private final AtomicInteger counter = new AtomicInteger();
public SequenceGenerator() { }
public void setPrefix(String prefix) {
this.prefix = prefix;
}
public void setSuffix(String suffix) {
this.suffix = suffix;
}
public void setInitial(int initial) {
this.initial = initial;
}
public String getSequence() {
StringBuilder builder = new StringBuilder();
builder.append(prefix)
.append(initial)
.append(counter.getAndIncrement())
.append(suffix);
return builder.toString();
}
}
- 2.1.1구성 클래스에서 @Configuration과 @Bean을 붙여 자바 POJO 생성하기
@Bean이 붙은 메소드에서 객체를 초기화 하고 리턴하면 IoC 컨테이너에서 POJO 인스턴스로 정의할 수 있다.
@Configuration
public class SequenceGeneratorConfiguration {
@Bean
SequenceGenerator sequenceGenerator() {
SequenceGenerator seqgen = new SequenceGenerator();
seqgen.setPrefix("30");
seqgen.setSuffix("A");
seqgen.setInitial(100000);
return seqgen;
}
}
- 2.1.2IoC 컨테이너를 초기화하여 애너테이션 스캐닝하기
애너테이션을 붙인 자바 클래스를 스캐닝하려면 IoC 컨테이너를 인스턴스화 해야한다.
스프링이 @Configuration, @Bean을 발견하고 IoC 컨테이너에 빈을 등록한다.
스프링은 기본 구현체인
1. 빈 팩토리와 이와 호환되는
2. 고급 구현체인 애플리케이션 컨텍스트,
두 가지 IoC 컨테이너를 제공한다. 구성 파일은 두 컨테이너 모두 동일하다.
애플리케이션 컨텍스트는 기본 기능에 충실하고 빈 팩토리보다 발전된 기능을 지니고 있으므로 리소스에 제약을 받는 상황이 아니라면 가급적 애플리케이션 컨텍스트를 사용하자!
ApplicationContext는 인터페이스라 사용하려면 구현체가 필요하다.
public class Main {
public static void main(String[] args) {
ApplicationContext context =
new AnnotationConfigApplicationContext(SequenceGeneratorConfiguration.class);
}
}
애플리케이션 컨텍스트를 인스턴스화한 이후에 context는 POJO 인스턴스 또는 빈에 액세스하는 창구 노릇을 한다.
- 2.1.3 IoC 컨테이너에서 POJO 인스턴스/빈 가져오기
구성 클래스에 선언된 빈을 애플리케이션 컨텍스에서 가져오려면 유일한 빈 이름을 getBean() 메서드의 인수로 호출한다.
SequenceGenerator generator2 = (SequenceGenerator) context.getBean("sequenceGenerator");
캐스팅을 안하고 가져올 수도 있다.
SequenceGenerator generator = context.getBean("sequenceGenerator", SequenceGenerator.class);
이런 식으로 POJO 인스턴스/빈을 스프링 외부에서 생성자를 이용해 여느 객체처럼 사용할 수 있다.
public class Main {
public static void main(String[] args) {
ApplicationContext context =
new AnnotationConfigApplicationContext(SequenceGeneratorConfiguration.class);
SequenceGenerator generator = context.getBean("sequenceGenerator", SequenceGenerator.class);
SequenceGenerator generator2 = (SequenceGenerator) context.getBean("sequenceGenerator");
System.out.println(generator.getSequence());
System.out.println(generator2.getSequence());
}
}
=>
301000000A
301000001A
- 2.1.4 POJO 클래스에 @Component를 붙여 DAO 빈 생성하기
Domain 클래스 및 DAO를 이용해 POJO를 생성해보자
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;
}
}
DAO 인터페이스 만들기
(편의상 시퀀스 인스턴스 및 값은 하드코딩)
public interface SequenceDao {
public Sequence getSequence(String sequenceId);
public int getNextValue(String sequenceId);
}
========================================================================
@Component("sequenceDao")
public class SequenceDaoImpl implements SequenceDao{
private final Map<String, Sequence> sequence = new HashMap<>();
private final Map<String, AtomicInteger> values = new HashMap<>();
public SequenceDaoImpl() {
sequence.put("IT", new Sequence("IT", "30", "A"));
values.put("IT", new AtomicInteger(100000));
}
@Override
public Sequence getSequence(String sequenceId) {
return sequence.get(sequenceId);
}
@Override
public int getNextValue(String sequenceId) {
AtomicInteger value = values.get(sequenceId);
return value.getAndIncrement();
}
}
SequenceDaoImpl 클래스에 @Component("sequenceDao")를 붙이면 스프링은 이 클래스를 이용해 POJO 생성.
@Component는 스프링이 발견할 수 있게 POJO에 붙이는 범용 애너테이션이다.
persistence(영속화) => @Repository
service(서비스) => @Service
presentatioon(표현) => @Controller
POJO의 쓰임새가 명확하지 않을 땐 @Component를 붙여도 되지만 특정 용도에 맞는 부가 혜택을 누리려면 구체적으로 명시하는 편이 좋다.
- 2.1.5 애너테이션을 스캐닝하는 필터로 IoC 컨테이너 초기화하기
기본적으로 스프링은 @Configuration, @Bean, @Component, @Repository, @Service, @Controller가 달린 클래스를 모두 감지한다. 이때 스캐닝 과정을 커스터마이징해 특정 애너테이션을 붙인 POJO를 스프링 애플리케이션에 넣거나 뺄 수 있다.
스프링이 지원하는 필터 표현식은 네 종류이다.
annotation, assignable은 각각 필터 대상 애너테이션 타입 및 클래스/인터페이스를 지정하며
regex, aspectj는 각각 정규표현식과 AspectJ 포인트컷 포현식 클래스를 매칭하는 용도로 쓰인다.
예를 들어보자
//@Component("sequenceDao")
public class SequenceDaoImpl implements SequenceDao{
private final Map<String, Sequence> sequence = new HashMap<>();
private final Map<String, AtomicInteger> values = new HashMap<>();
public SequenceDaoImpl() {
sequence.put("IT", new Sequence("IT", "30", "A"));
values.put("IT", new AtomicInteger(100000));
}
@Override
public Sequence getSequence(String sequenceId) {
return sequence.get(sequenceId);
}
@Override
public int getNextValue(String sequenceId) {
AtomicInteger value = values.get(sequenceId);
return value.getAndIncrement();
}
}
@ComponentScan(
basePackages = "com.spring.study.sequence",
includeFilters = {
@ComponentScan.Filter(
type = FilterType.REGEX,
pattern = {"com.spring.study.sequence.dao.*DaoImpl",
"com.spring.study.sequence.service.*Service"}
)
},
excludeFilters = {
@ComponentScan.Filter(
type = FilterType.ANNOTATION,
classes = {org.springframework.stereotype.Controller.class}
)
}
)
@Configuration
public class SequenceConfig {
private final SequenceDao sequenceDao;
public SequenceConfig(SequenceDao sequenceDao) {
this.sequenceDao = sequenceDao;
System.out.println(sequenceDao.getNextValue("IT"));
}
}
이름에 DaoImpl이나 Service가 포함된 클래스를 감지하려면 includeFilters를 적용하면된다. 그러면 어노테이션에 주석처리가 되어있어도 스프링이 자동 감지한다.
public class SequenceExample {
public static void main(String[] args) {
ApplicationContext context =
new AnnotationConfigApplicationContext("com.spring.study.sequence.config");
}
}
=>
100000
- 2.1.6 IoC 컨테이너에서 POJO 인스턴스/빈 가져오기
public class SequenceExample {
public static void main(String[] args) {
ApplicationContext context =
new AnnotationConfigApplicationContext("com.spring.study.sequence");
SequenceDao sequenceDao = context.getBean(SequenceDao.class);
System.out.println(sequenceDao.getNextValue("IT"));
System.out.println(sequenceDao.getNextValue("IT"));
}
}
'spring' 카테고리의 다른 글
스프링 레시피 CH2.3 POJO 레퍼런스와 자동 연결을 이용해 다른 POJO와 상호 작용하기 (0) | 2023.12.10 |
---|---|
스프링 레시피 CH2.2 생성자 호출해서 POJO 생성하기 (0) | 2023.12.10 |
AOP란? (0) | 2022.05.28 |
스프링 DI 지시서 작성하기(Spring bean Configuration) (0) | 2022.05.17 |
스프링 프레임워크 코어기능 : 종속 객체를 생성 조립해주는 도구 (0) | 2022.05.16 |