SpringBoot 원리 6)자동구성
[Meta Annotaion]
: 애노테이션 위에 또 다시 애노테이션을 달아주는 것
=> @Controller, @Service는 @Component의 기능을 그대로 활용할 수 있음
+부가적인 효과 + 역할 파악 (이건 WEB mvc에서 어떤 역할을 하는구나 ! 를 알수있음)
@Controller => 아 이거 Controller 구나 mapping 정보 찾아봐야지
※ 상속 개념은 아님
Retention 과 Target
- 합성 애노테이션 : 둘 이상의 Meta Annotation을 가지는 Meta 애노테이션
@RestController = @Controller (Meta: @Component) + @ResponseBody
=> 여러개의 annotation 효과를 한개의 annotation으로 대체 가능
[빈 오브젝트의 역할과 구분]
스프링 컨테이너 <– 여러가지 빈 => 성격이 다르고 각각 구성정보 작성이 달라질 수 있음
* 독립 실행형 구현 위한 bean : TomcatServletWebServerFactory, DispatcherServlet
-
애플리케이션 빈 : 개발자의 로직코드들을 담고 있는 객체들 => 개발자가 사용하기 위해 명시적으로 구성정보를 제공
-
컨테이너 인프라스트럭처 빈 : 스프링 컨테이너 자체와 관련됨 (컨테이너 스스로 동작하기 위해 등록하는 빈들) ex) 자기자신, 구성정보, Bean 처리자
=> 이런 빈들을 생성해달라고 요청하지 않아도 자동으로 등록되어 사용되고 있음
<-> 구성정보를 작성할 필요 없음
-
애플리케이션 로직 빈 : 비즈니스 로직을 담고 있는 (우리가 개발하는 부분) 기능을 담당
- => ‘사용자 구성정보’ ! 이용해 등록
-
ComponentScan 을 이용해 자동으로 자바코드 및 에노테이션으로부터 읽어옴
-
애플리케이션 인프라스트럭처 빈: 로직 동작 위해 만들어져있음(코드가 작성되어있음) -> 사용하겠다고 구성정보를 작성해줘야 사용됨
=> ‘자동 구성정보’ ! 이용해 등록
: AutoConfiguration (자동구성원리) 매커니즘 통해 등록 됨 !
<-> 구성정보는 작성해줘야함 ! BUT 그걸 우리가 하는게 아닌 Spring Boot가 대신 작성해줌 (vs 컨테이너 : 작성해줄 필요 없음)
[실습 : TomcatServletWebServerFactory , DispatcherServlet 를 자동구성 매커니즘으로 빈 등록 ]
Q. TomcatServletWebServerFactory , DispatcherServlet ?
이전에는 Servlet Container를 직접 등록해 사용했으니 빈으로 등록할 필요 x
=> Embedded 방식인 Spring boot에선 반드시 빈으로 명시적으로 선언해야함
<-> 애플리케이션 인프라스트럭처 빈 !
1. Component Scan 대상에서 제외 : @Import
Q. AutoConfiguration ?
-
애플리케이션 동작시 사용될 수 있는 인프라스트럭처 빈을 클래스로 등록해놓음
=> Spring Boot가 Application 실행시키면서 필요한 configuration을 생성해서 가져다 씀
=> Spring Boot에선 유저구성정보에 포함 시키지 않아야함 직접 빈으로 등록하지 않아도 뭔가 알아서 등록되도록 만들어야함 (Component Scan 대상에서 제외해야함)
-
Component 스캔대상이 있는 패키지 x (@ComponentScan 이 붙은 클래스와 다른 패키지에 있어야함)
-
@ComponentScan 대상이 아니더라도 어쨌던 구성정보에 포함해야함
=> @Import
@Import({TomcatWebServerConfig.class,DispatcherServletConfig.class}) : 하드코딩
-
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에서 읽어올수도, 외부 파일에 적어놓을 수도 있겠지?
- 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 배열의 클래스 이름으로 빈 객체 생성
- @EnableMyAutoConfig 에 @Import(MyAutoConfigImportSelector.class)
생성된 빈 객체들을 클래스 MyAutoConfigImportSelector 통째로 Import
<-> 이 클래스에 목록 작성해주어 빈 객체 생성하고, 이 클래스를 Import 하는 방식으로 바꿔 이 클래스만 빈 객체 목록 수정하면 자동으로 실제 application에 import
3. 파일 읽어서 하게끔 : @MyAutoConfiguration
외부 설정파일로 읽기: txt(구성정보 후보들)로 빼서 자바의 파일 읽기 기능
- 애노테이션 생성 @MyAutoConfiguration 생성
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Configuration
public @interface MyAutoConfiguration {
}
-
그렇다면, 이 load는 어디 파일에서 읽어오는가 ?
:이 애노테이션 이름으로 된 설정파일 생성해 자동구성에 사용할 클래스 목록 집어넣음
=> 파일생성해서, 풀네임의 애노테이션 이름으로 파일 생성 시 해당 파일안에 있는 클래스 이름을 읽어와 import한다
=> 형식 : META-INF/spring/full-qualified-annotation-name.imports
# tobyspring.config.MyAutoConfiguration.imports
tobyspring.config.autoConfig.DispatcherServletConfig
tobyspring.config.autoConfig.TomcatWebServerConfig
- 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가 반환되면 새로운 배열 생성
}
}
- 실제 자동구성 클래스 들에도 @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우리가 만든 기능을 테스트 하는게 아니라 남이 만든 코드로 간단한 샘플을 만들어 사용법을 이해하고 공부하기 위한 목적으로 좋음
댓글남기기