Spring Boot Application Context
Spring Boot in Practice Chapter 1 makalemde Application Context ifadesi çokça geçti. Application Context’i açıklamak gerekirse,
Türkçe karşılığını uygulama bağlamı olarak çevirebiliriz. Spring Boot uygulamaları başlangıçta application context olarak bilinen spring uygulama bağlamı üzerinde çalışır. ZetCode ‘ da application context için “Bir spring boot uygulamasının köşe taşıdır.” ifadesi geçmektedir. Uygulama bileşenlerine erişim için bean yönetimlerinden sorumludur.
Spring Application Context, spring uygulamları tarafından kullanılan bir containerdır. Bu container, uygulamanın tüm bileşenlerini yükler, yönetir ve bunların birbirleriyle etkileşimini sağlar. Application Context uygulamanın başlangıç noktasıdır ve uygulamanın diğer tüm bileşenleri bu context içerisinde yürütülür. Bu context, uygulama içindeki tüm spring bileşenlerini yükler ve yönetir. Ayrıca bu bileşenler, uygulamanın tüm modülleri tarafından kullanılabilir.
Yanlış hatırlamıyorsam, bir firmada arkadaşıma gelen bir mülakat sorusu vardı. Soru, “Spring Boot IoC container’larını nasıl yönetir” şeklinde bir soru olsa gerekti. (Yanlış da hatırlıyor olabilirim ama soru bu minvaldeydi.)
Spring Boot Inversion of Control (IoC) design patternine dayanan bir frameworktür. Spring Boot, IoC container’ları üzerinde tam olarak kontrol sağlar ve uygulamadaki tüm bileşenlerin yönetimini üstlenir.
Spring Boot, bir uygulamanın IoC container’ını başlatmak için,
@SpringBootApplication
annotationunu kullanır. Bu annotation uygulamadaki tüm Spring bileşenlerini tarar ve Spring tarafından yönetilecek olan bileşenlerin tanımlarını oluşturur. Bu tanımlar, Spring Boot tarafından otomatik olarak yapılandırılır ve yönetilir.
Spring Boot, uygulamanın bileşenlerini yönetmek için standart IoC container’ı olan Application Context’i kullanır. Bu container, uygulamanın bileşenlerini yönetir ve Spring Boot, runtime’da bu bileşenlere erişimi sağlar.
@RestController
@RequiredArgsConstructor
public class ApplicationContextExample {
private final ApplicationContext applicationContext;
Map<Object, Object> informations = new HashMap<>();
@GetMapping("/information")
public Map<Object, Object> informationsAboutApplicationContext(){
informations.put("Application Id : " , applicationContext.getId());
informations.put("StartUp Date : " ,
applicationContext.getStartupDate());
return informations;
}
}
Yukarıda çok basit bir controller classı mevcut ve bu classa ApplicationContext construct injection olarak verilmiş. ApplciationContext bir interfacedir. Yukarıdaki örnekte application context’in id ‘ si ve startup date ‘ i örnek olarak verilmiş.
Id, spring boot uygulamasının adıdır. Yani, application.yml / properties dosyasına verdiğiniz spring.application.name parametresidir.
Startup Date ise spring boot uygulamanızı ayağa kaldırdığınız tarihin long cinsinden değeridir. Yukarıdaki endpointe istek atacak olursak;
şeklinde bir çıktı alırız.
Peki ApplicationContext bir interface ise ve ben bu interface’i kendi component’lerimde implemente edersem…
@Component
public class MyBean implements ApplicationContextAware {
public String applicationId;
@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
applicationId = "My Application Context Example";
}
public String getApplicationId() {
return applicationId;
}
}
Yukarıdaki kod blockuna bakacak olursak, ApplicationContextAware interface’i implemente edilmiş. ApplicationContextAware interface’i ile ApplicationContext’i kodumuzun herhangi bir yerine getirebilir. ApplicationContext uyumlu bir bean oluşturmak için, classın ApplicationContextAware interface’ini uygulaması ve setApplicationContext() methodunu override etmesi gerekmektedir. Fakat burada ufak bir reklam arası verip @Component annotationu incelemek faydalı olacaktır.
Spring Boot’ta bir classın bir Spring bileşeni olduğunu belirtmek için kullanılan çeşitli annotationlar vardır. Bunlardan birisi de @Component annotationudur.
@Component annotationu, bir class’ın Spring Application Context içerisinde yönetilen bir bileşen olduğunu belirtmek için kullanılır. Bu annotationu kullanarak, classın otomatik olarak tespit edilmesi ve Spring tarafından yönetilmesi sağlanır.
Component annotationunu da tanıdığımıza göre, yukarıdaki kod blockunda context içerisinde applicationID’yi kendimize göre tanımladık. Controller classımızda ufak bir değişiklik yapıp tanımladığımız Componenti bean olarak okumak istediğimizi söylüyoruz.
@RestController
@RequiredArgsConstructor
public class ApplicationContextExample {
private final ApplicationContext applicationContext;
Map<Object, Object> informations = new HashMap<>();
@GetMapping("/information")
public Map<Object, Object> informationsAboutApplicationContext(){
informations.put("StartUp Date : " ,
applicationContext.getStartupDate());
/**
* ID uygulamanin adidir. Biz application.yaml dosyasinda
* spring.application.name alanina ne ad verdiysek ID alaninda
* o gozukur.
*/
informations.put("ID : " , applicationContext.getId());
informations.put("Get Bean : " ,
applicationContext.getBean(MyBean.class));
return informations;
}
}
postman çıktısı ise aşağıdaki gibi olacaktır.
MyBean Class’ımızı biraz değiştirirsek,
@Component
public class MyBean implements ApplicationContextAware {
public Map<Object, Object> contextInfo = new HashMap<>();
@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
String applicationId = "My Application Context Example";
long startDate = applicationContext.getStartupDate();
contextInfo.put("applicationId : " , applicationId);
contextInfo.put("startDate : " , startDate);
}
}
Yukarıda aslında, kendi custom application context’imizi oluşturup (override edip) içerisindeki bilgileri doldurduk. Postman çıktısı ise yukarıdaki örnekten çok farklı olmayacaktır.
Peki, bir ApplicationContext içerisindeki bir bean’i başka bir ApplicationContext içerisine almak mümkün müdür.?
Bu sorunun cevabı, evet mümkündür. Ama burada, Context’ler arasında bir parent — child ilişkisi olmak zorunda. Yani multimodule bir java projesinde parent ve child module’lar olmalı ki, birbirlerinin context’lerinin içerisinde var olan componentleri kullanabilelim.
Projede 2 tane module oluşturdum ve bunların arasında parent — child ilişkisi kurdum.
Parent module’umde, aşağıdaki gibi bir class oluşturdum.
@Component
public class ParentComponent implements ApplicationContextAware {
public String applicationId;
@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
applicationId = "Here is my parent component class";
}
public String getApplicationId() {
return applicationId;
}
}
Aslında önceki örneklerimizde yaptıklarımızın tamamen aynısı. Fakat, bu parent module’deki componenti, child module’un Application Context’inde kullanmak için @Import adında bir annotation’a ihtiyacımız var.
@Configuration
@Import(ParentComponent.class)
@Component
public class ChildComponent implements ApplicationContextAware {
//......
}
Yukarıdaki Import annotationuna parent module’deki component classımızın adını verip bunu configure ettiğimiz zaman aslında olay tamamen bitmiştir. Ve artık benim child Application Context’imde parent’ımdaki componentim mevcut. /information endpointimin olduğu controller methoduna
informations.put("Parent Bean : " ,
applicationContext.getBean(ParentComponent.class));
yukarıdaki kodu eklersem, postman çıktısı,
şeklinde olacaktır. Dikkat edilirse, parent bean içerisinde, Parent Componentimin geldiği görülebilir.
Yine benzer bir yöntemle aslında, child context, parent context’ten de çağrılabilir.
İletişim, İşbirlikleri ve Teklifler için,
github: https://github.com/fsk
bitbucket: https://bitbucket.org/furkandev
twitter: https://twitter.com/0xfsk
mail: furkansahinkulaksiz@gmail.com