1. 1. Foundations
1.1. SOLID Principles
1.1.1. Single Responsibility Principle (SRP)
1.1.1.1. - A class should have only one reason to change. - Focus on one responsibility per class.
1.1.1.1.1. Bad Example: class Employee { void calculateSalary() { ... } void saveToDatabase() { ... } // violates SRP } Here, Employee has two reasons to change: business logic & database logic. Good Example: class Employee { private String name; private double salary; // Only holds data and business logic void calculateSalary() { ... } } class EmployeeRepository { void save(Employee employee) { ... } // handles persistence } ✅ Each class has one responsibility.
1.1.2. O – Open/Closed Principle (OCP)
1.1.2.1. Classes should be open for extension, but closed for modification.
1.1.2.1.1. Example: Payment System interface Payment { void pay(double amount); } class CreditCardPayment implements Payment { public void pay(double amount) { ... } } class PayPalPayment implements Payment { public void pay(double amount) { ... } } // Adding new payment type → extend without changing existing code class BitcoinPayment implements Payment { public void pay(double amount) { ... } } Existing code doesn’t change; we just add new classes.
1.1.3. L – Liskov Substitution Principle (LSP)
1.1.3.1. Subtypes must be substitutable for their base types without breaking the program.
1.1.3.1.1. Violation Example: class Bird { void fly() { ... } } class Penguin extends Bird { void fly() { throw new UnsupportedOperationException(); } } Penguin cannot fly, so substituting it for Bird breaks expectations → violates LSP. Solution: Use interfaces for abilities: interface Flyable { void fly(); } class Sparrow extends Bird implements Flyable { ... } class Penguin extends Bird { ... } // no Flyable
1.1.4. I – Interface Segregation Principle (ISP)
1.1.4.1. - Clients should not be forced to implement methods they don’t use. - Prefer small, specific interfaces over large “fat” interfaces.
1.1.4.1.1. Bad Example: interface Worker { void work(); void eat(); // not all workers need this } Good Example: interface Workable { void work(); } interface Eatable { void eat(); } class Robot implements Workable { ... } // doesn’t implement eat()
1.1.5. D – Dependency Inversion Principle (DIP)
1.1.5.1. - High-level modules should not depend on low-level modules. Both should depend on abstractions. - Abstractions should not depend on details; details depend on abstractions.
1.1.5.1.1. Example: interface MessageService { void sendMessage(String message); } class EmailService implements MessageService { public void sendMessage(String message) { ... } } class Notification { private final MessageService service; public Notification(MessageService service) { this.service = service; // depends on abstraction } void notify(String msg) { service.sendMessage(msg); } } Notification can work with any service (email, SMS, push) without changing its code.
1.2. Exceptions
1.2.1. An exception is a problem that occurs during program execution, e.g., division by zero, file not found, null pointer. Exceptions are objects that describe the error.
1.2.1.1. Types of Exceptions
1.2.1.1.1. Checked
1.2.1.1.2. Unchecked
1.2.1.1.3. Error
1.3. Collections Framework
1.3.1. Map
1.3.1.1. I want to look up values by keys fast
1.3.1.2. LinkedHashMap
1.3.1.2.1. LinkedHashMap → insertion order
1.3.1.3. TreeMap
1.3.1.3.1. TreeMap → sorted keys
1.3.1.4. CurrenHashMap
1.3.1.4.1. ConcurrentHashMap → thread-safe
1.3.1.5. HashMap
1.3.1.5.1. HashMap → speed, unordered
1.3.1.6. put(key, value), get(key), remove(key), containsKey(), containsValue(), keySet(), values()
1.3.2. List
1.3.2.1. I care about order, duplicates are fine
1.3.2.2. LinkedList
1.3.2.2.1. Backed by a doubly linked list.
1.3.2.2.2. ✅ Fast insertions/deletions in middle (O(1) if you have reference).
1.3.2.2.3. ❌ Slow random access (get(index) is O(n)).
1.3.2.3. Vector / Stack
1.3.2.4. ArrayList
1.3.2.4.1. Backed by a dynamic array.
1.3.2.4.2. ✅ Fast random access (get(index) is O(1)).
1.3.2.4.3. ❌ Inserting/removing in the middle is slow (O(n)).
1.3.2.5. add(), add(index, element), get(index), set(index, element), remove(), size(), contains()
1.3.3. Set
1.3.3.1. I want unique items, order optional or sorted
1.3.3.2. LinkedHashList
1.3.3.2.1. Hash table + linked list. ✅ Maintains insertion order. Slightly slower than HashSet.
1.3.3.3. TreeSet
1.3.3.3.1. Backed by a Red-Black Tree.
1.3.3.3.2. ✅ Stores elements in sorted order.
1.3.3.3.3. ❌ Operations are O(log n) (slower than hash).
1.3.3.4. HashSet
1.3.3.4.1. Backed by a hash table.
1.3.3.4.2. ✅ Super fast (O(1) for add, remove, contains).
1.3.3.4.3. ❌ No ordering.
1.3.3.5. add(), remove(), contains(), size()
1.3.4. Queue/Deque
1.3.4.1. LinkedList
1.3.4.2. PriorityQueue
1.3.4.3. offer(), poll(), peek(), remove(), element()
1.4. Streams
1.4.1. intermediate operations
1.4.1.1. transform the stream but don’t produce a result immediately (e.g. filter, map, sorted). They’re lazy — they only run when a terminal operation is called.
1.4.1.2. filter(Predicate<T>)
1.4.1.2.1. Keep only elements that match a condition.
1.4.1.2.2. List<Integer> evens = numbers.stream() .filter(n -> n % 2 == 0) .toList();
1.4.1.3. map(Function<T,R>)
1.4.1.3.1. Transform each element into another form.
1.4.1.3.2. List<String> upper = words.stream() .map(String::toUpperCase) .toList();
1.4.1.4. flatMap(Function<T,Stream<R>>)
1.4.1.4.1. Flatten nested structures into a single stream.
1.4.1.4.2. List<List<Integer>> listOfLists = List.of(List.of(1,2), List.of(3,4)); List<Integer> flat = listOfLists.stream() .flatMap(List::stream) .toList(); // [1,2,3,4]
1.4.1.5. sorted(Comparator<T>)
1.4.1.5.1. Sort the stream (natural order or custom comparator).
1.4.1.5.2. List<String> sorted = words.stream() .sorted() .toList();
1.4.1.6. distinct()
1.4.1.6.1. Remove duplicates.
1.4.1.6.2. List<Integer> unique = numbers.stream() .distinct() .toList();
1.4.1.7. limit(n) / skip(n)
1.4.1.7.1. Truncate or skip elements.
1.4.1.7.2. List<Integer> first3 = numbers.stream() .limit(3) .toList();
1.4.2. terminal operations
1.4.2.1. They end the stream and produce a result (e.g.collect, forEach, reduce, count).
1.4.2.2. forEach(Consumer<T>)
1.4.2.2.1. Do something with each element (like printing).
1.4.2.2.2. words.stream().forEach(System.out::println);
1.4.2.3. collect(Collector)
1.4.2.3.1. Gather results into a collection, map, etc.
1.4.2.3.2. List<String> list = words.stream() .collect(Collectors.toList());
1.4.2.4. toList() (Java 16+)
1.4.2.4.1. A shortcut collector.
1.4.2.4.2. List<String> list = words.stream().toList();
1.4.2.5. reduce(...)
1.4.2.5.1. Combine all elements into one value.
1.4.2.5.2. int sum = numbers.stream() .reduce(0, (a, b) -> a + b);
1.4.2.6. count()
1.4.2.6.1. Count elements.
1.4.2.6.2. long count = words.stream() .filter(w -> w.length() > 4) .count();
1.4.2.7. min(Comparator) / max(Comparator)
1.4.2.7.1. Find smallest/largest element.
1.4.2.7.2. Optional<String> longest = words.stream() .max(Comparator.comparingInt(String::length));
1.4.2.8. anyMatch / allMatch / noneMatch
1.4.2.8.1. Check conditions.
1.4.2.8.2. boolean hasLong = words.stream().anyMatch(w -> w.length() > 6);
1.5. Maven (dependency management, build lifecycle)
1.5.1. Maven
1.5.1.1. 1. What is Maven?
1.5.1.1.1. Maven is a build automation and dependency management tool for Java projects. Think of it as a manager that: Downloads and manages libraries your project needs (dependencies) automatically. Defines how your project is built (compile, test, package, deploy) in a standardized way. Helps organize your project in a structured way.
1.5.1.2. 2. Core Concepts
1.5.1.2.1. a) POM (Project Object Model)
1.5.1.2.2. b) Dependencies
1.5.1.2.3. c) Repositories
1.5.1.2.4. d) Build Lifecycle
1.5.1.2.5. e) Plugins
1.5.1.2.6. f) Project Structure (Standard Maven)
2. 2. Spring Core
2.1. Beans Life Sycle
2.1.1. Constructor called
2.1.2. BeanNameAware
2.1.3. BeanFactoryAware
2.1.4. ApplicationContextAware
2.1.5. @PostConstruct
2.1.6. afterPropertiesSet()
2.1.7. Bean ready to use (when you get it from the context)
2.1.8. @PreDestroy
2.1.9. destroy()
2.2. AOP (Aspect-Oriented Programming)
2.2.1. Core Concepts of AOP
2.2.1.1. a) Aspect A class that contains cross-cutting logic.
2.2.1.1.1. Example: LoggingAspect that logs method calls.
2.2.1.2. b) Join Point A point in the execution of your program where you can insert behavior.
2.2.1.2.1. In Spring AOP, a join point is always a method execution.
2.2.1.3. c) Advice The action taken at a join point. Types of advice:
2.2.1.3.1. Before: runs before method execution
2.2.1.3.2. After: runs after method execution (regardless of outcome)
2.2.1.3.3. After Returning: runs after a successful method execution
2.2.1.3.4. After Throwing: runs if method throws an exception
2.2.1.3.5. Around: runs before and after method execution (most powerful)
2.2.1.4. d) Pointcut
2.2.1.4.1. A predicate that matches join points. Basically: where the advice should run.
2.2.1.4.2. Uses expressions to match methods/classes.
2.2.1.5. e) Weaving The process of applying aspects to your code.
2.2.1.5.1. Spring does runtime weaving using proxies.
2.3. Spring Boot configuration
2.3.1. APPLICATION PROPERTIES & YAML CONFIG
2.3.1.1. Both application.properties and application.yml are used to externalize configuration in Spring Boot. Spring Boot automatically reads them from src/main/resources. Properties file uses key=value format. YAML file uses hierarchical indentation and is more readable for nested configs.
2.3.1.2. application.properties
2.3.1.2.1. server.port=8081 spring.application.name=MyApp spring.datasource.url=jdbc:mysql://localhost:3306/mydb spring.datasource.username=root spring.datasource.password=secret
2.3.1.3. application.yml
2.3.1.3.1. server: port: 8081 spring: application: name: MyApp datasource: url: jdbc:mysql://localhost:3306/mydb username: root password: secret
2.3.2. CONDITIONAL BEANS
2.3.3. PROFILES
2.3.4. ACCESSING CONFIG IN THE CODE
2.3.4.1. You can inject properties using:
2.3.4.2. @Value annotation
2.3.4.2.1. @Value("${spring.application.name}") private String appName;
2.3.4.3. @ConfigurationProperties (better for groups of properties)
2.3.4.3.1. @Component @ConfigurationProperties(prefix = "spring.datasource") public class DataSourceConfig { private String url; private String username; private String password; // getters & setters }
2.4. Spring Beans Scopes
2.4.1. In Spring, scope basically answers: "How long does the container keep and reuse the bean instance?"
2.4.1.1. Singleton (the default)
2.4.1.1.1. One shared bean instance per Spring container.
2.4.1.1.2. Every time you @Autowired it, you get the same object.
2.4.1.2. Prototype
2.4.1.2.1. A new bean instance is created every time you request it.
2.4.1.3. Request (Web-aware only)
2.4.1.3.1. One bean per HTTP request.
2.4.1.3.2. Useful for things like request-specific data.
2.4.1.4. Session (Web-aware only)
2.4.1.4.1. One bean per HTTP session (tied to the user’s session).
2.4.1.5. Application
2.4.1.5.1. One bean per web application lifecycle (like a ServletContext).
2.4.1.6. WebSocket
2.4.1.6.1. One bean per WebSocket session.
3. 3. Spring Boot Essentials
3.1. Spring Boot Starters & Auto-Configuration
3.1.1. 🌟Starters (dependency bundles)
3.1.1.1. Instead of figuring out dozens of individual libraries, you add one starter dependency.
3.1.1.1.1. ⭐ Remember: Starters bundle dependencies — you rarely have to add each library individually.
3.1.1.1.2. Starter
3.1.1.1.3. What it brings (mainly)
3.1.1.1.4. Purpose
3.1.1.2. spring-boot-starter-web
3.1.1.2.1. Spring MVC, Jackson, embedded Tomcat, logging
3.1.1.2.2. Build REST APIs / web apps
3.1.1.3. spring-boot-starter-data-jpa
3.1.1.3.1. Spring Data JPA, Hibernate, JDBC
3.1.1.3.2. Database access with JPA
3.1.1.4. spring-boot-starter-security
3.1.1.4.1. Spring Security, core auth/authorization
3.1.1.4.2. Add security/authentication
3.1.1.5. spring-boot-starter-test
3.1.1.5.1. JUnit, Mockito, AssertJ
3.1.1.5.2. Unit/integration testing
3.1.1.6. spring-boot-starter-thymeleaf
3.1.1.6.1. Thymeleaf template engine
3.1.1.6.2. Server-side HTML rendering
3.1.1.7. spring-boot-starter-actuator
3.1.1.7.1. Health checks, metrics, info endpoints
3.1.1.7.2. Monitoring / production insights
3.1.1.8. spring-boot-starter-validation
3.1.1.8.1. Hibernate Validator
3.1.1.8.2. Bean validation (@Valid)
3.1.2. Auto-Configuration (beans wired automatically)
3.1.2.1. Spring Boot says: “Let me guess what you probably need, based on what’s on your classpath and what properties you’ve set.” If it sees spring-webmvc on the classpath, it configures a DispatcherServlet. 👉 You can still override any of these defaults with your own configuration, but Boot gives you a working app out-of-the-box.
3.1.2.1.1. ⭐ Remember: Auto-Configuration → detects those libraries and wires them up automatically.
3.1.2.1.2. Feature
3.1.2.1.3. Auto-configured by Boot
3.1.2.1.4. Trigger
3.1.2.2. DispatcherServlet
3.1.2.2.1. DispatcherServlet bean
3.1.2.2.2. spring-webmvc on classpath
3.1.2.3. Embedded Server
3.1.2.3.1. Tomcat / Jetty / Undertow
3.1.2.3.2. Starter-web present
3.1.2.4. JSON
3.1.2.4.1. Jackson ObjectMapper
3.1.2.4.2. jackson-databind present
3.1.2.5. DataSource
3.1.2.5.1. JDBC DataSource bean
3.1.2.5.2. JDBC driver on classpath + starter-data-jpa
3.1.2.6. JPA
3.1.2.6.1. EntityManagerFactory, TransactionManager
3.1.2.6.2. starter-data-jpa + DB driver
3.1.2.7. Security
3.1.2.7.1. Default login page, filter chain
3.1.2.7.2. starter-security present
3.1.2.8. Thymeleaf
3.1.2.8.1. TemplateResolver, ViewResolver
3.1.2.8.2. starter-thymeleaf present
3.1.3. 📝 IoC vs Auto-Configuration
3.1.3.1. IoC (Inversion of Control)
3.1.3.1.1. Spring creates and manages beans; handles dependency injection.
3.1.3.2. Auto-Configuration
3.1.3.2.1. Boot decides which beans to create automatically based on classpath/dependencies/properties.
3.1.3.3. 💡 Analogy: IoC = engine; Auto-Configuration = autopilot on top of the engine.
3.2. CommandLineRunner & ApplicationRunner
3.2.1. CommandLineRunner
3.2.1.1. Interface: org.springframework.boot.CommandLineRunner
3.2.1.2. Use case: Quick startup tasks, simple argument handling
3.2.1.3. @Component public class MyRunner implements CommandLineRunner { @Override public void run(String... args) { System.out.println("Args: " + Arrays.toString(args)); } }
3.2.1.3.1. Arguments: Raw String[] args from the command line.
3.2.2. 4️⃣ Extra Tips Can have multiple runners; control order with @Order or Ordered. Runs after Spring context initialization but before web server starts serving. Perfect for DB init, logging, or argument-based logic.
3.2.3. ApplicationRunner
3.2.3.1. Interface: org.springframework.boot.ApplicationRunner
3.2.3.2. Use case: When you need structured argument parsing.
3.2.3.3. @Component public class MyAppRunner implements ApplicationRunner { @Override public void run(ApplicationArguments args) { System.out.println("Options: " + args.getOptionNames()); System.out.println("Non-Options: " + args.getNonOptionArgs()); } }
3.2.3.3.1. Arguments: Parsed ApplicationArguments args.getOptionNames() → set of --options args.getOptionValues("option") → values of an option args.getNonOptionArgs() → plain arguments
3.3. Embedded Servers (Tomcat, Jetty, Undertow)
3.3.1. Undertow
3.3.1.1. Highly performant and low memory footprint. Supports non-blocking APIs. Common in reactive systems.
3.3.1.2. <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-undertow</artifactId> </dependency>
3.3.2. What is an Embedded Server? Spring Boot applications usually run with an embedded web server inside the app JAR/WAR → no need to deploy manually on an external Tomcat/Jetty/etc. Controlled via Spring Boot starters (spring-boot-starter-web, spring-boot-starter-tomcat, etc.).
3.3.3. TomCat
3.3.3.1. Starter: spring-boot-starter-web (brings Tomcat by default).
3.3.3.2. Most widely used. Good for servlet-based apps.
3.3.3.3. Configurable via application.properties: server.port=8081 server.tomcat.threads.max=200 server.tomcat.uri-encoding=UTF-8
3.3.4. Jetty
3.3.4.1. Lighter than Tomcat, often used in microservices. Good performance for async + non-blocking I/O.
3.3.4.2. <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jetty</artifactId> </dependency>
3.3.5. Switching Embedded Servers. Tomcat is included by default → To switch: Exclude Tomcat: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </exclusion> </exclusions> </dependency> Add Jetty or Undertow starter.
3.4. JVM & Memory Management
3.4.1. Stack
3.4.1.1. Stores method calls + local variables (references, not objects).
3.4.1.2. Last-in-first-out (like a stack of plates).
3.4.1.3. Cleared automatically when a method finishes.
3.4.2. Heap
3.4.2.1. Stores objects (new Something()).
3.4.2.2. Shared across the whole app.
3.4.2.3. Cleaned up by Garbage Collector (not immediately).
3.4.2.4. Heap Structure (simplified view)
3.4.2.4.1. Young Generation
3.4.2.4.2. Old Generation:
3.4.3. Garbage Collection (GC)
3.4.3.1. What it does:
3.4.3.1.1. Frees memory by removing unreachable objects.
3.4.3.2. Key idea:
3.4.3.2.1. If an object has no reference from stack → it’s “garbage.”
3.4.3.2.2. GC reclaims it eventually (not right away).
3.4.4. Reachability Rule
3.4.4.1. Reachable =
3.4.4.1.1. alive (still has at least one reference).
3.4.4.2. Unreachable =
3.4.4.2.1. eligible for GC.
4. 5. Spring Boot Web
4.1. Spring MVC
4.1.1. Controllers
4.1.1.1. 1. Controller Role
4.1.1.1.1. Acts as the “traffic cop” for requests.
4.1.1.1.2. Receives HTTP requests, calls the service/logic layer, and returns a response.
4.1.1.1.3. Response can be:
4.1.1.2. 2. Controller Annotations
4.1.1.2.1. @Controller → returns views (like Thymeleaf templates).
4.1.1.2.2. @RestController → returns data directly in the response body (JSON/XML).
4.1.2. Request Mapping
4.1.2.1. @GetMapping, @PostMapping, @PutMapping, @DeleteMapping → shorthand for @RequestMapping(method=…).
4.1.2.2. Path Variables → dynamic segments in URL:
4.1.2.2.1. @GetMapping("/users/{id}") public User getUser(@PathVariable Long id) { … }
4.1.2.3. Query Parameters → from URL like ?param=value:
4.1.2.3.1. @GetMapping("/users") public List<User> getUsers(@RequestParam Boolean active) { … }
4.1.2.4. Optional query params & defaults:
4.1.2.4.1. @RequestParam(required = false, defaultValue = "true") Boolean active
4.1.2.5. Quick Tip / Memory Aid
4.1.2.5.1. Path → @PathVariable (usually mandatory)
4.1.2.5.2. Query → @RequestParam (can be optional)
4.1.3. Validation
4.1.3.1. Annotations on DTOs: @NotNull, @Size, @Email, @Min, @Max, etc.
4.1.3.2. Trigger validation: @Valid or @Validated on method parameters
4.1.3.3. Example:
4.1.3.3.1. @PostMapping("/users") public User createUser(@Valid @RequestBody UserDTO userDTO) { … } Invalid input → MethodArgumentNotValidException → HTTP 400
4.1.4. Exception Handling
4.1.4.1. Local (controller-specific): @ExceptionHandler
4.1.4.2. Global (all controllers): @ControllerAdvice
4.1.4.3. Example (validation errors):
4.1.4.3.1. @ExceptionHandler(MethodArgumentNotValidException.class) public ResponseEntity<Map<String, String>> handleValidationExceptions( MethodArgumentNotValidException ex) { Map<String, String> errors = new HashMap<>(); ex.getBindingResult().getFieldErrors() .forEach(error -> errors.put(error.getField(), error.getDefaultMessage())); return ResponseEntity.badRequest().body(errors); }
4.1.4.4. Result: JSON with invalid fields + messages, HTTP 400
4.2. HATEOAS (Hypermedia as the Engine of Application State)
4.2.1. 1️⃣ Core Idea
4.2.1.1. REST responses should not only return data but also tell clients what actions are possible next.
4.2.1.2. Clients can navigate the API dynamically using links embedded in the response.
4.2.2. 2️⃣ Why HATEOAS?
4.2.2.1. Avoids hardcoding URLs on the client side.
4.2.2.2. Makes APIs self-descriptive.
4.2.2.3. Guides clients to valid actions (like update, delete, list related resources).
4.2.3. 3️⃣ How It Looks in JSON
4.2.3.1. Example for a user:
4.2.3.1.1. { "id": 42, "name": "Alice", "email": "alice@example.com", "_links": { "self": { "href": "/api/v1/users/42" }, "friends": { "href": "/api/v1/users/42/friends" }, "update": { "href": "/api/v1/users/42", "method": "PUT" } } }
4.2.3.2. _links contains all next possible actions
4.2.3.3. Each link usually has a rel (relation) and an href
4.2.4. 4️⃣ How to Implement in Spring
4.2.4.1. Use Spring HATEOAS library.
4.2.4.2. Wrap your resource in EntityModel or CollectionModel.
4.2.4.3. Add links using:
4.2.4.3.1. EntityModel<UserDTO> resource = EntityModel.of(user); resource.add(linkTo(methodOn(UserController.class).getUser(user.getId())).withSelfRel()); resource.add(linkTo(methodOn(UserController.class).getAllUsers()).withRel("all-users"));
4.2.5. 5️⃣ Quick Tip / Memory Aid
4.2.5.1. Think:
4.2.5.1.1. “Every resource I send should carry a map of next possible actions so the client doesn’t have to guess.”
4.3. Jackson Serialization/DeSerialization
4.3.1. 1️⃣ Setup
4.3.1.1. Maven:
4.3.1.1.1. <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.17.2</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.dataformat</groupId> <artifactId>jackson-dataformat-xml</artifactId> <version>2.17.2</version> </dependency>
4.3.2. 2️⃣ Basic Usage
4.3.2.1. ObjectMapper mapper = new ObjectMapper(); // JSON XmlMapper xmlMapper = new XmlMapper(); // XML // Serialize String json = mapper.writeValueAsString(object); String xml = xmlMapper.writeValueAsString(object); // Deserialize MyClass obj = mapper.readValue(json, MyClass.class); MyClass obj2 = xmlMapper.readValue(xml, MyClass.class);
4.3.3. 3️⃣ Common Annotations
4.3.3.1. Rename field in JSON/XML
4.3.3.1.1. @JsonProperty("name")
4.3.3.2. Skip field
4.3.3.2.1. @JsonIgnore
4.3.3.3. Format dates
4.3.3.3.1. @JsonFormat(pattern="yyyy-MM-dd")
4.3.3.4. Ignore null fields
4.3.3.4.1. @JsonInclude(Include.NON_NULL)
4.3.3.5. Use constructor for deserialization
4.3.3.5.1. @JsonCreator + @JsonProperty
4.3.4. 4️⃣ Collections & Nested Objects
4.3.4.1. class Person { private String name; private List<Friend> friends; } class Friend { private String name; private List<String> hobbies; }
4.3.4.2. JSON array → List Nested objects → nested JSON/XML objects Works with List, Set, Map
4.3.4.3. Example JSON:
4.3.4.3.1. { "name": "Alice", "friends": [ { "name": "Bob", "hobbies": ["Chess","Reading"] }, { "name": "Charlie", "hobbies": ["Soccer","Gaming"] } ] }
4.3.5. 5️⃣ Key Tips
4.3.5.1. JSON: lightweight, key–value pairs, widely used in REST APIs.
4.3.5.2. XML: tag-based, good for legacy systems, supported with XmlMapper.
4.3.5.3. Collections & nested objects work automatically.
4.3.5.4. Use annotations to control serialization, security, and formatting.
4.4. File uploads/downloads
4.4.1. REST APIs
4.4.1.1. 1️⃣ File Upload (REST)
4.4.1.1.1. Concept:
4.4.1.1.2. Key Spring classes:
4.4.1.1.3. Pseudo-example:
4.4.1.2. 2️⃣ File Download (REST)
4.4.1.2.1. Concept:
4.4.1.2.2. Key Spring classes:
4.4.1.2.3. Pseudo-example:
4.4.1.3. 3️⃣ Extra Tips
4.4.1.3.1. Always validate file names to prevent path traversal attacks.
4.4.1.3.2. Set max file size in application.properties:
4.4.1.3.3. For large files, consider streaming rather than reading fully into memory.
5. 6. Spring Security
5.1. Spring Security Core (Authentication & Authorization)
5.1.1. 1️⃣ Authentication (Who are you?)
5.1.1.1. Goal: Verify the user’s identity.
5.1.1.2. Key flow in Spring Security:
5.1.1.2.1. User submits credentials (username/password, token, etc.)
5.1.1.2.2. SecurityFilterChain intercepts request
5.1.1.2.3. AuthenticationManager delegates to an AuthenticationProvider
5.1.1.2.4. AuthenticationProvider loads user details (UserDetailsService) and checks password
5.1.1.2.5. If valid → Authentication object created and stored in SecurityContext
5.1.1.3. UserDetails object contains:
5.1.1.3.1. username (principal)
5.1.1.3.2. password (credentials, usually hashed)
5.1.1.3.3. authorities (roles or permissions)
5.1.1.4. Example (in-memory users):
5.1.1.4.1. UserDetails admin = User.withUsername("admin") .password(passwordEncoder.encode("admin123")) .roles("ADMIN") .build();
5.1.2. 2️⃣ Authorization (What are you allowed to do?)
5.1.2.1. Goal: Determine if the authenticated user can access a resource.
5.1.2.2. Two main types in Spring Security:
5.1.2.2.1. 1. URL-based rules (via SecurityFilterChain):
5.1.2.2.2. 2. Method-level rules (via @PreAuthorize):
5.1.3. 3️⃣ Authentication vs Authorization — Quick Comparison
5.1.3.1. | Feature | Authentication | Authorization | | ---------------- | ------------------------------------------------------------------- | ------------------------------------------------------------- | | Question | Who are you? | What can you do? | | Spring Component | AuthenticationManager / AuthenticationProvider / UserDetailsService | SecurityFilterChain / AccessDecisionManager / `@PreAuthorize` | | Result | Authentication object in SecurityContext | Access granted/denied |
5.1.4. 4️⃣ Flow in a Simple Example
5.1.4.1. 1. User accesses /admin
5.1.4.2. 2. Authentication check → logged in?
5.1.4.2.1. No → redirect to login
5.1.4.2.2. Yes → continue
5.1.4.3. 3. Authorization check → does the user have ADMIN role?
5.1.4.3.1. Yes → execute controller method
5.1.4.3.2. No → throw AccessDeniedException
5.2. UserDetailsService & PasswordEncoder
5.2.1. 1️⃣ UserDetailsService
5.2.1.1. Purpose: Load user-specific data for authentication.
5.2.1.2. Spring Security asks it:
5.2.1.2.1. “Hey, I got a login request with username X. Can you give me the user’s details?”
5.2.1.2.2. It must return a UserDetails object, which includes:
5.2.1.3. Example (in-memory implementation):
5.2.1.3.1. @Bean public UserDetailsService userDetailsService(PasswordEncoder encoder) { UserDetails admin = User.withUsername("admin") .password(encoder.encode("admin123")) .roles("ADMIN") .build(); UserDetails user = User.withUsername("user") .password(encoder.encode("password")) .roles("USER") .build(); return new InMemoryUserDetailsManager(admin, user); }
5.2.1.3.2. ➡️ Here, InMemoryUserDetailsManager is a ready-to-use implementation of UserDetailsService. ➡️ Later, for DB, we’d write a custom class that queries a users table instead.
5.2.2. 2️⃣ PasswordEncoder
5.2.2.1. Purpose: Hash + verify passwords safely.
5.2.2.2. Spring Security never stores or compares raw passwords.
5.2.2.3. When a user logs in:
5.2.2.3.1. Password from login form is encoded with the chosen encoder
5.2.2.3.2. Compared with stored (already encoded) password in UserDetails
5.2.2.4. Common implementation:
5.2.2.4.1. @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); }
5.2.2.4.2. BCrypt is recommended: it’s slow and salted → resistant to brute force & rainbow table attacks.
5.2.2.4.3. When creating users, always encode passwords before saving:
5.2.3. 3️⃣ How They Work Together
5.2.3.1. 1. User logs in with username/password
5.2.3.2. 2. UserDetailsService.loadUserByUsername(username) is called
5.2.3.3. 3. Returns UserDetails (includes encoded password)
5.2.3.4. 4. PasswordEncoder.matches(rawPassword, encodedPassword) checks if it’s valid
5.2.3.5. 5. If yes → Authentication succeeds → User stored in SecurityContext
5.2.4. ✅ Key Takeaways
5.2.4.1. UserDetailsService → “find the user by username”
5.2.4.2. PasswordEncoder → “encode & match passwords safely”
5.2.4.3. Together, they power the authentication step before any authorization happens.
5.3. JWT Authentication
5.3.1. 1. 🔑 What is JWT?
5.3.1.1. JSON Web Token is a compact, URL-safe token used to securely transfer claims between two parties.
5.3.1.2. It’s just a string divided into 3 parts:
5.3.1.2.1. header.payload.signature
5.3.1.3. Example:
5.3.1.3.1. eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1c2VyMTIzIn0.5A2l6Vq3qVn3iXZhJrKf4Q
5.3.2. 2. 🧩 JWT Structure
5.3.2.1. Header → algorithm & type (e.g., HS256, JWT)
5.3.2.2. Payload → claims (data like sub, roles, exp)
5.3.2.3. Signature → ensures integrity (secret key + algo)
5.3.2.4. Example payload:
5.3.2.4.1. { "sub": "user123", "role": "ADMIN", "exp": 1735689600 }
5.3.3. 3. ⚙️ JWT Authentication Flow
5.3.3.1. Login → User sends username & password.
5.3.3.2. Server validates credentials → Issues JWT.
5.3.3.3. Client stores JWT → Usually in Authorization: Bearer <token> header.
5.3.3.4. Request with JWT → Each API call includes token.
5.3.3.5. Server verifies JWT → If valid → allow; else → reject.
5.3.4. 4. 🛠️ Implementing JWT in Spring Boot (Step by Step)
5.3.5. 5. ✅ Best Practices for JWT
5.3.5.1. Use short expiration times (e.g., 15 mins).
5.3.5.2. Implement refresh tokens for session renewal.
5.3.5.3. Store tokens in HTTP-only cookies if possible (safer than localStorage).
5.3.5.4. Always use HTTPS in production.
5.3.5.5. For microservices: use public/private key (RSA) instead of a shared secret.
5.3.5.6. Never put sensitive info in payload (it’s only Base64-encoded, not encrypted).
5.3.6. 6. 🏆 Master-Level Features
5.3.6.1. Refresh Tokens → extend sessions safely.
5.3.6.2. Role-based Authorization → embed roles in JWT claims.
5.3.6.3. Token Blacklisting → for logout & revoked tokens.
5.3.6.4. Stateless scaling → no session storage needed.
5.3.6.5. OAuth2 + JWT → integrate with Google, GitHub, etc.
5.4. OAuth2 / OpenID Connect
5.4.1. OAuth2
5.4.1.1. OAuth 2.0: Authorization framework to allow a user to grant limited access to their resources on one site (resource server) to another site (client) without sharing credentials.
5.4.1.2. 4 Main Roles
5.4.1.2.1. Resource Owner: The User
5.4.1.2.2. Client: Your App (e.x PmLanner)
5.4.1.2.3. Authorization Server: who authenticates the user & gives out tokens (e.g., Google).
5.4.1.2.4. Resource Server: the API with protected data (e.g., Google Calendar API).
5.4.1.3. OAuth2 only answers: “Can this app access data on behalf of the user?”
5.4.1.4. | Grant Type | When to Use | Flow | | --------------------------------------- | ----------------------- | ------------------------------------------------------------------------------------------------ | | **Authorization Code** | Server-side apps | 1. Client redirects user → auth server. 2. Auth code returned → exchanged for token. **Secure**. | | **Implicit** | Browser-only apps | Token returned directly in URL fragment. **Less secure**; mostly deprecated. | | **Client Credentials** | Machine-to-machine | Client directly requests token from auth server. No user. | | **Resource Owner Password Credentials** | Legacy/trusted apps | User provides username/password directly. **Not recommended**. | | **Device Code** | Devices without browser | Device polls for token after user authorizes on another device. |
5.4.2. OpenID Connect (OIDC)
5.4.2.1. OIDC = Authentication layer on top of OAuth 2.0. Provides identity information (who the user is) in addition to OAuth's access delegation.
5.4.2.2. Key Components
5.4.2.2.1. ID Token: JWT containing user info (sub, name, email).
5.4.2.2.2. UserInfo Endpoint: API endpoint returning claims about the user.
5.4.2.2.3. Scopes:
5.4.2.3. | Flow | Use Case | Notes | | ---------------------- | ---------------- | ---------------------------------------------------------------------- | | **Authorization Code** | Server-side | Recommended. Can get ID token + access token. | | **Implicit** | Browser-only | ID token returned in URL fragment. Less secure. | | **Hybrid** | Server + Browser | Some tokens returned immediately, some via code. Flexible but complex. |
5.4.3. Token Types
5.4.3.1. | Token | Purpose | Format | | ------------- | --------------------------- | ---------------------------- | | Access Token | OAuth: authorize API access | Usually JWT or opaque string | | Refresh Token | OAuth: renew access token | Opaque string | | ID Token | OIDC: authenticate user | JWT (contains user claims) |
5.4.4. Key Endpoints
5.4.4.1. | Endpoint | Description | | -------------------------- | ----------------------------------------------------- | | **Authorization Endpoint** | Initiates auth request (user login + consent) | | **Token Endpoint** | Exchange auth code for tokens, refresh token requests | | **UserInfo Endpoint** | OIDC: fetch user profile info | | **Revocation Endpoint** | Revoke tokens |
5.4.5. :white_check_mark: Best Practices
5.4.5.1. Use Authorization Code + PKCE for public clients (SPA, mobile apps).
5.4.5.2. Always validate ID tokens:
5.4.5.2.1. Signature (JWT)
5.4.5.2.2. Issuer (iss)
5.4.5.2.3. Audience (aud)
5.4.5.2.4. Expiration (exp)
5.4.5.3. Use HTTPS everywhere.
5.4.5.4. Prefer short-lived access tokens + refresh tokens.
5.4.5.5. Avoid implicit flow if possible.
6. 4. Data & Persistence
6.1. Transaction Management
6.1.1. 1️⃣ What is a Transaction?
6.1.1.1. Ensures ACID: Atomicity, Consistency, Isolation, Durability.
6.1.1.2. A unit of work: all operations succeed → commit; any fails → rollback.
6.1.2. 2️⃣ How Spring Handles Transactions
6.1.2.1. Use @Transactional on methods or classes.
6.1.2.2. Works with JPA, JDBC, Hibernate, etc.
6.1.2.3. Default rollback behavior: runtime exceptions → rollback, checked exceptions → no rollback.
6.1.2.3.1. Can customize: @Transactional(rollbackFor = Exception.class)
6.1.3. 3️⃣ Key Attributes of @Transactional
6.1.3.1. propagation
6.1.3.1.1. How this transaction behaves with existing transactions (REQUIRED, REQUIRES_NEW, SUPPORTS)
6.1.3.2. isolation
6.1.3.2.1. How concurrent transactions interact (READ_COMMITTED, SERIALIZABLE, etc.)
6.1.3.3. readOnly
6.1.3.3.1. Hint to optimize read-only operations (@Transactional(readOnly = true))
6.1.3.4. timeOut
6.1.3.4.1. Max time allowed for transaction
6.2. Spring Data JPA
6.2.1. Repositories
6.2.1.1. prebuilt CRUD methods (save, findAll, delete…).
6.2.1.1.1. CrudRepository<T, ID> → Basic CRUD (save, findById, delete, count, etc.)
6.2.1.1.2. JpaRepository<T, ID> → Extends CrudRepository, adds pagination & sorting.
6.2.1.1.3. PagingAndSortingRepository<T, ID> → Adds pagination & sorting.
6.2.1.1.4. Custom Repositories → Define your own methods/impl.
6.2.2. Query Methods (Derived Queries)
6.2.2.1. Spring Data JPA generates queries from method names (finByEmail).
6.2.2.1.1. findByName(String name)
6.2.2.1.2. findByEmailAndStatus(String email, String status)
6.2.2.1.3. findTop5ByOrderByCreatedDateDesc()
6.2.2.1.4. findDistinctByDepartment(String dept)
6.2.3. JPQL (Java Persistence Query Language)
6.2.3.1. Works with entities & fields, not tables & columns. Use @Query annotation.
6.2.3.1.1. @Query("SELECT u FROM User u WHERE u.email = ?1") User findByEmailJPQL(String email); @Query("SELECT u FROM User u WHERE u.status = :status") List<User> findByStatus(@Param("status") String status);
6.2.4. Native Queries
6.2.4.1. Use SQL directly (table & column names). nativeQuery = true.
6.2.4.1.1. @Query("SELECT u FROM User u WHERE u.email = ?1") User findByEmailJPQL(String email);
6.2.4.1.2. @Query("SELECT u FROM User u WHERE u.status = :status") List<User> findByStatus(@Param("status") String status);
6.2.5. Pagination & Sorting
6.2.5.1. Page<User> findByStatus(String status, Pageable pageable); List<User> findByAgeGreaterThan(int age, Sort sort);
6.2.5.2. Usage: Pageable pageable = PageRequest.of(0, 10, Sort.by("name").ascending()); userRepository.findByStatus("ACTIVE", pageable);
6.2.6. Custom Repository Implementation
6.2.6.1. public interface UserRepositoryCustom { List<User> findActiveUsers(); } public class UserRepositoryImpl implements UserRepositoryCustom { @PersistenceContext private EntityManager em; public List<User> findActiveUsers() { return em.createQuery("SELECT u FROM User u WHERE u.active = true", User.class) .getResultList(); } }
6.3. Spring Data JDBC & R2DBC
6.3.1. JDBC
6.3.1.1. JDBC = blocking
6.3.1.2. Every database call blocks a thread until the DB responds
6.3.1.3. One request → one thread → thread stays busy until done
6.3.2. R2DBC
6.3.2.1. R2DBC = Non-blocking
6.3.2.2. Database calls return immediately (Mono / Flux)
6.3.2.3. The thread is freed and can handle other requests while DB is still working
6.3.3. Example
6.3.3.1. 2 users send requests at the same time
6.3.3.2. With JDBC
6.3.3.2.1. User A starts inserting 1M rows → thread is blocked
6.3.3.2.2. User B sends request → needs another thread
6.3.3.2.3. If thread pool is full, User B waits or times out
6.3.3.2.4. Result: system becomes sluggish
6.3.3.3. With R2DBC
6.3.3.3.1. User A starts insert → thread freed immediately
6.3.3.3.2. User B’s request starts right away
6.3.3.3.3. Both inserts run concurrently at DB level
6.3.3.3.4. Server stays responsive, even for more users
6.3.4. 🖥️ How I did demo it in Postman
6.3.4.1. Start a bulk insert
6.3.4.1.1. POST http://localhost:8080/users/bulk-insert?count=1000000
6.3.4.2. While it runs, send:
6.3.4.2.1. GET http://localhost:8080/users/random?n=5
6.3.4.2.2. GET http://localhost:8080/users/count
6.3.5. ✅ Real-world takeaway
6.3.5.1. JDBC is fine for simple apps, few users
6.3.5.2. R2DBC shines when:
6.3.5.2.1. many concurrent users
6.3.5.2.2. long-running DB operations
6.3.5.2.3. Need scalability & responsiveness
6.4. Database Migration
6.4.1. 🚨Why Migrations?
6.4.1.1. Track schema changes over time
6.4.1.2. Keep dev, staging, prod in sync
6.4.1.3. Automate DB evolution
6.4.2. 🟢 Flyway
6.4.2.1. Philosophy: Forward-only, simple, SQL-first
6.4.2.2. Migration files: V1__init.sql, V2__add_column.sql
6.4.2.3. Metadata table: flyway_schema_history
6.4.2.4. Rollback: ❌ Not supported (use forward fix migration instead)
6.4.2.5. Best for: Most apps, fast adoption, small/medium projects
6.4.3. 🔵 Liquibase
6.4.3.1. Philosophy: Powerful, rollback-friendly, multiple formats
6.4.3.2. Formats: SQL, XML, YAML, JSON
6.4.3.3. Changesets: Unique ID + Author + Change
6.4.3.4. Metadata tables: DATABASECHANGELOG, DATABASECHANGELOGLOCK
6.4.3.5. Rollback: ✅ Supported (must define <rollback> logic)
6.4.3.6. Best for: Enterprise, complex migrations, rollback needs
6.4.4. 🛠 Usage Flow
6.4.4.1. Write migration (SQL or changeset).
6.4.4.2. Run app or CLI → tool applies missing migrations.
6.4.4.3. Tool logs history in DB.
6.4.4.4. In Liquibase: can rollback migrations (reverse order).
6.4.5. 🔑 When to Use Rollback
6.4.5.1. ✅ Dev/testing: reset DB, experiment safely ✅ CI/CD pipelines: rollback after tests ⚠️ Prod: only if rollback is safe (or in emergencies) ❌ Otherwise: prefer forward-only migrations
6.4.6. 📝 Example Commands
6.4.6.1. Flyway CLI
6.4.6.1.1. flyway migrate flyway info
6.4.6.2. Liquibase CLI
6.4.6.2.1. liquibase update liquibase rollbackCount=1 liquibase rollbackToDate=2025-09-18
6.4.7. ⚡ Key Takeaway: Flyway → fast, simple, forward-only. Liquibase → powerful, rollback-capable, heavier.