Spring Properties Dosyası Kullanımı ve Değer Okuma

Properties dosyaları, uygulamanızı farklı konfigürasyon ayarlarıyla ayrı ayrı yapılandırılabilir ve güncellenebilir hale getirir. Uygulamanızda birden fazla properties dosyası tanımlayabilirsiniz ve kullanabilirsiniz. Bu yazımızda properties dosyalarını nasıl tanımlayacağınızı ve içindeki değerleri uygulamanızda nasıl elde edebileceğinizi göstereceğiz.

Properties kullanımı tanımlama ve değer alma olarak başlıklandırarak anlatmaya başlayalım.

1. Properties Dosyaları

Spring, projenin classpath ve resources dizininde bulunan .properties dosyalarını kullanarak konfigürasyonlarınızı haricileştirmenin bir yolunu sağlar. Bu, konfigürasyonlarınızın ortam bazlı dinamik olarak yapılandırılabilmesi ve çoklu ortam kurulumları için çok kullanışlıdır.

😉
resources dizini Spring tarafından değil, Maven ve Gradle dizin yapısı tarafından sağlanan bir özelliktir. Build adımı sırasında Maven ve Gradle dosyaları buradan alacak ve bunları runtime classpath'inde kullanmanız için uygun yere yerleştirecektir (Ör: Java'nın -cp seçeneğiyle). 
💡
Spring Boot için varsayılan properties dosyası, projenin src/main/resources/ dizininde bulunan application.properties dosyasıdır. Yeni bir Spring Boot projesi oluşturduğunuzda bu varsayılan properties dosyasının içi boş olarak oluşturulur.

Aşağıda örnek bir .properties dosya içeriği gösterilmektedir:

spring.datasource.username=user
logging.level.root=INFO

Bu örnek dosya, bir data source için kullanıcı adı ve uygulamanın root log kaydı düzeyi için değerler içerir. Properties dosyaları key=value sözdizimi ile oluşturulur.

2. YAML Properties Dosyaları

Alternatif olarak, varsayılan .properties sözdizimi yerine Spring, YAML formatındaki properties dosyalarını da destekler. Dolayısıyla, varsayılan application.properties dosyası yerine, özelliklerinizi YAML biçiminde tercih ederseniz, application.yml adlı yeni bir özellik dosyası oluşturabilirsiniz:

spring:
   datasource:
      username: user
logging:
    level: 
  	  root: INFO      

Bunun gerektireceği tek fark, dosya içindeki içeriğin biçimlendirilmesidir, bu nedenle seçim yalnızca tercihe bağlıdır.

3. Properties Dosyalarındaki Değerlere Erişme

Şimdi ana konuya, yani yukarıda bahsedilen properties dosyalarındaki değerlere nasıl ulaşabileceğimize geçiyoruz. Fakat başlıkları incelerken Spring Framework ve Spring Boot projeleri için farklı kullanım şekillerine dikkat ediniz!

3.1. @Value Anotasyonunu Kullanarak

Spring Boot projenizde, varsayılan properties dosyası olan application.properties dosyasında tanımladığınız key'lere erişebilmek için direkt @Value anotasyonunu kullanabilirsiniz. Örneği test edebilmek için bu properties dosyasına blog.name key'ini ekleyelim.

blog.name=Kerteriz Blog

Ardından Blog sınıfımızda yer alan name değişkenine bu değeri @Value anotasyonu ile atayalım.

@Getter
@Component
public class Blog {

    @Value("${blog.name}")
    private String name;
}

Şimdi, TestController sınıfımızda Blog sınıfımızı inject edelim ve /test endpointi açarak name değişkenini return edelim.

@RestController
public class TestController {

    private final Blog blog;

    public TestController(Blog blog) {
        this.blog = blog;
    }

    @GetMapping("/test")
    public String test() {
        return blog.getName();
    }
}

Hızlıca GET isteği atalım ve sonucu kontrol edelim.

GET http://localhost:8080/test

HTTP/1.1 200 
Content-Type: text/plain;charset=UTF-8
Content-Length: 13
Date: Sat, 21 May 2022 19:07:07 GMT
Keep-Alive: timeout=60
Connection: keep-alive

Kerteriz Blog

Response code: 200; Time: 151ms; Content length: 13 bytes

Response body içinde application.properties dosyası içinde verdiğimiz değeri görüyoruz.

🛑
Spring Boot veya Spring Framework projesi olduğu farketmeksizin, @Value anotasyonu ile value inject edebilmek için, @Value anotasyonunu kullandığınız sınıfların Spring Context içinde yer alması gerekmektedir.

Bu uyarıyı daha net hale getirelim. Yukarıdaki örnekte name değişkenine application dosyanızdaki blog.name değerini enjekte etmek için Blog sınıfınızın Spring Context içinde yer alması gerekmektedir. Bir sınıfın ise Spring Context'e eklenebilmesi için şunlardan birine uyması gerekmektedir;

  • @Configuration anotasyonu ile işaretlenmiş sınıflarda @Bean anotasyonu ile instance'ının üretilmesi
  • Stereo-type anotasyonlardan (@Component, @Controller, @Repository) biriyle işaretlenmiş olması

Bu konuda daha detaylı bir bilgi için önceki yazımızı okuyabilirsiniz:

Spring Context Nedir? Bean Nasıl Oluşturulur ve Kullanılır?
Spring Context, Application Context veya IoC Container olarak tanımlanan yapıya nasıl bean ekleyebileceğimizi anlatıyoruz.
🛑
Eğer, User sınıfının instance'ını Spring Context'ten alarak değil de direkt kendiniz üreterek kullanırsanız name değişkenine herhangi bir değer atanmayacaktır.

Küçük bir örnekle bunu da ispatlayalım:

@SpringBootApplication
public class PureSpringBootApplication {
    public static void main(String[] args) {
        SpringApplication.run(PureSpringBootApplication.class, args);

        Blog blog = new Blog();
        System.out.println("==>" + blog.getName());
    }
}

Konsola bakarsanız ==>null satırını göreceksiniz.

💬
Atamak istediğiniz key değeri herhangi bir properties dosyasında mevcut değilse java.lang.IllegalArgumentException hatasını alırsınız ve uygulamanız boot edemez.

Eğer Spring Framework projeniz ile @Value anotasyonunu kullanmak istiyorsanız, properties dosyanızın konumunu @PropertySource anotasyonu ile belirtmelisiniz. Çünkü application.properties dosyası Spring Boot projesinde varsayılan olarak atanır. Spring Framework projeniz için bu properties dosyalarını siz seçmelisiniz.

Benzer olarak, Spring Boot projesinde de application.properties dışında başka bir properties dosyası kullanmak istiyorsanız yine aynı şekilde @PropertySource anotasyonu ile bu dosyayı belirtmelisiniz.

Öyleyse @PropertySource kullanımına bakalım.

3.1.1 @PropertySource Anotasyonu Kullanımı

Spring @PropertySource anotasyonu, Spring Environment'a properties dosyası sağlamak için kullanılır. Bu anotasyon, @Configuration sınıflarıyla birlikte veya @Value anotasyonu kullandığınız sınıflarla birlikte kullanılır.

@PropertySource anotasyonunu şu durumlarda kullanmak istiyoruz:

  • Spring Boot projesinde application.properties dosyası haricinde properties dosyaları tanımlayacaksınız.
  • Spring Framework projesinde properties dosyaları tanımlayacaksınız.

Örneğimizde, resources dizininde blog.properties dosyasını oluşturarak blog.name isimli key'i buraya taşıyoruz. Ardından Blog isimli sınıfımıza @PropertySource anotasyonunu ekliyoruz.

@Getter
@Component
@PropertySource("classpath:blog.properties")
public class Blog {

    @Value("${blog.name}")
    private String name;
}

Başka bir User isimli sınıf daha ekleyelim ve aynı şekilde user.name değerine erişelim.

@Getter
@Component
@PropertySource("classpath:user.properties")
public class User {

    @Value("${user.name}")
    private String name;

    @Value("${blog.name}")
    private String blogName;
}

Örnekte dikkat edeceğiniz üzere, User sınıfımızda sadece user.properties dosyasını ayarlasak bile Blog sınıfında ayarladığımız blog.properties dosyasının içindeki key'lere erişebiliyoruz.

@PropertySource anotasyonunu sınıf tanımları üzerinde ayrı ayrı kullanmak programınızın karmaşıklığını artırıp bu gibi unutacağınız durumlara neden olabileceği için @Configuration anotasyonlu sınıflarda @PropertySource kullanmak daha iyi olacaktır.

@Configuration
@PropertySource(value = "classpath:user.properties")
@PropertySource(value = "classpath:blog.properties")
public class AppProperties {
}

3.2. @ConfigurationProperties Anotasyonunu Kullanarak

@ConfigurationProperties anotasyonu, @Value anotasyonundan daha kapsamlı bir yaklaşım sunar.

Bu yöntem, özelliklere ayrı ayrı erişilen önceki çözümün aksine, properties dosyası içinde yer alan bütün keyleri bir POJO'da depolayarak değerlere erişmek için kullanılır.

@ConfigurationProperties anotasyonunu kullanabilmek için maven veya gradle dosyanıza spring-boot-configuration-processor bağımlılığını eklemelisiniz.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
    <optional>true</optional>
</dependency>
dependencies {
    annotationProcessor "org.springframework.boot:spring-boot-configuration-processor"
}

Ardından Spring Boot veya Spring Framework projelerimizde örneğimiz için blog.properties dosyamızın içini şu şekilde ayarlayalım. Tabi bu properties dosyasını belirtmek için bir üstteki başlıkta anlattığımız gibi @PropertySource anotasyonunu kullanmayı unutmayınız:

blog.name=Kerteriz Blog
blog.url=kerteriz.net
blog.author=Ismet BALAT

Şimdi Blog sınıfımızı @ConfigurationProperties anotasyonu ile işaretleyelim. Ayrıca tüm keyler blog prefixi içerdiğinden, prefix parametresi ile bunu belirtelim.

@Data
@Component
@ConfigurationProperties(prefix = "blog")
public class Blog {

    private String name;
    private String url;
    private String author;
}

Şimdi, TestController sınıfımızda Blog sınıfımızı inject edelim ve /test endpointi açarak User sınıfımızın tüm fieldlarını toString methodu ile return edelim.

@RestController
public class TestController {

    private final Blog blog;

    public TestController(Blog blog) {
        this.blog = blog;
    }

    @GetMapping("/test")
    public String test() {
        return blog.toString();
    }
}

Hızlıca GET isteği atalım ve sonucu kontrol edelim.

HTTP/1.1 200 
Content-Type: text/plain;charset=UTF-8
Content-Length: 62
Date: Sat, 21 May 2022 20:41:38 GMT
Keep-Alive: timeout=60
Connection: keep-alive

Blog(name=Kerteriz Blog, url=kerteriz.net, author=Ismet BALAT)

Response code: 200; Time: 5ms; Content length: 62 bytes