Spring 핵심

공부한것 다 기록해·2023년 8월 3일
0

스프링 전체 기능

스프링 프로젝트 생성 및 실행

Spring Application을 Bootstraping(부팅)하는 과정

1. SpringApplication 생성(new SpringApplication())
	SpringApplication의 다양한 Property 설정
    
2. SpringApplication.run()
	실제 스프링 동작에 필요한 정보들을 생성하고 가져오고 메모리에 올리는 과정
    
    

SpringBootApplication Annotation

@SpringBootConfiguration 어노테이션이 붙어있는 클래스를 스프링부트에 대한 설정정보가 담긴 클래스로 인식하고, 해당클래스 내부에 @Bean 어노테이션이 담긴 메소드를 찾아 빈을 등록하고 생성해주며 싱글톤 패턴을 유지할 수 있도록 돕는 역할

뭘 공부 하지?

스프링 코어

  • DI, IoC, 컨테이너 : 스프링의 근간을 이루고 전체 어플리케이션을 통제하는데 활용되는 기술
  • Resource, AOP, Validation, SpEL : 많은 어플리케이션에서 공통적으로 활용하기 좋은 스프링에서 제공해주는 기능

스프링 Web MVC

  • Web MVC가 어떤 것인가
  • HTTP 요청/응답 처리 : Web MVC는 계속 어플리케이션이 요청 올때까지 대기를 하고 있다. 요청이 오면 그 즉시 처리를 하고 응답을 한다.
  • 필터, 인터셉터: 웹에서 들어오는 요청을 필터링 하거나 공통적인 처리를 하는 용도의 기술
  • 예외처리 : 웹에서 들어온 처리를 하던 중 오류가 발생했을 때 어떻게 해야 더 안전하게 처리할 수 있을까?

DI, IOC, 컨테이너

스프링과 레고

레고를 만들 때 편리한 점

  • 바닥에 넓은 판을 두고 그 위에 이것 저것 얹으면 편하다.
  • 동그란 끼우는 부분이 모두 동일하므로 어떤 것들이든 서로 끼워볼 수 있다.
  • 너무 높은 자유도가 없기 때문에(오히려) 뭔가 만들기 편리하다.

스프링을 쓰면 좋은점

  • 스프링이라는 넓은 판때기(컨테이너)위에 내가 만드는 클래스만 얹으면 개발이 되므로 편하다.
  • 스프링 Bean이라는 규격에 맞춰 만들기만 하면 서로가 서로를 가져다 쓰기가 좋다.
  • 역설적으로 너무 자유롭진 않아서 원리를 알면 그 안에서는 만들기가 편리하다.

DI(Dependency Injection)

의존성 주입

  • 의존성(A가 B를 사용한다. A가 B에 의존한다.)
  • 결제서비스가 머니어댑터와 편의점 할인을 사용한다.
  • 결제서비스라는 블럭에 머니어댑터, 편의점할인을 끼움

IoC(Inversion Of Control)

제어의 역전

  • 사용자가 직접 클래스를 생성(new Class())하지 않고 프레임워크(스프링)이 제어하도록 한다.

그래도 생성 전과 생성 후에 뭔가 하고 싶은게 있다면?

  • Bean LifeCycle callback(빈 생명주기 콜백함수)사용

CallBack함수란?

  • Call(호출) Back(나중에)하는 함수
  • 여기에 뭔가 담아주면 나중에(정의된 시점에) 호출해준다.

Bean LifeCycle

DI 설정

1. XML을 통한 빈 등록
  • 토피의 스프링, 스프링 3/4 시절까지 많이 사용
  • 설정이 외부로 명확히 분리된 것임을 알 수 있다.
  • 자동완성이나 컴파일 등으로 오타를 잡기 어렵고 타이핑 양이 많아진다.

Xml로 한번 등록해보자

@Configuration
public class ApplicationConfig {
    @Bean
    public ConveniencePayService conveniencePayService(){
        return new ConveniencePayService(
                discountByConvenience(),
                new HashSet<>(
                        Arrays.asList(moneyAdapter(), cardAdapter())
                )
        );
    }

    @Bean
    private static CardAdapter cardAdapter() {
        return new CardAdapter();
    }

    @Bean
    private static MoneyAdapter moneyAdapter() {
        return new MoneyAdapter();
    }

    @Bean
    private static DiscountByPayMethod discountByConvenience() {
        return new DiscountByPayMethod();
    }
}

XML 빈 등록

<?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">
    <bean id="conveniencePayService" class="com.sangyunpark.payservice.service.ConveniencePayService">
        <constructor-arg name="paymentInterfaceSet">
            <set>
                <ref bean="moneyAdapter"/>
                <ref bean="cardAdapter"/>
            </set>
        </constructor-arg>
        <constructor-arg name="discountInterface" ref="discountByConvenience"/>
    </bean>
    <bean id="cardAdapter" class="com.sangyunpark.payservice.service.CardAdapter"/>
    <bean id="moneyAdapter" class="com.sangyunpark.payservice.service.MoneyAdapter"/>
    <bean id="discountByConvenience" class="com.sangyunpark.payservice.service.DiscountByConvenience"/>
    <bean id="discountByPayMethod" class="com.sangyunpark.payservice.service.DiscountByPayMethod"/>
</beans>

생성자가 없는 경우에는 빈 tag를 사용해준다.

ApplicationContext applicationContext = new AnnotationConfigApplicationContext("spring-config.xml"); // context로 만들어!

xml로 등록한 후에도 bean이 잘 실행된다.

2. XML ComponentScan을 통한 빈 등록

  • 기본 빈 등록 방식은 클래스가 많을 경우 너무 번거로움
  • 자동으로 등록해줄 수는 없을까?
  • @Controller, @RestController, @Service, @Component, @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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

  	<context:compnent-scan base-package="com.sangyunpark.payservice"/>
  
</beans>

위와 같이 단 한줄로 구성이 가능하다.(payservice 하위에있는 모든 패키지들을 등록하겠다는 소리)
어노테이션이 없는 경우에는 빈으로 등록이되지 않는다! 꼭 어노테이션을 달아주어야한다.

3. Java Config를 통한 빈 등록

  • Spring 4때부터 XML이 아닌 JavaConfig가 많이 활용되기 시작함
  • XML 설정파일이 자바 코드화 된 것
  • 자동완성, 컴파일 시 정적 분석으로 오류를 잡아줌
package com.sangyunpark.payservice.config;

import com.sangyunpark.payservice.service.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.Arrays;
import java.util.HashSet;
@Configuration
public class ApplicationConfig {
    @Bean
    public ConveniencePayService conveniencePayService(){
        return new ConveniencePayService(
                discountByConvenience(),
                new HashSet<>(
                        Arrays.asList(moneyAdapter(), cardAdapter())
                )
        );
    }

    @Bean
    private static CardAdapter cardAdapter() {
        return new CardAdapter();
    }

    @Bean
    private static MoneyAdapter moneyAdapter() {
        return new MoneyAdapter();
    }

    @Bean
    private static DiscountByPayMethod discountByConvenience() {
        return new DiscountByPayMethod();
    }
}

4. JavaConfig ComponentScan을 통한 빈 등록

@Configuration
@ComponentScan(basePackages = "com.sangyunpark.payservice")
public class ApplicationConfig {

}
  • 스프링 부트에서는 이 방식이 기본으로 적용되어 있으므로, 편리하게 빈 등록이 가능하다.(따로 만들어주지 않아도 된다!)

빈 관련 설정하기

#1 빈의 구현체가 여러개인 경우 주입 받는 방법

  1. @Primary : 해당 빈을 최우선으로 주입
@Primary
public class DiscountByPayMethod implements DiscountInterface{	
  1. @Qualifier("beanName") : beanName으로 지정된 빈을 주입
@Qualifier("discountByConvenience") DiscountInterface discountInterface,
  1. Set 또는 List로 모두 받기
paymentInterfaceSet.forEach(
                paymentInterface -> paymentInterfaceMap.put(paymentInterface.getPayMethodType(), paymentInterface)
        );
  1. 프로퍼티 이름을 빈과 동일하게 하기 : 가장 흔하게 사용하는 방법

#2 빈의 스코프(Scope)

  • 싱글톤 : 일반적인 방법, 하나만 만들어서 계속 재활용
@Scope("singleton")
ConveniencePayService conveniencePayService =
                applicationContext.getBean("conveniencePayService",
                        ConveniencePayService.class);

        System.out.println("conveniencePayService : " + conveniencePayService);

        ConveniencePayService conveniencePayService2 =
                applicationContext.getBean("conveniencePayService",
                        ConveniencePayService.class);

        System.out.println("conveniencePayService : " + conveniencePayService2);

        ConveniencePayService conveniencePayService3 =
                applicationContext.getBean("conveniencePayService",
                        ConveniencePayService.class);

        System.out.println("conveniencePayService : " + conveniencePayService3);

출력결과

conveniencePayService : com.sangyunpark.payservice.service.ConveniencePayService@189aa67a
conveniencePayService : com.sangyunpark.payservice.service.ConveniencePayService@189aa67a
conveniencePayService : com.sangyunpark.payservice.service.ConveniencePayService@189aa67a
  • Prototype : 매번 새로 만드는 방법(데이터를 클렌징 해야할 때)
@Scope("prototype")

Prototype인 경우 출력 결과

conveniencePayService : com.sangyunpark.payservice.service.ConveniencePayService@56113384
conveniencePayService : com.sangyunpark.payservice.service.ConveniencePayService@373ebf74
conveniencePayService : com.sangyunpark.payservice.service.ConveniencePayService@c4ed84

(1) Request : 요청에 따라 계속 새로 만듦
(2) Session : 세션 마다 계속 새로 만듦
(3) WebSocket : 자주 사용되지는 않는다.

#3 스프링의 환경 설정 : 프로파일(Profile)

  • 현업에서는 환경을 다양하게 하여 해당 환경에서만 동작하는 Bean을 만드는 경우가 존재
  • 클래스단위에 적용하거나 메서드 단위에 적용 가능

클래스 단위

  • @Configuration @Profile("test")
  • @Component @Profile("test")

메서드 단위

  • @Bean @Profile("test")

  • -Dspring.profiles.active=sandbox, beta, production

  • 프로파일 표현식
    @Profile("!production")
    !not, &and, |or

0개의 댓글