programing tip

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

itbloger 2020. 6. 22. 08:04
반응형

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);
        }


    }

And created 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);
    }
}

So, this has helped me.


Using Spring context 5.0 I have successfully achieved loading correct property file based on system environment via the following annotation

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

Here MYENV value is read from system environment and if system environment is not present then default test environment property file will be loaded, if I give a wrong MYENV value - it will fail to start the application.

Note: for each profile, you want to maintain - you will need to make an application-[profile].property file and although I used Spring context 5.0 & not Spring boot - I believe this will also work on Spring 4.1


I faced the same issue as the author of the question. For our case answers in this question weren't enough since each of the members of my team had a different local environment and we definitely needed to .gitignore the file that had the different db connection string and credentials, so people don't commit the common file by mistake and break others' db connections.

On top of that when we followed the procedure below it was easy to deploy on different environments and as en extra bonus we didn't need to have any sensitive information in the version control at all.

Getting the idea from PHP Symfony 3 framework that has a parameters.yml (.gitignored) and a parameters.yml.dist (which is a sample that creates the first one through composer install),

I did the following combining the knowledge from answers below: https://stackoverflow.com/a/35534970/986160 and https://stackoverflow.com/a/35535138/986160.

Essentially this gives the freedom to use inheritance of spring configurations and choose active profiles through configuration at the top one plus any extra sensitive credentials as follows:

application.yml.dist (sample)

    spring:
      profiles:
        active: local/dev/prod
      datasource:
        username:
        password:
        url: jdbc:mysql://localhost:3306/db?useSSL=false&useLegacyDatetimeCode=false&serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8

application.yml (.gitignore-d on dev server)

spring:
  profiles:
    active: dev
  datasource:
    username: root
    password: verysecretpassword
    url: jdbc:mysql://localhost:3306/real_db?useSSL=false&useLegacyDatetimeCode=false&serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8

application.yml (.gitignore-d on local machine)

spring:
  profiles:
    active: local
  datasource:
    username: root
    password: rootroot
    url: jdbc:mysql://localhost:3306/xampp_db?useSSL=false&useLegacyDatetimeCode=false&serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8

application-dev.yml (extra environment specific properties not sensitive)

spring:
  datasource:
    testWhileIdle: true
    validationQuery: SELECT 1
  jpa:
    show-sql: true
    format-sql: true
    hibernate:
      ddl-auto: create-droop
      naming-strategy: org.hibernate.cfg.ImprovedNamingStrategy
    properties:
      hibernate:
        dialect: org.hibernate.dialect.MySQL57InnoDBDialect

Same can be done with .properties

참고URL : https://stackoverflow.com/questions/35531661/using-env-variable-in-spring-boots-application-properties

반응형