[java] Spring Boot의 application.properties에서 env 변수 사용

우리는 스프링 부트 웹앱을 개발 하고 있으며 우리가 사용하는 데이터베이스는 MySql이다 ;

  • 우리가 가지고있는 설정은 먼저 로컬에서 테스트하는 것입니다 (PC에 MySql을 설치해야 함을 의미합니다).

  • 그런 다음 Bitbucket으로 푸시합니다 .

  • Jenkins 는 Bitbucket에 대한 새로운 푸시를 자동으로 감지하고 빌드를 수행합니다 (Jenkins mvn 빌드가 통과하려면 Jenkins를 실행하는 가상 머신에 MySql도 설치해야 함).

  • Jenkins 빌드가 패스되면 OpenShift 의 응용 프로그램으로 코드를 푸시합니다 (Jenkins의 Openshift 배포 플러그인 사용).

우리가 이미 이해했을 수도있는 문제 는 다음과 같습니다.

  • 에서와 application.properties우리가하지 하드 코드 MySQL의 정보를 할 수 있습니다. 우리 프로젝트는 3 개의 다른 장소 ( local , JenkinsOpenShift ) 에서 실행 되므로 데이터 소스 필드를 동적으로 만들어야합니다 application.properties(우리는 다른 방법이 있지만 현재는이 솔루션을 연구하고 있습니다).

    spring.datasource.url =
    spring.datasource.username =
    spring.datasource.password = 

우리가 생각 해낸 해결책은 시스템 환경 변수를 로컬 및 Jenkins vm에서 작성하고 (OpenShift에서 이름을 지정하는 것과 동일한 이름으로) 각각 올바른 값을 지정하는 것입니다.

export OPENSHIFT_MYSQL_DB_HOST="jdbc:mysql://localhost"
export OPENSHIFT_MYSQL_DB_PORT="3306"
export OPENSHIFT_MYSQL_DB_USERNAME="root"
export OPENSHIFT_MYSQL_DB_PASSWORD="123asd"

우리는 이것을했고 작동합니다. 또한 Map<String, String> env = System.getenv();환경 변수를 다음과 같이 Java 변수로 만들 수 있는지 확인했습니다 .

String password = env.get("OPENSHIFT_MYSQL_DB_PASSWORD");
String userName = env.get("OPENSHIFT_MYSQL_DB_USERNAME");
String sqlURL = env.get("OPENSHIFT_MYSQL_DB_HOST");
String sqlPort = env.get("OPENSHIFT_MYSQL_DB_PORT");

이제 남은 것은 Java 변수를 사용해야 application.properties한다는 것입니다. 이것이 바로 우리가 어려움을 겪고있는 것입니다.

어떤 폴더에서, 어떻게, 우리가 할당해야 할 password, userName, sqlURL,과 sqlPort에 대한 변수 application.properties를보고 우리가 어떻게에 포함 할 수 있도록를 application.properties?

우리는 많은 것들 중 하나를 시도했습니다.

spring.datasource.url = ${sqlURL}:${sqlPort}/"nameofDB"
spring.datasource.username = ${userName}
spring.datasource.password = ${password}

지금까지 운이 없습니다. 우리는 아마도 이러한 env 변수를 올바른 클래스 / 폴더에 넣지 않았거나에서 잘못 사용하고 있습니다 application.properties.

도와 주셔서 감사합니다 !!

감사!



답변

Java 변수를 사용할 필요가 없습니다. 시스템 환경 변수를 포함 시키려면 application.properties파일에 다음을 추가 하십시오.

spring.datasource.url = ${OPENSHIFT_MYSQL_DB_HOST}:${OPENSHIFT_MYSQL_DB_PORT}/"nameofDB"
spring.datasource.username = ${OPENSHIFT_MYSQL_DB_USERNAME}
spring.datasource.password = ${OPENSHIFT_MYSQL_DB_PASSWORD}

그러나 @Stefan Isele 이 제안한 방식 이 더 바람직합니다.이 경우 env 변수를 하나만 선언해야하기 때문입니다 spring.profiles.active. Spring은 application-{profile-name}.properties템플릿에 의해 적절한 속성 파일을 자동으로 읽습니다 .


답변

환경에 따라 구성을 다르게하는 가장 쉬운 방법은 스프링 프로파일을 사용하는 것입니다. 외부화 된 구성을 참조하십시오 .

이것은 당신에게 많은 유연성을 제공합니다. 내 프로젝트에서 사용하고 있으며 매우 도움이됩니다. 귀하의 경우 3 개의 프로파일이 있습니다 : ‘local’, ‘jenkins’및 ‘openshift’

그런 다음 3 프로필 특정 속성 파일이 :
application-local.properties,
application-jenkins.properties, 및application-openshift.properties

관련 환경에 대한 속성을 설정할 수 있습니다. 앱을 실행할 때 다음과 같이 활성화 할 프로파일을 지정해야합니다.
-Dspring.profiles.active=jenkins

편집하다

스프링 문서에 따르면 시스템 환경 변수를 설정할 수 있습니다
SPRING_PROFILES_ACTIVE 를 프로파일을 활성화하고 매개 변수로 전달할 필요가 없습니다.

런타임에 웹 앱에 대한 활성 프로파일 옵션을 전달하는 방법이 있습니까?

스프링은 애플리케이션 컨텍스트를 구축 할 때 활성 프로파일을 첫 번째 단계 중 하나로 결정합니다. 그런 다음 활성 프로파일을 사용하여 읽을 특성 파일과 인스턴스화 할 Bean을 결정합니다. 응용 프로그램이 시작되면 변경할 수 없습니다.


답변

이것은 저의 평판이 직접 언급 할만큼 높지 않기 때문에 많은 의견에 대한 답변입니다.

응용 프로그램 컨텍스트가 아직로드되지 않은 한 런타임시 프로파일을 지정할 수 있습니다.

// Previous answers incorrectly used "spring.active.profiles" instead of
// "spring.profiles.active" (as noted in the comments).
// Use AbstractEnvironment.ACTIVE_PROFILES_PROPERTY_NAME to avoid this mistake.

System.setProperty(AbstractEnvironment.ACTIVE_PROFILES_PROPERTY_NAME, environment);
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/META-INF/spring/applicationContext.xml");


답변

으로 직접 환경 변수를 인식하지 못하는 Flayway application.properties (Spring-Boot V2.1)에 . 예 :

spring.datasource.url=jdbc:mysql://${DB_HOSTNAME}:${DB_PORT}/${DB_DATABASE}
spring.datasource.username=${DB_USER}
spring.datasource.password=${DB_PASS}

이 문제를 해결하기 위해이 환경 변수를 수행했습니다. 보통 .env 파일을 만듭니다.

SPRING_DATASOURCE_URL=jdbc:mysql://127.0.0.1:3306/place
SPRING_DATASOURCE_USERNAME=root
SPRING_DATASOURCE_PASSWORD=root

그리고 변수를 내 환경으로 내보내십시오.

export $(cat .env | xargs)

마지막으로 명령을 실행하십시오.

mvn spring-boot:run

또는 jar 파일을 실행하십시오.

java -jar target/your-file.jar

여기에 다른 접근 방식이 있습니다 : https://docs.spring.io/spring-boot/docs/2.1.0.BUILD-SNAPSHOT/maven-plugin/examples/run-env-variables.html


답변

다음 은 환경 체인을 통한 스 니펫 코드이며 다른 환경에 대해 특성 파일을로드하는 중입니다.

응용 프로그램 리소스 아래의 속성 파일 ( src / main / resources ) :-

 1. application.properties
 2. application-dev.properties
 3. application-uat.properties
 4. application-prod.properties

이상적으로 application.properties 에는 모든 환경에서 액세스 할 수있는 모든 공통 특성이 포함되며 환경 관련 특성은 환경을 지정하는 경우에만 작동합니다. 따라서 이러한 속성 파일을로드하는 순서는 다음과 같습니다.

 application.properties -> application.{spring.profiles.active}.properties.

여기에 코드 스 니펫 :-

    import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
    import org.springframework.core.io.ClassPathResource;
    import org.springframework.core.io.Resource;

    public class PropertiesUtils {

        public static final String SPRING_PROFILES_ACTIVE = "spring.profiles.active";

        public static void initProperties() {
            String activeProfile = System.getProperty(SPRING_PROFILES_ACTIVE);
            if (activeProfile == null) {
                activeProfile = "dev";
            }
            PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer
                    = new PropertySourcesPlaceholderConfigurer();
            Resource[] resources = new ClassPathResource[]
                    {new ClassPathResource("application.properties"),
                            new ClassPathResource("application-" + activeProfile + ".properties")};
            propertySourcesPlaceholderConfigurer.setLocations(resources);

        }
    }


답변

어쩌면 이것을 너무 늦게 쓸 수도 있지만 속성을 읽는 방법을 재정의하려고 할 때 비슷한 문제가 발생했습니다.

내 문제는 : 1)이 속성이 env에 설정된 경우 env에서 속성을 읽습니다 .2)이 속성이 시스템 속성에 설정된 경우 시스템 속성에서 속성을 읽습니다 .3) 마지막으로 응용 프로그램 속성에서 읽습니다.

따라서이 문제를 해결하기 위해 Bean 구성 클래스로 이동합니다.

@Validated
@Configuration
@ConfigurationProperties(prefix = ApplicationConfiguration.PREFIX)
@PropertySource(value = "${application.properties.path}", factory = PropertySourceFactoryCustom.class)
@Data // lombok
public class ApplicationConfiguration {

    static final String PREFIX = "application";

    @NotBlank
    private String keysPath;

    @NotBlank
    private String publicKeyName;

    @NotNull
    private Long tokenTimeout;

    private Boolean devMode;

    public void setKeysPath(String keysPath) {
        this.keysPath = StringUtils.cleanPath(keysPath);
    }
}

@PropertySource에서 팩토리를 덮어 씁니다. 그런 다음 속성을 읽기위한 자체 구현을 만들었습니다.

    public class PropertySourceFactoryCustom implements PropertySourceFactory {

        @Override
        public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException {
            return name != null ? new PropertySourceCustom(name, resource) : new PropertySourceCustom(resource);
        }


    }

그리고 PropertySourceCustom을 만들었습니다

public class PropertySourceCustom extends ResourcePropertySource {


    public LifeSourcePropertySource(String name, EncodedResource resource) throws IOException {
        super(name, resource);
    }

    public LifeSourcePropertySource(EncodedResource resource) throws IOException {
        super(resource);
    }

    public LifeSourcePropertySource(String name, Resource resource) throws IOException {
        super(name, resource);
    }

    public LifeSourcePropertySource(Resource resource) throws IOException {
        super(resource);
    }

    public LifeSourcePropertySource(String name, String location, ClassLoader classLoader) throws IOException {
        super(name, location, classLoader);
    }

    public LifeSourcePropertySource(String location, ClassLoader classLoader) throws IOException {
        super(location, classLoader);
    }

    public LifeSourcePropertySource(String name, String location) throws IOException {
        super(name, location);
    }

    public LifeSourcePropertySource(String location) throws IOException {
        super(location);
    }

    @Override
    public Object getProperty(String name) {

        if (StringUtils.isNotBlank(System.getenv(name)))
            return System.getenv(name);

        if (StringUtils.isNotBlank(System.getProperty(name)))
            return System.getProperty(name);

        return super.getProperty(name);
    }
}

그래서 이것은 나를 도왔습니다.


답변

Spring context 5.0을 사용하여 다음 주석을 통해 시스템 환경에 따라 올바른 속성 파일을 성공적으로로드했습니다.

@PropertySources({
    @PropertySource("classpath:application.properties"),
    @PropertySource("classpath:application-${MYENV:test}.properties")})

여기서 MYENV 값은 시스템 환경에서 읽히고 시스템 환경이 존재하지 않으면 기본 테스트 환경 속성 파일이로드됩니다. 잘못된 MYENV 값을 지정하면 응용 프로그램을 시작하지 못합니다.

참고 : 각 프로파일에 대해 유지 관리하고 싶습니다-application- [profile] .property 파일을 만들어야하며 Spring 부트가 아닌 Spring context 5.0을 사용했지만 스프링 4.1에서도 작동한다고 생각합니다.