Spring Boot và nhiều tệp cấu hình bên ngoài


125

Tôi có nhiều tệp thuộc tính mà tôi muốn tải từ classpath. Có một bộ mặc định theo /src/main/resourcesđó là một phần của myapp.jar. My springcontextmong đợi các tệp ở trên classpath. I E

<util:properties id="Job1Props"
    location="classpath:job1.properties"></util:properties>

<util:properties id="Job2Props"
    location="classpath:job2.properties"></util:properties>

Tôi cũng cần tùy chọn để ghi đè các thuộc tính này bằng một tập hợp bên ngoài. Tôi có một thư mục cấu hình bên ngoài trong cwd. Theo mùa xuân, thư mục cấu hình doc khởi động phải nằm trên classpath. Nhưng nó không rõ ràng từ doc nếu nó sẽ chỉ ghi đè applicaiton.propertiestừ đó hoặc tất cả các thuộc tính trong cấu hình.

Khi tôi kiểm tra nó, chỉ application.propertiesđược nhặt và phần còn lại của tài sản vẫn được chọn /src/main/resources. Tôi đã thử cung cấp chúng dưới dạng danh sách được phân tách bằng dấu phẩy spring.config.locationnhưng bộ mặc định vẫn không bị ghi đè.

Làm cách nào để đặt nhiều tệp cấu hình bên ngoài ghi đè các tệp mặc định?

Như cách giải quyết khác mà tôi hiện đang sử dụng app.config.location(thuộc tính ứng dụng cụ thể) mà tôi cung cấp thông qua dòng lệnh. I E

java -jar myapp.jar app.config.location=file:./config

và tôi đã đổi applicationcontextthành

<util:properties id="Job2Props"
    location="{app.config.location}/job2.properties"></util:properties>

Và đây là cách tôi thực hiện tách biệt giữa tệp và đường dẫn classpath trong khi tải Ứng dụng.
CHỈNH SỬA:

//psuedo code

if (StringUtils.isBlank(app.config.location)) {
            System.setProperty(APP_CONFIG_LOCATION, "classpath:");
}

Tôi thực sự không muốn sử dụng cách giải quyết ở trên và đã ghi đè mùa xuân lên tất cả các tệp cấu hình bên ngoài trên classpath giống như nó làm cho application.propertiestệp.


4
Các application.propertiessẽ luôn luôn được nạp, với spring.config.locationbạn có thể thêm địa điểm cấu hình bổ sung được kiểm tra cho các tập tin (có nghĩa là khi nó kết thúc bằng một /) tuy nhiên nếu bạn đặt một danh sách dấu phẩy tách trong đó mà điểm đến file đó sẽ được nạp. Điều này cũng được giải thích trong mùa xuân Boot Reference Guide đây
M. Deinum

Câu trả lời:


155

Khi sử dụng Spring Boot, các thuộc tính được tải theo thứ tự sau (xem Cấu hình bên ngoài trong hướng dẫn tham khảo Spring Boot).

  1. Đối số dòng lệnh.
  2. Thuộc tính hệ thống Java (System.getProperties ()).
  3. Các biến môi trường OS.
  4. Các thuộc tính JNDI từ java: comp / env
  5. Một RandomValuePropertySource chỉ có các thuộc tính ở dạng ngẫu nhiên. *.
  6. Thuộc tính ứng dụng bên ngoài jar được đóng gói của bạn (application.properties bao gồm YAML và các biến thể hồ sơ).
  7. Thuộc tính ứng dụng được đóng gói bên trong jar của bạn (application.properties bao gồm YAML và các biến thể hồ sơ).
  8. @PropertySource chú thích trên các lớp @Configuration của bạn.
  9. Thuộc tính mặc định (được chỉ định bằng SpringApplication.setDefaultProperties).

Khi giải quyết các thuộc tính (tức là @Value("${myprop}") giải được thực hiện theo thứ tự ngược lại (nên bắt đầu bằng số 9).

Để thêm các tệp khác nhau, bạn có thể sử dụng các spring.config.locationthuộc tính có danh sách các tệp thuộc tính hoặc vị trí tệp (thư mục) được phân tách bằng dấu phẩy.

-Dspring.config.location=your/config/dir/

Phần trên sẽ thêm một thư mục sẽ được tư vấn cho application.propertiescác tệp.

-Dspring.config.location=classpath:job1.properties,classpath:job2.properties

Thao tác này sẽ thêm tệp 2 thuộc tính vào các tệp được tải.

Các tệp và vị trí cấu hình mặc định được tải trước các tệp được chỉ định theo cá nhân spring.config.locationcó nghĩa là tệp sau sẽ luôn ghi đè các thuộc tính được đặt trong các tệp trước đó. (Xem thêm phần này của Hướng dẫn Tham khảo Khởi động Mùa xuân).

Nếu spring.config.locationchứa các thư mục (trái ngược với các tệp), chúng phải kết thúc bằng / (và sẽ được nối với các tên được tạo từ spring.config.nametrước khi được tải). Đường dẫn tìm kiếm mặc định classpath:,classpath:/config,file:,file:config/luôn được sử dụng, bất kể giá trị của spring.config.location. Bằng cách đó, bạn có thể thiết lập các giá trị mặc định cho ứng dụng của mình application.properties(hoặc bất kỳ tên cơ sở nào khác mà bạn chọn spring.config.name) và ghi đè nó trong thời gian chạy bằng một tệp khác, giữ nguyên giá trị mặc định.

CẬP NHẬT: Vì hành vi của spring.config.location hiện ghi đè mặc định thay vì thêm vào nó. Bạn cần sử dụng spring.config.additional-location để giữ các giá trị mặc định. Đây là sự thay đổi hành vi từ 1.x thành 2.x


2
Cảm ơn nhưng tôi đã đọc tài liệu tham khảo này rồi và việc làm theo khiến tôi khó hiểu "-Dspring.config.location = your / config / dir / Cái ở trên sẽ thêm một thư mục sẽ được tư vấn cho các tệp application.properties." Nó có nghĩa là gì bởi các tệp application.properties. Đó chỉ là một tập tin. Trong mọi trường hợp nếu nó có thể chọn toàn bộ thư mục với "/" ở cuối thì tôi không cần chỉ định mỗi danh sách là danh sách được phân tách bằng dấu phẩy. Tôi nghĩ rằng tôi đã thử cả hai cách tiếp cận như tôi đã đề cập trong bài đăng của mình nhưng tôi sẽ thử thêm một lần nữa
nir

Như đã nêu trong tài liệu, nó sẽ được chọn giống như các vị trí mặc định khác cho application.propertiesapplication-[env].properties. Nó không tính đến các tệp thuộc tính khác. Điều này cũng được nêu trong hướng dẫn tham khảo (trong phần liên kết dẫn đến và trích dẫn từ hướng dẫn tham khảo).
M. Deinum

1
Có nhưng đó là điều không có ý nghĩa với tôi .. tại sao chỉ xem xét một loại tệp từ một thư mục trên classpath thay vì toàn bộ thư mục. Nó buộc bạn chỉ sử dụng một tệp thuộc tính mà không phải là imo tốt. Giống như trong tomcat, tôi có thể cấu hình common.loader để đặt thư mục cụ thể (và mọi thứ bên trong nó) trên classpath tại sao boot classloader không thể hỗ trợ nó.
nir

3
Tài liệu trích dẫn không hữu ích. Nếu tài liệu rõ ràng (đủ? Theo cách đặc biệt cần thiết?) Thì câu hỏi sẽ không cần thiết. Ví dụ, trong trường hợp này, nó thực sự không rõ ràng về cách thức config.locationconfig.namestương tác, mặc dù nó có vẻ rõ ràng với những người đã biết cách họ tương tác. Bạn có thể cập nhật câu trả lời của mình để thêm điều gì đó vào tài liệu không?
Narfanator

13
Điều này nên được cập nhật, vì hành vi spring.config.locationhiện ghi đè mặc định thay vì thêm vào nó. Bạn cần sử dụng spring.config.additional-locationđể giữ nguyên giá trị mặc định. Đây là sự thay đổi hành vi từ 1.x thành 2.x.
Robin

32

Với khởi động Spring, spring.config.location hoạt động, chỉ cần cung cấp các tệp thuộc tính được phân tách bằng dấu phẩy.

xem đoạn mã dưới đây

@PropertySource(ignoreResourceNotFound=true,value="classpath:jdbc-${spring.profiles.active}.properties")
public class DBConfig{

     @Value("${jdbc.host}")
        private String jdbcHostName;
     }
}

người ta có thể đặt phiên bản mặc định của jdbc.properties bên trong ứng dụng. Các phiên bản bên ngoài có thể được thiết lập nói dối điều này.

java -jar target/myapp.jar --spring.config.location=classpath:file:///C:/Apps/springtest/jdbc.properties,classpath:file:///C:/Apps/springtest/jdbc-dev.properties

Dựa trên giá trị cấu hình được đặt bằng thuộc tính spring.profiles.active, giá trị của jdbc.host sẽ được chọn. Vì vậy, khi (trên cửa sổ)

set spring.profiles.active=dev

jdbc.host sẽ lấy giá trị từ jdbc-dev.properties.

cho

set spring.profiles.active=default

jdbc.host sẽ lấy giá trị từ jdbc.properties.


Tôi không tin rằng khối mã đầu tiên sẽ hoạt động. Tôi biết như tôi đã đặt mình vào câu trả lời này, và làm theo câu trả lời này . Xem jira.springsource.org/browse/SPR-8539 được tham chiếu trong câu trả lời để biết rõ hơn.
Sowka

27

Spring boot 1.X và Spring Boot 2.X không cung cấp các tùy chọn và hành vi giống nhau về Externalized Configuration .

Câu trả lời rất hay của M. Deinum đề cập đến các đặc điểm của Spring Boot 1.
Tôi sẽ cập nhật cho Spring Boot 2 tại đây.

Nguồn thuộc tính môi trường và thứ tự

Spring Boot 2 sử dụng một PropertySourcethứ tự rất đặc biệt được thiết kế để cho phép ghi đè hợp lý các giá trị. Các thuộc tính được xem xét theo thứ tự sau:

  • Thuộc tính cài đặt chung của Devtools trên thư mục chính của bạn (~ / .spring-boot-devtools.properties khi devtools đang hoạt động).

  • @TestPropertySource chú thích về các bài kiểm tra của bạn.

  • @SpringBootTest#propertiesthuộc tính chú thích trên các bài kiểm tra của bạn. Đối số dòng lệnh.

  • Thuộc tính từ SPRING_APPLICATION_JSON(JSON nội tuyến được nhúng trong biến môi trường hoặc thuộc tính hệ thống).

  • ServletConfig tham số init.

  • ServletContext tham số init.

  • Thuộc tính JNDI từ java:comp/env.

  • Thuộc tính hệ thống Java ( System.getProperties()).

  • Các biến môi trường OS.

  • A RandomValuePropertySourcechỉ có thuộc tính ngẫu nhiên. *.

  • Thuộc tính ứng dụng cụ thể theo cấu hình bên ngoài jar được đóng gói của bạn ( application-{profile}.propertiesvà các biến thể YAML).

  • Thuộc tính ứng dụng cụ thể theo cấu hình được đóng gói bên trong jar của bạn ( application-{profile}.propertiesvà các biến thể YAML).

  • Thuộc tính ứng dụng bên ngoài jar được đóng gói của bạn ( application.propertiesvà các biến thể YAML).

  • Thuộc tính ứng dụng được đóng gói bên trong jar của bạn ( application.propertiesvà các biến thể YAML).

  • @PropertySourcechú thích về các @Configurationlớp học của bạn . Thuộc tính mặc định (được chỉ định bởi cài đặt SpringApplication.setDefaultProperties).

Để chỉ định các tệp thuộc tính bên ngoài, các tùy chọn này sẽ khiến bạn quan tâm:

  • Thuộc tính ứng dụng cụ thể theo cấu hình bên ngoài jar được đóng gói của bạn ( application-{profile}.propertiesvà các biến thể YAML).

  • Thuộc tính ứng dụng bên ngoài jar được đóng gói của bạn ( application.propertiesvà các biến thể YAML).

  • @PropertySourcechú thích về các @Configurationlớp học của bạn . Thuộc tính mặc định (được chỉ định bởi cài đặt SpringApplication.setDefaultProperties).

Bạn chỉ có thể sử dụng một trong 3 tùy chọn này hoặc kết hợp chúng tùy theo yêu cầu của bạn.
Ví dụ đối với các trường hợp rất đơn giản chỉ sử dụng các thuộc tính dành riêng cho cấu hình là đủ nhưng trong các trường hợp khác, bạn có thể muốn sử dụng cả các thuộc tính dành riêng cho cấu hình, thuộc tính mặc định và@PropertySource .

Vị trí mặc định cho các tệp application.properties

Về application.propertiestệp (và biến thể), theo mặc định Spring sẽ tải chúng và thêm thuộc tính của chúng trong môi trường từ những tệp này theo thứ tự sau:

  • Thư mục con A / config của thư mục hiện tại

  • Thư mục hiện tại

  • Một gói classpath / config

  • Gốc classpath

Các ưu tiên cao hơn theo đúng nghĩa đen:
classpath:/,classpath:/config/,file:./,file:./config/ .

Làm thế nào để sử dụng các tệp thuộc tính với các tên cụ thể?

Các vị trí mặc định không phải lúc nào cũng đủ: các vị trí mặc định như tên tệp mặc định ( application.properties) có thể không phù hợp. Bên cạnh đó, như trong câu hỏi OP, bạn có thể cần chỉ định nhiều tệp cấu hình khác với application.properties(và biến thể).
Vì thếspring.config.name sẽ không đủ.

Trong trường hợp này, bạn nên cung cấp vị trí rõ ràng bằng cách sử dụng thuộc tính spring.config.locationmôi trường (là danh sách các vị trí thư mục hoặc đường dẫn tệp được phân tách bằng dấu phẩy).
Để được miễn phí về mẫu tên tệp, hãy ưu tiên danh sách đường dẫn tệp trên danh sách thư mục.
Ví dụ làm như thế:

java -jar myproject.jar --spring.config.location=classpath:/default.properties,classpath:/override.properties

Cách đó là chi tiết nhất chỉ xác định thư mục nhưng nó cũng là cách chỉ định rất tinh vi các tệp cấu hình của chúng tôi và ghi lại rõ ràng các thuộc tính được sử dụng hiệu quả.

spring.config.location hiện thay thế các vị trí mặc định thay vì thêm vào chúng

Với Spring Boot 1, spring.config.locationđối số thêm các vị trí được chỉ định trong môi trường Spring.
Nhưng từ Spring Boot 2, spring.config.locationthay thế các vị trí mặc định được Spring sử dụng bằng các vị trí được chỉ định trong môi trường Spring như đã nêu trong tài liệu .

Khi các vị trí cấu hình tùy chỉnh được định cấu hình bằng cách sử dụng spring.config.location, chúng sẽ thay thế các vị trí mặc định. Ví dụ: nếu spring.config.locationđược định cấu hình với giá trị classpath:/custom-config/, file:./custom-config/thứ tự tìm kiếm sẽ trở thành như sau:

  1. file:./custom-config/

  2. classpath:custom-config/

spring.config.locationbây giờ là một cách để đảm bảo rằng bất kỳ application.propertiestệp nào cũng phải được chỉ định rõ ràng.
Đối với các JAR uber không được dùng để đóng gói application.propertiestệp, điều đó khá hay.

Để giữ hành vi cũ spring.config.locationtrong khi sử dụng Spring Boot 2, bạn có thể sử dụng thuộc tính mới spring.config.additional-locationthay vì thuộc tính spring.config.locationđó vẫn thêm các vị trí như đã nêu trong tài liệu :

Ngoài ra, khi các vị trí cấu hình tùy chỉnh được định cấu hình bằng cách sử dụng spring.config.additional-location, chúng sẽ được sử dụng ngoài các vị trí mặc định.


Trong thực tế

Vì vậy, giả sử rằng như trong câu hỏi OP, bạn có 2 tệp thuộc tính bên ngoài để chỉ định và 1 tệp thuộc tính có trong uber jar.

Để chỉ sử dụng các tệp cấu hình bạn đã chỉ định:

-Dspring.config.location=classpath:/job1.properties,classpath:/job2.properties,classpath:/applications.properties   

Để thêm các tệp cấu hình vào các vị trí mặc định này:

-Dspring.config.additional-location=classpath:/job1.properties,classpath:/job2.properties

classpath:/applications.properties trong ví dụ cuối cùng là không bắt buộc vì các vị trí mặc định có điều đó và các vị trí mặc định ở đây không bị ghi đè mà được mở rộng.


Câu trả lời của bạn thực sự hoàn chỉnh ngoại trừ một điều: Spring sẽ tìm cấu hình bên ngoài job1.properties ở đâu trên đĩa nếu bạn chỉ định: "classpath: /job1.properties"? Bạn đã thêm thư mục chứa các thuộc tính bên ngoài của mình vào đường dẫn classpath ở đây như thế nào?
Tristan

@Tristan, về cơ bản, spring có thể đọc một application.propertiesvới tất cả các tham số và nhiều ${file_name}.propertiesvới các bộ thuộc tính được xác định một phần. Vì vậy, nếu bạn sử dụng @PropertySourcehoặc các liên kết mạnh khác tới tệp, bạn có thể tạo tệp bên ngoài khác và ghi đè các thuộc tính đó (Ví dụ: from classpath:file.properties).
Mister_Jesus

23

Hãy nhìn vào PropertyPlaceholderConfigurer, tôi thấy nó rõ ràng hơn để sử dụng hơn là chú thích.

ví dụ

@Configuration
public class PropertiesConfiguration {


    @Bean
    public PropertyPlaceholderConfigurer properties() {
        final PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer();
//        ppc.setIgnoreUnresolvablePlaceholders(true);
        ppc.setIgnoreResourceNotFound(true);

        final List<Resource> resourceLst = new ArrayList<Resource>();

        resourceLst.add(new ClassPathResource("myapp_base.properties"));
        resourceLst.add(new FileSystemResource("/etc/myapp/overriding.propertie"));
        resourceLst.add(new ClassPathResource("myapp_test.properties"));
        resourceLst.add(new ClassPathResource("myapp_developer_overrides.properties")); // for Developer debugging.

        ppc.setLocations(resourceLst.toArray(new Resource[]{}));

        return ppc;
    }

Cảm ơn bạn rất nhiều cho câu trả lời này. Bạn có thể vui lòng cho tôi biết làm cách nào tôi có thể đạt được điều tương tự trong một dự án có cấu hình XML như vậy cho những thứ khác nhau mà không có tệp XML cơ sở không? Câu trả lời của bạn ở trên đã giúp tôi trong dự án khác dựa trên chú thích. Cảm ơn một lần nữa vì điều đó.
Chetan

8

đây là một cách tiếp cận đơn giản bằng cách sử dụng khởi động lò xo

TestClass.java

@Configuration
@Profile("one")
@PropertySource("file:/{selected location}/app.properties")
public class TestClass {

    @Autowired
    Environment env;

    @Bean
    public boolean test() {
        System.out.println(env.getProperty("test.one"));
        return true;
    }
}

các app.properties bối cảnh này, trong vị trí được lựa chọn

test.one = 1234

ứng dụng khởi động mùa xuân của bạn

@SpringBootApplication

public class TestApplication {

    public static void main(String[] args) {
        SpringApplication.run(testApplication.class, args);
    }
}

và ngữ cảnh application.properties được xác định trước

spring.profiles.active = one

bạn có thể viết bao nhiêu lớp cấu hình tùy thích và bật / tắt chúng chỉ bằng cách thiết lập spring.profiles.active = tên / tên cấu hình {được phân tách bằng dấu phẩy}

như bạn có thể thấy khởi động mùa xuân rất tuyệt, nó chỉ cần đôi khi làm quen, điều đáng nói là bạn cũng có thể sử dụng @Value trên các trường của mình

@Value("${test.one}")
String str;

7

Tôi đã từng gặp vấn đề tương tự. Tôi muốn có khả năng ghi đè tệp cấu hình bên trong khi khởi động bằng tệp bên ngoài, tương tự như phát hiện ứng dụng Spring Boot. Trong trường hợp của tôi, đó là tệp user.properties nơi người dùng ứng dụng của tôi được lưu trữ.

Yêu cầu của tôi:

Tải tệp từ các vị trí sau (theo thứ tự này)

  1. Classpath
  2. A / config của thư mục hiện tại.
  3. Thư mục hiện tại
  4. Từ thư mục hoặc vị trí tệp được cung cấp bởi tham số dòng lệnh khi khởi động

Tôi đã đưa ra giải pháp sau:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.PathResource;
import org.springframework.core.io.Resource;

import java.io.IOException;
import java.util.Properties;

import static java.util.Arrays.stream;

@Configuration
public class PropertiesConfig {

    private static final Logger LOG = LoggerFactory.getLogger(PropertiesConfig.class);

    private final static String PROPERTIES_FILENAME = "user.properties";

    @Value("${properties.location:}")
    private String propertiesLocation;

    @Bean
    Properties userProperties() throws IOException {
        final Resource[] possiblePropertiesResources = {
                new ClassPathResource(PROPERTIES_FILENAME),
                new PathResource("config/" + PROPERTIES_FILENAME),
                new PathResource(PROPERTIES_FILENAME),
                new PathResource(getCustomPath())
        };
        // Find the last existing properties location to emulate spring boot application.properties discovery
        final Resource propertiesResource = stream(possiblePropertiesResources)
                .filter(Resource::exists)
                .reduce((previous, current) -> current)
                .get();
        final Properties userProperties = new Properties();

        userProperties.load(propertiesResource.getInputStream());

        LOG.info("Using {} as user resource", propertiesResource);

        return userProperties;
    }

    private String getCustomPath() {
        return propertiesLocation.endsWith(".properties") ? propertiesLocation : propertiesLocation + PROPERTIES_FILENAME;
    }

}

Bây giờ ứng dụng sử dụng tài nguyên classpath, nhưng cũng kiểm tra tài nguyên ở các vị trí nhất định khác. Tài nguyên cuối cùng tồn tại sẽ được chọn và sử dụng. Tôi có thể khởi động ứng dụng của mình bằng java -jar myapp.jar --properties.location = / directory / myproperties.properties để sử dụng vị trí thuộc tính làm nổi con thuyền của tôi.

Một chi tiết quan trọng ở đây: Sử dụng một Chuỗi trống làm giá trị mặc định cho các thuộc tính.location trong chú thích @Value để tránh lỗi khi thuộc tính không được đặt.

Quy ước cho một property.location là: Sử dụng một thư mục hoặc một đường dẫn đến một tệp thuộc tính dưới dạng property.location.

Nếu bạn chỉ muốn ghi đè các thuộc tính cụ thể, một PropertiesFactoryBean với setIgnoreResourceNotFound (true) có thể được sử dụng với mảng tài nguyên được đặt làm vị trí.

Tôi chắc chắn rằng giải pháp này có thể được mở rộng để xử lý nhiều tệp ...

BIÊN TẬP

Đây là giải pháp của tôi cho nhiều tệp :) Giống như trước đây, nó có thể được kết hợp với một PropertiesFactoryBean.

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.PathResource;
import org.springframework.core.io.Resource;

import java.io.IOException;
import java.util.Map;
import java.util.Properties;

import static java.util.Arrays.stream;
import static java.util.stream.Collectors.toMap;

@Configuration
class PropertiesConfig {

    private final static Logger LOG = LoggerFactory.getLogger(PropertiesConfig.class);
    private final static String[] PROPERTIES_FILENAMES = {"job1.properties", "job2.properties", "job3.properties"};

    @Value("${properties.location:}")
    private String propertiesLocation;

    @Bean
    Map<String, Properties> myProperties() {
        return stream(PROPERTIES_FILENAMES)
                .collect(toMap(filename -> filename, this::loadProperties));
    }

    private Properties loadProperties(final String filename) {
        final Resource[] possiblePropertiesResources = {
                new ClassPathResource(filename),
                new PathResource("config/" + filename),
                new PathResource(filename),
                new PathResource(getCustomPath(filename))
        };
        final Resource resource = stream(possiblePropertiesResources)
                .filter(Resource::exists)
                .reduce((previous, current) -> current)
                .get();
        final Properties properties = new Properties();

        try {
            properties.load(resource.getInputStream());
        } catch(final IOException exception) {
            throw new RuntimeException(exception);
        }

        LOG.info("Using {} as user resource", resource);

        return properties;
    }

    private String getCustomPath(final String filename) {
        return propertiesLocation.endsWith(".properties") ? propertiesLocation : propertiesLocation + filename;
    }

}

cách giải quyết tốt. Giống như cấu trúc java8 đó! dù sao thì tôi không thể sử dụng nó vì tôi cần nhiều thuộc tính bean không chỉ một. Nếu bạn thấy các CHỈNH SỬA của tôi, cách giải quyết của tôi khá giống và gọn gàng cho trường hợp sử dụng của tôi.
nir

Tôi gửi một phiên bản dành cho nhiều file, chỉ cần cho đầy đủ;)
mxsb

6

Spring boot cho phép chúng ta viết các cấu hình khác nhau để viết cho các môi trường khác nhau, ví dụ: chúng ta có thể có các tệp thuộc tính riêng biệt cho môi trường production, qa và local

tệp application-local.properties có cấu hình theo máy cục bộ của tôi là

spring.profiles.active=local

spring.data.mongodb.host=localhost
spring.data.mongodb.port=27017
spring.data.mongodb.database=users
spring.data.mongodb.username=humble_freak
spring.data.mongodb.password=freakone

spring.rabbitmq.host=localhost
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
spring.rabbitmq.port=5672

rabbitmq.publish=true

Tương tự, chúng ta có thể viết application-prod.properties và application-qa.properties nhiều tệp thuộc tính như chúng ta muốn

sau đó viết một số tập lệnh để khởi động ứng dụng cho các môi trường khác nhau, ví dụ:

mvn spring-boot:run -Drun.profiles=local
mvn spring-boot:run -Drun.profiles=qa
mvn spring-boot:run -Drun.profiles=prod

5

Tôi vừa gặp sự cố tương tự như vậy và cuối cùng đã tìm ra nguyên nhân: tệp application.properties có quyền sở hữu sai và thuộc tính rwx. Vì vậy, khi tomcat khởi động, tệp application.properties đã ở đúng vị trí nhưng thuộc sở hữu của người dùng khác:

$ chmod 766 application.properties

$ chown tomcat application.properties

Tôi nghĩ rằng tôi có vấn đề tương tự. Tôi đã cài đặt tomcat trong thư mục opt. Bạn đã đặt hồ sơ của mình ở đâu? Tôi cũng nên thay đổi các thuộc tính thư mục?
anakin59490

3

Một phiên bản sửa đổi của giải pháp @mxsb cho phép chúng tôi xác định nhiều tệp và trong trường hợp của tôi, đây là các tệp yml.

Trong application-dev.yml của tôi, tôi đã thêm cấu hình này cho phép tôi đưa tất cả các yml có -dev.yml vào chúng. Đây cũng có thể là danh sách các tệp cụ thể. "classpath: /test/test.yml,classpath: /test2/test.yml"

application:
  properties:
    locations: "classpath*:/**/*-dev.yml"

Điều này giúp nhận được một bản đồ thuộc tính.

@Configuration

public class PropertiesConfig {

private final static Logger LOG = LoggerFactory.getLogger(PropertiesConfig.class);

@Value("${application.properties.locations}")
private String[] locations;

@Autowired
private ResourceLoader rl;

@Bean
Map<String, Properties> myProperties() {
    return stream(locations)
            .collect(toMap(filename -> filename, this::loadProperties));
}

private Properties loadProperties(final String filename) {

    YamlPropertySourceLoader loader = new YamlPropertySourceLoader();
    try {
        final Resource[] possiblePropertiesResources = ResourcePatternUtils.getResourcePatternResolver(rl).getResources(filename);
        final Properties properties = new Properties();
        stream(possiblePropertiesResources)
                .filter(Resource::exists)
                .map(resource1 -> {
                    try {
                        return loader.load(resource1.getFilename(), resource1);
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                }).flatMap(l -> l.stream())
                .forEach(propertySource -> {
                    Map source = ((MapPropertySource) propertySource).getSource();
                    properties.putAll(source);
                });

        return properties;
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}
}

Tuy nhiên, nếu giống như trong trường hợp của tôi, tôi muốn phải chia nhỏ các tệp yml cho từng hồ sơ và tải chúng và tiêm trực tiếp vào cấu hình mùa xuân trước khi khởi chạy bean.

config
    - application.yml
    - application-dev.yml
    - application-prod.yml
management
    - management-dev.yml
    - management-prod.yml

... bạn có ý tưởng

Thành phần hơi khác

@Component
public class PropertiesConfigurer extends     PropertySourcesPlaceholderConfigurer
    implements EnvironmentAware, InitializingBean {

private final static Logger LOG = LoggerFactory.getLogger(PropertiesConfigurer.class);

private String[] locations;

@Autowired
private ResourceLoader rl;
private Environment environment;

@Override
public void setEnvironment(Environment environment) {
    // save off Environment for later use
    this.environment = environment;
    super.setEnvironment(environment);
}

@Override
public void afterPropertiesSet() throws Exception {
    // Copy property sources to Environment
    MutablePropertySources envPropSources = ((ConfigurableEnvironment) environment).getPropertySources();
    envPropSources.forEach(propertySource -> {
        if (propertySource.containsProperty("application.properties.locations")) {
            locations = ((String) propertySource.getProperty("application.properties.locations")).split(",");
            stream(locations).forEach(filename -> loadProperties(filename).forEach(source ->{
                envPropSources.addFirst(source);
            }));
        }
    });
}


private List<PropertySource> loadProperties(final String filename) {
    YamlPropertySourceLoader loader = new YamlPropertySourceLoader();
    try {
        final Resource[] possiblePropertiesResources = ResourcePatternUtils.getResourcePatternResolver(rl).getResources(filename);
        final Properties properties = new Properties();
        return stream(possiblePropertiesResources)
                .filter(Resource::exists)
                .map(resource1 -> {
                    try {
                        return loader.load(resource1.getFilename(), resource1);
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                }).flatMap(l -> l.stream())
                .collect(Collectors.toList());
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}

}


3

Nếu bạn muốn ghi đè các giá trị được chỉ định trong tệp application.properties của mình, bạn có thể thay đổi cấu hình đang hoạt động của mình trong khi chạy ứng dụng và tạo tệp thuộc tính ứng dụng cho cấu hình. Vì vậy, ví dụ: hãy chỉ định hồ sơ hoạt động "ghi đè" và sau đó, giả sử bạn đã tạo tệp thuộc tính ứng dụng mới của mình có tên "application-override.properties" theo / tmp, thì bạn có thể chạy

java -jar yourApp.jar --spring.profiles.active="override" --spring.config.location="file:/tmp/,classpath:/" 

Các giá trị được chỉ định trong spring.config.location được đánh giá theo thứ tự ngược lại. Vì vậy, trong ví dụ của tôi, classpat được đánh giá đầu tiên, sau đó là giá trị tệp.

Nếu tệp jar và tệp "application-override.properties" nằm trong thư mục hiện tại, bạn thực sự có thể sử dụng

java -jar yourApp.jar --spring.profiles.active="override"

vì Spring Boot sẽ tìm tệp thuộc tính cho bạn


1
Nó sẽ thông báo cho mùa xuân sử dụng hồ sơ "ghi đè" làm hồ sơ hoạt động của bạn; nó thực sự sẽ vượt qua giá trị được chỉ định trong tệp application.yml hoặc application.properties
acaruci 15/02/19

nó sẽ tìm bên trong thư mục cho bất kỳ tệp cấu hình nào .ymal hoặc .properties trong trường hợp của tôi, tôi chỉ đặt application-profile.yml thì nó sẽ diễn ra chính xác, Cảm ơn @acaruci, đó là một chuyến đi tốt đẹp
Ahmed Salem

0

Tôi nhận thấy đây là một mô hình hữu ích để làm theo:

@RunWith(SpringRunner)
@SpringBootTest(classes = [ TestConfiguration, MyApplication ],
        properties = [
                "spring.config.name=application-MyTest_LowerImportance,application-MyTest_MostImportant"
                ,"debug=true", "trace=true"
        ]
)

Ở đây chúng tôi ghi đè việc sử dụng "application.yml" để sử dụng "application-MyTest_LowerImportance.yml" và cả "application-MyTest_MostImportant.yml"
(Spring cũng sẽ tìm kiếm các tệp .properties)

Cũng được bao gồm như một phần thưởng bổ sung là cài đặt gỡ lỗi và theo dõi, trên một dòng riêng để bạn có thể nhận xét chúng nếu cần;]

Gỡ lỗi / theo dõi cực kỳ hữu ích vì Spring sẽ kết xuất tên của tất cả các tệp mà nó tải và những tệp mà nó cố gắng tải.
Bạn sẽ thấy các dòng như thế này trong bảng điều khiển khi chạy:

TRACE 93941 --- [   main] o.s.b.c.c.ConfigFileApplicationListener  : Skipped config file 'file:./config/application-MyTest_MostImportant.properties' (file:./config/application-MyTest_MostImportant.properties) resource not found
TRACE 93941 --- [   main] o.s.b.c.c.ConfigFileApplicationListener  : Skipped config file 'file:./config/application-MyTest_MostImportant.xml' (file:./config/application-MyTest_MostImportant.xml) resource not found
TRACE 93941 --- [   main] o.s.b.c.c.ConfigFileApplicationListener  : Skipped config file 'file:./config/application-MyTest_MostImportant.yml' (file:./config/application-MyTest_MostImportant.yml) resource not found
TRACE 93941 --- [   main] o.s.b.c.c.ConfigFileApplicationListener  : Skipped config file 'file:./config/application-MyTest_MostImportant.yaml' (file:./config/application-MyTest_MostImportant.yaml) resource not found
TRACE 93941 --- [   main] o.s.b.c.c.ConfigFileApplicationListener  : Skipped config file 'file:./config/application-MyTest_LowerImportance.properties' (file:./config/application-MyTest_LowerImportance.properties) resource not found
TRACE 93941 --- [   main] o.s.b.c.c.ConfigFileApplicationListener  : Skipped config file 'file:./config/application-MyTest_LowerImportance.xml' (file:./config/application-MyTest_LowerImportance.xml) resource not found
TRACE 93941 --- [   main] o.s.b.c.c.ConfigFileApplicationListener  : Skipped config file 'file:./config/application-MyTest_LowerImportance.yml' (file:./config/application-MyTest_LowerImportance.yml) resource not found
TRACE 93941 --- [   main] o.s.b.c.c.ConfigFileApplicationListener  : Skipped config file 'file:./config/application-MyTest_LowerImportance.yaml' (file:./config/application-MyTest_LowerImportance.yaml) resource not found
TRACE 93941 --- [   main] o.s.b.c.c.ConfigFileApplicationListener  : Skipped config file 'file:./application-MyTest_MostImportant.properties' (file:./application-MyTest_MostImportant.properties) resource not found
TRACE 93941 --- [   main] o.s.b.c.c.ConfigFileApplicationListener  : Skipped config file 'file:./application-MyTest_MostImportant.xml' (file:./application-MyTest_MostImportant.xml) resource not found
TRACE 93941 --- [   main] o.s.b.c.c.ConfigFileApplicationListener  : Skipped config file 'file:./application-MyTest_MostImportant.yml' (file:./application-MyTest_MostImportant.yml) resource not found
TRACE 93941 --- [   main] o.s.b.c.c.ConfigFileApplicationListener  : Skipped config file 'file:./application-MyTest_MostImportant.yaml' (file:./application-MyTest_MostImportant.yaml) resource not found
TRACE 93941 --- [   main] o.s.b.c.c.ConfigFileApplicationListener  : Skipped config file 'file:./application-MyTest_LowerImportance.properties' (file:./application-MyTest_LowerImportance.properties) resource not found
TRACE 93941 --- [   main] o.s.b.c.c.ConfigFileApplicationListener  : Skipped config file 'file:./application-MyTest_LowerImportance.xml' (file:./application-MyTest_LowerImportance.xml) resource not found
TRACE 93941 --- [   main] o.s.b.c.c.ConfigFileApplicationListener  : Skipped config file 'file:./application-MyTest_LowerImportance.yml' (file:./application-MyTest_LowerImportance.yml) resource not found
TRACE 93941 --- [   main] o.s.b.c.c.ConfigFileApplicationListener  : Skipped config file 'file:./application-MyTest_LowerImportance.yaml' (file:./application-MyTest_LowerImportance.yaml) resource not found
TRACE 93941 --- [   main] o.s.b.c.c.ConfigFileApplicationListener  : Skipped config file 'classpath:/config/application-MyTest_MostImportant.properties' resource not found
TRACE 93941 --- [   main] o.s.b.c.c.ConfigFileApplicationListener  : Skipped config file 'classpath:/config/application-MyTest_MostImportant.xml' resource not found
TRACE 93941 --- [   main] o.s.b.c.c.ConfigFileApplicationListener  : Skipped config file 'classpath:/config/application-MyTest_MostImportant.yml' resource not found
TRACE 93941 --- [   main] o.s.b.c.c.ConfigFileApplicationListener  : Skipped config file 'classpath:/config/application-MyTest_MostImportant.yaml' resource not found
TRACE 93941 --- [   main] o.s.b.c.c.ConfigFileApplicationListener  : Skipped config file 'classpath:/config/application-MyTest_LowerImportance.properties' resource not found
TRACE 93941 --- [   main] o.s.b.c.c.ConfigFileApplicationListener  : Skipped config file 'classpath:/config/application-MyTest_LowerImportance.xml' resource not found
TRACE 93941 --- [   main] o.s.b.c.c.ConfigFileApplicationListener  : Skipped config file 'classpath:/config/application-MyTest_LowerImportance.yml' resource not found
TRACE 93941 --- [   main] o.s.b.c.c.ConfigFileApplicationListener  : Skipped config file 'classpath:/config/application-MyTest_LowerImportance.yaml' resource not found
TRACE 93941 --- [   main] o.s.b.c.c.ConfigFileApplicationListener  : Skipped config file 'classpath:/application-MyTest_MostImportant.properties' resource not found
TRACE 93941 --- [   main] o.s.b.c.c.ConfigFileApplicationListener  : Skipped config file 'classpath:/application-MyTest_MostImportant.xml' resource not found
DEBUG 93941 --- [   main] o.s.b.c.c.ConfigFileApplicationListener  : Loaded config file 'file:/Users/xxx/dev/myproject/target/test-classes/application-MyTest_MostImportant.yml' (classpath:/application-MyTest_MostImportant.yml)
TRACE 93941 --- [   main] o.s.b.c.c.ConfigFileApplicationListener  : Skipped config file 'classpath:/application-MyTest_MostImportant.yaml' resource not found
TRACE 93941 --- [   main] o.s.b.c.c.ConfigFileApplicationListener  : Skipped config file 'classpath:/application-MyTest_LowerImportance.properties' resource not found
TRACE 93941 --- [   main] o.s.b.c.c.ConfigFileApplicationListener  : Skipped config file 'classpath:/application-MyTest_LowerImportance.xml' resource not found
DEBUG 93941 --- [   main] o.s.b.c.c.ConfigFileApplicationListener  : Loaded config file 'file:/Users/xxx/dev/myproject/target/test-classes/application-MyTest_LowerImportance.yml' (classpath:/application-MyTest_LowerImportance.yml)
TRACE 93941 --- [   main] o.s.b.c.c.ConfigFileApplicationListener  : Skipped config file 'classpath:/application-MyTest_LowerImportance.yaml' resource not found
TRACE 93941 --- [   main] o.s.b.c.c.ConfigFileApplicationListener  : Skipped config file 'file:./config/application-MyTest_MostImportant-test.properties' (file:./config/application-MyTest_MostImportant-test.properties) resource not found

-1

Tôi đã gặp phải rất nhiều vấn đề khi cố gắng tìm ra điều này. Đây là thiết lập của tôi,

Dev Env: Windows 10, Java: 1.8.0_25, Spring Boot: 2.0.3.RELEASE, Spring: 5.0.7.RELEASE

Những gì tôi tìm thấy là mùa xuân đang gắn bó với khái niệm "Giá trị mặc định hợp lý cho cấu hình". Điều này có nghĩa là, bạn phải có tất cả các tệp tài sản của mình như một phần của hồ sơ chiến tranh của bạn. Sau đó, bạn có thể ghi đè chúng bằng cách sử dụng thuộc tính dòng lệnh "--spring.config.additional-location" để trỏ đến các tệp thuộc tính bên ngoài. Nhưng điều này sẽ KHÔNG HOẠT ĐỘNG nếu tệp tài sản không phải là một phần của tệp chiến tranh ban đầu.

Mã demo: https://github.com/gselvara/spring-boot-property-demo/tree/master

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.