개념.log/Spring

0. 스프링 빈 톺아보기

_2J 2023. 1. 26. 23:33

스프링 빈 사용


* 클래스를 스프링 빈으로 만드는 스프링 빈 정의를 설정한다.

* 설정된 스프링 빈 정의를 스프링 빈 컨테이너가 찾을 수 있도록 설정한다.

* 서로 의존성이 있는 스프링 빈들을 조립할 수 있도록 설정한다.

 

 

 

 

스프링 애플리케이션의 시작과정

1. 스프링 빈 컨테이너 구현체에 따라 정해진 포맷의 설정파일 로딩 (XML, 자바 클래스, 그루비 등...)

    - 스프링 부트의 기본 스프링 빈 컨테이너 구현체는 ConfigurationApplicationConetext 이며 설정 포맷은 자바클래스이다.

 

2. 지정된 클래스 패스에 위치한 클래스들을 스캔하고, 스프링 빈 정의가 있으면 로딩.

3. 로딩 끝낸 후 스스프링 빈 컨테이너는 정의된 대로 스프링 빈으로 생성하고 컨테이너에서 관리.

4. 스프링 빈들 사이에 서로 의존성이 있는 객체들은 스프링 빈 컨테이너가 조립힌다.

5. 스프링 빈 컨테이너 구현 클래스에 따른 추가 작업

6. 애플리케이션 실행 준비 완료.

 

 

 

스프링 빈 분류

1. 스프링 프레임워크의 기능을 스프링 빈으로 정의한 것들.

    - org.springframework.core.env.Environment

    - org.springframework.context.ApplicationContext 등

    이 빈들은 개발자의 개입 없이 생성되는 것들.

 

2. 스프링 빈 컨테이너가 로딩하는 설정파일에 정의된 것들.

    - @Bean 애너테이션을 사용하여 정의, 개발자가 스프링 프레임 워크의 기능을 설정하거나 애플리케이션에서 공통으로 사용하는 스프링 빈 들을 설정하는데 주로 활용.

 

3. 스프링 빈 컨테이너가 설정된 패키지 경로를 스캔한 후 스프링 빈으로 정의되어 생성되는 스프링 빈들.

    - 스테레오 타입 애너테이션들을 사용하여 정의한다.

 

 

 

이렇게 생성된 스프링 빈들은 서로 의존성을 가질 수 있으며, 모든 스프링 빈 객체를 관리하는 스프링 빈 컨테이너가 설정에 따라 의존성을 주입할 수 있다.

 

 

 

 

스프링 빈 특징

- 스프링 빈 컨테이너가 관리하는 순수 자바(POJO) 객체이다.

- 스프링 빈 컨테이너가 스프링 빈을 생성, 주입, 종료까지 관리한다. ( 스프링 빈 생명주기 관리 )

- 스프링 빈이 다른 스프링 빈을 참조할 수 있다. 

- 스프링 빈의 이름이나 클래스 타입 정보를 사용하여 스프링 빈 컨테이너에서 적절한 빈객체를 찾을 수 있고, 다른 스프링 빈 객체의 맴버 변수, 정해진 메서드의 인수로도 넣어줄 수 있다.(DI)

- 스프링 빈은 이름, 클래스 타입, 객체로 구성된다. ( 같은 클래스 타입이라도 이름이 다르다면 다른 스프링 빈 ) -> 클래스 타입은 같지만 이름이 다른 여러 스프링 빈이 컨테이너에 있을 수 있다.

 

 

 

 

 

 

스프링 빈 정의 방법

1. 자바 설정 클래스에서 @Bean 애너테이션을 사용하여 정의
2. 스테레오 타입 애너테이션을 사용하여 정의
3. BeanDefinition 인터페이스를 구현하여 정의
4. XML 설정 방식을 사용하는 방법

 

1. Java Configuration

자바 클래스를 기반으로 애플리케이션을 설정하기 위해 애너테이션 제공

@Bean, @ComponentScan, @Configuration, @Import

 

1.1. @Bean 애너테이션

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})	--- 1
@Retention(RetentionPolicy.RUNTIME) 	--- 2
@Documented
public @interface Bean {

	String[] value() default {};	--- 3
	String[] name() default {}; 	--- 3

	@Deprecated
	Autowire autowire() default Autowire.NO;

	boolean autowireCandidate() default true;
	String initMethod() default "";
	String destroyMethod() default AbstractBeanDefinition.INFER_METHOD;

}

 

1. @Bean 애너테이션을 정의할 수 있는 타깃은 메서드, 애너테이션

2. 런타임 시점까지 @Bean애너테이션이 코드에 존재한다.

3. @Bean 애너테이션은 value 또는 name 속성을 가질 수 있으며, 서로 참조하므로 둘중 하나를 설정해도 기능은 똑같이 동작.

 

public class Test {

	@Bean(name="testObj")
	public TestObj testObj() {
		return new TestObj();
	}
    
	@Bean
	public TestObj noNameTestObj() {
		return new TestObj();
	}
}

- @Bean 에 name or value 값 설정시 해당 이름으로 빈등록, 미설정 시 메소드명으로 등록

 

 

 

 

1.2. @Configuration

 

- 가장 많이 사용하는 애너테이션

- 내부에 @Bean 으로 선언된 메서드 들을 포함할 수 있음.

@Configuration
public class ThreadPoolConfig {
	@Bean
	public ThreadPoolTaskExecutor threadPoolTaskExecutor() {
		...
	}
}

 

 

 

 

1.3. @ComponentScan

 

- 패키지와 하위 패키지 경로에 포함된 자바 설정 클래스들과 스트레오 타입 애너테이션들이 선언된 클래스들을 스캔한다.

@Configuration
@ComponentScan(
	basePackages = {"com.spring.toy"},
	excludeFilters = { 
		@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) 
	}
)
public class BootApplication {
	...
    
}

 

@ComponentScan은 자바 설정 클래스에 정의되어야 함. 따라서 @Configuration과 함께 사용.

 

 

 

 

1.4. @Import

@Configuration
@Import(value = {ThreadPoolConfig.class})
public class BootConfig{
	...
}

 

@ComponentScan과 달리 대상 자바 클래스를 명시적으로 지정.

 

 

 

 

2. 스테레오 타입 스프링 빈

스테레오 타입의 애너테이션들은 @ComponentScan으로 스캔된다.

 

@Component, @Controller, @Service, @Repository 애너테이션이 있다.

 

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {

	@AliasFor(annotation = Component.class)
	String value() default "";

}

----------------------------------------------

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Indexed
public @interface Component {

	String value() default "";

}

 

어차피 기능은 Component와 같음, 클래스 목적이나 패턴에 따라 명확하게 정의해서 사용하기 위해 분리.

 

 

 

 


1. 스프링 빈 ( Spring Bean )

- 스프링 빈은 객체와 이름, 클래스 타입의 정보가 스프링 컨테이너로 관리되는 객체를 의미한다.

 

2. 자바 빈 ( Java Bean )

- 자바 빈은 기본 생성자가 선언되어 있어야 함.

- getter / setter 패턴으로 클래스 내부 속성에 접근할 수 있어야 한다.

- java.io.Serializable을 구현하고 있어야한다.

 

3. DTO ( Data Transfer Object )

- DTO는 소프트 웨어 사이에 데이터를 전달하는 객체를 의미한다.

- 데이터를 전달하는 객체이므로 DTO내부에는 비즈니스 로직이 없어야한다. ( getter 제외 )

 

4. 값 객체 ( Value Object, VO )

- 값 객체는 특정 데이터를 추상화 하여 데이터를 표현하는 객체를 의미한다.

- equals 메서드를 재정의해서 클래스가 표현하는 값을 서로 비교하면 좋다.

 

 

불변클래스

getter, setter 메서드를 무분별하게 사용하지 않도록 주의해야한다.

 

불변 클래스를 선언하는 방법은

- 클래스를 반드시 final로 선언한다.

- 클래스의 맴버변수들을 반드시 final로 선언한다.

- 생성자를 직접 선언하여 기본생성자가 있지 않도록 한다.

- 맴버 변수에서는 setter 메서드를 만들지 않고 getter메서드를 만들어 사용한다.

 

 

불변 클래스 예시) Money

public final class Money implements Serializable {
    private final Long value;
    private final Currency currency;

    public Money(Long value, Currency currency) {
        if (value == null || value < 0) {
            throw new IllegalArgumentException("invalid value = " + value);
        }

        if (currency == null) {
            throw new IllegalArgumentException("invalid currency");
        }
        
        this.value = value;
        this.currency = currency;
    }

    public Long getValue() {
        return value;
    }

    public Currency getCurrency() {
        return currency;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Money money = (Money) o;

        if (!getValue().equals(money.getValue())) return false;
        return getCurrency().equals(money.getCurrency());
    }

}

 

불변 클래스를 설계할 떄는 반드시 final 키워드를 클래스에 선언해야 한다.

final 키워드를 선언하지 않으면 클래스 상속이 발생할 수 있고 Money의 메서드들이 오버라이드 될 수 있다.