[20240122 TIL] Spring Container와 Spring Framework

Haizel·2024년 1월 22일
1
post-thumbnail

01. Spring Container


✔️ Spring Container 란?

Spring Container는 스프링에서 자바 객체를 관리하는 공간을 말한다.

  • 스프링에선 자바 객체를 빈(Bean) 이라고 한다.
  • 즉 스프링 컨테이너가 개발자 대신해 빈의 생성부터 소멸까지, Bean의 수명 주기를 관리하는 공간이라고 할 수 있다.
  • Container는 Spring Context, 또는 IoC Container라고 불리기도 한다. (Spring Container == Spring Context == Spring IoC Container)
    - IoC(Inversion of Control) : 제어의 역전

✔️ Spring Container의 종류

Spring Container는 크게 두 종류로 나눌 수 있다.

BeanFactory
ApplicationContext

OoPiAge.png

❶ BeanFactory

  • 스프링 컨테이너의 최상위 인터페이스로, Spring Bean을 관리하고 조회하는 역할을 담당한다.
  • .getBean() 메서드 등 다양한 Bean 관련 메소드를 제공한다.

❷ ApplicationContext

  • BeanFactory 기능을 모두 상속 받아 제공한다.
  • Spring Bean을 관리하고 조회하는 기능 외에 App 개발에 필요한 편리한 부가 기능들을 제공한다.
  • 사실, BeanFactory는 거의 사용하지 않고 ApplicationContext를 사용하기 때문에 ApplicationContext를 스프링 컨테이너라 한다.
  • 자바 설정 클래스를 기반으로 스프링 컨테이너를 만들 수 있다.
    - new AnnotationConfigApplicationContext(AppConfig.class)ApplicationContext 인터페이스의 구현체

💡 ApplicationContext가 제공하는 부가 기능 (인터페이스)

  • 메시지 소스를 활용한 국제화기능 (MessageSource)
  • 환경변수 (EnvironmentCapable)
  • 애플레케이션 이벤트 (ApplicationEventPublisher)
  • 편리한 리소스 조회(ResourceLoder)

02. Java Bean vs Spring Bean


① Java Bean(POJO, Plain Old Java Object)

  • 모든 자바 객체는 POJO로, 자바 객체를 만드는 오래된 방식을 뜻한다.
  • 주로 특정 자바 모델이나 기능, 프레임워크를 따르지 않는 Java Object를 지칭한다.
  • 대표적으로 Java Bean가 있다.
class pojo {
	private String text;

	private int number;

	public String toString() {
		return text + ":" + number;
	}
}

✔️ Java Bean 제한 사항

  1. public no-arg 생성자
  • java는 기본적으로 생성자 생성 시 no-arg를 기본으로 한다.
class JavaBean {
	private int number;

	//1. no-arg 생성자 (자동 설정으로 생략 가능)
	public JavaBean() {
	}
}
  1. getters와 setters
class JavaBean {
	private int number;

	//1. no-arg 생성자 (자동 설정으로 생략 가능)
	public JavaBean() {
	}

	//2. getters and setters
	public int getNumber() {
		return number;
	}
	public int setNumber(int number) {
		this.number = number;
	}
}
  1. 직렬화 가능(Serializable)
  • Serializable는 인터페이스로, 해당 클래스가 직렬화가 가능해야 한다면 직렬화 인터페이스를 구현하면 된다.
class JavaBean implements Serializable {
	private int number;

	//1. no-arg 생성자 (자동 설정으로 생략 가능)
	public JavaBean() {
	}

	//2. getters and setters
	public int getNumber() {
		return number;
	}
	public int setNumber(int number) {
		this.number = number;
	}
}

② Spring Bean

빈(Bean) : 스프링 컨테이너에 의해 관리되는 재사용 가능한 소프트웨어 컴포넌트이다. 

  • Bean은 인스턴스화된 객체를 의미하며, 스프링 컨테이너에 등록되어 스프링 컨테이너에 의해 관리되는 객체를 Spring Bean이라고 한다.
  • 주로 new 키워드 대신 사용한다.

❓ 스프링 빈(Bean)을 사용하는 이유

  • 가장 큰 이유는 스프링 간 객체가 의존관계를 관리하도록 하는 것에 가장 큰 목적이 있다. 
  • 객체가 의존관계를 등록할 때 스프링 컨테이너에서 해당하는 Bean을 찾고, 그 Bean과 의존성을 만든다.

 

✔️ 스프링 빈(Bean) 등록 방법

대표적인 Spring Bean을 등록하는 방법으론 3가지가 있다.

  1. xml에 직접 등록
  2. @Bean 어노테이션을 이용
  3. @Component, @Controller, @Service, @Repository 어노테이션을 이용

1. XML에 직접 등록하는 방법

<bean id="helloService" class="com.example.myapp.di.HelloService"/>

<bean id="helloController" class="com.example.myapp.di.HelloController" p:helloService-ref="helloService">		
</bean>
  • bean 태그를 사용한다.
  • application-config.xml 을 resource 아래에 만들어 준다.

2. @Bean 어노테이션을 이용

인스턴스를 반환하고 Bean에 등록하는 코드

package com.example.myapp.di;

import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ImportResource;

@Configurable
@ComponentScan(basePackages= {"com.example.myapp"})
@ImportResource(value= {"classpath:application-config.xml"})
public class AppConfig {
		@Bean
		public IHelloService helloService() {
			return new HelloService();
		}
		
		@Bean
		public HelloController helloController() {
			HelloController controller = new HelloController();
			controller.setHelloService(helloService());
			return controller;
		}
}
  • 메서드 위에 @Bean를 사용하고 AppConfig 객체 위에 @Configurable, @ComponentScan, @ImportResource 어노테이션을 선언해준다. 

이때 어노테이션을 사용하기 위해선 꼭 application-xml에서 context를 추가해주어야 한다!

package com.example.myapp.di;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;

public class HelloMain {

	public static void main(String[] args) {
		AbstractApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); 

		System.out.println("-----------------------");
		HelloController controller = context.getBean("helloController", HelloController.class);
		controller.hello("홍길동");
		System.out.println("=====================");
		context.close();
		}
}
  • 그리고 메인에서 Annotation을 사용하기 위해AnnotationConfigApplicationContext 를 사용한다.

3. @Component, @Controller, @Service, @Repository 어노테이션을 이용

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:p="http://www.springframework.org/schema/p"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">
	
	<context:component-scan base-package="com.example.myapp.hr"/>

</beans>

application-config.xml 파일

  • 여기서 namespace에서 context 추가하고 아래에 context를 추가해준다. 
package com.example.myapp.hr;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;


@Controller
public class EmpController {
	
	private IEmpService empService;
	
	@Autowired
	public EmpController(IEmpService empService) {
		this.empService = empService;
	}
	
	void printInfo() {
		int count = empService.getEmpCount(50);
		System.out.println("사원의 수 : " + count);
	}
}

👆 @Controller@Autowired를 이용해 의존성 주입까지 한 코드이다.


03. Annotation(어노테이션)


주석은 개발자가 좀 더 직관적으로 쉽게 코드를 이해할 수 있게 해주며, 다른 사람에게 설명할 수 있도록 정보를 제공하는 역할을 한다.

✷ Annotation(어노테이션)이란?

Annotation(어노테이션) 또한 사전적 의미로는 주석을 의미하긴 하지만, 자바에서 Annotation(@)은 코드와 코드 사이에 특별한 의미, 기능을 수행하도록 하는 역할을 한다. 프로그램 코드의 일부가 아닌 프로그램에 관한 데이터를 제공하고, 코드에 정보를 추가하는 정형화된 방법이다.

주석과 비교해보자면, 주석은 사람에게 정보를 제공한다면 어노테이션은 특정 코드를 사용하는 프로그램에게 정보를 전달한다.

💡 어노테이션은 @을 사용해 작성하며, 해당 타겟에 대한 동작을 수행하는 프로그램 외에는 다름 프로그램에게 영향을 주지 않는다.

✔️ Annotation 역할

  • 컴파일러에게 문법 에러를 체크하도록 정보를 제공한다.
  • 프로그램을 빌드할 때 코드를 자동으로 생성할 수 있도록 정보를 제공한다.
  • 런타임에 특정 기능을 실행하도록 정보를 제공한다.

✔️ Annotation 사용 순서

어노테이션을 사용하기 위한 순서는 아래와 같다.
1. 어노테이션을 정의한다.
2. 클래스에 어노테이션을 배치한다.
3. 코드가 실행되는 중에 Reflection을 이용해 추가 정보를 획득하고 기능을 실시한다.


✷ Annotation(어노테이션)의 종류

어노테이션은 크게 세 가지로 구분된다.
자바에서 기본적으로 제공되는 표준 어노테이션과 어노테이션을 정의하는데 사용되는 메타 어노테이션, 마지막으로 사용자 어노테이션이 있다.

① @SpringBootApplication

SpringBoot를 자동으로 실행시켜주는 어노이테이션으로, Bean 등록은 두 단계로 진행된다.
1. @ComponentScan을 통해 Component들을 Bean으로 등록한다.
2. @EnableAutoConfiguration을 통해 미리 정의해둔 자바 설정 파일들을 Bean으로 등록한다.

  • Bean : 스프링 IoC 컨테이너에 의해 인스턴스화되어 조립되거나 관리되는 객체

② @Component

  • 개발자가 직접 작성한 Class를 Bean으로 등록하기 위한 어노이테이션이다.
  • @ComponentScan 선언에 의해 특정 패키지 안의 클래스들을 자동으로 스캔하여 @Component 어노테이션이 있는 클래스들에 대해 Bean 인스턴스를 생성한다.
@Component
public class Student {
	public Stdudent() {
    	System.out.println("hello");
    }
}

@Component(value="mystudent")
public class Student {
	public Stdudent() {
    	System.out.println("hello");
    }
}
  • Component에 대한 추가 정보가 없다면 Class의 이름을 camelCase로 변경하여 Bean id로 사용한다.
  • 하지만 @Bean과 달리 @Component는 name이 아닌 value를 이용해 Bean 이름을 지정 한다.

③ @Bean

@Bean은 개발자가 직접 제어 불가능한 외부 라이브러리 등을 Bean으로 만들려할 때 사용한다.

@Configuration
public class ApplicationConfig {
	@Bean
    public ArrayList<String> array() {
    	return new ArrayList<String>();
    }
}
  • 위 코드와 같이 ArrayList같은 라이브러리 등을 Bean으로 등록하기 위해선 별도로 해당 라이브러리 객체를 반환하는 Method를 만들고 @Bean을 사용하면 된다.
  • @Bean에 아무런 값을 지정하지 않았으므로 Bean id 값으로 → Method 이름을 CamelCase로 변경한 문자열로 등록된다.
    - 즉, 위 코드에선 arrayList가 Bean id가 된다.
@Configuration
public class ApplicationConfig {
	@Bean(name="myarray")
    public ArrayList<String> array() {
    	return new ArrayList<String>();
    }
}
  • 만약 별도의 Bean id를 등록하고 싶다면, 위와 같이 name값을 이용하면 원하는 id로 Bean을 등록할 수 있다.

✔️ Bean을 주입받는 방식 3가지

  1. 생성자 기반
    • 생성자 주입을 사용하면 @Autowired 어노테이션을 작성하지 않아도 자동으로 와이어링된다. 👈 생성자 주입의 가장 큰 장점!
    • 스프링에서는 생성자를 통한 의존성 주입 방식을 권장한다.
      - 생성자는 단 하나의 생성자 메소드만으로 초기화가 모두 이뤄지는 @AllArgsConstructor를 사용하기 때문이다.
      - 초기화가 되면 빈을 사용할 수 있게 된다.
      - 즉 생성자를 통한 의존성 주입 방식은 되는 가장 간단한 방법이다!
class BurgerChef {
    private BurgerRecipe burgerRecipe;

    public BurgerChef(BurgerRecipe burgerRecipe) {
        this.burgerRecipe = burgerRecipe;
    }
}

class BurgerRestaurantOwner {
    private BurgerChef burgerChef = new BurgerChef(new HamburgerRecipe());

    public void changeMenu() {
        burgerChef = new BurgerChef(new CheeseBurgerRecipe());
    }
}
  1. setter(수정자) 기반
class BurgerChef {
    private BurgerRecipe burgerRecipe = new HamburgerRecipe();

    public void setBurgerRecipe(BurgerRecipe burgerRecipe) {
        this.burgerRecipe = burgerRecipe;
    }
}
  1. Field 기반 → @Autowired 명시

💡 @Bean Vs @Component 차이는 ?

👉 Bean은 외부 라이브러리가 제공 하는 객체를 사용할 때 활용하고, Component내부에서 직접 접근 가능 한 클래스를 사용할 때 활용한다.

Heading@Component@Bean
Where?모든 자바클래스에 사용특정 메소드 위에만 사용(보통 스프링 설정 클래스에 이용)
Eese of useSuper Easy(코드 앞에 어노테이션 추가)조금 복잡(Bean 생성을 위해 코드 전체 작성 필요)
Autowiring생성자/수정자(setter)/필드 주입 방식 중 선택 가능자동 와이어링을 위해선 특정 메서드 호출 필요
Who Creates beans?스프링 프레임워크가 생성개발자가 생성(Bean 생성을 위한 코드 작성)
Recommended For대부분의 경우외부라이브러리가 제공하거나 빈 생성 전에 자체적인 작업 로직이 많이 사용되는 경우

👉 이 외에도 선언 위치나 자세한 사용법의 차이는 해당 블로그를 참조하자.


④ @Configuration

스프링 IoC 컨테이너에게 해당 클래스가 Bean 구성 Class임을 알려주는 어노테이션이다.
@Bean을 해당 클래스의 메소드에 적용하면 @AutoWired로 Bean을 부를 수 있다.


⑤ @ComponentScan

@Component, @Service, @Repository, @Controller, @Configuration이 붙은 빈들을 찾아서 Context에 Bean을 등록해 주는 어노테이션이다.
@Component 어노테이션이 있는 클래스에 대해 Bean 인스턴스를 생성한다.


@Component 어노테이션을 다 붙이면 되는거 아닌가?

Spring에서 @Component로 다 쓰지 않고 @Repository, @Controller 등을 사용하는 이유는 무엇일까?

예를 들어 @Repository는 DAO의 메소드에서 발생할 수 있는 unchecked exception들을 스프링의 DataAccessException으로 처리할 수 있기 때문이다.
+ 또한 가독성면에서도 해당 어노테이션이 갖는 클래스가 무엇을 하는지 단 번에 알 수 있다는 장점이 있다.

➕ Add !

  • @Named : @Component의 대안
  • @Injects : @Autowired의 대안

04. 의존성 주입(DI, Dependency Injection)의 3가지 방법


Spring Boot에서 어노테이션을 통해 자동으로 Bean을 컨테이너에 설정하는 경우, 같은 인터페이스의 구현체 클래스 두 개 이상이 Bean으로 등록되면 아래와 같은 오류가 발생한다.

  • 1개의 bean만이 매칭되어야 하는데, 2개 이상의 bean이 존재한다는 오류

이럴 경우 Spring Boot가 어떤 Bean을 주입해야 하는지 알려주어야 하는데(의존성 주입), 그 방법으로는 크게 3가지가 있다.

@Autowired된 field의 이름을 Bean 이름으로 설정하는 방법
@Primary 어노테이션을 사용하는 방법
@Qualifier 어노테이션을 사용하는 방법


⓿ Bean과 구현체 예시

  • 동물 Interface와 그걸 구현한 개와 고양이를 구현 클래스로 만들어 → Bean을 등록하고, AnimalService 클래스 생성자에 주입되는 코드
  • ⛔️ 타입이 갖는 빈이 두개 이상이므로 현재 오류가 발생한다.
public interface Animal {
	String sound();
}

@Component
public class Dog implements Animal {
	public String sound() {
		return "왈왈";
	}
}
@Component
public class Cat implements Animal {
	public String sound() {
		return "야옹";
	}
}

@Service
public class AnimalService {
	private final Animal animal;
	@Autowired
	public AnimalService(Animal animal) {
			this.animal = animal;
	}
}

❶ @Autowired

필드, setter 메서드, 생성자에 사용하며, Type에 따라 알아서 Bean을 주입해주는 역할을 한다. 즉 @Autowired 사용을 통해 자동으로 객체에 대한 의존성을 주입시킬 수 있다.
Controller 클래스에서 DAO나 Service에 과한 객체들을 주입시킬 때 많이 사용한다.

❗️스프링에서는 @Autowired를 사용해 의존성을 주입한다.


이처럼 스프링에서는 @Autowired이 Type을 확인한 후 알아서 Bean을 주입해주는 역할을 하고 있다. 그런데 만약 동일한 타입을 가진 Bean 객체가 두 개나 있다면 어떻게 될까?


✔️ 동일 타입의 Bean 객체가 한 개 이상이라면?

  • 스프링은 어떤 Bean을 주입해야 하는지 알 수 없기 때문에(판단할 수 없음) 스프링 컨테이너를 초기화하는 과정에서 Exception을 발생시킨다.
    - @Autowired의 주입 대상이 한 개여야 하는데, 만약 두 개 이상의 빈이 존재한다면, 어떤 빈을 선택해 주입해야 할지 알 수 없기 때문이다.
    - 단, @Autowired가 적용된 필드나 설정 메서드의 proterty의 이름과 같은 이름을 가진 빈 객체가 존재할 경우엔 → 이름과 같은 빈 객체를 주입 받는다.

❷ Primary

가장 간단한 방법으로는 같은 타입의 여러 Bean이 있을 때 기본적으로 선택될 Bean에 @Primary 어노테이션을 붙여주면 자동적으로 해당 빈이 선택된다.

주입받을 때보다 모든 코드에 모든 코드에 @Qualifier 어노테이션을 붙여주는 것보다 훨씬 간단한 방법이다.

@Component
@Primary
public class Dog implements Animal {
	public String sound() {
		return "왈왈";
	}
}
@Component
public class Cat implements Animal {
	public String sound() {
		return "야옹";
	}
}

@Service
public class AnimalService {
	private final Animal animal;
	@Autowired
	public AnimalService(Animal animal) {
			this.animal = animal;
	}
}

❸ Qualifier

@Qualifier 어노테이션Bean에 추가 구분자를 붙여주는 방법으로, 생성자에서 해당 구분자를 명시하면 그 구분자(한정자)를 가진 Bean을 주입해준다.

⛔️ Caution!

이때 @Qualifier 어노테이션을 통해 Bean의 이름을 변경하는 것이 아니라, Bean 이름은 변경되지 않고, 추가적인 구분자가 생기는 것이다.


✔️ @Qualifier 사용방법

  1. 설정에서 bean의 한정자 값을 설정한다.
  2. @Autowired 어노테이션이 주입된 대상에 @Qualifier 어노테이션을 설정한다.
    • 이때 @Qualifier의 값으로 앞서 설정한 구분자(한정자)를 사용한다.
@Component
@Qualifier("dogdog") 
public class Dog implements Animal { 
	public String sound() { 
		return "왈왈";
	 } 
 }
 
@Component
@Qualifier("catcat") 
public class Cat implements Animal {
	public String sound() {
		 return "야옹"; 
	 }
  }
  
@Service 
public class AnimalService { 
	private final Animal animal;
	
	@Autowired 
	public AnimalService(@Qualifier("dogdog") Animal animal) {
	 this.animal = animal;
  }
}
  • 이때 구분자 없이 @Qualifier 어노테이션을 사용할 순 있지만, 코드의 명확성아 떨어져 유지보수가 어려워질 수 있으니 Bean 이름과 @Qualifier는 별도로 관리하는 것이 좋다.
    - 만약 @Qualifier 에 구분자를 지정하지 않는다면, Bean id를 기반으로 camelCase로 변환된 name을 사용한다.

❗️ @Qualifier 사용 시 주의할 점

  • @Qualifier에 지정한 한정자 값을 갖는 bean 객체가 존재하지 않는다면 Exception이 발생한다.
    - (하지만 사실상 이 예외는 발생하기 힘들다. bean 객체를 만들지도 않았는데 Qualifier 처리를 할리 없으니까.)
  • 결과 : @Qualifier 에 해당하는 빈 객체를 찾지 못하기 때문에, 스프링 컨테이너를 생성하는데 실패한다.

✔️ 스프링 @Autowired 어노테이션 적용 시 의존 객체를 찾는 순서

  1. 타입이 같은 bean 객체를 검색한다.
    • 타입이 같은 bean 객체가 하나라면 : 해당 bean 객체를 사용한다.
    • 만약 @Qualifier가 명시되어 있는 경우, 같은 값을 갖는 bean객체여야 한다.
  2. 타입이 같은 bean 객체가 두 개 이상이고 && @Qualifier없는 경우
    • 이 경우, 이름이 같은 빈 객체를 찾아 해당 객체를 사용한다.
  3. 타입이 같은 bean 객체가 두 개 이상이고 && @Qualifier있는 경우
    • 이 경우, @Qualifier로 지정한 Bean 객체를 사용한다.
  4. 위 경우 모두 해당되지 않으면 컨테이너가 Exception을 발생시킨다.

✔️ 언제 @Primary vs @Qualifier 사용할까

👉 @Primary@Qualifier 둘 다 존재할 땐 @Qualifier이 우선이다.

  • 스프링은 기본적으로 자동보다 수동으로 지정한 것에 높은 우선 순위를 부여한다.
  • 따라서 자동적으로 Bean을 선택해주는 @Primary보다 명시적으로 지정하는 @Qualifier이 더 높은 우선 순위를 가진다.
  • @Primary는 같은 타입의 multiple beans이 존재할 때 → 이들의 선호도를 정의하여 하나의 구현체를 Default 값으로 사용한다. 따라서 기본값으로 주입되어야 하는 Bean을 특정하고 싶을 때 유용하다.

  • 반면 다른 Bean을 같은 injection point에서 사용해야 하는 순간도 있을 것이다. 이 경우엔 @Qualifier를 사용해 default로 주입된 bean이 아닌, 다른 틀정 Bean을 주입하도록 지정할 수 있다.

  • WHY? 👉 기본적으로 @Primary는 default값을 정의하는 반면, @Qualifier는 specific한 값을 정의하기 때문이다.

만약 메인 데이터베이스와 서브 데이터베이스가 있다 가정한다면,

  • 메인 데이터베이스 커넥션엔 → @Primary를 사용해 default 로 설정하고,
  • 서브 데이터베이스 커넥션엔 → @Qualifier로 특정go 명시적으로 의존성을 주입받아 사용할 수 있다.

05. Bean의 지연 초기화와 이른 초기화


Spring Bean의 기본 초기화는 이른 초기화방식이다.

@Lazy

  • 지연 로딩을 지원하는 어노테이션으로, 주로 @Component@Bean
    어노테이션이랑 같이 사용된다.
  • Class가 로드될 때 스프링에서 바로 bean 등록을 마치는 것이 아니라, 실제로 사용될 때 로딩되게 하는 방법이다.
@Component
class ClassA {
}

@Component
@Lazy
class ClassB {
	private ClassA classA;

	public ClassB(ClassA classA ){
		this.classA = class.A;
	}
}

💡 이른 초기화 vs 지연 초기화 무엇이 좋을까

결론부터 얘기하면, 이른 초기화를 추천한다.
👉 스프링 설정에 오류가 발생한 경우, 이른 초기화를 사용하면 애플리케이션 시작 시 즉시 발견된다.

Heading이른 초기화지연 초기화
Bean 초기화 시점애플리케이션 또는 스프링 컨텍스트의 시작 시어플리케이션에서 처음 사용될 때
DefaultdefaultNot Default
사용@Lazy(value=false) 또는 (Absence of @Lazy)@Lazy(value=true)
초기화 오류 발생 시오류로 인해 어플리케이션이 시작하지 않음런타임 예외
Usage자주매우 드믐
메모리 소모모든 bean이 시작 시 초기화되므로, 시작 시 바로 로딩됨빈이 초기화되기 전에는 소모 메모리가 상대적으로 적음
추천 상황거의 모든 Bean에 권장해당 bean이 애플리케이션에서 매우 드물게 사용되고, bean이 사용되는 특정 시점에 로딩하고 싶은 경우

06. Spring Bean 생성의 유효범위


✷ @Scrope 어노테이션

일반적으로 @Component, @Service, @Repository 등으로 자동 스캐닝한 Java Bean은 싱글톤 형태로 하나만 생성하는데, 이를 변경하기 위해 @Scope 어노테이션을 사용한다.

@Scope 어노테이션을 사용해 Bean의 유효 범위를 설정한다.

🗣️ 싱글톤 패턴

  • 클래스의 인스턴스가 오직 하나만 생성되는 것을 보장하는 디자인 패턴
  • Spring Container는 별도로 싱글톤 패턴을 적용하지 않아도, 객체 인스턴스를 싱글톤으로 관리한다.
  • 즉 Bean(객체)를 스프링 컨테이너에 등록하고 bean 조회 요청 시 → 새로 생성하지 않고 스프링 컨테이너에서 bean을 찾아 반환한다.

✔️ @Scrope 문법과 유효 범위

❗️ Important! - 싱글톤 유효 범위와 프로토타입 유효 범위

  • 스프링 IoC 컨테이너에 대해 동일한 인스턴스를 다시 사용(재사용)하고 싶다면 👉 싱글톤 유효 범위
  • 매번 새로운 인스턴스를 만들어 사용하고 싶다면 👉 프로토타입 유효 범위
@Component
@Scope("유효 범위") // 또는
@Scope(value=유효 범위) 
class Ojc {
	...
}
  1. singleton : IoC 컨테이너 당 하나의 bean 인스턴스 생성
  2. prototype : 요구가 있을 때마다 새로운 bean 인스턴스 생성
    • 즉 많은 객체 인스턴스가 스프링 IoC 컨테이너마다 생성될 수 있다.
  3. request : HTTP request 객체 당 하나의 bean 인스턴스 생성
  4. session : HTTP session 당 하나의 bean을 리턴
  5. globalSession : 전체 모든 세션에 대해 하나의 bean 인스턴스 생성
  6. Application : 전체 웹 애플리케이션 당 하나의 bean 인스턴스 생성
  7. Websocket : 웹 소켓 인스턴스 당 하나의 bean 인스턴스 생성

✔️ Java 싱글톤 == Spring 싱글톤?

  • 자바 싱글톤은 자바 가상 머신 당 1개의 객체 인스턴스를 의미한다.
    - 즉 전체 자바 가상 머신에서 오직 1개의 객체만 주어진다.
  • 스프링 싱글톤은 스프링 IoC 컨테이너당 1개의 객체 인스턴스를 의미한다.

💡 결론 !

Java 싱글톤과 Spring 싱글톤은 99% 일치하지만, 완전히 동일하다고 할 수는 없다.
⌙ 자바 가상 머신에서 여러 개의 스프링 IoC 컨테이너를 실행한다면 Java 싱글톤과 Spring 싱글톤은 다르게 된다.
⌙ 하지만 일반적으로 위와 같이 실행하진 않기 때문에 99% 동일하다고 한다.


07. PostConstruct 및 PreDestroy


① @PostConstruct

스프링은 자동 와이어링이 되자마자, @PostConstruct으로 어노테이션된 메소드를 호출한다.

  • @PostConstruct 어노테이션은 의존성 주입이 완료되어 초기화가 수행된 후 실행되어야 하는 메소드에서 사용된다.
  • 해당 메소드는 클래스가 사용되기 전에 호출되어야 하고, 다른 Bean이 이 특정 Bean을 사용하기 전에 호출된다.
    - 그리고 이때 애플리케이션이 시작되며 Bean이 로딩된다.
@PostConstruct
public void initialize() {
	someDependency.getReady();
}

② @PreDestroy

@PreDestroy는 애플리케이션이 종료되기 전, Bean이 컨텍스트에서 제거되기 전에 사용한다.

  • @PreDestroy로 어노테이션된 메소드는 일반적으로 보유하고 있던 리소스를 해체하는 데 사용된다.
@PreDestroy 
public void cleanup() {
	System.out.println("Cleanup");
}

08. 자바 스프링 XML 설정


✷ ClassPathXmlApplicationContext

✷ 스프링 XML 설정 파일 포맷

① 기본 포맷

Bean 등록과 설정 모두 XML에 작성할 경우 아래 포맷을 사용한다.

  • 네임 스페이스와 스키마 속성을 갖는 bean 태그 안에 스프링 bean 설정을 작성한다.
<?xml version="1.0" encoding="UTF-8"?>
	<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> 
</beans>

② 어노테이션 설정을 사용한 포맷

Bean 등록은 XML, Bean 설정은 어노테이션으로 할 경우 아래 포맷을 사용한다.

  • context 네임 스페이스와 <context:annotaion-cofig /> 코드를 추가하면 Bean 클래스의 어노테이션을 통해 Bean 설정을 할 수 있다.
<?xml version="1.0" encoding="UTF-8"?>
	<beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> 
		<context:annotation-config/>
	</beans>

✔️ XML vs 자바 어노테이션을 통한 XML 설정 비교

HeadingXML ConfigurationAnnotations
사용 편의성매우 쉬움번거로움
bean 인스턴스 생성 시 패키지 이름을 포함하여 클래스의 전체 이름을 지정해야함
특정 구문을 따라야 하고, 자동 와이어링 과정 등 구문이 다소 복잡함
Clean POJOsYesNo(POJO는 스프링 프레임워크애 대해 전혀 알지 못한다.)
쉬운 유지관리NoYes
사용 빈도극히 드믐자주 사용
디버깅MediumHard

❗️XML과 어노테이션 설정은 혼용하지 않는 것이 좋다.



📂 참고자료

profile
한입 크기로 베어먹는 개발지식 🍰

0개의 댓글

관련 채용 정보