4 분 소요

[Meta Annotaion]

: 애노테이션 위에 또 다시 애노테이션을 달아주는 것

image-20230811111350171

=> @Controller, @Service는 @Component의 기능을 그대로 활용할 수 있음

+부가적인 효과 + 역할 파악 (이건 WEB mvc에서 어떤 역할을 하는구나 ! 를 알수있음)

@Controller => 아 이거 Controller 구나 mapping 정보 찾아봐야지

※ 상속 개념은 아님

Retention 과 Target

  1. 합성 애노테이션 : 둘 이상의 Meta Annotation을 가지는 Meta 애노테이션

@RestController = @Controller (Meta: @Component) + @ResponseBody

=> 여러개의 annotation 효과를 한개의 annotation으로 대체 가능

[빈 오브젝트의 역할과 구분]

스프링 컨테이너 <– 여러가지 빈 => 성격이 다르고 각각 구성정보 작성이 달라질 수 있음

​ * 독립 실행형 구현 위한 bean : TomcatServletWebServerFactory, DispatcherServlet

image-20230811113452679

  • 애플리케이션 빈 : 개발자의 로직코드들을 담고 있는 객체들 => 개발자가 사용하기 위해 명시적으로 구성정보를 제공

  • 컨테이너 인프라스트럭처 빈 : 스프링 컨테이너 자체와 관련됨 (컨테이너 스스로 동작하기 위해 등록하는 빈들) ex) 자기자신, 구성정보, Bean 처리자

​ => 이런 빈들을 생성해달라고 요청하지 않아도 자동으로 등록되어 사용되고 있음

​ <-> 구성정보를 작성할 필요 없음

image-20230811113737945

  • 애플리케이션 로직 빈 : 비즈니스 로직을 담고 있는 (우리가 개발하는 부분) 기능을 담당

    => ‘사용자 구성정보’ ! 이용해 등록

    ComponentScan 을 이용해 자동으로 자바코드 및 에노테이션으로부터 읽어옴

  • 애플리케이션 인프라스트럭처 빈: 로직 동작 위해 만들어져있음(코드가 작성되어있음) -> 사용하겠다고 구성정보를 작성해줘야 사용됨

    => ‘자동 구성정보’ ! 이용해 등록

​ : AutoConfiguration (자동구성원리) 매커니즘 통해 등록 됨 !

​ <-> 구성정보는 작성해줘야함 ! BUT 그걸 우리가 하는게 아닌 Spring Boot가 대신 작성해줌 (vs 컨테이너 : 작성해줄 필요 없음)

[실습 : TomcatServletWebServerFactory , DispatcherServlet 를 자동구성 매커니즘으로 빈 등록 ]

Q. TomcatServletWebServerFactory , DispatcherServlet ?

이전에는 Servlet Container를 직접 등록해 사용했으니 빈으로 등록할 필요 x

=> Embedded 방식인 Spring boot에선 반드시 빈으로 명시적으로 선언해야함

<-> 애플리케이션 인프라스트럭처 빈 !

1. Component Scan 대상에서 제외 : @Import

Q. AutoConfiguration ?

image-20230811114427499

  • 애플리케이션 동작시 사용될 수 있는 인프라스트럭처 빈을 클래스로 등록해놓음

    => Spring Boot가 Application 실행시키면서 필요한 configuration을 생성해서 가져다 씀

    => Spring Boot에선 유저구성정보에 포함 시키지 않아야함 직접 빈으로 등록하지 않아도 뭔가 알아서 등록되도록 만들어야함 (Component Scan 대상에서 제외해야함)

image-20230811120749026

  1. Component 스캔대상이 있는 패키지 x (@ComponentScan 이 붙은 클래스와 다른 패키지에 있어야함)

  2. @ComponentScan 대상이 아니더라도 어쨌던 구성정보에 포함해야함

    => @Import

    @Import({TomcatWebServerConfig.class,DispatcherServletConfig.class}) : 하드코딩

  3. Annotaion 분리 : @EnableMyAutoConfig

    => @Import만 따로 관리하고 싶음!

package tobyspring.config;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import(MyAutoConfigImportSelector.class) // 다른 패키지의 Config 빈들을 등록하기 위함 (설정 정보로 불러옴)
public @interface EnableMyAutoConfig {
}

2. Import 동적으로 : selectImports()

  • Import 동적으로 ! 매번 @EnableMyAutoConfiguarion의 Import 구문 코딩을 수정하는게 아닌 자동으로 !

    모든 스프링부트 어플리케이션에서 저걸 쓰는게 아니면 직접 쓰는게 아닌 설정 파일로 동적으로 작성되어야함

ImportSelector (interface)

return String[] -> import할 Config 데이터(@Configuration 붙은 클래스들)를 String 으로 변환해 반환하면 이 데이터를 컨테이너가 구성정보로 사용

=> 어떤 설정파일을 사용할지 코드에 의해 그때그때 결정됨

  • 한번 확장해서 DeferredImportSelector (구현)

    다른 @Configuartion 붙은 클래스의 구성정보 생성작업이 끝난 후 Import Selector 수행하도록 순서를 좀 바꿈 함

=> 그 읽어오는 클래스 목록은 db에서 읽어올수도, 외부 파일에 적어놓을 수도 있겠지?

  1. MyAutoConfigImportSelector 로 DeferredImportSelector inferface 구현
public class MyAutoConfigImportSelector implements DeferredImportSelector {
    
	@Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        
        return new String[]{
                // 사용할 configuration 클래스를 Stiring 으로 반환
                // => Spring 이 이걸 읽어 빈 생성 + 사용
                // 소스코드로 직접
                "tobyspring.config.autoConfig.DispatcherServletConfig",
                "tobyspring.config.autoConfig.TomcatWebServerConfig"
        };
    } 

이 반환된 String 배열의 클래스 이름으로 빈 객체 생성

  1. @EnableMyAutoConfig 에 @Import(MyAutoConfigImportSelector.class)

생성된 빈 객체들을 클래스 MyAutoConfigImportSelector 통째로 Import

<-> 이 클래스에 목록 작성해주어 빈 객체 생성하고, 이 클래스를 Import 하는 방식으로 바꿔 이 클래스만 빈 객체 목록 수정하면 자동으로 실제 application에 import

3. 파일 읽어서 하게끔 : @MyAutoConfiguration

외부 설정파일로 읽기: txt(구성정보 후보들)로 빼서 자바의 파일 읽기 기능

  1. 애노테이션 생성 @MyAutoConfiguration 생성
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Configuration
public @interface MyAutoConfiguration {
}
  1. 그렇다면, 이 load는 어디 파일에서 읽어오는가 ?

    :이 애노테이션 이름으로 된 설정파일 생성해 자동구성에 사용할 클래스 목록 집어넣음

    => 파일생성해서, 풀네임의 애노테이션 이름으로 파일 생성 시 해당 파일안에 있는 클래스 이름을 읽어와 import한다

=> 형식 : META-INF/spring/full-qualified-annotation-name.imports

# tobyspring.config.MyAutoConfiguration.imports 
tobyspring.config.autoConfig.DispatcherServletConfig
tobyspring.config.autoConfig.TomcatWebServerConfig
  1. MyAutoConfigImportSelector - selectImports
public class MyAutoConfigImportSelector implements DeferredImportSelector {

    private final ClassLoader classloader;

    public MyAutoConfigImportSelector(ClassLoader classloader){
        this.classloader=classloader;  // 이렇게 하면 스프링이 class load할때 사용하는 빈 넣어줌
    }
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {

        List<String> autoConfigs = new ArrayList<>();

        // 애노테이션 클래스 정보
        /* classLoader : 애플리케이션에서 resource(파일 등)을 읽어올 땐 classLoader 사용
                    => 스프링이 빈을 생성할 떄 사용할 구성정보 파일을 읽어올때 사용하도록 같이 넣어줘야함
                    => 자동구성정보로 쓸 것이기 때문에 규격화된 방식으로 일어와야함
                    => 작성되어있는 클래스파일을 모두 사용하는게 아니라 후보들을 넣어놓고 스마트하게 고름 ~
         */
        ImportCandidates.load(MyAutoConfiguration.class,classloader).forEach(autoConfigs::add);
        // META-INF/spring/full-qualified-annotation-name.imports on the classpath

     //    return StreamSupport.stream(candidates.spliterator(),false).toArray(String[]::new) ;
        // return autoConfigs.toArray(String[]::new);
        // Arrays.copyOf(autoConfigs.toArray(), autoConfigs.size(), String[].class) ;
        return autoConfigs.toArray(new String[0]); // 빈 String array를 넣어줌 => 컬렉션의 사이즈보다 작은 array가 반환되면 새로운 배열 생성


    }
}

  1. 실제 자동구성 클래스 들에도 @MyAutoConfiguration 로 바꿔줌

설정파일 이름을 애노테이션을 하나 만들고, 거기에 imports 붙여줘 이름 만듬

=> 얘 자체가 기능이 있다기보단 의미를 명확히 함

@MyAutoConfiguration 파일에 적힌 목록에 의해 자동 구성되는 클래스 들이다~ 라는 의미를 명확히 함

4. @Configuration(proxyBeanMethods = false)

=> Proxy로 만들어지지 않고 생성됨

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Configuration(proxyBeanMethods = false) // proxyBeanMethods = false로 바꾼 configuration이 적용된다
public @interface MyAutoConfiguration {
    // 이 애노테이션 이름으로 된 설정파일을 만들어 거기에 config 으로 쓸 클래스 목록을 작성해줄거임
}

  • proxyBeanmethods 실습

+) Test Code

  • 학습 테스트 : ConfigurationTest우리가 만든 기능을 테스트 하는게 아니라 남이 만든 코드로 간단한 샘플을 만들어 사용법을 이해하고 공부하기 위한 목적으로 좋음

댓글남기기