케네스로그

[스프링] 스프링의 빈, 컨테이너, 메이븐&그래들 본문

Dev/스프링

[스프링] 스프링의 빈, 컨테이너, 메이븐&그래들

kenasdev 2022. 4. 11. 23:57
반응형

 

스프링이란?

스프링(spring)은 보통 스프링 프레임워크를 뜻하며, 이 프레임워크는 아래의 특징을 지닌다.

  • Dependency Injection
  • Aspect-Oriented Programming
  • MVC 웹 프레임워크
  • JDBC, JPA연동, 선언적 트랜잭션 처리 등 DB 연동 지원

 

핵심 기능들은 위와 같으나 스케쥴링, 메시지 연동, 이메일 발송, 테스트 등과 같은 추가 기능 또한 존재한다. 실제로 스프링 프레임워크 뿐 만 아니라 스프링 데이터, 스프링 시큐리티, 스프링 배치, 스프링 인티그레이션, 스프링 하둡 등 다양한 프레임워크들이 존재하며, 스프링과 함께 사용된다.

 

 

프로젝트 구성 도구

스프링 프레임워크에는 다양한 모듈이 존재하며, 핵심 모듈을 포함하여 경우에 따라 추가적으로 모듈을 설치해서 사용해야 한다. 우리가 Java에서 Math 라이브러리를 통해 수학 관련 메소드들을 사용하듯, 스프링에서 특정 기능을 사용하기 위해서는 해당 기능을 포함하는 모듈(라이브러리)를 설치 및 추가하여 사용해야 한다.

 

 

 

주요 모듈

spring-core spring-beans spring-context spring-aop spring-webmvc spring-jdbc spring-tx

 

 

프로젝트 관리 도구

스프링에서 사용되는 프로젝트 관리 도구(빌드 도구)는 크게 메이븐(maven)과 그래이들(gradle)이 있다. 프로젝트에 필요로 되는 모듈들은 Apache의 Maven Central Repository에서 제공된다. 

 

 

  • 의존 설정
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-context</artifactId>
	<version>5.0.2.RELEASE</version>
<dependency>

위의 코드는 메이븐에서 xml파일을 통해 의존 설정을 하는 예시이다.

 

 

메이븐은 모듈을 아티팩트(artifact)라는 단위로 관리하며, 위와 같은 방식으로 해당 아티팩트가 프로젝트에 요구되어짐을 명시한다.

이를 영어로 의존(dependency)라고 표현하는 것이다.

‘의존 추가'라는 말은, 자바 어플리케이션의 클래스패스에 특정 모듈을 추가한다는 것이다.

자바 프로젝트가 컴파일될 때, 메이븐은 각 아티팩트(모듈)의 .jar 파일을 클래스패스에 추가한다.

 

 

 

  • Maven Repository

메이븐, 그래이들 같은 빌드 도구들은 명시된 모듈(아티팩트)들을 Maven Centrl Repository라는 원격 저장소에서 다운로드받고, 로컬에 저장하여 프로젝트에 사용되도록 한다. 

 

 

 

  • 의존 전이 Transitive Dependencies

특정 모듈을 사용하기 위해서 다른 모듈을 필요로 하는 경우가 있다. 이런 경우, 의존이 전이(transition dependency)되었다고 표현한다. 즉, A를 사용하기 위해서는 B,C가 있어야 한다는 말이다. 예시로, spring-context는 spring-aop, spring-beans와 같은 다른 아티팩트가 있어야 사용할 수 있다. 이런 경우, 메이븐은 해당 아티팩트 뿐 만 아니라 필요로 하는 다른 아티팩트도 함께 다운로드 한다.

 

Maven vs Gradle
Maven, Gradle 둘 모두 필요한 라이브러리를 가져오고 관리해준다. 요새는 Gradle을 주로 사용하며, 이전 레거시 프로젝트는 Maven인 경우도 있다고 한다.

 

 

 

빈과 컨테이너

스프링은 객체(bean)를 생성하고 초기화하는 기능을 갖고 있으며, 생성된 객체를 컨테이너를 통해 관리한다.

 

public class Greeter {
	private String name;

	public String greet(String guest) {
			return "Hello, "+this.name;
	}

	public void setName(String name) {
		this.name = name;
	}
}

위의 Greeter클래스는 name이라는 멤버변수를 갖고 있으며, setter를 통해 설정된다. 또한, greet()메소드를 통해 name을 포함하는 인사말 문자열을 리턴한다.

 

 

빈 Bean, 설정 클래스 AppContext

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppContext {
	@Bean
	public Greeter greeter() {
		Greeter g = new Greeter();
		g.setName("kenneth");
		return g;
	}
}
  • @Configuration 애노테이션은 해당 클래스를 스프링 설정 클래스로 지정한다.
  • @Bean 애노테이션은 해당 메소드가 생성한 객체를 스프링이 관리하는 빈(Bean)으로 등록한다.

 

 

컨테이너 AnnotationConfigApplicationContext

import org.springframework.context.annotation.AnnotationConfigApplicatrionContext;

public class Main {
	public static void main(String[] args) {
		AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppContext.class);
		Greeter g = ac.getBean("greeter", Greeter.class);
		String message = g.greet("kenneth");
		System.out.println(message); // Hello, kenneth
		ac.close();
	}
}
  • AnnotationConfigApplicationContext클래스는 빈 객체를 생성하고 관리한다.
  • AnnotationConfigApplicationContext객체를 생성할 땐 스프링 설정 클래스를 인자로 전달하며, 생성된 객체는 등록된 Bean을 조회하고 생성한다.
  • getBean()은 AnnotationConfigApplicationContext가 생성한 빈을 검색하며, 인자로 빈(Bean)의 이름 과 타입(class)를 받는다.

 

 

스프링의 핵심 기능은 객체를 생성하고 관리하는 것이다. 이것을 실질적으로 담당하는 클래스는 AnnotationConfigApplicationContext이며 자바 클래스에서 정보를 읽어와 객체를 생성하고 초기화한다. ApplicationContext 인터페이스에 관련 기능이 정의되어 있으며, 위의 클래스는 해당 인터페이스를 구현한 클래스 중 하나이다.

스프링의 핵심 기능들은 여러 인터페이스와 클래스들의 계층적 조합으로 구성되어 있다. 이러한 인터페이스들의 구현체가 컨테이너로 역할을 수행하며, Bean과 같은 객체들을 관리하는 것이다.

 

 

 

AnnotationConfigApplicationContext 클래스 계층도

 

위의 계층도처럼 여러 인터페이스와 클래스를 상속받아 구현한 객체(컨테이너) 중 하나가 AnnotationConfigApplicationContext이다. 약간만 설명을 하자면, 최상위 BeanFactory 인터페이스는 객체 생성과 검색에 대한 기능을 정의하며, 싱그톤/프로토타입 빈인지 확인하는 기능을 제공한다. ApplicationContext 인터페이스는 메시지, 프로필/환경 변수 등을 처리할 수 있는 기능을 추가로 정의한다. 이처럼 각 인터페이스와 클래스는 고유의 기능을 명시한다.

 

 

 

최종적으로 구현된 컨테이너 객체는 다음과 같다

  • AnnotationConfigApplicationContext: 자바 애노테이션을 이용한 클래스로부터 빈(Bean) 정보를 가져온다.
  • GenericXmlApplicationContext: XML로부터 빈(Bean)정보를 가져온다.
  • GenericGroovyApplicationContext: 그루비 코드를 이용해 빈(Bean)정보를 가져온다.

 

 

컨테이너와 싱글톤

기본적으로 스프링은 한 개의 빈 객체만을 생성한다. 이 빈 객체는 싱글톤의 범위를 가지며, 다른 범위로는 프로토타입 등이 있다.

  • 싱글톤 패턴을 이용한 객체 생성
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppContext.class);
Greeter g1 = ac.getBean("greeter", Greeter.class);
Greeter g2 = ac.getBean("greeter", Greeter.class);

System.out.println(g1==g2);
System.out.println(g1.equals(g2));
System.out.println(g1);
System.out.println(g2);

String msg = g1.greeting();
System.out.println(msg);
ac.close();

getBean()메소드를 통해서 g1, g2 두개의 Greeter객체를 생성했다. 앞서 말했듯, 스프링에서는 기본 빈 스코프로 싱글톤을 지원한다. 따라서, 둘을 비교하면 true값을 확인할 수 있고, 둘의 레퍼런스가 같다.

 

 

  • 일반적인 객체 생성

일반적으로, new 키워드를 통해 객체를 생성하면 메모리에 각자 할당되기 때문에 서로 다른 레퍼런스를 가지게 된다. 아래는 싱글콘이 아닌 new키워드를 통한 객체 생성의 예시이다. 둘을 비교하면 서로 다른 레퍼런스를 참조할 수 있다.

Greeter g1 = new Greeter();
Greeter g2 = new Greeter();

System.out.println(g1);
System.out.println(g2);
System.out.println(g1.equals(g2));
System.out.println(g1==g2);

💡 스프링에서 싱글톤을 지원하는 이유
스프링에서 싱글톤을 지원하지 않는다면, 어플리케이션에서 요청이 발생할때마다 새로운 객체를 생성해서 응답해야한다.

예를 들어, 카페에서 음료를 제공하는 알바생이 존재한다. 음료 주문이 들어올때마다 새로운 알바생을 뽑는다면 매우 비효율적인 상황이 될것이다. 카페에서 음료 제공(service)을 담당하는 알바생(bean)을 오직 한명만 두어 손님(client)의 요청(request)를 처리하도록 한다. 
참조: https://stackoverflow.com/questions/21828664/why-spring-bean-is-singleton-scope

 

 

 

Spring 포스팅 시리즈는 "초보 웹 개발자를 위한 스프링5 프로그래밍 입문(최범균 저)"을 독학하며 정리한 글입니다. 책 내용을 기반으로 정리하며, 개인적으로 궁금한점을 첨언하거나 소스코드를 더 이해하기 쉽게 작성합니다.

잘못된 내용이 있다면 언제든 피드백 부탁드립니다🙏

반응형