가수면

[Spring] 기본 본문

Java

[Spring] 기본

니비앙 2023. 11. 29. 14:43

개념

Spring은 의존성 주입을 활용하여 객체의 생성과 의존성 관리를 자동화함으로써, 개발자가 객체 간의 결합도를 낮추고 유지 보수가 용이한 코드를 작성할 수 있게 해줌

 

1. Spring Container(= Spring context, Spring IOC Container)

스프링 Bean과 생명 주기를 관리

1-1) Bean Factory

기본 Spring Container

1-2) Application Context

엔터프라이즈 전용 기능을 제공하는 Spring Container

웹 애플리케이션을 구축하기 쉬움

국제화가 쉬움

Spring AOP, 스프링 측면 지향 프로그래밍과 통합하기 쉬움

 

2. Java Bean과 Spring Bean

각각 Java와 Spring에서 관리하는 객체

2-1) POJO

단순하고 표준적인 자바 객체

2-2) Java Bean

생성자에 인수가 없어야 함

게터와 세터가 필요

interface로 Serializable 설정

2-3) Spring Bean

스프링 프레임워크에서 관리하는 자바 객체

 

3. Dispatcher Servlet

프론트 컨트롤러 - 공통된 로직을 처리하기 위해 먼저 모든 요청을 받아 처리해 뿌려주는 컨트롤러

Dispatcher Servlet은 Spring MVC의 프론트 컨트롤러

maven과 eclipse IDE를 이용한 방법

https://start.spring.io/

 

프로젝트 파일 생성 후 이클립스에서 import

 

스프링 기본 방식

package com.in28minutes.learnspringframwork;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import com.in28minutes.learnspringframwork.game.GameRunner;
import com.in28minutes.learnspringframwork.game.MarioGame;
import com.in28minutes.learnspringframwork.game.PacmanGame;
import com.in28minutes.learnspringframwork.game.SuperContraGame;

public class App02HelloWorldSpring {

	public static void main(String[] args) {
		// 1. 스프링 context 실행
		var context = new AnnotationConfigApplicationContext(HelloWorldConfiguration.class); // JVM 내에 스프링 context 생성
		
		// 2. 스프링이 관리하도록 설정 (설정 클래스 세팅)
		//	HelloWorldConfiguration 클래스 생성
		//	name 이라는 Bean 생성
		
		// 3. 스프링의 name Bean 꺼내기
		System.out.println(context.getBean("name"));
		System.out.println(context.getBean(Address.class));
		
	}
}

 

Bean 활용

package com.in28minutes.learnspringframwork;

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

record Person (String name, int age, Address address) {}
record Address (String country, String city) {}

// 스프링 컨테이너를 만들기 위한 인풋
@Configuration	// 하나 이상의 Bean(스프링에서 관리하는 것들) 메서드를 선언해야 함
public class HelloWorldConfiguration {
	@Bean	// Bean을 호출
	public String name() {
		return "홍길동";
	}
	
	@Bean
	public int age() {
		return 20;
	}
    
	@Bean
	public Person person() {
		return  new Person("김철수", 12, new Address("대한민국", "서울시"));
	}
    
	@Bean
	public Person person2() {
		return  new Person(name(), age(), address());	// 메소드 호출도 가능
	}
    
	@Bean
	public Person person3(String name, int age, Address address2) {	// 파라미터를 이용한 방식
		return  new Person(name, age, address2);
	}
    
	@Bean(name = "address2") // Bean이름을 address2로 변경 (이렇게도 메소드 이름 수정 가능)
	public Address address() {
		return  new Address("대한민국", "서울시");
	}
}

중복되는 Bean을 사용하기 위한 방법

예를 들어 address2, address3처럼 Address라는 동일한 클래스를 사용하는 여러 개의 bean이 존재할 경우 Address 클래스를 사용하게되면 여러 개가 있다며 오류가 발생한다.

System.out.println(context.getBean(Address.class));

//
	
	@Bean(name = "address2")
	public Address address() {
		return  new Address("대한민국", "서울시");
	}
	
	@Bean(name = "address3")
	public Address address3() {
		return  new Address("대한민국", "남양주시");
	}

이런 경우 해결 방법

 

1. @Primary 설정

이 경우 @Primary를 설정해주면 해당 Bean을 디폴트값으로 가지게 되어 디폴트값을 사용하게 된다.

	@Bean
	public Person person4(String name, int age, Address address) {	// address라는 없는 Bean을 사용해도 Address 타입 지정으로 인해 Primary Bean을 사용
		return  new Person(name, age, address);
	}
	
	@Bean(name = "address2") // Bean 이름을 address2로 변경 (이렇게도 메소드 이름 수정 가능)
	@Primary
	public Address address() {
		return  new Address("대한민국", "서울시");
	}
	
	@Bean(name = "address3")
	public Address address3() {
		return  new Address("대한민국", "남양주시");
	}

Primary를 유지하면서 특정 한정자를 사용하고자 할 경우 @Qualifier를 사용

	@Bean
	public Person person5(String name, int age, @Qualifier("address3qualifier") Address address) {
		return  new Person(name, age, address);
	}
    
    .
    .
    .
    
	@Bean(name = "address3")
	@Qualifier("address3qualifier")
	public Address address3() {
		return  new Address("대한민국", "남양주시");
	}

Sping 프레임 워크가 자동으로 클래스 Bean으로 만들도록 하기

@Component와 @ComponentScan 사용

클래스의 인스턴스를 Spring이 관리하도록 해 Bean 생성을 자동화하여 코드를 생략할 수 있음

1. spring context의 설정을 자기 자신으로 설정

2. Bean으로 만들 파일들에 @Component 설정

3. spring context 클래스에 @ComponentScan(" @Component를 설정한 패키지 경로")를 설정 (@ComponentScan처럼 스캔 경로를 지정해주지 않을 경우 현재 파일을 대상으로 함)

@Configuration
@ComponentScan("com.in28minutes.learnspringframwork.game")
public class GamingAppLauncherApplication {

	public static void main(String[] args) {
		try(var context = new AnnotationConfigApplicationContext(GamingAppLauncherApplication.class)) {
			context.getBean(GamingConsole.class).up();
			context.getBean(GameRunner.class).run();
		}
	}
}

//

package com.in28minutes.learnspringframwork.game;

import org.springframework.stereotype.Component;

@Component
public class GameRunner {
	private GamingConsole game;
	
	public GameRunner(GamingConsole game) {
		this.game = game;
	}

	public void run() {
		System.out.println("실행 중인 게임: " + game);
		game.up();
		game.down();
		game.left();
		game.right();
	}
}

고유한 Bean이 아닐 경우 (동일한 interface가 설정된 component가 2개 이상일 경우)

1. 하나에 @Primary를 설정

우선권 부여

2. @Qualifier를 설정

와이어링시키고 싶은 곳에서 각각 설정하고 사용해  조건문처럼 사용

Primary보다 우선권이 더 높다고할 수 있음

@Qualifier 어노테이션이 없다면 Bean 이름을 한정자로 사용할 수 있음

@Component	// @Qualifier이 설정되지 않은 경우
class RadixSort implemets SortingAlgorithm

@Component
class AnotherComplexALgorithm
	@Autowired @Qualifier("radixSort")
	private SortingAlgorithm iWantToUseRadixSortOnly

@Component와 @Bean의 차이

  @Component @Bean
사용처 클래스에 사용 context 설정 파일의 메소드에 사용
방식 자동 수동으로 작성
의존성 주입 방식 필드, 세터, 생성자 인자에 메소드 호출, 매개변수에 설정
추천도 대부분의 경우 권장 Bean 생성 전 수행해야하는 비즈니스 로직이 많은 경우, 3rd 라이브러리 Bean을 인스턴스화할 때

의존성 주입 방식 3가지

의존성 주입 - Bean과 의존성을 식별하고 모두 와이어링하는 작업 프로세스

제어 반전 (IOC)라고도 함 (개발자가 직접 제어하지 않고 스프링이 관리하기 때문)

1. 필드로 주입

@Component
class YourBusinessClass {
	
	@Autowired
	Dependency1 dependency1;
	@Autowired
	Dependency2 dependency2;
	
	public String toString() {
		return "Using " + dependency1 + " and " + dependency2;
	}
}

@Component
class Dependency1 {
	
}
@Component
class Dependency2 {
	
}

2. 세터로 주입

@Component
class YourBusinessClass {
	
	Dependency1 dependency1;
	Dependency2 dependency2;
	
	@Autowired
	public void setDependency1(Dependency1 dependency1) {
		System.out.println("세터 주입:: setDependency1");
		this.dependency1 = dependency1;
	}
	@Autowired
	public void setDependency2(Dependency2 dependency2) {
		System.out.println("세터 주입:: setDependency2");
		this.dependency2 = dependency2;
	}

	public String toString() {
		return "Using " + dependency1 + " and " + dependency2;
	}
}

@Component
class Dependency1 {
	
}
@Component
class Dependency2 {
	
}

3. 생성자로 주입

모든 초기화가 하나의 메소드에서 발생하기 때문에 스프링 팀은 이 방식을 추천함

@Component
class YourBusinessClass {
	
	Dependency1 dependency1;
	Dependency2 dependency2;
	
	@Autowired	// 생성자에선 생략 가능
	public YourBusinessClass(Dependency1 dependency1, Dependency2 dependency2) {
		super();
		System.out.println("생성자 주입:: YourBusinessClass");
		this.dependency1 = dependency1;
		this.dependency2 = dependency2;
	}

	public String toString() {
		return "Using " + dependency1 + " and " + dependency2;
	}
}

@Component
class Dependency1 {
	
}
@Component
class Dependency2 {
	
}

※엔티티의 경우 의존성 주입이 필요없다.

의존성 주입은 컴포넌트 간의 결합도를 낮추고 유연성과 확장성을 향상시키는 것에 초점을 두고 있는 반면, 엔티티는 데이터베이스와의 상호작용에 초점을 맞추고 있으므로 애초에  의존성을 주입받아야 할 비즈니스 로직을 포함하고 있지 않다.

그렇기에 필요한 경우 new를 통해 직접 인스턴스화하면 된다.

'Java' 카테고리의 다른 글

H2, JDBC, JPA, Hibernate  (2) 2023.12.02
[Spring Boot] 기본  (0) 2023.12.01
[Spring] 심화  (0) 2023.11.30
[Java] 기본  (0) 2023.11.29
개발 환경 설정 (feat. Tomcat)  (0) 2023.11.27
Comments