Kompletny przewodnik z odpowiedziami
== od equals() w Javie?== pyta "czy to ten sam obiekt w pamięci?", equals() pyta "czy mają tę samą zawartość?"int, boolean...) używaj ==, dla obiektów (String, klasy własne) używaj equals().equals() z Object działa jak == - trzeba je nadpisać (razem z hashCode()!).== - porownuje referencje (adresy w pamieci). Dla typow prymitywnych porownuje wartosci.equals() - porownuje zawartosc obiektow (jesli metoda jest nadpisana).String a = new String("test");
String b = new String("test");
a == b; // false - rozne obiekty w pamieci
a.equals(b); // true - ta sama zawartosc
ArrayList a LinkedListList - obie przechowują elementy w kolejności, ale w inny sposób w pamięci.| Cecha | ArrayList | LinkedList |
|---|---|---|
| Struktura | Tablica dynamiczna | Lista dwukierunkowa |
| Dostep po indeksie | O(1) | O(n) |
| Dodawanie/usuwanie na koncu | O(1) amortyzowane | O(1) |
| Dodawanie/usuwanie w srodku | O(n) | O(1) jesli mamy referencje |
| Pamiec | Mniejsza | Wieksza (przechowuje wskazniki) |
free()) - łatwo o wyciek lub błąd. Java robi to za Ciebie.Garbage Collector (GC) - automatyczny mechanizm zarzadzania pamiecia, ktory usuwa nieuzywane obiekty.
Jak dziala:
Generacje pamieci:
Popularne algorytmy GC: G1 (domyslny od Java 9), ZGC, Shenandoah
List<String> to lista konkretnie Stringów.List<String> i List<Integer> to w runtime to samo.? extends T (tylko odczyt) i ? super T (tylko zapis) - reguła PECS: Producer Extends, Consumer Super.Generics pozwalaja parametryzowac typy, zapewniajac bezpieczenstwo typow w czasie kompilacji.
// Bez generics - mozliwy ClassCastException w runtime
List list = new ArrayList();
list.add("tekst");
Integer num = (Integer) list.get(0); // blad w runtime!
// Z generics - blad wykryty w czasie kompilacji
List<String> list = new ArrayList<>();
list.add("tekst");
list.add(123); // blad kompilacji!
final, finally i finalizefinal - modyfikator niezmienności: klasa nie do dziedziczenia, metoda nie do nadpisania, zmienna nie do zmiany.finally - blok w try/catch, który zawsze się wykona (nawet przy wyjątku) - do sprzątania zasobów (zamykanie plików, połączeń).finalize() - stara metoda wołana przez GC przed usunięciem obiektu. Nie używać! Deprecated od Java 9, zastąpiona przez try-with-resources.| Slowo kluczowe | Zastosowanie |
|---|---|
final |
Modyfikator: klasa nie moze byc dziedziczona, metoda nie moze byc nadpisana, zmienna nie moze byc zmieniona |
finally |
Blok kodu wykonywany zawsze po try/catch (do sprzatania zasobow) |
finalize |
Metoda wywolywana przez GC przed usunieciem obiektu (deprecated od Java 9) |
final int MAX = 100; // stala
try {
// kod
} catch (Exception e) {
// obsluga bledu
} finally {
// zawsze sie wykona - zamkniecie zasobow
}
String, Integer, BigDecimal, LocalDate, List.of().HashMap, łatwiejsze debugowanie.final, pola private final, brak setterów, wszystko ustawiasz w konstruktorze, dla kolekcji zwracaj kopie lub widoki niemodyfikowalne.Obiekt niemutowalny - po utworzeniu jego stan nie moze byc zmieniony.
// String jest immutable
String s = "hello";
s.toUpperCase(); // nie zmienia s, zwraca nowy obiekt
// Tworzenie immutable klasy
public final class Person {
private final String name;
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
// brak setterow!
}
Serializable, Comparable).default - granica się zatarła, ale nadal interfejs nie trzyma stanu.| Cecha | Interfejs | Klasa abstrakcyjna |
|---|---|---|
| Definicja | Typ referencyjny definiujący kontrakt | Niekompletna klasa bazowa do rozszerzenia |
| Dziedziczenie | Klasa może implementować wiele interfejsów | Klasa może rozszerzyć tylko jedną klasę |
| Relacja | „Może zachowywać się jak” (capability) | „Jest typem” (is-a) |
| Pola | Tylko public static final (stałe) |
Dowolne: private, protected, final, mutable |
| Konstruktor | Brak – nie można przekazać zależności przez konstruktor | Tak – można inicjalizować pola wspólne i wymuszać zależności |
| Metody | Abstrakcyjne, default, static, private (od Java 9) | Abstrakcyjne i konkretne |
| Stan obiektu | Nie przechowuje stanu instancji | Może przechowywać stan instancji (pola) |
| Widoczność metod | Metody public (domyślnie) | Dowolna: private / protected / public |
| Zastosowanie | Definicja kontraktu API, wiele implementacji, luźne sprzężenie | Wspólna implementacja, szkielet algorytmu, kontrola przepływu, częściowa implementacja |
abstract class Account {
protected BigDecimal balance;
public Account(BigDecimal balance) { this.balance = balance; }
public void deposit(BigDecimal amount) { balance = balance.add(amount); }
public abstract void withdraw(BigDecimal amount);
}
abstract class ReportGenerator {
public final void generate() {
fetchData();
format();
export();
}
protected abstract void format();
private void fetchData() {}
private void export() {}
}
abstract class BaseService {
protected final Repository repository;
protected BaseService(Repository repository) { this.repository = repository; }
}
PaymentProcessor z metodą process(Payment)findFirst).Stream API (od Java 8) - deklaratywne przetwarzanie kolekcji.
List<String> names = List.of("Anna", "Jan", "Adam", "Ewa");
// Filtrowanie i mapowanie
List<String> result = names.stream()
.filter(name -> name.startsWith("A"))
.map(String::toUpperCase)
.sorted()
.collect(Collectors.toList());
// ["ADAM", "ANNA"]
// Redukcja
int sum = List.of(1, 2, 3, 4, 5).stream()
.reduce(0, Integer::sum); // 15
// Grupowanie
Map<Integer, List<String>> byLength = names.stream()
.collect(Collectors.groupingBy(String::length));
Operacje:
filter, map, sorted,
distinctcollect, forEach, reduce,
countnull (i ryzykować NPE), zwracasz Optional<T> - kompilator zmusza wołającego do obsługi "pustki".map(), filter(), orElse(), orElseThrow(), ifPresent().findById).Optional - kontener, ktory moze zawierac wartosc lub byc pusty.
// Zamiast zwracac null
public Optional<User> findById(Long id) {
User user = repository.find(id);
return Optional.ofNullable(user);
}
// Uzycie
Optional<User> userOpt = findById(1L);
// Bezpieczne pobieranie
String name = userOpt
.map(User::getName)
.orElse("Unknown");
// Rzucenie wyjatku jesli brak
User user = userOpt
.orElseThrow(() -> new NotFoundException("User not found"));
// Wykonanie akcji jesli istnieje
userOpt.ifPresent(u -> System.out.println(u.getName()));
@Transactionalnew).@Autowired, @Component).Dependency Injection (DI) - wzorzec, w ktorym zaleznosci sa dostarczane z zewnatrz, zamiast byc tworzone wewnatrz klasy.
// 1. Constructor Injection (ZALECANY)
@Service
public class OrderService {
private final ProductRepository repository;
public OrderService(ProductRepository repository) {
this.repository = repository;
}
}
// 2. Setter Injection
@Service
public class OrderService {
private ProductRepository repository;
@Autowired
public void setRepository(ProductRepository repository) {
this.repository = repository;
}
}
// 3. Field Injection (NIEZALECANY)
@Service
public class OrderService {
@Autowired
private ProductRepository repository;
}
@Component, @Service, @Repository i
@Controller@Component - ogólny bean (generyczny).@Service - logika biznesowa (warstwa serwisów).@Repository - dostęp do danych + automatyczne tłumaczenie wyjątków DB na Spring DataAccessException.@Controller / @RestController - warstwa webowa (HTTP endpoints).Wszystkie sa specjalizacjami @Component i oznaczaja bean zarzadzany przez Springa.
| Adnotacja | Warstwa | Dodatkowe funkcje |
|---|---|---|
@Component |
Ogolna | Bazowa adnotacja |
@Service |
Logika biznesowa | Semantyczna, brak dodatkowych funkcji |
@Repository |
Dostep do danych | Automatyczna translacja wyjatkow SQL na DataAccessException |
@Controller |
Web (MVC) | Obsluguje zadania HTTP, zwraca widoki |
@RestController |
Web (REST) | = @Controller + @ResponseBody |
Spring Security dziala jako lancuch filtrow przechwytujacych zadania HTTP.
Glowne filtry:
SecurityContextPersistenceFilter - laduje kontekst bezpieczenstwaUsernamePasswordAuthenticationFilter - obsluguje loginBasicAuthenticationFilter - HTTP Basic AuthBearerTokenAuthenticationFilter - JWTExceptionTranslationFilter - obsluga wyjatkowAuthorizationFilter - sprawdzanie uprawnien@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.csrf(csrf -> csrf.disable())
.sessionManagement(session ->
session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/public/**").permitAll()
.requestMatchers("/api/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
.oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()))
.build();
}
}
@RestController od @Controller?@Controller - klasyczny kontroler MVC, zwraca nazwy widoków (Thymeleaf, JSP). Renderuje HTML.@RestController = @Controller + @ResponseBody na każdej metodzie. Zwraca dane (JSON/XML) zamiast widoków.@RestController. Strona WWW z szablonami → @Controller.@ResponseBody mówi "serializuj ten obiekt przez Jacksona do JSON-a i wyślij jako body odpowiedzi".// @Controller - zwraca nazwy widokow (MVC)
@Controller
public class WebController {
@GetMapping("/home")
public String home(Model model) {
model.addAttribute("message", "Hello");
return "home"; // szuka templates/home.html
}
@GetMapping("/api/data")
@ResponseBody // potrzebne do zwrocenia JSON
public Data getData() {
return new Data();
}
}
// @RestController = @Controller + @ResponseBody na kazdej metodzie
@RestController
public class ApiController {
@GetMapping("/api/data")
public Data getData() {
return new Data(); // automatycznie serializowane do JSON
}
}
@PostConstruct), 4) gotowy do użycia, 5) destrukcja (@PreDestroy).singleton (domyślnie, jeden na kontekst), prototype (nowy przy każdym wstrzyknięciu), request/session (webowe).@DependsOn).@Component
public class MyBean {
@PostConstruct
public void init() {
// wykonywane po utworzeniu beana i wstrzyknieciu zaleznosci
}
@PreDestroy
public void cleanup() {
// wykonywane przed zniszczeniem beana
}
}
@Transactional i jak dziala propagacja transakcji?@Transactional z tej samej klasy omija proxy - transakcja się nie uruchomi.RuntimeException, nie dla checked. Ustaw rollbackFor=Exception.class jeśli chcesz zawsze.REQUIRED (dołącz, domyślne), REQUIRES_NEW (zawsze nowa), NESTED (savepoint).@Transactional - zarzadza transakcjami deklaratywnie (przez AOP proxy).
@Service
public class OrderService {
@Transactional
public void createOrder(Order order) {
// wszystko w jednej transakcji
orderRepository.save(order);
inventoryService.decreaseStock(order.getItems());
// jesli wystapi wyjatek - rollback calosci
}
}
| Propagation | Opis |
|---|---|
REQUIRED (domyslny) |
Uzywa istniejacej lub tworzy nowa |
REQUIRES_NEW |
Zawsze tworzy nowa (zawieszajac obecna) |
NESTED |
Tworzy zagniezzona transakcje (savepoint) |
SUPPORTS |
Uzywa istniejacej lub dziala bez transakcji |
NOT_SUPPORTED |
Zawiesza istniejaca, dziala bez transakcji |
MANDATORY |
Wymaga istniejacej transakcji |
NEVER |
Rzuca wyjatek jesli istnieje transakcja |
@TransactionalOprocz propagacji i poziomu izolacji, @Transactional posiada kilka
niezaleznych wlasciwosci, ktore wplywaja na zachowanie transakcji.
| Wlasciwosc | Opis | Przyklad / Uwagi |
|---|---|---|
readOnly |
Oznacza transakcje jako tylko-do-odczytu. Moze pozwolic ORM i bazie na optymalizacje. |
@Transactional(readOnly = true)Nie blokuje zapisu technicznie, ale zapis jest bledem logicznym. |
timeout |
Maksymalny czas trwania transakcji (w sekundach). Po przekroczeniu nastepuje rollback. |
@Transactional(timeout = 5)Chroni przed "wiszacymi" transakcjami. |
rollbackFor |
Okresla, dla jakich wyjatkow ma nastapic rollback. |
Domyslnie rollback jest tylko dla RuntimeException.@Transactional(rollbackFor = Exception.class)
|
noRollbackFor |
Wyjatki, dla ktorych rollback nie powinien nastapic. |
@Transactional(noRollbackFor = BusinessException.class)
|
isolation |
Poziom izolacji transakcji (ACID). |
READ_COMMITTED, REPEATABLE_READ, SERIALIZABLEChroni przed dirty read, non-repeatable read itd. |
transactionManager |
Wskazuje konkretny menedzer transakcji. |
Wazne przy wielu zrodlach danych@Transactional(transactionManager = "orderTxManager")
|
readOnly = true nie blokuje zapisu na poziomie Javy – to wskazowka dla ORM/bazy.checked exception.application.yml lub application.properties - właściwości pod spring.datasource.* i spring.jpa.*.jdbc:postgresql://...), username, password. Spring Boot sam wykryje sterownik z URL-a.application-dev.yml, application-prod.yml - różne DB dla różnych środowisk.# application.yml
spring:
datasource:
url: jdbc:postgresql://localhost:5432/mydb
username: ${DB_USERNAME:admin}
password: ${DB_PASSWORD:secret}
driver-class-name: org.postgresql.Driver
hikari:
maximum-pool-size: 10
minimum-idle: 5
idle-timeout: 300000
jpa:
hibernate:
ddl-auto: validate # none, validate, update, create, create-drop
show-sql: false
properties:
hibernate:
format_sql: true
dialect: org.hibernate.dialect.PostgreSQLDialect
JPA to specyfikacja Javy do mapowania obiektowo-relacyjnego (ORM). Hibernate jest jej najpopularniejszą implementacją.
Reprezentuje tabelę w bazie danych. Każda instancja to jeden wiersz.
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
}
Główny interfejs do operacji na encjach (zarządza cyklem życia obiektów):
entityManager.persist(user); // INSERT - zapisuje nowy obiekt
entityManager.merge(user); // UPDATE - aktualizuje odłączony obiekt
entityManager.remove(user); // DELETE - usuwa obiekt
entityManager.find(User.class, id); // SELECT - znajduje po kluczu głównym
| Stan | Opis |
|---|---|
| New/Transient | Obiekt utworzony, ale nie zarządzany przez JPA |
| Managed | Po persist() lub find() — zmiany automatycznie synchronizowane z bazą (dirty checking) |
| Detached | Po zamknięciu EntityManager — obiekt odłączony, zmiany nie są śledzone |
| Removed | Po remove() — zaplanowany do usunięcia |
Hibernate jako implementacja JPA:
NULL.
-- INNER JOIN - tylko pasujace rekordy z obu tabel
SELECT c.name, o.amount
FROM customers c
INNER JOIN orders o ON c.id = o.customer_id;
-- Zwraca tylko klientow, ktorzy maja zamowienia
-- LEFT JOIN - wszystkie z lewej + pasujace z prawej
SELECT c.name, o.amount
FROM customers c
LEFT JOIN orders o ON c.id = o.customer_id;
-- Zwraca WSZYSTKICH klientow, nawet bez zamowien (amount = NULL)
| Typ JOIN | Opis | Kiedy używać |
|---|---|---|
| INNER JOIN | Tylko rekordy pasujące w obu tabelach | Gdy potrzebujesz tylko kompletnych danych |
| LEFT JOIN | Wszystkie z lewej + pasujące z prawej (NULL jeśli brak) | Gdy chcesz wszystkich z głównej tabeli |
| RIGHT JOIN | Wszystkie z prawej + pasujące z lewej | Rzadko używany (zamień tabele i użyj LEFT) |
| FULL OUTER JOIN | Wszystkie z obu tabel (NULL gdzie brak dopasowania) | Gdy potrzebujesz kompletnego obrazu obu tabel |
Indeks - struktura danych przyspieszajaca wyszukiwanie (najczesciej B-tree).
-- Tworzenie indeksu
CREATE INDEX idx_customer_email ON customers(email);
-- Indeks zlozony
CREATE INDEX idx_order_customer_date ON orders(customer_id, created_at);
-- Indeks unikalny
CREATE UNIQUE INDEX idx_user_username ON users(username);
| Wlasciwosc | Opis | Przyklad |
|---|---|---|
| Atomicity (Atomowosc) | Transakcja wykonuje sie w calosci lub wcale | Przelew: obie operacje (debit i credit) musza sie udac |
| Consistency (Spojnosc) | Baza zawsze przechodzi z jednego spojnego stanu do drugiego | Suma sald przed i po przelewie jest taka sama |
| Isolation (Izolacja) | Rownolegle transakcje nie wplywaja na siebie | Dwa przelewy z tego samego konta nie powoduja race condition |
| Durability (Trwalosc) | Zatwierdzone zmiany sa trwale (nawet po awarii) | Po COMMIT dane sa bezpieczne na dysku |
| Cecha | SQL | NoSQL |
|---|---|---|
| Schemat | Staly, zdefiniowany | Elastyczny/brak |
| Struktura | Tabele z relacjami | Dokumenty, klucz-wartosc, grafy |
| Skalowanie | Wertykalne (mocniejszy serwer) | Horyzontalne (wiecej serwerow) |
| ACID | Pelne wsparcie | Czesto eventual consistency |
| Zapytania | SQL - potezny jezyk zapytan | Ograniczone, zalezne od typu |
order.getCustomer() w pętli → 1 + 100 = 101 zapytań zamiast jednego z JOIN.JOIN FETCH w JPQL, @EntityGraph, projekcje DTO, @BatchSize (zapytania w paczkach).show_sql=true), Hibernate statistics, profiler.Problem N+1 - wykonanie 1 zapytania po liste + N zapytan po powiazane dane.
// Problem: 1 zapytanie po zamowienia + N zapytan po klientow
List<Order> orders = orderRepository.findAll();
for (Order order : orders) {
System.out.println(order.getCustomer().getName()); // kazde wywolanie = nowe zapytanie!
}
Rozwiazania:
// 1. JOIN FETCH (JPQL)
@Query("SELECT o FROM Order o JOIN FETCH o.customer")
List<Order> findAllWithCustomers();
// 2. @EntityGraph
@EntityGraph(attributePaths = {"customer"})
List<Order> findAll();
// 3. @BatchSize (Hibernate)
@ManyToOne
@BatchSize(size = 25) // laduje klientow partiami po 25
private Customer customer;
| Poziom | Dirty Read | Non-repeatable Read | Phantom Read |
|---|---|---|---|
| READ_UNCOMMITTED | mozliwe | mozliwe | mozliwe |
| READ_COMMITTED | - | mozliwe | mozliwe |
| REPEATABLE_READ | - | - | mozliwe |
| SERIALIZABLE | - | - | - |
Problemy:
Poziomy izolacji okreslaja jak bardzo transakcje sa od siebie odseparowane oraz jakie mechanizmy kontroli wspolbieznosci stosuje baza (MVCC, locki, snapshoty).
W PostgreSQL (i wiekszosci nowoczesnych baz) dominuje MVCC – Multi Version Concurrency Control, czyli baza nie blokuje odczytow, tylko przechowuje wiele wersji wierszy.
W teorii: mozna czytac dane jeszcze przed COMMIT-em.
W praktyce (PostgreSQL): ten poziom NIE istnieje – jest mapowany do READ COMMITTED, poniewaz Postgres nigdy nie pozwala na dirty read.
Co widzi transakcja?
Kazdy SELECT widzi tylko dane zatwierdzone przed rozpoczeciem zapytania.
Problem:
Dwa SELECT-y w tej samej transakcji moga zwrocic inne wyniki.
Kiedy uzywac?
W praktyce – nigdy jako swiadomy wybor.
Jak to dziala?
Ale!
Jesli inna transakcja zrobi COMMIT pomiedzy Twoimi SELECT-ami —
kolejny SELECT zobaczy nowe dane.
Co NIE jest blokowane?
Zalety:
Wady:
Typowe zastosowania:
Senior tip:
~90% aplikacji produkcyjnych dziala na READ COMMITTED.
Kluczowa roznica:
Snapshot tworzony jest raz – na poczatku transakcji.
Oznacza to:
Co blokuje baza?
Nie blokuje odczytow – nadal korzysta z MVCC.
Ale przy zapisie moze dojsc do:
Baza wtedy zabija jedna z transakcji.
Bardzo wazne:
W PostgreSQL phantom read praktycznie nie wystepuje na tym poziomie,
poniewaz snapshot jest stabilny.
Kiedy uzywac?
Minus:
wieksze ryzyko rollbackow przy duzej konkurencji.
Cel:
Zachowanie jakby transakcje wykonywaly sie jedna po drugiej.
Jak robi to Postgres?
Nie przez ciezkie locki — tylko przez wykrywanie konfliktow
(SSI – Serializable Snapshot Isolation).
Jesli baza wykryje niebezpieczna zaleznosc:
ERROR: could not serialize access due to read/write dependencies
Jedna transakcja musi zostac powtorzona.
Zalety:
Wady:
Kiedy uzywac?
Czy wyzszy poziom izolacji zawsze jest lepszy?
👉 Nie.
To zawsze trade-off:
Dlatego:
Najpierw wybieramy mozliwie najnizszy poziom,
ktory nadal gwarantuje poprawna logike biznesowa.
MVCC (Multi-Version Concurrency Control) - mechanizm umozliwiajacy wspolbiezny dostep do bazy danych poprzez przechowywanie wielu wersji danych. Transakcje dzialaja na podstawie migawki (snapshot) bazy danych.
-- Stan poczatkowy (transakcja 10 utworzyla wiersz)
-- id=1 | status=NEW | xmin=10 | xmax=NULL
-- Transakcja 20 wykonuje UPDATE:
UPDATE orders SET status = 'PAID' WHERE id = 1;
-- MVCC tworzy NOWA wersje wiersza (nie nadpisuje starej!):
-- id=1 | status=NEW | xmin=10 | xmax=20 ← stara wersja (oznaczona jako usunieta przez tx 20)
-- id=1 | status=PAID | xmin=20 | xmax=NULL ← nowa wersja (utworzona przez tx 20)
-- Transakcja 15 (rozpoczeta PRZED tx 20) nadal widzi:
-- id=1 | status=NEW (bo xmin=10 < 15, xmax=20 > 15)
-- Transakcja 25 (rozpoczeta PO tx 20) widzi:
-- id=1 | status=PAID (bo xmin=20 < 25, xmax=NULL)
| Zaleta | Opis |
|---|---|
| Readers nie blokuja Writers | SELECT nie blokuje UPDATE/DELETE - kazdy czyta swoja wersje |
| Writers nie blokuja Readers | UPDATE nie blokuje SELECT - stara wersja nadal dostepna |
| Spojny snapshot | Transakcja widzi stabilny obraz danych z momentu rozpoczecia |
| Mniej deadlockow | Minimalizuje blokady = mniejsze ryzyko zakleszczen |
| Lepsza wydajnosc odczytu | Odczyty nie musza czekac na zakonczenie zapisow |
| Cecha | Tradycyjne blokowanie | MVCC |
|---|---|---|
| SELECT podczas UPDATE | Czeka na zwolnienie blokady | Czyta stara wersje natychmiast |
| Zuzycie pamieci | Niskie | Wyzsze (przechowuje wiele wersji) |
| Wymaga czyszczenia | Nie | Tak (VACUUM w PostgreSQL) |
| Bazy danych | Starsze systemy | PostgreSQL, MySQL InnoDB, Oracle |
-- PostgreSQL: Reczne czyszczenie starych wersji
VACUUM orders;
-- Automatyczne czyszczenie (domyslnie wlaczone)
-- autovacuum dziala w tle
GET - pobierz zasób. Bezpieczne (nie zmienia stanu), idempotentne.POST - utwórz zasób / wykonaj akcję. Nie idempotentne - dwa razy = dwa zasoby.PUT - zastąp cały zasób. Idempotentne (kilka razy = ten sam efekt).PATCH - częściowa aktualizacja (tylko wybrane pola).DELETE - usuń zasób. Idempotentne.Metody HTTP - opisy i typowe odpowiedzi:
| Metoda | Opis | Typowe odpowiedzi |
|---|---|---|
| GET | Pobiera okreslony zasob wedlug podanego identyfikatora | 200 (OK) - z zasobem zwracanym 404 (Not Found) - jesli zasobu nie odnaleziono |
| POST | Tworzy nowy zasob. Wykorzystywany do operacji nie wpisujacych sie w inne metody. Moze sluzyc do pobierania danych gdy potrzebne sa parametry w body | 201 (Created) - Header Location powinien zawierac link do zasobu 409 (Conflict) - jesli zasob juz istnieje |
| PUT | Aktualizuje (zastepuje) caly zasob. Moze tworzyc nowy zasob jesli jego ID jest znany | 200 (OK) lub 204 (No Content) 404 (Not Found) - jesli nie odnaleziono ID 409 (Conflict) - jesli ID w ciele rozni sie od ID w sciezce |
| PATCH | Aktualizuje czesc wskazanego zasobu (tylko wybrane pola) | 200 (OK) lub 204 (No Content) 404 (Not Found) - jesli nie odnaleziono ID 409 (Conflict) - jesli ID w ciele rozni sie od ID w sciezce |
| DELETE | Usuwa okreslony zasob wedlug identyfikatora | 200 (OK) lub 204 (No Content) 404 (Not Found) - jesli nie odnaleziono ID |
| HEAD | Jak GET, ale zwraca tylko naglowki (bez body) | 200 (OK) 404 (Not Found) |
| OPTIONS | Zwraca dostepne metody dla zasobu (uzywane w CORS) | 200 (OK) lub 204 (No Content) |
Charakterystyka metod:
Idempotentna - wielokrotne wywolanie daje ten sam efekt co pojedyncze (mozna bezpiecznie ponowic przy
timeout).
Bezpieczna - nie zmienia stanu serwera, tylko odczytuje dane. Kazda metoda bezpieczna jest tez
idempotentna.
| Metoda | Idempotentna | Bezpieczna | Request Body | Response Body |
|---|---|---|---|---|
| GET | ✓ | ✓ | ✗ | ✓ |
| POST | ✗ | ✗ | ✓ | ✓ |
| PUT | ✓ | ✗ | ✓ | ✓ |
| DELETE | ✓ | ✗ | ✗ | ✗ |
| PATCH | ✗ | ✗ | ✓ | ✓ |
name, email, age. PUT z tylko name = skasuje email i age. PATCH z name = zmieni tylko name.{age: age+1}).// Zasob poczatkowy
{
"id": 1,
"name": "Jan Kowalski",
"email": "jan@example.com",
"phone": "123456789"
}
// PUT /users/1 - zastepuje CALY zasob
// Musisz wyslac wszystkie pola!
{
"name": "Jan Nowak",
"email": "jan@example.com",
"phone": "123456789"
}
// PATCH /users/1 - aktualizuje TYLKO podane pola
{
"name": "Jan Nowak"
}
// Pozostale pola (email, phone) pozostaja bez zmian
GET /products, nie /getProducts./customers/5/orders/12/items - relacje jako zagnieżdżone zasoby./products, /orders - kolekcje są mnogie./api/v1/... w URL-u lub nagłówku Accept.?page=2&size=20), filtrowanie, sortowanie, walidacja, autoryzacja, kody błędów.# Produkty
GET /api/products # lista produktow (z paginacja)
GET /api/products/{id} # szczegoly produktu
POST /api/products # dodaj produkt (admin)
PUT /api/products/{id} # aktualizuj produkt
DELETE /api/products/{id} # usun produkt
# Kategorie
GET /api/categories # lista kategorii
GET /api/categories/{id}/products # produkty w kategorii
# Koszyk
GET /api/cart # zawartosc koszyka
POST /api/cart/items # dodaj do koszyka
PUT /api/cart/items/{productId} # zmien ilosc
DELETE /api/cart/items/{productId} # usun z koszyka
# Zamowienia
GET /api/orders # historia zamowien
GET /api/orders/{id} # szczegoly zamowienia
POST /api/orders # zloz zamowienie
# Filtrowanie i paginacja
GET /api/products?category=electronics&minPrice=100&page=0&size=20&sort=price,asc
1xx - Informacyjne:
| Kod | Znaczenie | Uzycie |
|---|---|---|
| 100 | Continue | Serwer otrzymal naglowki, klient moze wyslac body |
| 101 | Switching Protocols | Serwer akceptuje zmiane protokolu (np. na WebSocket) |
| 102 | Processing | Serwer przetwarza zadanie (WebDAV) - zapobiega timeout |
| 103 | Early Hints | Wczesne ladowanie zasobow (Link headers) przed pelna odpowiedzia |
2xx - Sukces:
| Kod | Znaczenie | Uzycie |
|---|---|---|
| 200 | OK | GET, PUT, PATCH - sukces |
| 201 | Created | POST - zasob utworzony |
| 202 | Accepted | Zadanie przyjete do przetwarzania (async) |
| 204 | No Content | DELETE - sukces bez tresci |
3xx - Przekierowania:
| Kod | Znaczenie | Uzycie |
|---|---|---|
| 301 | Moved Permanently | Zasob przeniesiony na stale (SEO, nowy URL) |
| 302 | Found (Temporary Redirect) | Tymczasowe przekierowanie |
| 303 | See Other | Przekierowanie po POST (PRG pattern) |
| 304 | Not Modified | Cache - zasob nie zmieniony, uzyj lokalnej kopii |
| 307 | Temporary Redirect | Jak 302, ale zachowuje metode HTTP |
| 308 | Permanent Redirect | Jak 301, ale zachowuje metode HTTP |
4xx - Blad klienta:
| Kod | Znaczenie | Uzycie |
|---|---|---|
| 400 | Bad Request | Bledne dane wejsciowe |
| 401 | Unauthorized | Brak/nieprawidlowe uwierzytelnienie |
| 403 | Forbidden | Brak uprawnien |
| 404 | Not Found | Zasob nie istnieje |
| 409 | Conflict | Konflikt (np. duplikat) |
| 422 | Unprocessable Entity | Blad walidacji |
5xx - Blad serwera:
| Kod | Znaczenie | Uzycie |
|---|---|---|
| 500 | Internal Server Error | Nieobsluzony wyjatek |
| 502 | Bad Gateway | Problem z upstream serwerem |
| 503 | Service Unavailable | Serwer przeciazony/maintenance |
X-API-Key) - dla server-to-server, brak informacji o userze. Prosto, ale ograniczone.1. JWT (JSON Web Token):
// Struktura: header.payload.signature
@PostMapping("/login")
public TokenResponse login(@RequestBody LoginRequest request) {
// 1. Weryfikacja credentials
// 2. Generowanie JWT
String token = Jwts.builder()
.setSubject(user.getUsername())
.setExpiration(new Date(System.currentTimeMillis() + 86400000))
.signWith(secretKey)
.compact();
return new TokenResponse(token);
}
// Klient wysyla: Authorization: Bearer <token>
Dodatkowe zabezpieczenia: HTTPS, Rate limiting, Input validation, CORS configuration, Audit logging
app.com przed wywołaniem api.com. CORS to legalna furtka.Access-Control-Allow-Origin, Allow-Methods, Allow-Headers - mówi "tak, ci mogą mnie wołać".@CrossOrigin na kontrolerze lub globalna konfiguracja w WebMvcConfigurer.CORS (Cross-Origin Resource Sharing) - mechanizm pozwalajacy na zadania z innej domeny niz serwer.
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("http://localhost:3000", "https://myapp.com")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowedHeaders("*")
.allowCredentials(true)
.maxAge(3600);
}
}
| Cecha | Monolit | Mikroserwisy |
|---|---|---|
| Deployment | Cala aplikacja naraz | Niezalezny dla kazdego serwisu |
| Skalowanie | Calosc | Kazdy serwis osobno |
| Technologia | Jedna dla calosci | Rozne dla roznych serwisow |
| Komunikacja | Wywolania metod | Siec (HTTP, messaging) |
| Baza danych | Wspolna | Kazdy serwis ma wlasna |
| Zlozonosc | Prostszy start | Wieksza zlozonosc operacyjna |
| Metoda | Uzycie | Zalety | Wady |
|---|---|---|---|
| REST | CRUD, synchroniczne | Prosty, standardowy | Latency, coupling |
| Messaging (Kafka/RabbitMQ) | Eventy, async | Loose coupling, resilience | Zlozonosc, eventual consistency |
| gRPC | High-performance | Szybki, typowany | Mniej czytelny |
// Producer - Kafka
@Service
public class OrderService {
private final KafkaTemplate<String, OrderEvent> kafka;
public void createOrder(Order order) {
orderRepository.save(order);
kafka.send("order-events", new OrderCreatedEvent(order));
}
}
// Consumer w innym serwisie
@KafkaListener(topics = "order-events")
public void handleOrderCreated(OrderCreatedEvent event) {
sendEmail(event.getCustomerEmail());
}
api.example.com. Gateway rozpoznaje routing po URL-u.API Gateway - punkt wejscia do systemu mikroserwisow.
Funkcje: Routing, Load balancing, Authentication, Rate limiting, Caching, SSL termination, Request/Response transformation
W mikroserwisach nie ma wspolnej transakcji - kazdy serwis ma wlasna baze.
Saga Pattern - sekwencja lokalnych transakcji z kompensacjami (Eventual Consistency).
Implementacje: Choreography (zdarzenia) lub Orchestration (centralny koordynator)
1. Two-Phase Commit (2PC) - protokol zapewniajacy ACID w transakcjach rozproszonych.
// Przyklad z Atomikos (JTA Transaction Manager)
@Bean
public JtaTransactionManager transactionManager() {
return new JtaTransactionManager(
new UserTransactionManager()
);
}
@Transactional(transactionManager = "jtaTransactionManager")
public void createOrder(OrderRequest request) {
productRepo.reserve(request); // XA Resource 1
orderRepo.save(order); // XA Resource 2
paymentRepo.create(payment); // XA Resource 3
// Automatyczny COMMIT lub ROLLBACK przez Atomikos
}
2. TCC (Try-Confirm-Cancel) - wzorzec z rezerwacja zasobow (soft-lock).
| Cecha | Saga | 2PC | TCC |
|---|---|---|---|
| Spojnosc | Eventual | Strong (ACID) | Eventual |
| Dostepnosc | Wysoka | Niska (blocking) | Wysoka |
| Wydajnosc | Wysoka | Niska (locks) | Srednia |
| Izolacja | Brak | Pelna | Soft locks |
| Single Point of Failure | Nie | Tak (coordinator) | Nie |
| Przypadek uzycia | Mikroserwisy | Monolit / 2-3 DB | Rezerwacje (hotele, bilety) |
Kiedy uzywac:
order-service, adres: 10.0.1.5:8080"). Klient pyta rejestr "gdzie jest order-service?".Service Discovery - mechanizm automatycznego wykrywania lokalizacji serwisow.
Narzedzia: Netflix Eureka, Consul, Kubernetes DNS
# application.yml serwisu
eureka:
client:
serviceUrl:
defaultZone: http://eureka:8761/eureka/
// Wywolanie po nazwie serwisu (nie IP)
restTemplate.getForObject("http://product-service/api/products/1", Product.class);
Trzy filary obserwowalnosci:
# Spring Boot Actuator + Micrometer
management:
endpoints:
web:
exposure:
include: health, metrics, prometheus
metrics:
export:
prometheus:
enabled: true
Docker - platforma do konteneryzacji aplikacji.
Problemy ktore rozwiazuje:
docker build tworzy obraz, docker run tworzy i uruchamia kontener.| Obraz (Image) | Kontener (Container) |
|---|---|
| Szablon (blueprint) | Uruchomiona instancja obrazu |
| Niezmienialny (immutable) | Ma swoj stan (mozna zapisywac) |
| Warstwy tylko do odczytu | Warstwa zapisu na gorze |
| docker build / docker pull | docker run |
| Jak klasa w OOP | Jak obiekt w OOP |
docker-compose.yml.docker run to koszmar.docker-compose up (uruchom wszystko), down (zatrzymaj), logs (loguj).Docker Compose - narzedzie do definiowania i uruchamiania wielu kontenerow.
# docker-compose.yml
version: '3.8'
services:
product-service:
build: ./services/product-service
ports:
- "8081:8080"
environment:
- SPRING_DATASOURCE_URL=jdbc:postgresql://db:5432/products
depends_on:
- db
db:
image: postgres:15
environment:
POSTGRES_PASSWORD: secret
volumes:
- postgres-data:/var/lib/postgresql/data
volumes:
postgres-data:
alpine (5MB) zamiast ubuntu (70MB). Dla Javy: jre-slim zamiast jdk.# Stage 1: Build
FROM eclipse-temurin:21-jdk AS builder
WORKDIR /app
# Najpierw kopiuj tylko pliki zaleznosci (lepszy cache)
COPY pom.xml .
COPY .mvn .mvn
COPY mvnw .
RUN ./mvnw dependency:go-offline
# Potem kod zrodlowy
COPY src ./src
RUN ./mvnw package -DskipTests
# Stage 2: Runtime (mniejszy obraz)
FROM eclipse-temurin:21-jre
WORKDIR /app
# Non-root user
RUN addgroup --system app && adduser --system --group app
USER app
COPY --from=builder /app/target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
@SpringBootTest, prawdziwa baza (Testcontainers), pełen kontekst.| Cecha | Test jednostkowy | Test integracyjny |
|---|---|---|
| Zakres | Pojedyncza klasa/metoda | Wiele komponentow razem |
| Zaleznosci | Mockowane | Prawdziwe (DB, HTTP) |
| Szybkosc | Bardzo szybkie (ms) | Wolniejsze (sekundy) |
| Izolacja | Pelna | Czesciowa |
| Cel | Logika biznesowa | Wspolpraca komponentow |
mock(Class) - utwórz mockwhen(...).thenReturn(...) - zdefiniuj zachowanie (stubbing)verify(...) - sprawdź że metoda została wywołana@Mock, @InjectMocks, @Spy, @MockBean (w Springu).Mockito - biblioteka do tworzenia mockow (atrap) obiektow w testach.
@ExtendWith(MockitoExtension.class)
class ProductServiceTest {
@Mock
private ProductRepository productRepository;
@InjectMocks
private ProductService productService;
@Test
void shouldReturnProduct() {
// given - konfiguracja mockow
Product product = new Product(1L, "Laptop");
when(productRepository.findById(1L)).thenReturn(Optional.of(product));
// when
ProductDTO result = productService.findById(1L);
// then
assertThat(result.getName()).isEqualTo("Laptop");
verify(productRepository).findById(1L);
}
}
Glowne funkcje: @Mock, @InjectMocks,
when(...).thenReturn(...), verify(...), @Spy, @Captor
TDD (Test-Driven Development) - technika, gdzie najpierw piszesz test, potem kod.
@WebMvcTest - tylko warstwa web, bez DB i serwisów (mockujesz). Szybki.@SpringBootTest + MockMvc - pełen kontekst. Wolniejszy, ale bardziej realistyczny.@SpringBootTest(webEnvironment=RANDOM_PORT) + TestRestTemplate/WebTestClient - prawdziwe HTTP.mockMvc.perform(get("/api/users")).@WebMvcTest(ProductController.class) // laduje tylko warstwe web
class ProductControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean // mock dla beana w kontekscie Springa
private ProductService productService;
@Test
void shouldReturnProductById() throws Exception {
// given
ProductDTO product = new ProductDTO(1L, "Laptop", BigDecimal.valueOf(999));
when(productService.findById(1L)).thenReturn(product);
// when & then
mockMvc.perform(get("/api/products/1")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.id").value(1))
.andExpect(jsonPath("$.name").value("Laptop"));
}
}
OutOfMemoryError.equals()/hashCode() w kluczach HashMapremove()jmap, jcmd), analiza w Eclipse MAT / VisualVM, jstat do GC, profilery (JProfiler, YourKit).Memory Leak (wyciek pamieci) - sytuacja, gdy aplikacja alokuje pamiec, ale nigdy jej nie zwalnia, mimo ze juz jej nie potrzebuje. W Javie oznacza to obiekty, ktore nie sa uzywane, ale Garbage Collector nie moze ich usunac, bo wciaz istnieja do nich referencje.
1. Identyfikacja:
# Monitoring heap
jstat -gc <pid> 1000
# Heap dump przy OutOfMemoryError
java -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/heap.hprof -jar app.jar
2. Typowe przyczyny:
1. Statyczna kolekcja (cache bez limitu):
// ZLE - memory leak! Kolekcja rosnie w nieskonczonosc
public class UserCache {
private static final Map<Long, User> cache = new HashMap<>();
public void addUser(User user) {
cache.put(user.getId(), user); // Nigdy nie usuwane!
}
}
// DOBRZE - ograniczony cache z LRU eviction
public class UserCache {
private static final int MAX_SIZE = 1000;
private static final Map<Long, User> cache = new LinkedHashMap<>(MAX_SIZE, 0.75f, true) {
@Override
protected boolean removeEldestEntry(Map.Entry<Long, User> eldest) {
return size() > MAX_SIZE; // Usuwa najstarszy gdy przekroczy limit
}
};
}
// JESZCZE LEPIEJ - Caffeine/Guava Cache
Cache<Long, User> cache = Caffeine.newBuilder()
.maximumSize(1000)
.expireAfterWrite(10, TimeUnit.MINUTES)
.build();
2. Niezamkniete zasoby (InputStream, Connection):
// ZLE - memory leak! Stream nigdy nie zamkniety
public String readFile(String path) throws IOException {
FileInputStream fis = new FileInputStream(path);
byte[] data = fis.readAllBytes();
return new String(data);
// fis.close() NIGDY nie wywolane - wyciek!
}
// DOBRZE - try-with-resources (automatyczne zamkniecie)
public String readFile(String path) throws IOException {
try (FileInputStream fis = new FileInputStream(path)) {
byte[] data = fis.readAllBytes();
return new String(data);
} // fis.close() wywolane automatycznie (nawet przy uzyciu wyjatku)
}
3. ThreadLocal bez czyszczenia:
// ZLE - memory leak w aplikacji webowej!
public class UserContext {
private static final ThreadLocal<User> currentUser = new ThreadLocal<>();
public static void setUser(User user) {
currentUser.set(user); // Watek z puli trzyma referencje na zawsze!
}
}
// DOBRZE - zawsze czyscimy w finally lub uzyj filtra
public class UserContext {
private static final ThreadLocal<User> currentUser = new ThreadLocal<>();
public static void setUser(User user) { currentUser.set(user); }
public static void clear() { currentUser.remove(); } // KRYTYCZNE!
}
// W filtrze Spring:
@Component
public class UserContextFilter implements Filter {
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) {
try {
UserContext.setUser(extractUser(req));
chain.doFilter(req, res);
} finally {
UserContext.clear(); // ZAWSZE czyscimy po uzyciu zadania
}
}
}
4. Listenery/Observery bez wyrejestrowania:
// ZLE - memory leak! Listener trzyma referencje do obiektu
@Service
public class OrderService {
@Autowired
private EventPublisher publisher;
@PostConstruct
public void init() {
publisher.addListener(this::onEvent); // Zarejestrowany na zawsze!
}
}
// DOBRZE - wyrejestrowanie w @PreDestroy
@Service
public class OrderService {
@Autowired
private EventPublisher publisher;
private Consumer<Event> listener;
@PostConstruct
public void init() {
listener = this::onEvent;
publisher.addListener(listener);
}
@PreDestroy
public void cleanup() {
publisher.removeListener(listener); // Wyrejestrowanie!
}
}
5. Klasy wewnetrzne (inner class) trzymajace referencje:
// ZLE - non-static inner class trzyma referencje do outer class
public class HeavyObject {
private byte[] data = new byte[10_000_000]; // 10MB
public Runnable createTask() {
return new Runnable() { // Anonimowa klasa trzyma ref do HeavyObject!
public void run() { System.out.println("Task"); }
};
}
}
// DOBRZE - static nested class lub lambda bez referencji
public class HeavyObject {
private byte[] data = new byte[10_000_000];
public Runnable createTask() {
return () -> System.out.println("Task"); // Lambda bez ref do this
}
// Lub static nested class:
private static class MyTask implements Runnable {
public void run() { System.out.println("Task"); }
}
}
EXPLAIN ANALYZE - pokazuje plan wykonania zapytania przez bazę. Bez tego to zgadywanie.LIMIT/paginacja, unikanie SELECT *, denormalizacja.JOIN FETCH.ANALYZE (aktualizacja statystyk), VACUUM (PostgreSQL), monitoring (pg_stat_statements).W srodowiskach o duzym obciazeniu, optymalizacja wydajnosci transakcji jest niezbedna. Im krocej trwa zapytanie, tym krocej trzyma locki.
-- Analiza z EXPLAIN ANALYZE
EXPLAIN ANALYZE
SELECT * FROM orders WHERE created_at > '2024-01-01';
Typowe problemy i rozwiazania:
WHERE YEAR(created_at) = 2024 wymaga full scan. Rozwiazanie: WHERE
created_at BETWEEN '2024-01-01' AND '2024-12-31' lub functional index.
LIKE '%tekst' nie moze uzyc indeksu B-tree, bo nie zna
poczatku szukanego ciagu. Rozwiazanie: Full-Text Search (np. PostgreSQL tsvector, Elasticsearch) lub trigram
index (pg_trgm).
OFFSET 100000 wymusza przeskanowanie i pomieniecie 100k wierszy. Im
wieksza strona, tym wolniej. Rozwiazanie: keyset pagination (cursor-based) - zamiast OFFSET uzywaj WHERE id >
last_seen_id ORDER BY id LIMIT 20. Zawsze stala wydajnosc niezaleznie od numeru strony.
Poprawia wydajnosc zapytan, ale wymaga rownowagi - zbyt wiele indeksow wydluza czas zapisu (INSERT/UPDATE).
-- Zamiast wolnego zapytania:
SELECT * FROM orders
WHERE user_id = 123
ORDER BY created_at DESC;
-- Utworz indeks kompozytowy:
CREATE INDEX idx_orders_user_created
ON orders(user_id, created_at DESC);
W JPA/Hibernate:
@Entity
@Table(
name = "orders",
indexes = {
@Index(name = "idx_orders_user_created", columnList = "user_id, created_at")
}
)
public class Order {
// ...
}
Pobieraj tylko potrzebne kolumny - zmniejsza transfer danych i obciazenie pamieci.
-- ZLE: pobiera wszystkie kolumny
SELECT * FROM orders WHERE user_id = 123;
-- DOBRZE: tylko potrzebne kolumny
SELECT id, status, created_at FROM orders WHERE user_id = 123;
W JPA - projekcja do DTO:
// DTO
public record OrderSummaryDto(
Long id,
OrderStatus status,
LocalDateTime createdAt
) {}
// Repository - projekcja zamiast calej encji
@Query("""
SELECT new projekt.dto.OrderSummaryDto(
o.id,
o.status,
o.createdAt
)
FROM Order o
WHERE o.userId = :userId
""")
List<OrderSummaryDto> findOrderSummariesByUserId(@Param("userId") Long userId);
Zamiast wielu pojedynczych zapytan, uzyj jednego z IN (...).
// ZLE - N zapytan (petla)
List<Long> userIds = List.of(1L, 2L, 3L);
List<Order> allOrders = new ArrayList<>();
for (Long userId : userIds) {
allOrders.addAll(orderRepository.findByUserId(userId));
}
// Generuje N zapytan:
// SELECT * FROM orders WHERE user_id = 1;
// SELECT * FROM orders WHERE user_id = 2;
// SELECT * FROM orders WHERE user_id = 3;
// DOBRZE - jedno zapytanie z IN
List<Order> orders = orderRepository.findByUserIdIn(userIds);
// Repository:
interface OrderRepository extends JpaRepository<Order, Long> {
List<Order> findByUserIdIn(List<Long> userIds);
}
// Generuje: SELECT * FROM orders WHERE user_id IN (1, 2, 3);
Grupuje operacje zapisu w celu zmniejszenia liczby round-tripow do bazy.
// ZLE - N operacji INSERT
for (Order o : orders) {
orderRepository.save(o); // Kazdy save = oddzielny INSERT
}
// DOBRZE - batch insert
orderRepository.saveAll(orders); // Jeden batch
// Konfiguracja w application.yml:
spring:
jpa:
properties:
hibernate:
jdbc:
batch_size: 50
order_inserts: true
order_updates: true
Ponowne wykorzystanie polaczen zamiast tworzenia nowych (kosztowna operacja).
# HikariCP - domyslny w Spring Boot
spring:
datasource:
hikari:
maximum-pool-size: 20 # Max polaczen w puli
minimum-idle: 5 # Min polaczen gotowych
connection-timeout: 30000 # Timeout na pobranie polaczenia (ms)
idle-timeout: 600000 # Czas bezczynnosci przed zamknieciem
Row-level locking zamiast table-level - pozwala na rownoczesne transakcje na roznych wierszach.
-- Row-level lock (tylko wiersz id=1)
SELECT * FROM orders WHERE id = 1 FOR UPDATE;
-- NOWAIT - nie czekaj, rzuc blad jesli zablokowane
SELECT * FROM orders WHERE id = 1 FOR UPDATE NOWAIT;
-- SKIP LOCKED - pomin zablokowane wiersze (dobre dla queue)
SELECT * FROM tasks WHERE status = 'PENDING'
ORDER BY created_at LIMIT 1 FOR UPDATE SKIP LOCKED;
Rozklad obciazenia przez replikacje - zapisy do mastera, odczyty z replik.
// Spring - @Transactional(readOnly = true) kieruje na replike
@Transactional(readOnly = true)
public List<Order> findAllOrders() {
return orderRepository.findAll(); // Idzie na replike
}
@Transactional
public void createOrder(Order order) {
orderRepository.save(order); // Idzie na mastera
}
| Technika | Problem | Rozwiazanie |
|---|---|---|
| Indeksowanie | Wolne wyszukiwanie | CREATE INDEX na WHERE, JOIN, ORDER BY |
| Projekcja | SELECT * pobiera za duzo | DTO z tylko potrzebnymi polami |
| Batching | N+1 / petla zapytan | findByIdIn(), saveAll() |
| Connection Pool | Kosztowne tworzenie polaczen | HikariCP z odpowiednim pool size |
| Row-level locking | Blokowanie calej tabeli | FOR UPDATE, SKIP LOCKED |
| Read Replicas | Master przeciazony | @Transactional(readOnly=true) |
Kluczowe elementy:
@Valid), obsługa błędów (@ControllerAdvice), DTO (nie ujawniaj encji), właściwe statusy HTTP.extends JpaRepository - darmowe metody save, findById, delete.Pageable), sortowaniu, transakcjach, testach.// Entity
@Entity
@Data
public class Product {
@Id @GeneratedValue
private Long id;
@NotBlank
private String name;
@NotNull @Positive
private BigDecimal price;
}
// Controller
@RestController
@RequestMapping("/api/products")
@RequiredArgsConstructor
public class ProductController {
private final ProductService service;
@GetMapping
public Page<ProductResponse> getAll(Pageable pageable) {
return service.findAll(pageable);
}
@GetMapping("/{id}")
public ProductResponse getById(@PathVariable Long id) {
return service.findById(id);
}
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public ProductResponse create(@Valid @RequestBody ProductRequest request) {
return service.create(request);
}
@PutMapping("/{id}")
public ProductResponse update(@PathVariable Long id,
@Valid @RequestBody ProductRequest request) {
return service.update(id, request);
}
@DeleteMapping("/{id}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void delete(@PathVariable Long id) {
service.delete(id);
}
}
Host, Accept, User-Agent, Authorization, Cookie)Content-Type, Set-Cookie, Cache-Control, Location)Content-Length, Content-Encoding)Content-Type - jaki format body (JSON, XML, form)Authorization - dane uwierzytelniające (Bearer token)Accept - jaki format klient akceptuje w odpowiedziCache-Control - jak cache'ować odpowiedźNaglowki HTTP to metadane przesylane miedzy klientem a serwerem. Dziela sie na kategorie:
Naglowki zadania (Request Headers):
| Naglowek | Opis | Przyklad |
|---|---|---|
Authorization |
Dane uwierzytelniajace (token, credentials) | Bearer eyJhbGciOiJIUzI1NiIs... |
Content-Type |
Typ MIME wysylanej tresci | application/json |
Accept |
Jakie typy odpowiedzi klient akceptuje | application/json, text/html |
Accept-Language |
Preferowany jezyk odpowiedzi | pl-PL, en-US;q=0.9 |
Accept-Encoding |
Akceptowane kompresje | gzip, deflate, br |
User-Agent |
Informacje o kliencie (przegladarka, OS) | Mozilla/5.0 (Windows NT 10.0)... |
Host |
Domena docelowa (wymagany w HTTP/1.1) | api.example.com |
Cookie |
Ciasteczka wysylane do serwera | session_id=abc123; theme=dark |
Origin |
Zrodlo zadania (CORS) | https://myapp.com |
Referer |
URL strony, z ktorej przyszlo zadanie | https://google.com/search?q=... |
X-Requested-With |
Identyfikuje zadania AJAX | XMLHttpRequest |
If-None-Match |
Warunkowe zadanie (cache) - ETag | "abc123" |
If-Modified-Since |
Warunkowe zadanie - data modyfikacji | Wed, 21 Oct 2024 07:28:00 GMT |
Naglowki odpowiedzi (Response Headers):
| Naglowek | Opis | Przyklad |
|---|---|---|
Content-Type |
Typ MIME odpowiedzi | application/json; charset=utf-8 |
Content-Length |
Rozmiar odpowiedzi w bajtach | 1234 |
Content-Encoding |
Uzyta kompresja | gzip |
Set-Cookie |
Ustawia ciasteczko w przegladarce | session=abc; HttpOnly; Secure |
Location |
URL przekierowania (3xx) | https://example.com/new-page |
Cache-Control |
Dyrektywy cache | max-age=3600, must-revalidate |
ETag |
Identyfikator wersji zasobu (cache) | "abc123" |
Last-Modified |
Data ostatniej modyfikacji | Wed, 21 Oct 2024 07:28:00 GMT |
Expires |
Kiedy cache wygasa | Thu, 01 Dec 2024 16:00:00 GMT |
WWW-Authenticate |
Wymagana metoda uwierzytelnienia (401) | Bearer realm="api" |
Naglowki CORS:
| Naglowek | Opis | Przyklad |
|---|---|---|
Access-Control-Allow-Origin |
Dozwolone originy | https://myapp.com lub * |
Access-Control-Allow-Methods |
Dozwolone metody HTTP | GET, POST, PUT, DELETE |
Access-Control-Allow-Headers |
Dozwolone naglowki w zadaniu | Content-Type, Authorization |
Access-Control-Allow-Credentials |
Czy wysylac cookies | true |
Access-Control-Max-Age |
Jak dlugo cachowac preflight | 3600 |
Access-Control-Expose-Headers |
Naglowki widoczne dla JS | X-Custom-Header |
Naglowki bezpieczenstwa:
| Naglowek | Opis | Przyklad |
|---|---|---|
Strict-Transport-Security |
Wymusza HTTPS (HSTS) | max-age=31536000; includeSubDomains |
X-Content-Type-Options |
Blokuje MIME sniffing | nosniff |
X-Frame-Options |
Ochrona przed clickjacking | DENY lub SAMEORIGIN |
X-XSS-Protection |
Filtr XSS przegladarki | 1; mode=block |
Content-Security-Policy |
Polityka ladowania zasobow | default-src 'self'; script-src 'self' |
Referrer-Policy |
Co wysylac w Referer | strict-origin-when-cross-origin |
Cache-Control: max-age=3600)ETag, Last-Modified)public - każdy może cache'ować (CDN też)private - tylko przeglądarka użytkownikano-cache - zawsze sprawdź czy aktualneno-store - nigdy nie cache'uj (dane wrażliwe)If-None-Match: "hash", serwer odpowiada 304 Not Modified jeśli to samo.// Spring - ustawianie naglowkow cache
@GetMapping("/products/{id}")
public ResponseEntity<Product> getProduct(@PathVariable Long id) {
Product product = productService.findById(id);
return ResponseEntity.ok()
.cacheControl(CacheControl.maxAge(1, TimeUnit.HOURS))
.eTag(String.valueOf(product.hashCode()))
.body(product);
}
⚠️ Polskich filmów wprost o Keycloak jest niewiele — w praktyce warto sięgnąć po anglojęzyczne JavaTechie/Amigoscode.
@PreAuthorize("hasRole('ADMIN')"), @Secured, filtry w SecurityFilterChain.| Cecha | Autentykacja (Authentication) | Autoryzacja (Authorization) |
|---|---|---|
| Pytanie | "Kim jestes?" | "Co mozesz robic?" |
| Cel | Weryfikacja tozsamosci | Sprawdzenie uprawnien |
| Kiedy | Przed autoryzacja | Po autentykacji |
| Przyklad | Login + haslo, token JWT | Role: ADMIN, USER, uprawnienia |
| Blad HTTP | 401 Unauthorized | 403 Forbidden |
Keycloak - open-source'owy serwer Identity and Access Management (IAM) od Red Hat.
Funkcje:
W Keycloak kazdy klient ma Capability config - ustawienia okreslajace jak klient moze sie uwierzytelniac i jakie ma uprawnienia.
| Parametr | Opis | Analogia | Bezpieczenstwo |
|---|---|---|---|
| client_id | Publiczny identyfikator aplikacji w Keycloak. Unikatowa nazwa klienta zarejestrowanego w realm. | Jak login/username - mozna go pokazac publicznie | PUBLICZNY - moze byc widoczny w kodzie frontendu, URL-ach |
| client_secret | Tajny klucz znany tylko aplikacji i Keycloak. Generowany automatycznie przez Keycloak (zakladka Credentials). | Jak haslo - musi byc trzymany w sekrecie | TAJNY - tylko w backendzie, w zmiennych srodowiskowych, NIGDY w kodzie frontendu! |
# Gdzie znajdziesz te wartosci w Keycloak:
# 1. Zaloguj sie do Keycloak Admin Console
# 2. Wybierz realm (np. car-shop)
# 3. Clients -> wybierz klienta
# client_id:
# Zakladka "Settings" -> pole "Client ID"
# Np: api-communication-client
# client_secret:
# Zakladka "Credentials" -> pole "Client secret"
# Np: BeY47DCttqijpIOaU9oF2QVRbR7EXzHF
# (widoczne tylko gdy Client authentication = ON)
| Ustawienie | Opis | Kiedy uzywac |
|---|---|---|
| ON (Confidential) | Klient musi podac client_secret przy kazdym zadaniu tokena. Secret jest generowany przez
Keycloak.
|
Aplikacje backendowe (server-to-server), gdzie secret mozna bezpiecznie przechowac |
| OFF (Public) | Klient NIE potrzebuje sekretu. Kazdy moze uzywac client_id. |
Aplikacje frontendowe (SPA, mobile), gdzie nie mozna ukryc sekretu |
| Ustawienie | Opis |
|---|---|
| ON | Wlacza zaawansowane zarzadzanie uprawnieniami (policies, permissions, resources). Pozwala na fine-grained access control - np. "user X moze edytowac tylko swoje zasoby". |
| OFF | Prosta autoryzacja oparta tylko na rolach (RBAC). Wystarczajace dla wiekszosci aplikacji. |
| Flow | Opis | Kiedy uzywac |
|---|---|---|
| Standard flow (Authorization Code) |
1. Uzytkownik przekierowany do Keycloak 2. Loguje sie (login + haslo) 3. Keycloak zwraca authorization_code4. Aplikacja wymienia code na tokeny |
Aplikacje frontendowe z logowaniem uzytkownika (SPA, web apps). Najbezpieczniejszy flow! |
| Direct access grants (Resource Owner Password) |
Aplikacja bezposrednio wysyla login i haslo uzytkownika do Keycloak.POST /token z grant_type=password
|
Legacy aplikacje, testowanie. NIE ZALECANE w produkcji - haslo przechodzi przez aplikacje! |
| Service accounts (Client Credentials) |
Aplikacja uwierzytelnia sie sama soba (nie w imieniu uzytkownika).POST /token z grant_type=client_credentials
|
Komunikacja backend-to-backend, mikroserwisy, batch jobs |
| Implicit flow | Token zwracany bezposrednio w URL (bez wymiany code). Przestarzaly! | NIE UZYWAC - zastapiony przez Standard flow + PKCE |
# KLIENT 1: Frontend - uzytkownik loguje sie przez przegladarke
# Ustawienia w Keycloak:
# - Client authentication: OFF
# - Standard flow: ON
# - Valid redirect URIs: http://localhost:3000/*
# Frontend (React) - przekierowanie do Keycloak
const loginUrl = `
https://keycloak.example.com/realms/car-shop/protocol/openid-connect/auth
?client_id=frontend-client
&response_type=code
&redirect_uri=http://localhost:3000/callback
&scope=openid`;
window.location.href = loginUrl;
# KLIENT 2: Backend service - komunikacja miedzy mikroserwisami
# Ustawienia w Keycloak:
# - Client authentication: ON
# - Service accounts roles: ON
# - Standard flow: OFF
# application.yml
spring:
security:
oauth2:
client:
registration:
api-communication-client:
client-id: api-communication-client
client-secret: BeY47DCttqijpIOaU9oF2QVRbR7EXzHF # z Keycloak -> Credentials
authorization-grant-type: client_credentials
scope: openid
provider:
keycloak:
token-uri: https://keycloak.example.com/realms/car-shop/protocol/openid-connect/token
client_credentials grant. Token nie reprezentuje uzytkownika, ale sam
serwis. Role przypisujesz do "Service Account" klienta w Keycloak.
spring-boot-starter-oauth2-resource-server.application.yml ustawiasz issuer-uri (adres realm Keycloak). Spring sam pobierze klucze publiczne do weryfikacji tokenów.Authorization: Bearer ... → Spring weryfikuje podpis i claims.GrantedAuthority (może wymagać JwtAuthenticationConverter).@WithMockUser lub Testcontainers z Keycloak.# application.yml
spring:
security:
oauth2:
resourceserver:
jwt:
issuer-uri: http://keycloak:8080/realms/my-realm
jwk-set-uri: http://keycloak:8080/realms/my-realm/protocol/openid-connect/certs
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/public/**").permitAll()
.requestMatchers("/api/admin/**").hasRole("ADMIN")
.requestMatchers("/api/user/**").hasAnyRole("USER", "ADMIN")
.anyRequest().authenticated()
)
.oauth2ResourceServer(oauth2 -> oauth2
.jwt(jwt -> jwt.jwtAuthenticationConverter(jwtAuthConverter()))
)
.sessionManagement(session ->
session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.build();
}
// Konwersja rol z Keycloak do Spring Security
@Bean
public JwtAuthenticationConverter jwtAuthConverter() {
JwtGrantedAuthoritiesConverter converter = new JwtGrantedAuthoritiesConverter();
converter.setAuthoritiesClaimName("realm_access.roles");
converter.setAuthorityPrefix("ROLE_");
JwtAuthenticationConverter jwtConverter = new JwtAuthenticationConverter();
jwtConverter.setJwtGrantedAuthoritiesConverter(converter);
return jwtConverter;
}
}
header.payload.signature (base64url, oddzielone kropkami).sub (user ID), iss (kto wydał), exp (wygasa), iat (wydany kiedy), aud (dla kogo).realm_access.roles, resource_access, preferred_username, email.exp + aud.// JWT sklada sie z 3 czesci: HEADER.PAYLOAD.SIGNATURE
// HEADER (algorytm i typ)
{
"alg": "RS256",
"typ": "JWT",
"kid": "key-id-123"
}
// PAYLOAD (claims - dane)
{
"exp": 1699999999, // expiration time
"iat": 1699996399, // issued at
"iss": "http://keycloak:8080/realms/my-realm", // issuer
"sub": "user-uuid-123", // subject (user ID)
"aud": "my-client", // audience
"typ": "Bearer",
"azp": "my-client", // authorized party
"preferred_username": "jan.kowalski",
"email": "jan@example.com",
"realm_access": {
"roles": ["USER", "ADMIN"]
},
"resource_access": {
"my-client": {
"roles": ["view-products", "edit-products"]
}
}
}
// SIGNATURE - weryfikuje integralnosc tokena
// RSASHA256(base64(header) + "." + base64(payload), privateKey)
/token → dostaje nowy accessHttpOnly cookie| Cecha | Access Token | Refresh Token |
|---|---|---|
| Cel | Autoryzacja dostepu do zasobow | Odswiezenie Access Tokena |
| Czas zycia | Krotki (5-15 minut) | Dlugi (dni/tygodnie) |
| Gdzie wysylany | Do Resource Server (API) | Tylko do Authorization Server (Keycloak) |
| Przechowywanie | Pamiec (JS), localStorage | HttpOnly Cookie (bezpieczniej) |
| Zawiera | Dane uzytkownika, role, uprawnienia | Tylko identyfikator sesji |
// Odswiezanie tokena
POST /realms/my-realm/protocol/openid-connect/token
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token
&refresh_token=eyJhbGciOiJIUzI1NiIs...
&client_id=my-client
&client_secret=secret123
| Cecha | Haszowanie | Szyfrowanie | Kodowanie |
|---|---|---|---|
| Odwracalnosc | NIE (jednokierunkowe) | TAK (z kluczem) | TAK (bez klucza) |
| Cel | Weryfikacja integralnosci, hasla | Ochrona poufnosci | Transformacja formatu |
| Klucz | Nie wymaga | Wymaga | Nie wymaga |
| Uzycie | Hasla, sumy kontrolne, podpisy | Dane wrażliwe, komunikacja | URL, Base64, Unicode |
| Przyklady | SHA-256, bcrypt, Argon2 | AES, RSA, ChaCha20 | Base64, URL encoding, UTF-8 |
Do hasel (wolne, z sola):
| Algorytm | Opis | Zalecany |
|---|---|---|
bcrypt |
Adaptacyjny, z wbudowana sola, konfigurowalny koszt | TAK |
Argon2 |
Zwyciesca Password Hashing Competition, odporny na GPU | TAK (najlepszy) |
scrypt |
Memory-hard, odporny na ataki sprzetowe | TAK |
PBKDF2 |
Starszy standard, wiele iteracji | Akceptowalny |
MD5, SHA-1 |
Szybkie, zlamane kryptograficznie | NIE do hasel! |
Do integralnosci danych (szybkie):
| Algorytm | Dlugosc | Uzycie |
|---|---|---|
SHA-256 |
256 bit | Podpisy cyfrowe, JWT, sumy kontrolne |
SHA-512 |
512 bit | Wyzsze bezpieczenstwo |
SHA-3 |
256/512 bit | Najnowszy standard NIST |
HMAC |
Zalezna od funkcji | Weryfikacja z kluczem (API signatures) |
// Spring Security - haszowanie hasla z bcrypt
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(12); // cost factor 12
}
// Uzycie
String hash = passwordEncoder.encode("password123");
// $2a$12$LQv3c1yqBWVHxkd0LHAkCOYz6TtxMQJqhN8/X4.mfIgPLGCOqK6Fi
boolean matches = passwordEncoder.matches("password123", hash); // true
Co znaczy "odporny na GPU"?
Algorytm jest zaprojektowany tak, aby wymagac duzej ilosci pamieci RAM
(memory-hard). Karty GPU maja ogromna moc obliczeniowa, ale ograniczona pamiec
na pojedynczy watek, przez co nie moga efektywnie wykonywac milionow prob
haszowania rownolegle.
Atak polega na wykorzystaniu gotowych tabel zawierajacych pary: hash → oryginalne haslo. Atakujacy nie musi liczyc hasha – tylko sprawdza go w ogromnej bazie.
Dlaczego to dziala?
Jak sie bronic?
Atak polega na sprawdzaniu kazdej mozliwej kombinacji znakow az do znalezienia poprawnego hasla.
Cechy:
Obrona:
Atakujacy testuje liste najpopularniejszych hasel oraz slow ze slownika. Jest znacznie szybszy niz brute force.
Przyklady hasel latwych do zlamania:
Obrona:
Wykorzystuje dane wyciekle z innych serwisow. Atakujacy automatycznie probuje kombinacji email + haslo na wielu stronach.
Dlaczego jest skuteczny?
Obrona:
Zamiast probowac wielu hasel dla jednego konta, atakujacy probuje jednego popularnego hasla dla wielu kont.
Cel: ominiecie blokad konta.
Obrona:
Wykorzystuje karty graficzne lub specjalizowany sprzet (ASIC), aby liczyc miliony hashy na sekunde.
Obrona:
Atak socjotechniczny – uzytkownik sam podaje haslo na falszywej stronie przypominajacej prawdziwy serwis.
Obrona:
| Koncept | Opis | Przechowywanie |
|---|---|---|
| Salt (sol) | Losowy ciag dodawany do hasla przed haszowaniem, unikalny dla kazdego hasla | Razem z hashem w bazie |
| Pepper (pieprz) | Staly sekret dodawany do wszystkich hasel | W konfiguracji/env, NIE w bazie |
Wyciek bazy danych (data breach) to sytuacja, w której nieuprawniona osoba uzyskuje dostęp do danych przechowywanych w systemie, np. użytkowników, haseł, tokenów, numerów kart czy danych osobowych.
Najważniejsze: Zakłada się, że wyciek kiedyś nastąpi. Dlatego system projektuje się tak, aby skradzione dane były bezużyteczne.
Atakujący wstrzykuje zapytanie SQL przez formularz lub API.
// Złe (SQL injection możliwy)
SELECT * FROM users WHERE email = '" + email + "' AND password = '" + password + "';
// Dobre (parametryzowane zapytania)
userRepository.findByEmail(email);
✅ ORM i parametryzowane zapytania eliminują SQL Injection.
Obrona: Vault / AWS Secret Manager / GCP Secret Manager, rotacja kluczy, zasada least privilege.
✅ DB powinna być dostępna tylko z prywatnej sieci (VPC).
✅ Regularne aktualizacje, skanery zależności (Snyk, Dependabot).
Najważniejsze pytanie: Czy atakujący może użyć tych danych?
Absolutny MUST: Argon2 + salt + wysoki cost.
PESEL, numer karty, adres → użycie encryption at rest, np. AES-256.
Aplikacja nie powinna mieć uprawnień: SUPERUSER, DROP TABLE, ALTER DATABASE, tylko SELECT/INSERT/UPDATE.
Internet
↓
Load Balancer
↓
Backend
↓
Private Network
↓
Database
✅ Baza nigdy nie powinna być publiczna.
Stack: Prometheus, Grafana, SIEM
Największe ryzyko: ktoś kradnie bazę i łamie hasła lokalnie. ✅ Dlatego szybkie hashe (SHA256) są złe, wolne (Argon2) są dobre.
System projektuję tak, jakby wyciek bazy był nieunikniony. Hasła haszuję Argon2 z salt, dane wrażliwe szyfruję, stosuję least privilege i izoluję bazę w prywatnej sieci. Dzięki temu nawet w przypadku breach attacker nie może łatwo wykorzystać danych.
Szyfrowanie symetryczne (ten sam klucz):
| Algorytm | Opis | Uzycie |
|---|---|---|
AES-256 |
Standard, bardzo bezpieczny | Szyfrowanie danych, dyskow, baz |
ChaCha20 |
Szybki, bezpieczny, dobry na mobile | TLS, VPN |
Szyfrowanie asymetryczne (klucz publiczny + prywatny):
| Algorytm | Opis | Uzycie |
|---|---|---|
RSA |
Klasyczny, klucze 2048-4096 bit | Podpisy JWT, wymiana kluczy, TLS |
ECDSA |
Krotsze klucze, szybszy | JWT, certyfikaty, blockchain |
Ed25519 |
Nowoczesny, bardzo szybki | SSH, podpisy |
Animal, Dog musi tam pasować.SOLID - 5 zasad projektowania obiektowego, ktore prowadza do czytelnego, elastycznego i latwo testowalnego kodu.
| Litera | Zasada | Opis |
|---|---|---|
| S | Single Responsibility | Klasa powinna mieć tylko jedną odpowiedzialność i tylko jeden powód do zmiany. Pojedyncza klasa nie powinna pełnić wielu ról jednocześnie, bo wtedy staje się trudna w utrzymaniu, testowaniu i rozwoju. |
| O | Open/Closed | Klasy powinny być otwarte na rozszerzenia, ale zamknięte na modyfikacje. Nową funkcjonalność dodajemy przez rozszerzenie (np. dziedziczenie, wzorce projektowe), a nie przez modyfikację istniejącego kodu. |
| L | Liskov Substitution | Obiekty klasy bazowej powinny być wymienialne z obiektami klasy pochodnej bez wpływu na poprawność działania programu. Podklasa nie powinna łamać zachowania oczekiwanego od klasy bazowej (np. jeśli Ptak ma metodę fly(), to Pingwin nie powinien dziedziczyć po Ptak, bo nie lata). |
| I | Interface Segregation | Zamiast jednego dużego interfejsu należy definiować wiele mniejszych, specyficznych interfejsów. Klasy implementują tylko te metody, które są dla nich rzeczywiście potrzebne - kod staje się bardziej modularny i łatwiejszy do testowania. |
| D | Dependency Inversion | Klasa powinna zależeć od interfejsu, nie od konkretnej klasy. Np. UserService powinien przyjmować UserRepository jako interfejs, a nie MySQLUserRepository - dzięki temu łatwo podmienisz bazę danych na inną lub użyjesz mocka w testach. |
User zarządza danymi usera i wysyła powiadomienia").Zasada: Klasa powinna mieć tylko jedną odpowiedzialność i tylko jeden powód do zmiany. Pojedyncza klasa nie powinna pełnić wielu ról jednocześnie, ponieważ wtedy staje się trudna w utrzymaniu, testowaniu i rozwoju.
// ZLE - klasa ma wiele odpowiedzialnosci
public class UserService {
public void createUser(User user) { /* ... */ }
public void sendWelcomeEmail(User user) { /* ... */ }
public String generateReport(List<User> users) { /* ... */ }
public void exportToPdf(String report) { /* ... */ }
}
// DOBRZE - kazda klasa ma jedna odpowiedzialnosc
public class UserService {
public void createUser(User user) { /* ... */ }
}
public class EmailService {
public void sendWelcomeEmail(User user) { /* ... */ }
}
public class UserReportGenerator {
public String generateReport(List<User> users) { /* ... */ }
}
public class PdfExporter {
public void export(String content) { /* ... */ }
}
switch/if-else po typie - zamiast tego użyj polimorfizmu.if (payment instanceof CardPayment) ... else if (BlikPayment), zdefiniuj interfejs PaymentProcessor i wiele implementacji.Zasada: Oprogramowanie (klasy, moduły) powinno być otwarte na rozszerzenia, ale zamknięte na modyfikacje. Nową funkcjonalność dodajemy poprzez rozszerzenie (np. przez dziedziczenie lub wzorce projektowe), a nie przez bezpośrednią modyfikację istniejących klas.
// ZLE - trzeba modyfikowac klase przy kazdym nowym typie
public class DiscountCalculator {
public BigDecimal calculate(Order order) {
if (order.getCustomerType() == CustomerType.REGULAR) {
return order.getTotal().multiply(new BigDecimal("0.05"));
} else if (order.getCustomerType() == CustomerType.VIP) {
return order.getTotal().multiply(new BigDecimal("0.15"));
}
// Dodanie nowego typu wymaga modyfikacji tej klasy!
return BigDecimal.ZERO;
}
}
// DOBRZE - rozszerzamy przez nowe implementacje
public interface DiscountStrategy {
BigDecimal calculate(Order order);
}
public class RegularDiscount implements DiscountStrategy {
public BigDecimal calculate(Order order) {
return order.getTotal().multiply(new BigDecimal("0.05"));
}
}
public class VipDiscount implements DiscountStrategy {
public BigDecimal calculate(Order order) {
return order.getTotal().multiply(new BigDecimal("0.15"));
}
}
// Dodanie nowego typu = nowa klasa, BEZ modyfikacji istniejacych
public class BlackFridayDiscount implements DiscountStrategy {
public BigDecimal calculate(Order order) {
return order.getTotal().multiply(new BigDecimal("0.30"));
}
}
Square extends Rectangle - kwadrat to matematycznie prostokąt, ale programistycznie nie da się go wstawić tam gdzie Rectangle (bo setWidth też zmienia height).instanceofZasada: Obiekty klasy bazowej powinny być wymienialne z obiektami klasy pochodnej bez wpływu na poprawność działania programu. Podklasa nie powinna łamać zachowania oczekiwanego od klasy bazowej.
// ZLE - lamie LSP (kwadrat nie jest prostokatem w sensie zachowania)
public class Rectangle {
protected int width, height;
public void setWidth(int w) { width = w; }
public void setHeight(int h) { height = h; }
public int getArea() { return width * height; }
}
public class Square extends Rectangle {
@Override
public void setWidth(int w) { width = height = w; }
@Override
public void setHeight(int h) { width = height = h; }
}
// Problem:
Rectangle rect = new Square();
rect.setWidth(5);
rect.setHeight(10);
rect.getArea(); // Oczekujemy 50, dostajemy 100! Zlamana zasada.
// DOBRZE - oddzielne klasy lub wspolny interfejs
public interface Shape {
int getArea();
}
public class Rectangle implements Shape {
private final int width, height;
public Rectangle(int w, int h) { width = w; height = h; }
public int getArea() { return width * height; }
}
public class Square implements Shape {
private final int side;
public Square(int s) { side = s; }
public int getArea() { return side * side; }
}
UnsupportedOperationException.Worker z metodami work(), eat(), sleep(), zrób osobne Workable, Eatable, Sleepable - robot implementuje tylko Workable.Zasada: Zamiast jednego dużego interfejsu powinno się definiować wiele mniejszych, specyficznych interfejsów, tak aby klasy implementowały tylko te metody, które są dla nich rzeczywiście potrzebne. Dzięki temu kod staje się bardziej modularny i łatwiejszy do testowania.
// ZLE - jeden duzy interfejs
public interface Worker {
void work();
void eat();
void sleep();
void code();
void attendMeeting();
}
// Robot nie je ani nie spi - musi implementowac puste metody
public class Robot implements Worker {
public void work() { /* ok */ }
public void eat() { /* nie dotyczy! */ }
public void sleep() { /* nie dotyczy! */ }
public void code() { /* ok */ }
public void attendMeeting() { /* nie dotyczy! */ }
}
// DOBRZE - wiele malych interfejsow
public interface Workable {
void work();
}
public interface Eatable {
void eat();
}
public interface Sleepable {
void sleep();
}
public interface Programmer extends Workable {
void code();
}
// Robot implementuje tylko to co potrzebuje
public class Robot implements Workable, Programmer {
public void work() { /* ... */ }
public void code() { /* ... */ }
}
// Czlowiek implementuje wiecej
public class Human implements Workable, Eatable, Sleepable, Programmer {
/* wszystkie metody */
}
PostgresRepository - powinien zależeć od interfejsu UserRepository, który Postgres implementuje.Zasada: Moduły wysokiego poziomu nie powinny zależeć od modułów niskiego poziomu. Oba powinny zależeć od abstrakcji (np. interfejsów), a nie od konkretnych implementacji. Prowadzi to do słabego sprzężenia i większej elastyczności kodu.
// ZLE - bezposrednia zaleznosc od konkretnej implementacji
public class OrderService {
private MySqlOrderRepository repository = new MySqlOrderRepository();
private SmtpEmailSender emailSender = new SmtpEmailSender();
public void createOrder(Order order) {
repository.save(order);
emailSender.send(order.getCustomerEmail(), "Order created");
}
}
// Nie mozna latwo zmienic bazy ani sposobu wysylania maili
// Nie mozna testowac bez prawdziwej bazy i serwera SMTP
// DOBRZE - zaleznosc od abstrakcji (interfejsow)
public interface OrderRepository {
void save(Order order);
}
public interface NotificationService {
void notify(String recipient, String message);
}
@Service
public class OrderService {
private final OrderRepository repository;
private final NotificationService notificationService;
// Dependency Injection przez konstruktor
public OrderService(OrderRepository repository,
NotificationService notificationService) {
this.repository = repository;
this.notificationService = notificationService;
}
public void createOrder(Order order) {
repository.save(order);
notificationService.notify(order.getCustomerEmail(), "Order created");
}
}
// Mozna latwo podmienic implementacje:
// - MySqlOrderRepository, MongoOrderRepository, InMemoryOrderRepository
// - SmtpEmailSender, SmsNotification, MockNotification (w testach)
/users, /orders. Nadal wszystko POST-em, ale jest podział na zasoby.Richardson Maturity Model - model opisujacy poziomy dojrzalosci API RESTowego. Stworzony przez Leonarda Richardsona.
| Poziom | Nazwa | Opis | Przyklad |
|---|---|---|---|
| 0 | The Swamp of POX | Jeden URL, jedna metoda (POST), wszystko w body | POST /api z akcja w JSON |
| 1 | Resources | Rozne URLe dla roznych zasobow | POST /users, POST /orders |
| 2 | HTTP Verbs | Poprawne uzycie metod HTTP | GET/POST/PUT/DELETE /users |
| 3 | Hypermedia (HATEOAS) | Odpowiedzi zawieraja linki do powiazanych akcji | JSON z _links |
/api/service){"action": "getUser"})Jedno URL jako tunel dla wszystkich operacji. Praktycznie RPC over HTTP.
// Wszystko przez jeden endpoint
POST /api/service HTTP/1.1
Content-Type: application/json
{
"action": "getUser",
"userId": 123
}
POST /api/service HTTP/1.1
{
"action": "createOrder",
"customerId": 456,
"products": [...]
}
POST /api/service HTTP/1.1
{
"action": "deleteUser",
"userId": 123
}
POST /api/service + {"action": "getUser", "id": 5}POST /users/5 + {"action": "get"}/getUser, /createOrder - jesteś na poziomie 1. Rzeczowniki (/users, /orders) = krok do poziomu 2.Rozne URLe dla roznych zasobow, ale nadal jedna metoda (zazwyczaj POST).
// Osobne endpointy dla zasobow
POST /users/get
{
"userId": 123
}
POST /users/create
{
"name": "Jan",
"email": "jan@example.com"
}
POST /orders/create
{
"customerId": 456,
"products": [...]
}
GET /users/5 - pobierz (nie modyfikuje)POST /users - utwórzPUT /users/5 - zastąpDELETE /users/5 - usuńPoprawne uzycie metod HTTP i kodow statusu. To jest standard dla wiekszosc API REST.
// Pobieranie
GET /users/123
// Response: 200 OK + dane uzytkownika
// Tworzenie
POST /users
{ "name": "Jan", "email": "jan@example.com" }
// Response: 201 Created + Location: /users/123
// Aktualizacja
PUT /users/123
{ "name": "Jan Nowak", "email": "jan@example.com" }
// Response: 200 OK
// Usuniecie
DELETE /users/123
// Response: 204 No Content
// Bledy
GET /users/999
// Response: 404 Not Found
POST /users
{ "email": "invalid" }
// Response: 400 Bad Request
{
"id": 5,
"status": "pending",
"_links": {
"self": "/orders/5",
"cancel": "/orders/5/cancel",
"pay": "/orders/5/pay"
}
}
HATEOAS (Hypermedia as the Engine of Application State) - odpowiedzi zawieraja linki do mozliwych akcji.
GET /orders/123
{
"id": 123,
"status": "PENDING",
"total": 299.99,
"customer": {
"id": 456,
"name": "Jan Kowalski"
},
"_links": {
"self": {
"href": "/orders/123"
},
"customer": {
"href": "/customers/456"
},
"cancel": {
"href": "/orders/123/cancel",
"method": "POST"
},
"pay": {
"href": "/orders/123/payment",
"method": "POST"
},
"items": {
"href": "/orders/123/items"
}
}
}
// Po oplaceniu zamowienia - inne dostepne akcje
GET /orders/123
{
"id": 123,
"status": "PAID",
"_links": {
"self": { "href": "/orders/123" },
"ship": { "href": "/orders/123/ship", "method": "POST" },
"refund": { "href": "/orders/123/refund", "method": "POST" }
// "cancel" i "pay" juz niedostepne!
}
}
// Spring HATEOAS
@GetMapping("/orders/{id}")
public EntityModel<Order> getOrder(@PathVariable Long id) {
Order order = orderService.findById(id);
EntityModel<Order> model = EntityModel.of(order);
model.add(linkTo(methodOn(OrderController.class).getOrder(id)).withSelfRel());
model.add(linkTo(methodOn(CustomerController.class).getCustomer(order.getCustomerId())).withRel("customer"));
if (order.getStatus() == Status.PENDING) {
model.add(linkTo(methodOn(OrderController.class).cancelOrder(id)).withRel("cancel"));
model.add(linkTo(methodOn(PaymentController.class).pay(id)).withRel("pay"));
}
return model;
}
private.| Filar | Opis | Slowo kluczowe |
|---|---|---|
| Enkapsulacja | Ukrywanie wewnetrznej implementacji, dostep przez metody | private, gettery/settery |
| Abstrakcja | Pokazywanie tylko istotnych cech, ukrywanie szczegulow | abstract, interface |
| Dziedziczenie | Tworzenie nowych klas na bazie istniejacych | extends, implements |
| Polimorfizm | Jeden interfejs, wiele implementacji | @Override, przeciazanie |
private, dostęp przez gettery/settery, ewentualnie protected dla podklas.Enkapsulacja - ukrywanie wewnetrznego stanu obiektu i udostepnianie kontrolowanego dostepu przez metody.
// ZLE - publiczne pola, brak kontroli
public class BankAccount {
public BigDecimal balance; // kazdy moze zmienic!
}
BankAccount account = new BankAccount();
account.balance = new BigDecimal("-1000"); // ujemne saldo?!
// DOBRZE - prywatne pola, kontrola przez metody
public class BankAccount {
private BigDecimal balance;
public BankAccount(BigDecimal initialBalance) {
if (initialBalance.compareTo(BigDecimal.ZERO) < 0) {
throw new IllegalArgumentException("Initial balance cannot be negative");
}
this.balance = initialBalance;
}
public BigDecimal getBalance() {
return balance; // tylko odczyt
}
public void deposit(BigDecimal amount) {
if (amount.compareTo(BigDecimal.ZERO) <= 0) {
throw new IllegalArgumentException("Amount must be positive");
}
balance = balance.add(amount);
}
public void withdraw(BigDecimal amount) {
if (amount.compareTo(balance) > 0) {
throw new InsufficientFundsException("Not enough balance");
}
balance = balance.subtract(amount);
}
}
List to abstrakcja - klient używa metod, nie wie czy to ArrayList, LinkedList, czy coś innego.Abstrakcja - pokazywanie tylko istotnych cech, ukrywanie szczegulow implementacji.
// Abstrakcja - definiuje CO ma byc zrobione, nie JAK
public interface PaymentProcessor {
PaymentResult processPayment(PaymentRequest request);
}
// Uzytkownik nie musi znac szczegulow implementacji
public class OrderService {
private final PaymentProcessor paymentProcessor;
public void checkout(Order order) {
// Nie wiem czy to Stripe, PayPal czy przelew - i nie musza wiedziec!
PaymentResult result = paymentProcessor.processPayment(
new PaymentRequest(order.getTotal())
);
}
}
// Rozne implementacje - ukryte szczegoly
public class StripePaymentProcessor implements PaymentProcessor {
public PaymentResult processPayment(PaymentRequest request) {
// Cala logika Stripe API tutaj
}
}
public class PayPalPaymentProcessor implements PaymentProcessor {
public PaymentResult processPayment(PaymentRequest request) {
// Cala logika PayPal API tutaj
}
}
extends), wielokrotna implementacja interfejsów (implements).Square extends Rectangle - łamie LSP)Dziedziczenie - mechanizm pozwalajacy klasie przejac pola i metody innej klasy.
// Klasa bazowa
public abstract class Vehicle {
protected String brand;
protected int year;
public void start() {
System.out.println("Vehicle starting...");
}
public abstract void move(); // musi byc zaimplementowana
}
// Klasy pochodne
public class Car extends Vehicle {
private int numberOfDoors;
@Override
public void move() {
System.out.println("Car driving on road");
}
}
public class Boat extends Vehicle {
@Override
public void move() {
System.out.println("Boat sailing on water");
}
}
Dog i Cat nadpisują makeSound().List<Animal> animals, pętla for (a : animals) a.makeSound() - każde zwierzę zrobi swój dźwięk.List<String>, List<Integer>).Polimorfizm - mozliwosc traktowania obiektow roznych klas przez wspolny interfejs.
// Polimorfizm - rozne zachowanie, jeden interfejs
public interface NotificationSender {
void send(String recipient, String message);
}
public class EmailSender implements NotificationSender {
public void send(String recipient, String message) {
System.out.println("Sending EMAIL to " + recipient);
}
}
public class SmsSender implements NotificationSender {
public void send(String recipient, String message) {
System.out.println("Sending SMS to " + recipient);
}
}
public class PushSender implements NotificationSender {
public void send(String recipient, String message) {
System.out.println("Sending PUSH to " + recipient);
}
}
// Polimorficzne uzycie - kod nie zalezy od konkretnej implementacji
public class NotificationService {
private final List<NotificationSender> senders;
public void notifyAll(String recipient, String message) {
for (NotificationSender sender : senders) {
sender.send(recipient, message); // rozne zachowanie!
}
}
}
// Typy polimorfizmu:
// 1. Compile-time (przeciazanie metod - overloading)
public void print(String s) { }
public void print(int i) { }
public void print(String s, int count) { }
// 2. Runtime (nadpisywanie metod - overriding)
NotificationSender sender = new EmailSender(); // lub SmsSender
sender.send("user@email.com", "Hello"); // wywolanie zalezne od typu obiektu
Dog extends Animal. Silne sprzężenie, struktura statyczna.Car ma Engine. Luźne sprzężenie, elastyczność.| Cecha | Dziedziczenie | Kompozycja |
|---|---|---|
| Relacja | "jest" (IS-A) | "ma" (HAS-A) |
| Powiazanie | Silne (compile-time) | Luźne (runtime) |
| Elastycznosc | Mniejsza | Wieksza |
| Testowanie | Trudniejsze | Latwiejsze (mozna mockowac) |
| Slowo kluczowe | extends |
Pole w klasie |
// DZIEDZICZENIE - Car IS A Vehicle
public class Car extends Vehicle {
// dziedziczy wszystko z Vehicle
}
// KOMPOZYCJA - Car HAS AN Engine (preferowane!)
public class Car {
private final Engine engine; // HAS-A
private final Transmission transmission;
private final List<Wheel> wheels;
public Car(Engine engine, Transmission transmission) {
this.engine = engine;
this.transmission = transmission;
}
public void start() {
engine.start(); // delegacja do komponentu
}
}
// Zalety kompozycji:
// - Mozna latwo podmienic Engine (ElectricEngine, DieselEngine)
// - Latwiejsze testowanie (mock Engine)
// - Brak problemow z hierarchia klas
User (szablon)new User("Anna") (konkretny user)new User("Jan") (inny user tej samej klasy)new).| Pojecie | Opis | Analogia |
|---|---|---|
| Klasa | Szablon/blueprint definiujacy strukture i zachowanie | Przepis na ciasto |
| Obiekt | Konkretny egzemplarz klasy w pamieci | Upieczone ciasto |
| Instancja | Synonim obiektu - utworzony egzemplarz klasy | To samo upieczone ciasto |
// Klasa - szablon
public class User {
private String name;
private String email;
public User(String name, String email) {
this.name = name;
this.email = email;
}
}
// Obiekty/Instancje - konkretne egzemplarze
User user1 = new User("Jan", "jan@email.com"); // pierwsza instancja
User user2 = new User("Anna", "anna@email.com"); // druga instancja
// Kazdy obiekt ma wlasny stan (dane) ale wspoldzieli zachowanie (metody)
static): należą do klasy, nie do obiektu. Jedna kopia wspólna dla wszystkich instancji.User.count, Math.max(a, b) - przez nazwę klasyuser.getName() - przez referencję do obiektupublic static final)Math.abs)this, trudne do mockowania.| Cecha | Statyczne (static) | Instancyjne |
|---|---|---|
| Przynaleznosc | Do klasy | Do obiektu |
| Pamiec | Jedna kopia dla wszystkich | Osobna dla kazdego obiektu |
| Dostep | ClassName.method() |
object.method() |
| Dostep do this | NIE | TAK |
| Uzycie | Utility, stale, fabryki | Stan i zachowanie obiektu |
public class Counter {
// Pole statyczne - wspolne dla wszystkich instancji
private static int totalCount = 0;
// Pole instancyjne - osobne dla kazdego obiektu
private int instanceCount = 0;
public Counter() {
totalCount++; // wspolny licznik
instanceCount++; // licznik tej instancji
}
// Metoda statyczna - dostep bez tworzenia obiektu
public static int getTotalCount() {
return totalCount;
}
// Metoda instancyjna - wymaga obiektu
public int getInstanceCount() {
return instanceCount;
}
}
// Uzycie
Counter c1 = new Counter();
Counter c2 = new Counter();
Counter.getTotalCount(); // 2 (statyczna - bez obiektu)
c1.getInstanceCount(); // 1 (instancyjna - z obiektem)
tryLock z timeoutemjstack) pokazuje deadlocki.Deadlock (zakleszczenie) - sytuacja, w ktorej dwa lub wiecej watkow/procesow czeka na siebie nawzajem, blokujac sie wzajemnie na zawsze.
public class DeadlockExample {
private final Object lockA = new Object();
private final Object lockB = new Object();
public void method1() {
synchronized(lockA) { // 1. Zablokuj A
synchronized(lockB) { // 2. Czekaj na B
// praca
}
}
}
public void method2() {
synchronized(lockB) { // 1. Zablokuj B
synchronized(lockA) { // 2. Czekaj na A DEADLOCK!
// praca
}
}
}
}
// ZAWSZE blokuj w tej samej kolejnosci: A przed B
public void method1() {
synchronized(lockA) {
synchronized(lockB) { /* praca */ }
}
}
public void method2() {
synchronized(lockA) { // Zmieniona kolejnosc!
synchronized(lockB) { /* praca */ }
}
}
UPDATE users SET ... WHERE id=1 (lock na wierszu 1)UPDATE users SET ... WHERE id=2 (lock na wierszu 2)UPDATE users SET ... WHERE id=2 (czeka)UPDATE users SET ... WHERE id=1 (czeka - DEADLOCK)DeadlockLoserDataAccessExceptionBazy danych automatycznie wykrywaja deadlocki i przerywaja jedna z transakcji.
-- Jak zapobiegac deadlockom w SQL:
-- 1. Ustalona kolejnosc dostępu do tabel (np. alfabetyczna)
-- 2. Krotkie transakcje (mniej czasu na konflikt)
-- 3. Niski poziom izolacji jesli mozliwe
-- 4. SELECT ... FOR UPDATE NOWAIT (nie czekaj, rzuc blad)
SELECT * FROM users WHERE id = 1 FOR UPDATE NOWAIT;
-- Jesli zablokowane, natychmiast rzuci wyjatek zamiast czekac
1. Zapobieganie (Prevention) - projektuj system tak, aby unikac deadlockow:
| Technika | Opis | Przyklad |
|---|---|---|
| Kolejnosc zasobow | Zawsze zadaj zasobow w ustalonej kolejnosci | Najpierw Account, potem Order (alfabetycznie) |
| Timeout | Przerwij transakcje po przekroczeniu czasu oczekiwania | lock_timeout = 5s |
| Wywlaszczenie (Preemption) | System odbiera lock i cofa transakcje | Baza automatycznie wybiera "ofiare" |
| Krotkie transakcje | Im krotsza transakcja, tym mniejsze ryzyko | Unikaj dlugotrwalych operacji w transakcji |
// Java/JPA - ustalona kolejnosc zasobow
// ZAWSZE najpierw blokuj Account, potem Order (np. alfabetycznie)
@Transactional
public void transferMoney(Long accId, Long orderId) {
// 1. Najpierw Account
Account acc = em.find(Account.class, accId, LockModeType.PESSIMISTIC_WRITE);
// 2. Potem Order
Order ord = em.find(Order.class, orderId, LockModeType.PESSIMISTIC_WRITE);
// ... operacje
}
// Timeout na poziomie JPA
@QueryHints({
@QueryHint(name = "javax.persistence.lock.timeout", value = "5000") // 5 sekund
})
Account findByIdWithLock(Long id);
2. Wykrywanie i rozwiazywanie (Detection & Resolution) - pozwol na deadlocki, ale wykrywaj i rozwiazuj:
# 1. Połączenie z bazą danych przez terminal
psql -h localhost -p 5434 -U product_user -d product
# Parametry połączenia:
# -h localhost = host bazy danych
# -p 5434 = port (domyślny PostgreSQL to 5432)
# -U product_user = nazwa użytkownika
# -d product = nazwa bazy danych
# Po połączeniu możesz wykonywać zapytania SQL:
-- 2. Wszystkie aktywne locki w systemie
SELECT * FROM pg_locks;
-- 3. Sprawdzenie które procesy są zablokowane i przez kogo
SELECT
pid,
usename,
pg_blocking_pids(pid) AS blocked_by,
query
FROM pg_stat_activity
WHERE cardinality(pg_blocking_pids(pid)) > 0;
-- 4. Szczegółowe informacje o lockach z zapytaniami
SELECT
l.locktype,
l.relation::regclass,
l.mode,
l.granted,
a.usename,
a.query
FROM pg_locks l
JOIN pg_stat_activity a ON l.pid = a.pid
WHERE l.relation IS NOT NULL;
-- 5. Ustawienie timeout na poziomie sesji (zapobieganie długim oczekiwaniom)
SET lock_timeout = '5s';
SET statement_timeout = '30s';
-- 6. Zabicie zablokowanego procesu (ostrożnie!)
SELECT pg_terminate_backend(pid); -- gdzie pid = ID procesu do zabicia
3. Obsluga deadlocka w kodzie Java:
@Service
public class OrderService {
private static final int MAX_RETRIES = 3;
@Transactional
public void processOrder(Long orderId) {
int attempt = 0;
while (attempt < MAX_RETRIES) {
try {
doProcessOrder(orderId);
return; // Sukces - wyjdz z petli
} catch (PessimisticLockingFailureException | CannotAcquireLockException e) {
attempt++;
log.warn("Deadlock detected, retry {}/{}", attempt, MAX_RETRIES);
if (attempt >= MAX_RETRIES) {
throw new BusinessException("Failed after " + MAX_RETRIES + " retries");
}
// Losowe opoznienie przed ponowna proba (unikniecie livelock)
Thread.sleep((long) (Math.random() * 100));
}
}
}
}
counter++ to 3 operacje (read, modify, write) - dwa wątki mogą nadpisać swoje zmiany.synchronized, AtomicInteger, locki, immutable| Problem | Opis | Przyklad |
|---|---|---|
| Deadlock | Watki zablokowane na zawsze, NIC nie robia | A czeka na B, B czeka na A |
| Livelock | Watki ciagle reaguja na siebie, ale nie robia postepu | Dwoch ludzi w korytarzu - obaj ustepuja w te sama strone |
| Starvation | Watek nigdy nie dostaje zasobu (inni go "wyprzedzaja") | Watek niskiego priorytetu nigdy nie dostaje CPU |
| Race Condition | Wynik zalezy od kolejnosci wykonania watkow | Dwa watki inkrementuja counter bez synchronizacji |
// PROBLEM: Race condition
private int counter = 0;
public void increment() {
counter++; // NIE jest atomowe! (read → modify → write)
}
// ROZWIAZANIE 1: synchronized
public synchronized void increment() {
counter++;
}
// ROZWIAZANIE 2: AtomicInteger (lepsze)
private AtomicInteger counter = new AtomicInteger(0);
public void increment() {
counter.incrementAndGet(); // Atomowa operacja
}
// Dwoch "uprzejmych" watkow - obaj ustepuja i nikt nie przechodzi
while (otherThreadWantsResource) {
releaseResource(); // "Prosze, idz pierwszy"
Thread.sleep(100);
acquireResource(); // "Dobra, teraz ja... o, znowu chcesz?"
}
// Oba watki ciagle zwalniaja i pobieraja - zadnego postepu
// ROZWIAZANIE: Losowe opoznienie (jak w Ethernet CSMA/CD)
Thread.sleep(new Random().nextInt(100));
AtomicInteger, AtomicReference dla prostych operacjiConcurrentHashMap, CopyOnWriteArrayListCompletableFuture, Reactive (Project Reactor), Virtual Threads (Java 21).| Technika | Opis | Kiedy uzywac |
|---|---|---|
| synchronized | Blokada na poziomie metody/bloku | Proste przypadki, maly kod krytyczny |
| ReentrantLock | Jawna blokada z tryLock(), timeout | Potrzeba timeout lub fairness |
| Atomic* | AtomicInteger, AtomicReference (CAS) | Pojedyncze zmienne, wysokia wydajnosc |
| volatile | Zapewnia widocznosc zmian miedzy watkami | Flagi (np. isRunning), pojedyncze odczyty/zapisy |
| Immutability | Niemutowalne obiekty sa thread-safe | Najlepsza praktyka - unikaj stanu mutowalnego |
| ThreadLocal | Kazdy watek ma wlasna kopie | Kontekst per-thread (np. user session) |
| Concurrent Collections | ConcurrentHashMap, CopyOnWriteArrayList | Wspoldzielone kolekcje |
private final ReentrantLock lock = new ReentrantLock();
public void safeMethod() {
try {
// Probuj zdobyc blokade przez max 1 sekunde
if (lock.tryLock(1, TimeUnit.SECONDS)) {
try {
// sekcja krytyczna
} finally {
lock.unlock();
}
} else {
// Nie udalo sie zdobyc blokady - obsluz inaczej
log.warn("Could not acquire lock, skipping operation");
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
version (lub updated_at)WHERE id=? AND version=?@VersionOptimisticLockException.Optimistic i Pessimistic Locking to strategie zarzadzania wspolbieznym dostepem do danych w transakcjach.
| Cecha | Optimistic Locking | Pessimistic Locking |
|---|---|---|
| Zalozenie | Konflikty sa rzadkie | Konflikty sa czeste |
| Mechanizm | Wersjonowanie (@Version) | Blokada rekordu (SELECT FOR UPDATE) |
| Kiedy sprawdza | Przy COMMIT (czy wersja sie zgadza) | Przy odczycie (blokuje od razu) |
| Wydajnosc | Lepsza przy malej liczbie konfliktow | Lepsza przy duzej liczbie konfliktow |
| Deadlock | Nie wystepuje | Moze wystapic |
| Kiedy uzywac | Odczyty >> Zapisy, aplikacje webowe | Krytyczne operacje finansowe, rezerwacje |
Pozwala na wykonywanie transakcji bez ograniczen, sprawdzajac konflikty dopiero przed zatwierdzeniem. W przypadku wykrycia konfliktu transakcja jest wycofywana.
Java (JPA @Version):
@Entity
public class Counter {
@Id
private Long id;
private int value;
@Version // Automatycznie inkrementowane przy UPDATE
private Long version;
}
// Uzycie w serwisie
@Transactional
public void incrementCounter(Long id) {
Counter counter = counterRepository.findById(id).orElseThrow();
counter.setValue(counter.getValue() + 1);
counterRepository.save(counter); // Hibernate sprawdzi version przy COMMIT
}
// Obsluga konfliktu
try {
incrementCounter(1L);
} catch (OptimisticLockException e) {
// Konflikt! Ktos inny zmodyfikowal rekord - ponow probe lub poinformuj uzytkownika
log.warn("Optimistic lock conflict, retrying...");
}
SQL (co generuje Hibernate):
-- Odczyt rekordu (bez blokady)
SELECT id, value, version FROM counter WHERE id = 1;
-- Wynik: id=1, value=10, version=5
-- UPDATE z warunkiem na version
UPDATE counter
SET value = 11, version = 6
WHERE id = 1 AND version = 5;
-- Jesli affected_rows = 0 → ktos inny zmienil rekord → OptimisticLockException
-- Jesli affected_rows = 1 → sukces
Blokuje zasoby potrzebne transakcji od razu przy odczycie, uniemozliwiajac innym dostep do nich do czasu zwolnienia blokady. Zapobiega to konfliktom, ale moze zmniejszyc wspolbieznosc i prowadzic do deadlockow.
SQL (SELECT FOR UPDATE):
-- Blokada wiersza - inne transakcje czekaja
SELECT * FROM counter WHERE id = 1 FOR UPDATE;
-- Warianty:
SELECT ... FOR UPDATE NOWAIT; -- Nie czekaj, rzuc blad jesli zablokowane
SELECT ... FOR UPDATE SKIP LOCKED; -- Pomin zablokowane wiersze
-- Po SELECT FOR UPDATE mozna bezpiecznie UPDATE
UPDATE counter SET value = value + 1 WHERE id = 1;
COMMIT; -- Blokada zwolniona
Java (EntityManager):
// Sposob 1: EntityManager.find() z LockModeType
@Transactional
public void incrementCounterPessimistic(Long id) {
Counter counter = entityManager.find(
Counter.class,
id,
LockModeType.PESSIMISTIC_WRITE // SELECT ... FOR UPDATE
);
counter.setValue(counter.getValue() + 1);
// Blokada trzymana do COMMIT
}
// Sposob 2: Repository z @Lock
public interface CounterRepository extends JpaRepository<Counter, Long> {
@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("SELECT c FROM Counter c WHERE c.id = :id")
Counter findByIdWithLock(@Param("id") Long id);
}
// Sposob 3: Z timeout (unikniecie nieskonczonego czekania)
Map<String, Object> hints = new HashMap<>();
hints.put("javax.persistence.lock.timeout", 5000); // 5 sekund
Counter counter = entityManager.find(
Counter.class, id,
LockModeType.PESSIMISTIC_WRITE,
hints
);
| LockModeType | SQL | Opis |
|---|---|---|
| PESSIMISTIC_READ | FOR SHARE | Shared lock - inne moga czytac, nie moga pisac |
| PESSIMISTIC_WRITE | FOR UPDATE | Exclusive lock - nikt inny nie ma dostepu |
| PESSIMISTIC_FORCE_INCREMENT | FOR UPDATE + version++ | Exclusive lock + inkrementacja wersji |
| OPTIMISTIC | - | Sprawdza version przy COMMIT |
| OPTIMISTIC_FORCE_INCREMENT | - | Zawsze inkrementuje version przy COMMIT |
class Car { Engine engine; }. Różnica jest w semantyce i zarządzaniu cyklem życia.Agregacja i Kompozycja to relacje "has-a" w programowaniu obiektowym, różniące się siłą powiązania.
| Cecha | Agregacja | Kompozycja |
|---|---|---|
| Relacja | Luźna ("has-a") | Silna ("owns-a") |
| Cykl życia | Obiekt może istnieć niezależnie | Obiekt nie istnieje bez właściciela |
| Przykład | Uniwersytet → Studenci | Samochód → Silnik |
// AGREGACJA - Student istnieje niezależnie od Uniwersytetu
public class University {
private List<Student> students; // studenci mogą istnieć bez uniwersytetu
public void addStudent(Student student) {
students.add(student); // student stworzony na zewnątrz
}
}
// KOMPOZYCJA - Silnik nie istnieje bez Samochodu
public class Car {
private final Engine engine;
public Car() {
this.engine = new Engine(); // silnik tworzony wewnątrz, umiera z samochodem
}
}
Łączenie danych z wielu serwisów w jedną odpowiedź.
// API Gateway agreguje dane z wielu serwisów
@GetMapping("/dashboard/{userId}")
public DashboardResponse getDashboard(@PathVariable Long userId) {
User user = userService.getUser(userId);
List<Order> orders = orderService.getOrders(userId);
PaymentSummary payments = paymentService.getSummary(userId);
return new DashboardResponse(user, orders, payments); // agregacja
}
@Transactional gdy już jest transakcja?
try/catch).Context z Project Reactor.Propagacja oznacza przekazywanie informacji między warstwami lub komponentami systemu.
| Typ propagacji | Opis | Przykład |
|---|---|---|
| Propagacja transakcji | Jak transakcja zachowuje się przy wywołaniu innej metody | REQUIRED, REQUIRES_NEW, NESTED |
| Propagacja kontekstu | Przekazywanie security context, traceId między serwisami | JWT token, correlation ID w nagłówkach |
| Propagacja błędów | Jak wyjątki przechodzą między warstwami | Exception → HTTP 500 |
// Propagacja błędów - wyjątek domenowy → HTTP response
@Service
public class OrderService {
public void createOrder(Order order) {
if (order.getTotal().compareTo(BigDecimal.ZERO) <= 0) {
throw new InvalidOrderException("Total must be positive");
}
}
}
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(InvalidOrderException.class)
public ResponseEntity<ErrorResponse> handleInvalidOrder(InvalidOrderException e) {
return ResponseEntity.badRequest().body(new ErrorResponse(e.getMessage()));
}
}
Math.pow(2, 3), czyste funkcje bez stanu.Random.nextInt(), LocalDateTime.now(), UUID.randomUUID(), czytanie pliku/sieci.DETERMINISTIC może być indeksowana, optymalizator może cachować wyniki.System deterministyczny - dla tych samych danych wejściowych zawsze daje ten sam wynik. Brak losowości i efektów ubocznych.
| Deterministyczne | Niedeterministyczne |
|---|---|
| Funkcje czyste (pure functions) | Zależne od czasu (LocalDateTime.now()) |
| Haszowanie (ten sam input → ten sam hash) | Zależne od losowości (Random) |
| Sortowanie stabilne | Zależne od kolejności wątków |
// DETERMINISTYCZNA - zawsze ten sam wynik dla tych samych argumentów
public int add(int a, int b) {
return a + b;
}
// NIEDETERMINISTYCZNA - wynik zależy od czasu
public String generateId() {
return UUID.randomUUID().toString(); // losowy wynik
}
// Jak uczynić kod deterministycznym w testach:
public class OrderService {
private final Clock clock; // wstrzykujemy zegar
public Order createOrder() {
return new Order(LocalDateTime.now(clock)); // deterministyczne w testach
}
}
Deserializacja - proces zamiany danych z formatu tekstowego/binarnego (JSON, XML) na obiekty w pamięci.
| Kierunek | Nazwa | Przykład |
|---|---|---|
| Obiekt → JSON/XML | Serializacja | User → {"name": "Jan"} |
| JSON/XML → Obiekt | Deserializacja | {"name": "Jan"} → User |
// Jackson - deserializacja JSON → Object
ObjectMapper mapper = new ObjectMapper();
// Deserializacja
String json = "{\"name\": \"Jan\", \"age\": 30}";
User user = mapper.readValue(json, User.class);
// Serializacja
String jsonOutput = mapper.writeValueAsString(user);
// Spring automatycznie deserializuje body requestu
@PostMapping("/users")
public User createUser(@RequestBody User user) { // JSON → User
return userService.save(user);
}
Ryzyka bezpieczeństwa:
@PathVariable - pobiera część ścieżki URL: /users/{id} → /users/5. Obowiązkowy, identyfikuje zasób.@RequestParam - pobiera parametr query string: /users?name=Anna&age=30. Zwykle opcjonalne, filtrowanie/sortowanie.@QueryParam - to samo co @RequestParam, ale z JAX-RS (nie Spring). W Springu używaj @RequestParam.?page=2&size=20)@RequestBody (całe body jako obiekt), @RequestHeader (nagłówek), @CookieValue.Adnotacje służące do pobierania parametrów z żądania HTTP.
| Adnotacja | Źródło | Przykład URL | Framework |
|---|---|---|---|
| @PathVariable | Ścieżka URL | /users/123 | Spring |
| @RequestParam | Query string (?key=value) | /users?age=30 | Spring |
| @QueryParam | Query string | /users?age=30 | JAX-RS |
// Spring - @PathVariable i @RequestParam
@GetMapping("/users/{id}")
public User getUser(
@PathVariable Long id, // /users/123
@RequestParam(required = false) String fields // ?fields=name,email
) { ... }
// Paginacja i sortowanie
@GetMapping("/products")
public List<Product> getProducts(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size,
@RequestParam(defaultValue = "name") String sortBy
) { ... }
// JAX-RS - @QueryParam
@GET
@Path("/users")
public List<User> getUsers(@QueryParam("age") int age) { ... }
4532-1234-5678-9012 (numer karty)tok_abc123xyz (bezsensowny ciąg)Tokenowanie - zamiana danych wrażliwych na token (losowy identyfikator). Token sam w sobie nie zawiera danych.
| Cecha | Tokenowanie | Szyfrowanie | Haszowanie |
|---|---|---|---|
| Dane | Przechowywane w bezpiecznym storage | Zamienione matematycznie | Zamienione jednokierunkowo na skrót (hash) |
| Odwracalność | Tylko przez lookup w bazie tokenów | Przez klucz deszyfrujący | Nieodwracalne (one-way function) |
| Token | Losowy, bez związku z danymi | Matematycznie powiązany z danymi | Deterministyczny - te same dane = ten sam hash |
// Tokenowanie numeru karty płatniczej
@Service
public class TokenizationService {
private final TokenRepository tokenRepository;
public String tokenize(String cardNumber) {
String token = UUID.randomUUID().toString();
tokenRepository.save(new TokenMapping(token, encrypt(cardNumber)));
return token; // "tok_abc123" zamiast "4111111111111111"
}
public String detokenize(String token) {
return tokenRepository.findByToken(token)
.map(m -> decrypt(m.getEncryptedData()))
.orElseThrow();
}
}
Zastosowania: płatności (PCI DSS), dane osobowe (RODO), sesje użytkowników
DNS - system tłumaczący nazwy domenowe na adresy IP.
| Typ rekordu | Opis | Przykład |
|---|---|---|
| A | IPv4 address | example.com → 93.184.216.34 |
| AAAA | IPv6 address | example.com → 2606:2800:220:1:... |
| CNAME | Alias do innej domeny | www.example.com → example.com |
| MX | Mail server | example.com → mail.example.com |
Znaczenie w architekturze:
/api/v1/users (najpopularniejsze, proste, łatwe do cachowania)/users?version=1 (elastyczne, ale zaśmieca URL)API-Version: 1 (czyste URL-e, trudniejsze do testowania)Accept: application/vnd.api.v1+json (najbardziej RESTful, ale skomplikowane)/api/v1/... - jasny podział, łatwa separacja w kodzie, łatwe routing w Gateway.Deprecation, dokumentacja.Prefix - wspólny początek ścieżki URL dla grupy endpointów.
// Prefix na poziomie kontrolera
@RestController
@RequestMapping("/api/v1/users") // prefix: /api/v1/users
public class UserController {
@GetMapping // GET /api/v1/users
public List<User> getAll() { ... }
@GetMapping("/{id}") // GET /api/v1/users/123
public User getById(@PathVariable Long id) { ... }
}
// Globalny prefix w application.yml
server:
servlet:
context-path: /api
Strategie wersjonowania API:
| Strategia | Przykład | Zalety/Wady |
|---|---|---|
| URL path | /api/v1/users | Proste, czytelne |
| Query param | /api/users?version=1 | Elastyczne, ale mniej RESTful |
| Header | X-API-Version: 1 | Czyste URL, trudniejsze testowanie |
Nexus - repozytorium artefaktów używane w CI/CD.
<!-- Maven - publikacja do Nexusa -->
<distributionManagement>
<repository>
<id>nexus-releases</id>
<url>http://nexus.company.com/repository/maven-releases/</url>
</repository>
</distributionManagement>
Gitea - self-hosted Git server, lżejsza alternatywa dla GitHub/GitLab.
Jenkinsfile) ciąg etapów do wykonania przy każdym commit / PR.mvn package)Jenkins Pipeline - narzędzie CI/CD z pipeline'em jako kodem (Jenkinsfile).
// Jenkinsfile - Declarative Pipeline
pipeline {
agent any
stages {
stage('Build') {
steps {
sh 'mvn clean compile'
}
}
stage('Test') {
steps {
sh 'mvn test'
}
post {
always {
junit '**/target/surefire-reports/*.xml'
}
}
}
stage('Security Scan') {
steps {
sh 'mvn dependency-check:check'
}
}
stage('Deploy') {
when {
branch 'main'
}
steps {
sh 'kubectl apply -f k8s/'
}
}
}
post {
failure {
mail to: 'team@company.com', subject: 'Build Failed'
}
}
}
GitOps - podejście, w którym Git jest jedynym źródłem prawdy dla infrastruktury i aplikacji.
Argo CD - narzędzie do continuous deployment na Kubernetes, implementujące GitOps.
# ArgoCD Application manifest
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: my-app
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/company/my-app.git
targetRevision: HEAD
path: k8s/
destination:
server: https://kubernetes.default.svc
namespace: production
syncPolicy:
automated:
prune: true # usuń zasoby nieobecne w Git
selfHeal: true # napraw drift automatycznie
standalone/deployments - serwer automatycznie podnosi aplikację.WildFly (dawniej JBoss AS) - serwer aplikacyjny Java EE / Jakarta EE.
| Cecha | WildFly | Spring Boot |
|---|---|---|
| Typ | Serwer aplikacyjny (full) | Embedded server (Tomcat) |
| Specyfikacja | Jakarta EE (EJB, CDI, JPA, JTA) | Spring Framework |
| Deployment | WAR/EAR na serwer | Executable JAR |
| Wersja enterprise | JBoss EAP (Red Hat) | Spring Enterprise |
Funkcje serwera aplikacyjnego:
Dwa popularne systemy do komunikacji asynchronicznej między serwisami.
| Cecha | RabbitMQ | Kafka |
|---|---|---|
| Model | Message broker (kolejki) | Event streaming platform (log) |
| Przechowywanie | Usuwa po konsumpcji | Przechowuje przez określony czas |
| Konsumenci | Competing consumers | Consumer groups + replay |
| Routing | Exchange + routing keys | Partycje + klucze |
| Skalowalność | Dobra | Bardzo wysoka (miliony msg/s) |
| Kiedy używać | Task queues, RPC, krótkie wiadomości | Event sourcing, duże strumienie danych, analityka |
// Spring - RabbitMQ Producer
@Service
public class OrderProducer {
private final RabbitTemplate rabbitTemplate;
public void sendOrder(Order order) {
rabbitTemplate.convertAndSend("orders-exchange", "order.created", order);
}
}
// RabbitMQ Consumer
@RabbitListener(queues = "orders-queue")
public void handleOrder(Order order) {
orderService.process(order);
}
// Spring - Kafka Producer
@Service
public class EventProducer {
private final KafkaTemplate<String, OrderEvent> kafkaTemplate;
public void publishEvent(OrderEvent event) {
kafkaTemplate.send("order-events", event.getOrderId(), event);
}
}
// Kafka Consumer
@KafkaListener(topics = "order-events", groupId = "inventory-service")
public void handleEvent(OrderEvent event) {
inventoryService.updateStock(event);
}
142.250.185.46 zamiast google.com.nslookup, dig, host.DNS to system tłumaczący nazwy domenowe (np. google.com) na adresy IP. Działa jak "książka telefoniczna internetu".
| Typ rekordu | Opis | Przykład |
|---|---|---|
| A | Mapuje domenę na adres IPv4 | example.com → 93.184.216.34 |
| AAAA | Mapuje domenę na adres IPv6 | example.com → 2606:2800:220:1:... |
| CNAME | Alias - wskazuje na inną domenę | www.example.com → example.com |
| MX | Serwer poczty dla domeny | example.com → mail.example.com |
| TXT | Dowolny tekst (weryfikacja, SPF) | Weryfikacja własności domeny |
| NS | Serwery nazw dla domeny | ns1.cloudflare.com |
api.example.com → inny serwerProxy to serwer pośredniczący między klientem a serwerem docelowym.
| Cecha | Forward Proxy | Reverse Proxy |
|---|---|---|
| Pozycja | Przed klientem | Przed serwerem |
| Kto wie o proxy | Klient (musi skonfigurować) | Serwer ukryty, klient nie wie |
| Zastosowanie | Anonimowość, filtrowanie, cache | Load balancing, SSL, ochrona |
| Przykłady | VPN, Squid, proxy firmowe | Nginx, HAProxy, Cloudflare |
nginx.conf - bloki server, location, upstream.Nginx (wymowa: "engine-x") to wysokowydajny serwer HTTP i reverse proxy. Znany z obsługi ogromnej liczby jednoczesnych połączeń przy minimalnym zużyciu pamięci.
| Funkcja | Opis |
|---|---|
| Web Server | Serwowanie plików statycznych (HTML, CSS, JS, obrazy) |
| Reverse Proxy | Przekazywanie requestów do serwerów backendowych |
| Load Balancer | Rozkładanie ruchu między wieloma instancjami (round-robin, least_conn, ip_hash) |
| SSL Termination | Obsługa HTTPS - backend może używać HTTP |
| Cache | Cachowanie odpowiedzi, zmniejszenie obciążenia backendu |
| Rate Limiting | Ograniczanie liczby requestów (ochrona przed DDoS) |
| Kompresja | Gzip/Brotli - zmniejszenie transferu danych |
# Przykład konfiguracji Nginx jako reverse proxy
upstream backend {
server localhost:8080;
server localhost:8081;
server localhost:8082;
}
server {
listen 80;
server_name api.example.com;
# Redirect HTTP → HTTPS
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name api.example.com;
ssl_certificate /etc/ssl/cert.pem;
ssl_certificate_key /etc/ssl/key.pem;
# Kompresja
gzip on;
gzip_types application/json text/plain;
# Rate limiting
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
location /api/ {
limit_req zone=api burst=20;
proxy_pass http://backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# Pliki statyczne
location / {
root /var/www/html;
try_files $uri $uri/ /index.html;
}
}
localhost:8080) na publiczny URL dostępny z internetu.Ngrok (wymowa: "en-grok") to narzędzie do tworzenia tuneli HTTP/TCP, które wystawia lokalny serwer na świat przez publiczny URL. Idealne do developmentu i testowania webhooków.
| Zastosowanie | Opis |
|---|---|
| Testowanie webhooków | Stripe, GitHub, Slack mogą wysyłać eventy na localhost |
| Prezentacje | Pokazanie lokalnej aplikacji klientowi bez deploymentu |
| Mobile testing | Testowanie API z telefonu bez konfiguracji sieci |
| Debugging | Inspekcja ruchu HTTP w dashboardzie ngrok |
# Uruchomienie ngrok - eksponuje port 8080
$ ngrok http 8080
# Wynik:
Forwarding https://a1b2c3d4.ngrok.io → http://localhost:8080
# Teraz https://a1b2c3d4.ngrok.io jest dostępne z internetu!
# Przydatne opcje:
$ ngrok http 8080 --domain=my-app.ngrok.io # stała subdomena (płatne)
$ ngrok http 8080 --basic-auth="user:pass" # podstawowa autoryzacja
$ ngrok tcp 3306 # tunel TCP (np. baza danych)
Cloudflare to globalna sieć CDN i usług bezpieczeństwa. Działa jako reverse proxy między użytkownikami a Twoim serwerem.
| Usługa | Opis |
|---|---|
| CDN (Content Delivery Network) | Cache statycznych zasobów na 300+ serwerach na świecie - szybsze ładowanie |
| DDoS Protection | Ochrona przed atakami - Cloudflare absorbuje złośliwy ruch |
| WAF (Web Application Firewall) | Blokowanie SQL Injection, XSS, i innych ataków |
| SSL/TLS | Darmowy certyfikat HTTPS, automatyczne odnowienie |
| DNS | Szybki i niezawodny DNS z ochroną przed atakami |
| Workers | Serverless functions na edge (blisko użytkownika) |
| Rate Limiting | Ograniczanie requestów z danego IP |
| Bot Protection | CAPTCHA, challenge dla podejrzanego ruchu |
// Cloudflare Workers - kod na edge
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request));
});
async function handleRequest(request) {
// Dodaj custom header
const response = await fetch(request);
const newResponse = new Response(response.body, response);
newResponse.headers.set('X-Custom-Header', 'Hello from Edge!');
return newResponse;
}
Jak te technologie współpracują w realnym systemie:
Model OSI to teoretyczny model referencyjny opisujący komunikację sieciową w postaci 7 warstw. Każda warstwa świadczy usługi warstwie wyższej i korzysta z usług warstwy niższej. Został opracowany przez ISO w 1984 r. jako standard opisu sieci komputerowych.
Po co w ogóle powstał model OSI?
Żeby producenci sprzętu i twórcy oprogramowania mogli pracować niezależnie. Bez wspólnego modelu Ethernet od jednej firmy nie rozmawiałby z kablem od innej, a przeglądarka nie wiedziałaby jak dogadać się z serwerem. OSI dzieli komunikację na warstwy, żeby każdy robił swoje.
🔑 Główne założenia modelu OSI
🧩 Kluczowe cechy, które odróżniają warstwy
💡 Najważniejsze rzeczy do zapamiętania
| # | Warstwa | Funkcja | Jednostka danych (PDU) | Protokoły / przykłady |
|---|---|---|---|---|
| 7 | Aplikacji (Application) | Interfejs dla aplikacji użytkownika, udostępnianie usług sieciowych | Dane (Data) | HTTP, HTTPS, FTP, SMTP, DNS, SSH, IMAP, POP3 |
| 6 | Prezentacji (Presentation) | Kodowanie, szyfrowanie, kompresja i konwersja formatów danych | Dane (Data) | TLS/SSL, JPEG, PNG, ASCII, UTF-8, MIME |
| 5 | Sesji (Session) | Nawiązywanie, utrzymywanie i kończenie sesji między aplikacjami | Dane (Data) | NetBIOS, RPC, PPTP, SOCKS |
| 4 | Transportowa (Transport) | Niezawodny transport end-to-end, kontrola przepływu, segmentacja, kontrola błędów | Segment (TCP) / Datagram (UDP) | TCP, UDP, QUIC |
| 3 | Sieciowa (Network) | Routing pakietów między sieciami, logiczne adresowanie (IP) | Pakiet (Packet) | IP (IPv4/IPv6), ICMP, routery, protokoły routingu (OSPF, BGP) |
| 2 | Łącza danych (Data Link) | Przesył ramek w jednej sieci lokalnej, adresy fizyczne MAC, detekcja błędów | Ramka (Frame) | Ethernet, Wi-Fi (802.11), ARP, switche, MAC |
| 1 | Fizyczna (Physical) | Przesył surowych bitów przez medium transmisyjne (elektryczne, optyczne, radiowe) | Bit | Kable miedziane, światłowody, radio, huby, RJ-45, napięcia, sygnały |
Nadawca: dane z warstwy wyższej są pakowane (enkapsulacja) i uzupełniane o nagłówek każdej kolejnej warstwy w dół. Odbiorca wykonuje proces odwrotny (dekapsulacja) - każda warstwa zdejmuje swój nagłówek i przekazuje dane wyżej.
Nadawca Odbiorca
L7 Application [Data] [Data] L7
L6 Presentation [Data] [Data] L6
L5 Session [Data] [Data] L5
L4 Transport [TCP|Data] [TCP|Data] L4
L3 Network [IP|TCP|Data] [IP|TCP|Data] L3
L2 Data Link [Eth|IP|TCP|Data|FCS] [Eth|...] L2
L1 Physical 010110100101... → 010110100101... L1
| Model OSI (7 warstw) | Model TCP/IP (4 warstwy) |
|---|---|
| Aplikacji + Prezentacji + Sesji | Aplikacji (HTTP, DNS, SMTP) |
| Transportowa | Transportowa (TCP, UDP) |
| Sieciowa | Internetowa (IP, ICMP) |
| Łącza danych + Fizyczna | Dostępu do sieci (Ethernet, Wi-Fi) |
Cel mapy: pokazać gdzie w systemie (na której warstwie) dany atak się odbywa, jak działa i czym się bronić. Tak myślą architekci sieci i specjaliści bezpieczeństwa - operacyjnie, nie tylko teoretycznie.
Po co mapować ataki na warstwy OSI?
Bo gdzie atak się odbywa, tam trzeba go blokować. Nie zatrzymasz SQL Injection firewallem sieciowym (bo SQLi jest na L7), tak jak nie zatrzymasz SYN Flood walidacją formularza (bo to atak na L4). Mapa mówi ci: "problem jest tu, więc obrona musi być tu".
🔑 Główne założenia myślenia o bezpieczeństwie
🧩 Jak rozpoznać, na której warstwie jest atak
💡 Najważniejsze rzeczy do zapamiętania
Rzadziej omawiana, ale nadal realna - wymaga dostępu fizycznego.
| Atak | Mechanizm | Jak działa | Obrona |
|---|---|---|---|
| Physical Tampering | Fizyczny dostęp do sprzętu | Atakujący podłącza się do kabla, wyjmuje dysk, instaluje sniffer sprzętowy (np. Komputer → Kabel → Sniffer) | Kontrola dostępu do pomieszczeń, monitoring, szyfrowanie dysków, zamykane szafy rack |
Ataki w sieciach lokalnych (LAN) - dotyczą adresów MAC i switchy.
| Atak | Mechanizm | Jak działa | Obrona |
|---|---|---|---|
| ARP Spoofing | Fałszywe mapowanie IP → MAC | Atakujący wysyła rozgłoszenie: "Router ma MAC: AA:BB:CC" zamiast prawdziwego "DD:EE:FF" → ruch trafia do atakującego (MITM w LAN) | ARP inspection (DAI), statyczne wpisy ARP, segmentacja VLAN, HTTPS (szyfrowanie nawet w razie MITM) |
| MAC Flooding | Przepełnienie tablicy MAC w switchu | Atak wysyła tysiące fałszywych adresów MAC → switch zaczyna działać jak hub (broadcast wszystkiego) → podsłuch ruchu | Port security, ograniczenie liczby MAC na port |
Tu dzieje się routing i adresowanie IP.
| Atak | Mechanizm | Jak działa | Obrona |
|---|---|---|---|
| IP Spoofing | Podszywanie się pod inny IP | Pakiet wygląda jak From: 192.168.1.1, ale naprawdę pochodzi z
From: attacker
|
Ingress filtering (BCP38), firewall, analiza ruchu, uwierzytelnianie na wyższych warstwach |
| ICMP Flood | Zalewanie pakietami ICMP | Ogromna liczba pingów (ping ping ping...) → serwer nie nadąża z odpowiadaniem | Rate limiting ICMP, firewall (blokada/limit ICMP), scrubbing DDoS |
TCP i UDP - porty, sesje, handshake.
| Atak | Mechanizm | Jak działa | Obrona |
|---|---|---|---|
| SYN Flood | Niepełne połączenia TCP | Normalny handshake: SYN → SYN-ACK → ACK. Atak: SYN → SYN-ACK → brak ACK → serwer trzyma otwarte sesje → wyczerpanie zasobów | SYN cookies, firewall, load balancer, limity połączeń półotwartych |
| Port Scanning | Sprawdzanie otwartych portów | Atakujący testuje porty (22, 80, 443, 3306...) szukając otwartych usług i ich wersji | Firewall (domyślnie DROP), zamknięte porty, IDS/IPS (np. Snort, Suricata), port knocking |
Tu dzieje się większość nowoczesnych ataków.
| Atak | Warstwa | Mechanizm | Jak działa | Obrona |
|---|---|---|---|---|
| MITM (Man in the Middle) | 2–7 | Przejęcie komunikacji | Ty → Atakujący → Serwer (atakujący czyta/modyfikuje ruch) | HTTPS, certyfikaty TLS, HSTS, certificate pinning, VPN firmowy |
| Phishing | 7 | Manipulacja użytkownikiem | Fałszywy mail: "Kliknij tutaj → login" → strona kradnie hasło | MFA, szkolenia użytkowników, filtry antyspamowe, SPF/DKIM/DMARC |
| SQL Injection | 7 | Manipulacja zapytaniami SQL | Input ' OR 1=1 -- zmienia logikę zapytania SQL |
Prepared statements (parametryzacja), walidacja danych, ORM, least privilege na DB, WAF |
| XSS (Cross-Site Scripting) | 7 | Wstrzyknięcie JavaScript | Kod <script>stealCookies()</script> wykonuje się
w przeglądarce ofiary
|
Escape'owanie outputu, CSP (Content Security Policy), HttpOnly cookies, walidacja inputu |
| CSRF | 7 | Wykonanie akcji w imieniu użytkownika | Strona atakującego wysyła transferMoney() z ciasteczkami
zalogowanego użytkownika
|
CSRF tokens, SameSite cookies, sprawdzanie Referer/Origin, ponowne uwierzytelnienie |
| Malware / Trojan | Wszystkie | Instalacja złośliwego oprogramowania | Program VPN.exe działa, ale instaluje backdoor |
Antywirus/EDR, whitelist aplikacji, aktualizacje, sandboxing |
| Ransomware | Wszystkie | Szyfrowanie plików | Program szyfruje dane → żąda okupu za klucz | Backup offline (3-2-1), segmentacja sieci, EDR, ograniczenie uprawnień, testowanie odtworzeń |
| Supply Chain Attack | 7 | Zainfekowana aktualizacja / zależność | Legalny program/biblioteka zawiera ukryty malware (np. SolarWinds, event-stream npm) | Weryfikacja podpisów, kontrola zależności (SCA), SBOM, pinowanie wersji, audyt aktualizacji |
| DDoS | 3–7 | Przeciążenie systemu | Typy: UDP flood (L4), HTTP flood (L7), DNS amplification (L7 amplified), Slowloris (L7) | Rate limiting, CDN (Cloudflare, Akamai), load balancing, WAF, auto-scaling, scrubbing center |
1. Najwięcej ataków dzieje się na warstwie 7 (aplikacji).
Nie w kablach, nie w routerach - w aplikacjach. Stąd tak ważne są walidacja
inputu, testy bezpieczeństwa i code review.
2. Najlepszą obroną jest warstwowość (Defense in Depth).
Nie pojedyncze zabezpieczenie, tylko kombinacja:
Firewall + TLS + MFA + Monitoring + WAF + Least privilege
Każda warstwa łapie to, co przepuściła poprzednia.
3. Najsłabszym elementem jest użytkownik.
Najczęstsze wektory to phishing, malware i błędna konfiguracja - nie sprzęt ani
protokoły. Stąd MFA, szkolenia i zasada najmniejszych uprawnień.
Wygenerowano przez Claude AI