가수면
[Spring] 기본 본문
개념
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를 이용한 방법
프로젝트 파일 생성 후 이클립스에서 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 |