MS Lesson6: IoC, DI, Spring Context, ApplicationContext
1. IoC - Inversion of Control
- Əvvəl (klassik Java): sən obyektləri özün yaradırsan (new Service(), new Repo()), kim kimdən asılıdır hamısını özün bağlayırsan.
- IoC-də: bu nəzarəti container-ə verirsən (Spring). O obyektləri özü yaradır və bağlayır.
- əvvəl: sən idarə edirdin
- indi: framework idarə edir
2. DI - Dependency Injection
- TestController-a StudentIdGeneratorService lazımdır.
- Constructor ilə veririk:
- new TestController(service) (unit testdə)
- və ya Spring onu avtomatik verir (runtime-da)
- constructor injection (ən yaxşı)
- setter injection
- field injection
3. Spring Context
- @Component, @Service, @Repository, @Controller, @Configuration tapır
- bunlardan bean yaradır
- DI edir
- bean lifecycle idarə edir (init, destroy)
- config/property binding edir
4. ApplicationContext
- BeanFactory daha əsas/sadə container API-dir
- ApplicationContext onun üstündə daha geniş funksionallıq verir:
- event publishing
- internationalization
- AOP/auto-config ilə daha tam inteqrasiya
- environment/profiles və s.
- IoC = prinsip (idarəni framework-ə vermək)
- DI = mexanizm (dependency-ni inject etmək)
- ApplicationContext (Spring Context) = bunu edən container
- Spring Context = ümumi danışıq adıdır.
- ApplicationContext = bunun real, texniki adıdır (Spring-də ən çox istifadə edilən container interfeysi).
- “maşın” deyəndə ümumi addır
- “Toyota Corolla” deyəndə konkret modeldir
- “Spring Context” = maşın
- “ApplicationContext” = konkret model (ən çox işlənən)
Applicationda olan butun bean-lari gosterir
@Bean
ApplicationRunner runner(ApplicationContext ctx) {
return args -> {
String[] names = ctx.getBeanDefinitionNames();
Arrays.sort(names);
for (String name : names) {
System.out.println(name);
}
};
}
SpringApplication.run(...) işləyir
Bu metod 3 əsas iş görür:
- Spring Boot konfiqurasiyasını oxuyur (
@SpringBootApplication,application.yaml, profillər və s.) - Hansı tip application olduğuna qərar verir (web, reactive, cli və s.)
- ApplicationContext obyektini YARADIR
Bu məqamdan sonra artıq “Spring Container” (yəni ApplicationContext) var.
ApplicationContext yaradılır
Məsələn, sənin proyektində bu tip bir class olur:
AnnotationConfigServletWebServerApplicationContext
Bu obyekt:
- Daxilində bir BeanFactory saxlayır (bean-ları yaradan mexanizm)
- Bütün application-ın “beyni” olur:
- Hansı bean var?
- Hansının asılılıqları nədir?
- Həyat dövrləri nədir? (singleton, prototype və s.)
Cümlə ilə:
“Spring işə düşəndə ilk iş kimi bir dənə ApplicationContext obyekti yaradır. Bu obyekt sonra bütün bean-ları tapıb yaradan və idarə edən konteynerdir.”
Component scan: Bean-ların tapılması
ApplicationContext yaradıldıqdan sonra Spring belə edir:
@SpringBootApplicationolan sinfin paketindən başlayır- Bu paket və alt paketlərdə:
@Component@Service@Repository@Controller/@RestController@Configuration@ControllerAdvicevə s.
olan bütün sinifləri tapır.
Hər tapdığı sinif üçün bean definition yaradır:
- Class adı
- Scope (singleton, prototype və s.)
- Lazım olan dependency-lər (
@Autowired, constructor parametrləri) - Proxy lazımdırmı? (transaction, AOP və s.)
Bu mərhələdə hələ obyektlər tam yaradılmır, sadəcə “planı çəkilir”.
Bean-ların yaradılması (instantiate + dependency injection)
Sonra ApplicationContext aşağıdakıları edir:
Singleton bean-ları (default scope) start zamanı yaradır:
StudentControllerStudentServiceImplStudentRepositoryCourseRepositoryDataSource(HikariCP)EntityManagerFactoryTransactionManager- və s.
Hər bean üçün:
- Constructor tapılır
- Lazım olan dependency-lər müəyyən olunur
- Məsələn
StudentServiceImplüçünStudentRepository
- Məsələn
- Əvvəl dependency yaradılır, sonra həmin dependency-lə birlikdə bu bean yaradılır
- Field / setter injection varsa, onlar da set olunur
Bu mərhələyə “instantiate + dependency injection” kimi baxmaq olar.
Xüsusi bean-lar: DataSource, Hikari, EntityManagerFactory
Spring Boot starter-lar sayəsində avtomatik bunları da yaradır:
DataSource→ HikariCP connection poolEntityManagerFactory→ JPA/Hibernate üçün “zavod”PlatformTransactionManager→@Transactionalüçün transaction idarəçisi- JPA
Repositories(StudentRepository,CourseRepository) üçün proxy-lər
Bu bean-lar da ApplicationContext-in içindədir, sadəcə avtoconfiguration tərəfindən yaradılır (sən yazmamısan, Spring Boot sənin yerinə yazıb).
Bean life-cycle callback-lər (PostConstruct, Initializers)
Bu mərhələdə Spring:
@PostConstructannotasiyalı metodları çağırırInitializingBeanimplement edənlər üçünafterPropertiesSet()çağırırApplicationRunner/CommandLineRunnerbean-larını işə salır
Web server-in işə düşməsi (Tomcat, Jetty və s.)
Əgər bu web tətbiqdirsə (səndə Spring MVC var):
- Spring Boot embedded Tomcat bean-ını yaradır (
TomcatWebServer) - Tomcat 8080 portunda
listenetməyə başlayır - Spring MVC
DispatcherServletTomcat-ə qeydiyyatdan keçir @RestControllerendpoint-lər route kimi qeyd olunur
SpringContext vs ApplicationContext
Spring Context vs Application Context
Spring Context — ümumi bir anlayışdır. Spring-in IoC (Inversion of Control) konteynerini ifadə edir. Yəni bean-ləri yaradan, idarə edən və aralarındakı asılılıqları inject edən mexanizmdir.
ApplicationContext isə bu konteynerin konkret bir interface-idir. BeanFactory-nin genişləndirilmiş versiyasıdır və əlavə xüsusiyyətlər təqdim edir:
- Event publication
- Internationalization (i18n)
- Resource loading
- AOP dəstəyi
Spring Boot-da praktiki olaraq:
Spring Boot işə düşəndə avtomatik olaraq bir ApplicationContext yaradır. Hansı tip yaradılır, layihənin növündən asılıdır:
- Web layihəsi →
AnnotationConfigServletWebServerApplicationContext - Reactive layihə →
AnnotationConfigReactiveWebServerApplicationContext - Adi layihə →
AnnotationConfigApplicationContext
@SpringBootApplication
public class MyApp {
public static void main(String[] args) {
// Bu ApplicationContext qaytarır
ApplicationContext ctx = SpringApplication.run(MyApp.class, args);
// Bean-ə əl ilə çatmaq
MyService service = ctx.getBean(MyService.class);
}
}
Bean Lifecycle
1️⃣ Container Started
Spring işə düşür, ApplicationContext yaradılır. Spring hansı bean-lərin olduğunu scan edir (@Component, @Service, @Repository və s.)
2️⃣ Bean Instantiated
Spring bean-in obyektini yaradır. Yəni new əməliyyatı baş verir.
ApplicationContext başlayanda bütün bean-ləri yaradıb öz içinə atır (saxlayır).
ApplicationContext əslində bir bean anbarı kimidir. Yaradır, saxlayır, lazım olanda verir.
ApplicationContext yalniz singleton beanlari icinde saxlayir:
package com.amazon.demoapp;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.web.context.WebApplicationContext;
@SpringBootApplication
@RequiredArgsConstructor
public class DemoAppApplication implements CommandLineRunner {
private final ApplicationContext applicationContext;
public static void main(String[] args) {
ConfigurableApplicationContext run = SpringApplication.run(DemoAppApplication.class, args);
System.out.println(run.getClass().getName());
}
@Override
public void run(String... args) throws Exception {
Object student1 = applicationContext.getBean("getStudent");
Object student2 = applicationContext.getBean("getStudent");
System.out.println(student1 == student2);
System.out.println(System.identityHashCode(student1));
System.out.println(System.identityHashCode(student2));
}
}
1) Singleton — düzgün istifadə
- Heç bir field yoxdur (state yoxdur)
- Hər istifadəçi eyni metodu çağırır
- 1 instance bütün app üçün kifayətdir
2) Singleton — SƏHV istifadə
- İstifadəçi A addItem("kitab") edir
- İstifadəçi B addItem("qələm") edir
- İstifadəçi A getItems() çağırır
- Cavab: [kitab, qələm] — yəni B-nin məlumatı da görünür
3) Prototype — düzgün istifadə
- İstifadəçi A üçün ayrı instance yaranır
- İstifadəçi B üçün ayrı instance yaranır
- Hər birinin öz items listı var
- Data qarışmır
3️⃣ Dependencies Injected
Bean-in ehtiyac duyduğu digər bean-lər inject edilir (@Autowired, constructor injection və s.)
@Service
public class MyService {
@Autowired
private MyRepository repository; // ← bu mərhələdə inject olunur
}
Circular Injection
package com.amazon.demoapp.conflict;
public interface Aservice {
}
package com.amazon.demoapp.conflict;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class AserviceImpl implements Aservice{
@Autowired
private Bservice bservice;
}
package com.amazon.demoapp.conflict;
public interface Bservice {
}
package com.amazon.demoapp.conflict;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class BserviceImpl implements Bservice{
@Autowired
private Aservice aservice;
}
Hell yollari:
1. @Lazy
- Bean əvvəlcə yaradılmır, ilk istifadədə yaranır.
- Circular dependency texniki olaraq aradan qalxır.
- Amma real problem həll olunmur, sadəcə "gizlədilir".
@Lazy ilə problem
- Bean ilk istifadədə yaranır.
- Yəni startup-da problem yoxdur, amma runtime-da NullPointerException və ya gözlənilməz davranış ola bilər.
- Debugging çətinləşir — problem startup-da deyil, istifadə anında çıxır.
- Kod oxuyan developer "niyə @Lazy var?" sualını verəcək.
2. Setter injection
Setter injection ilə problem
- final istifadə edilə bilmir → immutability yoxdur.
- Setter çağrılmamışsa field null qalır → NPE riski.
- Test-də setter əl ilə çağrılmalıdır.
- "Bu dependency vacibdirmi, optional-dırmı?" sualı açıq qalır.
3. Dizayni yeniden dushunmek (en yaxshi yol)
Helli:
4️⃣ Custom init() method
Bean tamamilə hazır olduqdan sonra işə düşür. Məsələn DB connection yoxlamaq, cache doldurmaq və s.
@Component
public class MyService {
@PostConstruct // ← init method
public void init() {
System.out.println("Bean hazırdır, işə başlayıram!");
}
}
5️⃣ Custom Utility method
Bean artıq tam işlək vəziyyətdədir. Bütün iş məntiqi (business logic) burada icra olunur. Yəni application işlədiyi müddətdə bu mərhələdə qalır.
6️⃣ Custom destroy() method
Application bağlananda və ya bean silinəndə çağırılır. Resursları sərbəst buraxmaq üçün istifadə olunur.
Комментарии
Отправить комментарий