[Spring Boot] 스프링부트 원리 - 의존성 관리, 자동 설정

dsunni·2020년 7월 28일
1

참고

velog/@adam2/자동환경설정


목차

  1. 의존성 관리
  2. 자동 설정
  3. 내장 서블릿 컨테이너

1. 의존성 관리 기능

<!-- Inherit defaults from Spring Boot -->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.0.3.RELEASE</version>
</parent>

<!-- Add typical dependencies for a web application -->
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

pom.xml에서 의존성을 정의할 때 버전을 명시하지 않았음에도 불구하고 Spring Boot는 알아서 적절한 버전을 가져와준다. 어떻게 가능한 걸까?

Spring Boot의 의존성 관리 기능 때문이다.

Spring Boot 이전에는 버전이 달라서 에러가 생기는 문제가 빈번히 일어났다.

자동 환경 설정은 스프링부트의 큰 장점이다. 스프링부트는 jdbc, h2 등 약 100여가지의 자동 설정(starter)를 제공한다.

요즘엔 쉽게 Starter 위주로 의존성을 추가하면 부모(spring-boot-starter-parent)로부터 의존성을 상속받기 때문에 UTF-8 인코딩, 자바 버전, 플러그인 등 뿐만 아니라 버전도 자동으로 버전이 매핑된다. 따라서 Spring Boot Dependency에서 관리하는 의존성들은 굳이 버전을 명시하지 않아도 된다.

2. 의존성 관리 기능 활용

(1) 버전 관리 해주는 의존성 추가

Spring Data JPA라는 의존성을 추가

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
  • spring-boot-starter 를 추가했기 때문에 자동으로 의존성이 추가된다

(2) 버전 관리 안해주는 외부 의존성 추가

https://mvnrepository.com/

Maven dependency 검색 사이트

<dependency>
    <groupId>org.modelmapper</groupId>
    <artifactId>modelmapper</artifactId>
    <version>2.1.0</version>
</dependency>
  • 외부에서 가져오는 의존성은 버전을 명시해주는 것이 좋다.

(3) 기존 의존성 버전 변경하기

spring-boot-dependencies-2.0.3.RELEASE.pom에 버전들이 정리되어있다

...

<snakeyaml.version>1.19</snakeyaml.version>
<solr.version>6.6.4</solr.version>
<spring.version>5.0.7.RELEASE</spring.version>
<spring-amqp.version>2.0.4.RELEASE</spring-amqp.version>
<spring-batch.version>4.0.1.RELEASE</spring-batch.version>
<spring-cloud-connectors.version>2.0.2.RELEASE</spring-cloud-connectors.version>
<spring-data-releasetrain.version>Kay-SR8</spring-data-releasetrain.version>
<spring-hateoas.version>0.24.0.RELEASE</spring-hateoas.version>

...

Spring Boot에서 관리해주는 의존성 버전을 변경하고 싶을 때는 properties에 버전을 추가해 Overriding 해주면 된다.

<properties>
    <spring.version>5.0.6.RELEASE</spring.version>
</properties>
  • 5.0.7 RELESASE 에서 5.0.6 RELEASE로 변경된 것을 확인할 수 있다.


3. 자동 설정 이해 (Auto Configuration)

Configuration이란?

Spring의 Bean을 등록하는 JAVA 설정 파일

여러 설정을 일으키며 내장된 톰캣 서버에서 Web Application이 실행될 수 있는 이유 중에 하나는 Auto Configuration 때문이다.

@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}

@SpringBootApplication 어노테이션 안에 위와 같이 설정되어 있기 때문이다.


// @SpringBootApplication
@SpringBootConfiguration
@ComponentScan
@EnableAutoConfiguration
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

스프링부트에서는 @EnableAutoConfiguration 또는 @SpringBootApplication 중 하나만 사용하면 자동 환경설정이 가능하다.

@SpringBootApplication 어노테이션을 사용하는 것은 다음의 세 어노테이션을 사용하는 것과 같다.

  • @SpringBootConfiguration : 스프링부트 설정을 나타내는 어노테이션이다. 스프링의 @Configuration을 대체한다.
  • @ComponentScan : basePackages 프로퍼티 값에 별도의 경로를 설정하지 않으면 해당 어노테이션이 위치한 패키지가 루트 경로가 된다. 하위 루트까지 쭉 가면서 Bean을 등록한다.
    • @Component 가 붙어있는 클래스를 찾아가서 모든 인스턴스를 생성해 빈으로 등록한다.
  • @EnableAutoConfiguration : 미리 정의된 Bean을 가져와서 등록해준다.
    • 미리 정의된 Bean은 어디에 있을까?
      • 외부 라이브러리 중 sring-boot-autoconfigure에 META-INF 디렉토리 하위의 spring.factories에 자동으로 가져올 Bean들이 등록되어있다.

(1) Spring Boot Application의 Bean 등록하는 두 단계

  1. ComponentScan 수행하면서 Bean 등록
  2. EnableAutoConfiguration으로 추가적으로 읽어온 Bean들을 등록

(2) @EnableAutoConfiguration


spring.factories

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.cloud.CloudAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\
org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraRepositoriesAutoConfiguration,\

등..
  • org.springframework.boot.autoconfigure.EnableAutoConfiguration 키 값 밑에 설정되어 있는 클래스들은 전부 auto configuration 적용 대상이 된다
    • 사실 전부는 아니고 조건에 따라 달라진다.^)^
      • ConditionlOn 으로 시작

(3) ComponentScan

메인 애플리케이션 클래스부터 하위의 Component라는 애노테이션을 가진 클래스들을 스캔해서 Bean으로 등록한다.

  • @Component
  • @Configuration @Repository @Service @Controller @RestController


4. 자동 설정 만들기 1 : Starter와 Autoconfigure

  • xxx-spring-boot-autoconfigure 모듈 : 자동설정
  • xxx-spring-boot-starter 모듈 : 필요 의존성 정의
  • xxx-spring-boot-starter : 하나로 만들기

(1) 새로운 프로젝트 생성 후 의존성 추가

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-autoconfigure</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-autoconfigure-processor</artifactId>
        <optional>true</optional>
    </dependency>
    </dependencies>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>2.0.3.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
  • autoconfigure 의존성
  • autoconfigure-processor 의존성
  • 의존성 관리해주는 dependencyManagement 추가
    • spring-boot-dependencies

(2) @Configuration 파일 작성

Holoman.java

package me.dsunni;

public class Holoman {
    String name;
    int howLong;

    public String getName() {
        return name;
    }

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

    public int getHowLong() {
        return howLong;
    }

    public void setHowLong(int howLong) {
        this.howLong = howLong;
    }

    @Override
    public String toString() {
        return "Holoman{" +
                "name='" + name + '\'' +
                ", howLong=" + howLong +
                '}';
    }
}

HolomanConfiguration.java

package me.dsunni;

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

@Configuration
public class HolomanConfiguration {
    @Bean
    public Holoman holoman() {
        Holoman holoman = new Holoman();
        holoman.setHowLong(5);
        holoman.setName("dsunni");

        return holoman;
    }
}
  • holoman이라는 bean을 리턴하는 설정파일

(3) META-INF에 spring.factories 파일 만들기

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  me.dsunni.HolomanConfiguration
  • 설정파일을 명시적으로 정의해주면 이 key에 해당하는 Bean 설정을 사용할 수 있게된다

(4) mvn install

  • Maven - Lifecycle - install
    • 빌드해서 jar파일을 생성해 로컬 저장소에 설치

(5) 원래 프로젝트에서 의존성 추가

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>me.dsunni</groupId>
        <artifactId>dsunni-spring-boot-starter</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
</dependencies>

위와 같이 정상적으로 들어와 있는 것을 확인할 수 있다.


Application.java

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        // SpringApplication.run(Application.class, args);
        SpringApplication application = new SpringApplication(Application.class);
        application.setWebApplicationType(WebApplicationType.NONE);
        application.run(args);
    }
}
  • 조금 더 빨리 뜨기 위해 WebApplicationType을 NONE으로 설정

메인 애플리케이션 수정 후 추가한 Holoman Bean이 있는지 확인해보자


(6) Holoman Bean 확인

HolomanRunner.java

package me.dsunni;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

@Component
public class HolomanRunner implements ApplicationRunner {
    @Autowired
    Holoman holoman;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println(holoman);
    }
}
  • ApplicationRunner 인터페이스
    • Spring Boot Application을 띄웠을 때 자동으로 실행되는 Bean을 만들고 싶을 때 사용

📌 Unregistering JMX-exposed beans on shutdown: ERROR

  • HolmanRunner에 @Component 추가


5. 자동설정만들기 2 - @ConfigurationProperties

application.properties 파일을 통해 Bean을 정의해서 동적으로 생성하도록 해보자

application.properties

스프링부트는 application.properties 파일을 통해 설정을제공한다. 이 파일에는 부트가 제공하는 프로퍼티 뿐 아니라 커스텀 프로퍼티를 추가할 수 있다.

그 중 설정 프로퍼티 클래스를 사용하면 관련 설정을 한 클래스에서 관리할 수 있다.

동일한 key 값인 holoman들을 묶어서 Bean으로 등록할 수 있다


HolomanRunner는 application.properties에서 설정한 값을 읽어와 Holoman 빈을 재정의하게 된다.

HolomanProperties

package me.dsunni;

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties("holoman")
public class HolomanProperties {
    private String name;
    private int howLong;

    public String getName() {
        return name;
    }

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

    public int getHowLong() {
        return howLong;
    }

    public void setHowLong(int howLong) {
        this.howLong = howLong;
    }
}

위 HolomanProperties 클래스는 property 파일을 통해 값을 가져오는 역할을 수행한다.


@ConfigurationProperties

자동 설정 클래스 내의 스프링 빈을 property 파일을 통해 재정의 하고자 할 때는 이 어노테이션을 사용한다.

이 어노테이션을 사용하기 위해서는 먼저 다음과 같은 dependency를 추가해야한다.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
    <optional>true</optional>
</dependency>

@ConfigurationProperties("holoman")

application.properties 파일에서 등록했던 Bean의 이름(holoman)을 괄호 안에 넣어준다.

그 후 properties 파일에 있던 name, how-long을 받을 수 있는 변수를 정의하고, getter와 setter를 만들어준다. 이렇게 하면 변수에 값을 받을 수 있게된다.


HolomanConfiguration.java

package me.dsunni;

import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableConfigurationProperties(HolomanProperties.class)
public class HolomanConfiguration {
    @Bean
    @ConditionalOnMissingBean
    public Holoman holoman(HolomanProperties properties) {
        Holoman holoman = new Holoman();
        holoman.setHowLong(properties.getHowLong());
        holoman.setName(properties.getName());

        return holoman;
    }
}

@EnableConfigurationPropertes

이 어노테이션을 통해 HolomanConfiguration 클래스를 프로퍼티 파일(HolomanProperties) 정보를 담고 있는 클래스로 자동 등록하게 해준다.


@ConditionalOnMissingBean

이 어노테이션은 스프링 부트 프로젝트 상에서 같은 이름의 스프링 빈이 있을 때에는 그 스프링 빈을 상요하고, 만약 없다면 자동 등록한 빈을 쓰게끔 유도해준다.

profile
https://dsunni.tistory.com/ 이사갑니답

0개의 댓글