🌱 Spring Framework — Üretim Odaklı Rehber
Bu bölümde Spring dünyasının çekirdeği (IoC/DI), Spring Boot, MVC/REST, Data JPA, Security, Cloud ve üretim ortamı pratikleri detaylı şekilde ele alınır. Kod kesitleri, püf noktaları ve “best practice” odaklıdır.
🔎 Genel Bakış
- IoC/DI: Gevşek bağlılık, test edilebilirlik; bean yaşam döngüsü ve kapsamları.
- Boot: Auto-configuration, starter’lar, dışarıdan konfigürasyon, Actuator.
- MVC/REST: Controller, DTO, validasyon, global hata yönetimi.
- Data JPA: Repositories, query metodlar, JPQL/Native, performans.
- Security: Filter chain, yetkilendirme, JWT, method-level security.
- Cloud: Config, Gateway, Discovery, Feign, Resilience4j, Tracing.
🧠 IoC/DI & Bean Yönetimi
📦 Bean Tanımlama & Enjeksiyon
@Configuration
public class AppConfig {
@Bean
public ObjectMapper objectMapper() {
return new ObjectMapper().findAndRegisterModules();
}
}
@Service
public class ReportService {
private final ObjectMapper mapper; // ✅ Constructor injection (önerilir)
public ReportService(ObjectMapper mapper) { this.mapper = mapper; }
}
- Constructor injection → null-safety, test kolaylığı.
- @Primary / @Qualifier → çoklu bean senaryoları.
- Scopes:
singleton(varsayılan),prototype, web:request,session.
🪄 Yaşam Döngüsü İpuçları
@PostConstructyerine SmartLifecycle / ApplicationRunner tercih edilebilir.- Konfigürasyon değerleri için
@ConfigurationProperties→ tip güvenli, test edilebilir.
@ConfigurationProperties(prefix = "app.mail")
public record MailProps(String host, int port, String from) {}
// application.yml: app: mail: host: smtp ...
⚙️ Spring Boot Esasları
🚀 Starter’lar & Auto-Configuration
- Starter: kütüphane setlerini hazır getirir (web, data-jpa, security …).
- Auto-config: sınıf yolundaki bağımlılıklara göre varsayılan bean’ler.
- application.yml ile dışarıdan konfigürasyon ve profiller.
server:
port: 8080
spring:
datasource:
url: jdbc:postgresql://localhost:5432/app
username: app
password: secret
jpa:
hibernate:
ddl-auto: validate
properties:
hibernate:
format_sql: true
logging:
level:
org.hibernate.SQL: DEBUG
org.hibernate.orm.jdbc.bind: TRACE
🧪 Devtools & Config Tips
- spring-boot-devtools ile hızlı geliştirme; prod’da devtools ekleme.
- YAML hiyerarşisi okunabilirlik sağlar; gizli bilgiler için Vault/KMS.
🌐 Spring MVC & REST
🛣️ Controller + DTO
@RestController
@RequestMapping("/api/users")
@RequiredArgsConstructor
public class UserController {
private final UserService userService;
@GetMapping
public Page<UserView> list(Pageable pageable) {
return userService.list(pageable);
}
@PostMapping
public ResponseEntity<UserView> create(@Valid @RequestBody UserCreate dto) {
return ResponseEntity.status(HttpStatus.CREATED).body(userService.create(dto));
}
}
- DTO kullan, Entity’yi dışarı açma.
- Pageable otomatik bağlanır:
?page=0&size=20&sort=name,asc - İçerik türleri:
produces/consumes.
🧭 Path/Query Var.
@GetMapping("/{id}")
public UserView get(@PathVariable Long id, @RequestParam(defaultValue="false") boolean withRoles) {
return service.get(id, withRoles);
}
✅ Validasyon & Global Hata Yönetimi
🧾 Bean Validation
public record UserCreate(
@NotBlank String username,
@Email String email,
@Size(min=8) String password
) {}
🧯 @ControllerAdvice
@RestControllerAdvice
public class ApiExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
ResponseEntity<Map<String,Object>> handleValidation(MethodArgumentNotValidException ex) {
Map<String,Object> body = new LinkedHashMap<>();
body.put("error", "validation");
body.put("details", ex.getBindingResult().getFieldErrors().stream()
.map(f -> Map.of("field", f.getField(), "msg", f.getDefaultMessage()))
.toList());
return ResponseEntity.badRequest().body(body);
}
}
💾 Spring Data JPA
📚 Repository Desenleri
@Entity class User { @Id @GeneratedValue Long id; String username; String email; }
public interface UserRepo extends JpaRepository<User, Long> {
Optional<User> findByUsername(String username);
@Query("select u from User u where lower(u.email) like lower(concat('%', :q, '%'))")
Page<User> search(@Param("q") String q, Pageable p);
}
- Query method kolay, karmaşıkta @Query (JPQL/Native).
- Projection: Interface/DTO ile minimal veri.
- EntityGraph ve fetch join ile N+1 azalt.
🧠 İpuçları
- save() hem insert hem update yapabilir; id null → insert.
- deleteInBatch / deleteAllInBatch bulk siler; cascade ve orphanRemoval davranışlarını bil.
🔐 Transaction Yönetimi
⚖️ @Transactional
@Transactional(readOnly = true)
public Page<UserView> list(Pageable pageable) { /* only reads */ }
@Transactional
public UserView create(UserCreate dto) { /* write ops */ }
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void audit(AuditEvent e) { /* bağımsız log */ }
- readOnly=true → gereksiz dirty checking yok.
- Propagation:
REQUIRED,REQUIRES_NEW,MANDATORY… - İzolasyon:
READ_COMMITTEDçoğu ortamda yeterli; yarışta daha katı.
🧪 Test Stratejileri
🔹 Dilimler & Spring Boot Test
@DataJpaTest // Sadece JPA bileşenleri
class UserRepoTest { /* ... */ }
@WebMvcTest(UserController.class) // Controller + MVC slice
class UserControllerTest { /* MockMvc ... */ }
@SpringBootTest // Tüm konteyner (yavaş, entegre)
class AppIT { /* ... */ }
🐳 Testcontainers (önerilir)
@Testcontainers
class RepoIT {
@Container static PostgreSQLContainer<?> db = new PostgreSQLContainer<>("postgres:15");
// spring.datasource.url = db.getJdbcUrl() ...
}
🛡️ Spring Security (5/6+)
🧱 Filter Chain Config (Java DSL)
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(auth -> auth
.requestMatchers("/actuator/**").permitAll()
.requestMatchers(HttpMethod.POST, "/api/auth/**").permitAll()
.anyRequest().authenticated())
.oauth2ResourceServer(oauth -> oauth.jwt()) // JWT resource server
.build();
}
@Bean PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); }
- Method-level:
@EnableMethodSecurity+@PreAuthorize("hasRole('ADMIN')") - Stateless API: session yok, JWT ile kimlik doğrulama.
☁️ Spring Cloud Esasları
🔌 OpenFeign & Resilience4j
@FeignClient(name="order", url="${services.order.url}", configuration = FeignConfig.class)
public interface OrderClient {
@GetMapping("/api/orders/{id}") OrderDto get(@PathVariable Long id);
}
@Service
public class OrderGateway {
private final OrderClient client;
public OrderGateway(OrderClient client) { this.client = client; }
@CircuitBreaker(name="order", fallbackMethod="fallback")
@Retry(name="order")
public OrderDto get(Long id) { return client.get(id); }
OrderDto fallback(Long id, Throwable t) { return new OrderDto(id, "N/A"); }
}
🧭 Discovery, Gateway, Config
- Service Discovery (Eureka/Consul): client-side load balancing.
- API Gateway (Spring Cloud Gateway): route, rate limit, auth.
- Config Server: merkezi konfig,
spring.cloud.config.
📊 Actuator & İzleme
📈 Health, Metrics, Info
management:
endpoints:
web:
exposure:
include: "health,info,metrics,env,threaddump,httpexchanges"
endpoint:
health:
show-details: "always"
@Component
public class AppHealth implements HealthIndicator {
public Health health() {
return Health.up().withDetail("service", "ok").build();
}
}
- Micrometer + Prometheus/Grafana ile metrik toplama.
- Tracing: Micrometer Tracing + OpenTelemetry/Zipkin/Jaeger.
⚡ Caching / Async / Scheduling
🧠 Cache
@EnableCaching
@SpringBootApplication
class App {}
@Service
class ProductService {
@Cacheable(cacheNames="product", key="#id")
Product get(Long id) { /* DB call */ }
@CacheEvict(cacheNames="product", key="#id")
void evict(Long id) {}
}
🧵 @Async & ⏰ @Scheduled
@EnableAsync @EnableScheduling
@Configuration class AsyncCfg {
@Bean Executor taskExecutor() { return Executors.newVirtualThreadPerTaskExecutor(); /* veya ThreadPoolTaskExecutor */ }
}
@Service
class Mailer {
@Async public void sendMail(...) { /* async */ }
@Scheduled(cron = "0 0 * * * *") public void hourlyTask() { /* ... */ }
}
🧩 Config & Profiles
🌿 Profiller & Dış Konfig
spring:
profiles:
active: "dev"
---
spring:
config:
activate:
on-profile: "prod"
server:
port: 8080
- @Profile("prod") ile koşullu bean yükleme.
- Gizli bilgiler → ENV veya Config Server / Vault.
🔭 Logging & Gözlemlenebilirlik
📝 Logback & MDC
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} %-5level [%X{traceId:-} %X{spanId:-}] %logger - %msg%n</pattern>
</encoder>
</appender>
<root level="INFO"><appender-ref ref="STDOUT"/></root>
</configuration>
- MDC ile korelasyon id’si; Micrometer Tracing otomatik doldurur.
- Log seviyelerini
logging.levelile yönet.
🚀 Performans & Antipattern
❌ Kaçınılması Gerekenler
- Controller’da Entity döndürmek (döngüsel referans, alan sızıntısı).
- Geniş object graph’ı EAGER ile yüklemek → bellek/sorgu patlaması.
- @Transactional sınırlarını servis katmanı dışında tanımlamak.
- OFFSET/FETCH ile devasa sayfa atlamak → keyset pagination kullan.
✅ İyi Pratikler
- Constructor injection + final alanlar.
- DTO Projections & EntityGraph ile hedefli veri çekimi.
- Actuator + Metrics + Tracing ile görünürlük.
- Resilience4j ile dış çağrılarda circuit breaker/timeout/bulkhead.
❓ Mülakat Notları (Kısa Cevaplı)
1) IoC nedir?
Kontrolün tersine çevrilmesi: nesnelerin oluşturulması ve bağımlılıklarının yönetimini konteyner üstlenir.
2) @Component vs @Bean farkı?
@Component sınıf seviyesinde taranır; @Bean ise @Configuration içinde fabrika metodu olarak tanımlanır.
3) @Transactional readOnly ne sağlar?
Kirlenme kontrolünü azaltır; bazı sürücülerde optimizasyon; yazma girişimleri hata üretebilir (motor davranışına bağlı).
4) Spring Security 6’da WebSecurityConfigurerAdapter?
Kaldırıldı. Yerine SecurityFilterChain @Bean yaklaşımı kullanılır.
5) Feign + Resilience4j birlikte nasıl?
Feign client’ı bir servisle wrap et; metodlarda @CircuitBreaker/@Retry kullan, fallback metodları tanımla.