본문 바로가기

강의/토비의 스프링부트

동적인 자동 구성 정보 등록

동적이라는 것은

내가 어떤 configuration을 가져올지를 db에서 읽어와서 참고할 수도 있고 외부 설정파일로도 가져올 수 있다. 코드에 의해

EnableMyAutoConfiguration을 고치지 않아도 되어야 한다는 것이다.

동적으로 가져올 때는 @import 로는 부족하다.

ImportSelector 라는 interface를 살펴보자.

annotation이라는 meta data를 받고 return을 String[] 에다가 클래스 이름을 넣어서 주면 된다.

 

ImportSelector를 확장헌 DefferedImportSelector를 사용할 것이다.

 

Import가 가져오는 것은 Configuration 클래스인데

특별히 ImportSelector 인터페이스를 구현한 클래스를 가져오면

그 안에 있는 메서드를 실행해서 결과로 돌아오는 String 값에 해당하는 클래스만 로딩하도록 해줌.

 

이것을 이용하면 Configuration을 조금 더 자유롭게 들고오도록 할 수 있다.

변경 전과 변경 후를 비교해보자.

 

변경 

package com.example.config;

import com.example.config.autoconfig.DispatcherServletConfig;
import com.example.config.autoconfig.TomcatWebServerConfig;
import org.springframework.context.annotation.Import;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
// type : class, interface, enum을 대상으로 부여
@Target(ElementType.TYPE)
@Import({DispatcherServletConfig.class, TomcatWebServerConfig.class})
public @interface EnableMyAutoConfiguration {
}

 변경 후 

package com.example.config;

import org.springframework.context.annotation.DeferredImportSelector;
import org.springframework.core.type.AnnotationMetadata;

public class MyAutoConfigImportSelector implements DeferredImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[] {
                "com.example.config.autoconfig.DispatcherServletConfig",
                "com.example.config.autoconfig.TomcatWebServerConfig",
        };
    }
}

 

package com.example.config;

import org.springframework.context.annotation.Import;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
// type : class, interface, enum을 대상으로 부여
@Target(ElementType.TYPE)
@Import(MyAutoConfigImportSelector.class)
public @interface EnableMyAutoConfiguration {
}

 

아직 문자열로 박혀있기 때문에

완전 동적으로 바뀌었다고 볼 수는 없다.

 

조금 더 동적으로 변경해보자.

외부 파일에서 configuration 정보를 읽어오도록 수정해보자

 

package com.example.config;

import org.springframework.boot.context.annotation.ImportCandidates;
import org.springframework.context.annotation.DeferredImportSelector;
import org.springframework.core.type.AnnotationMetadata;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.StreamSupport;

public class MyAutoConfigImportSelector implements DeferredImportSelector {

    private final ClassLoader classLoader;

    public MyAutoConfigImportSelector(ClassLoader classLoader) {
        this.classLoader = classLoader;
    }

    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {

        // 자동 구성 대상 후보들을 읽어온다.
        // 우리가 어떤 어플리케이션의 클래스 패스에서 resource, file들을 읽어올때 classLoader를 사용한다.
        // 파일에다가 설정해둘 configuration 목록들이 들어가 있을 것이다.
        // Iterable<String> candidates = ImportCandidates.load(MyAutoConfiguration.class, classLoader);
        // return StreamSupport.stream(candidates.spliterator(), false).toArray(String[]::new);

        // 변경1
        List<String> autoConfigs = new ArrayList<>();
        /*for (String candidate : ImportCandidates.load(MyAutoConfiguration.class, classLoader)) {
            autoConfigs.add(candidate);
        }*/

        // 변경2
        /*ImportCandidates.load(MyAutoConfiguration.class, classLoader).forEach(candidate ->
                autoConfigs.add(candidate)
        );*/

        // 변경 3
        ImportCandidates.load(MyAutoConfiguration.class, classLoader).forEach(autoConfigs::add);

        return autoConfigs.toArray(new String[0]);
//        return autoConfigs.toArray(String[]::new);
//        return Arrays.copyOf(autoConfigs.toArray(), autoConfigs.size(), String[].class)
        // 이중에서 어떤 것이 사용되게 할 것인지는 스마트한 방법으로 결정을 할 것이다.

    }
}

 

어떤 파일에서 정보를 읽어오냐면

ImportCandidates 클래스의 load라는 메서드에서 설명을 읽어보면 된다.

META-INF/spring/full-qualified-annotation-name (패키지 이름까지 포함한 이름)

 

 

com.example.config.autoConfig.DispatcherServletConfig
com.example.config.autoConfig.TomcatWebServletConfig