Java Spring Complete Notes - MD
Java Spring Complete Notes - MD
TABLE OF CONTENTS
1. Java Fundamentals Review
2. Spring Framework Core
3. Spring Boot Essentials
4. RESTful Web Services
5. Database Integration & JPA
6. Microservices Architecture
7. Spring Cloud Components
8. Security & Authentication
9. Spring AI Integration
10. Testing & Best Practices
11. Deployment & DevOps
Important Methods:
add(), remove(), contains(), size()
Iterator and enhanced for-loop
Stream API integration
Functional Interfaces:
Predicate<T> - boolean test(T t)
Function<T,R> - R apply(T t)
Consumer<T> - void accept(T t)
Supplier<T> - T get()
Stream API:
Intermediate operations: filter(), map(), sorted()
Terminal operations: collect(), forEach(), reduce()
Parallel streams for performance
Method References:
Static: ClassName::staticMethod
Instance: instance::instanceMethod
Constructor: ClassName::new
Best Practices:
Try-catch-finally blocks
Try-with-resources (AutoCloseable)
Custom exceptions
Exception chaining
Never catch Exception/Throwable broadly
Synchronization:
synchronized keyword
Lock interface and ReentrantLock
volatile keyword
Atomic classes (AtomicInteger, etc.)
Concurrent Collections:
ConcurrentHashMap
CopyOnWriteArrayList
BlockingQueue
Spring Modules:
Core Container (IoC, Beans, Context)
Data Access/Integration (JDBC, ORM, JMS)
Web (MVC, WebSocket)
AOP (Aspect-Oriented Programming)
Test (Unit & Integration testing)
java
@Component
public class UserService {
private final UserRepository repository;
@Autowired
public UserService(UserRepository repository) {
[Link] = repository;
}
}
Setter Injection:
java
@Component
public class UserService {
private UserRepository repository;
@Autowired
public void setRepository(UserRepository repository) {
[Link] = repository;
}
}
Field Injection:
java
@Component
public class UserService {
@Autowired
private UserRepository repository;
}
Lifecycle Annotations:
java
@PostConstruct
public void init() {
// Initialization logic
}
@PreDestroy
public void cleanup() {
// Cleanup logic
}
Prototype:
New instance every time requested
Container doesn't manage complete lifecycle
Web Scopes:
Request: One instance per HTTP request
Session: One instance per HTTP session
Application: One instance per ServletContext
WebSocket: One instance per WebSocket session
java
@Component
@Scope("prototype")
public class PrototypeBean { }
2.6 Annotations
Core Annotations:
@Component - Generic stereotype
@Service - Service layer
@Repository - Data access layer
@Controller - Web layer (MVC)
@RestController - RESTful web services
@Configuration - Java-based configuration
@Bean - Bean definition in @Configuration
@Autowired - Dependency injection
@Qualifier - Specify which bean to inject
@Primary - Default bean when multiple candidates
@Value - Inject property values
@PropertySource - Load properties file
xml
Java Configuration:
java
@Configuration
public class AppConfig {
@Bean
public UserService userService() {
return new UserService(userRepository());
}
}
Annotation-based:
java
@ComponentScan(basePackages = "[Link]")
@Configuration
public class AppConfig { }
Advice Types:
@Before - Before method execution
@After - After method execution
@AfterReturning - After successful return
@AfterThrowing - After exception
@Around - Wrap method execution
Example:
java
@Aspect
@Component
public class LoggingAspect {
@Before("execution(* [Link].*.*(..))")
public void logBefore(JoinPoint joinPoint) {
[Link]("Executing: " + [Link]());
}
}
src/
├── main/
│ ├── java/
│ │ └── com/example/myapp/
│ │ ├── [Link] (main class)
│ │ ├── controller/
│ │ ├── service/
│ │ ├── repository/
│ │ ├── model/
│ │ └── config/
│ └── resources/
│ ├── [Link] / [Link]
│ ├── static/ (CSS, JS, images)
│ └── templates/ (Thymeleaf, etc.)
└── test/
└── java/
properties
[Link]=8080
[Link]=my-app
# Database
[Link]=jdbc:mysql://localhost:3306/mydb
[Link]=root
[Link]=secret
# JPA
[Link]-auto=update
[Link]-sql=true
# Logging
[Link]=INFO
[Link]=DEBUG
yaml
server:
port: 8080
spring:
application:
name: my-app
datasource:
url: jdbc:mysql://localhost:3306/mydb
username: root
password: secret
jpa:
hibernate:
ddl-auto: update
show-sql: true
Profile-Specific Configuration:
[Link]
[Link]
Activate: [Link]=dev
3.5 Auto-Configuration
How it Works:
@SpringBootApplication enables auto-configuration
Scans classpath for libraries
Configures beans automatically
Conditional on presence of classes/beans
java
@SpringBootApplication =
@SpringBootConfiguration +
@EnableAutoConfiguration +
@ComponentScan
Customizing Auto-Configuration:
java
@SpringBootApplication(exclude = {[Link]})
public class MyApp { }
Setup:
xml
<dependency>
<groupId>[Link]</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
Enable in [Link]:
properties
[Link]=health,info,metrics
[Link]-details=always
java
@Component
public class CustomHealthIndicator implements HealthIndicator {
@Override
public Health health() {
// Check custom health logic
return [Link]().withDetail("custom", "All good").build();
}
}
REST Constraints:
1. Client-Server architecture
2. Stateless
3. Cacheable
4. Uniform interface
5. Layered system
6. Code on demand (optional)
java
@RestController
@RequestMapping("/api/users")
public class UserController {
@Autowired
private UserService userService;
@GetMapping
public List<User> getAllUsers() {
return [Link]();
}
@GetMapping("/{id}")
public ResponseEntity<User> getUserById(@PathVariable Long id) {
return [Link](id)
.map(ResponseEntity::ok)
.orElse([Link]().build());
}
@PostMapping
public ResponseEntity<User> createUser(@RequestBody User user) {
User saved = [Link](user);
return [Link]([Link]).body(saved);
}
@PutMapping("/{id}")
public ResponseEntity<User> updateUser(
@PathVariable Long id,
@RequestBody User user) {
return [Link](id, user)
.map(ResponseEntity::ok)
.orElse([Link]().build());
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
[Link](id);
return [Link]().build();
}
}
java
@GetMapping("/users/{id}/posts/{postId}")
public Post getPost(@PathVariable Long id, @PathVariable Long postId) {
// Logic
}
Query Parameters:
java
@GetMapping("/users")
public List<User> searchUsers(
@RequestParam(required = false) String name,
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size) {
// Logic
}
Request Headers:
java
@GetMapping("/users")
public List<User> getUsers(
@RequestHeader("Authorization") String token) {
// Logic
}
Request Body:
java
@PostMapping("/users")
public User createUser(@RequestBody User user) {
return [Link](user);
}
4.5 Validation
Bean Validation Annotations:
java
public class User {
@Min(18)
@Max(100)
private Integer age;
@Pattern(regexp = "^\\+?[1-9]\\d{1,14}$")
private String phone;
}
Controller Validation:
java
@PostMapping("/users")
public ResponseEntity<?> createUser(@Valid @RequestBody User user,
BindingResult result) {
if ([Link]()) {
return [Link]().body([Link]());
}
return [Link]([Link](user));
}
java
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler([Link])
public ResponseEntity<ErrorResponse> handleNotFound(
ResourceNotFoundException ex) {
ErrorResponse error = new ErrorResponse(
HttpStatus.NOT_FOUND.value(),
[Link](),
[Link]()
);
return [Link](HttpStatus.NOT_FOUND).body(error);
}
@ExceptionHandler([Link])
public ResponseEntity<Map<String, String>> handleValidation(
MethodArgumentNotValidException ex) {
Map<String, String> errors = new HashMap<>();
[Link]().getFieldErrors().forEach(error ->
[Link]([Link](), [Link]())
);
return [Link]().body(errors);
}
@ExceptionHandler([Link])
public ResponseEntity<ErrorResponse> handleGeneral(Exception ex) {
ErrorResponse error = new ErrorResponse(
HttpStatus.INTERNAL_SERVER_ERROR.value(),
"An error occurred",
[Link]()
);
return [Link](HttpStatus.INTERNAL_SERVER_ERROR)
.body(error);
}
}
java
@GetMapping(value = "/users/{id}",
produces = {MediaType.APPLICATION_JSON_VALUE,
MediaType.APPLICATION_XML_VALUE})
public User getUser(@PathVariable Long id) {
return [Link](id);
}
Consuming JSON/XML:
java
@PostMapping(value = "/users",
consumes = {MediaType.APPLICATION_JSON_VALUE,
MediaType.APPLICATION_XML_VALUE})
public User createUser(@RequestBody User user) {
return [Link](user);
}
java
@GetMapping("/{id}")
public EntityModel<User> getUserById(@PathVariable Long id) {
User user = [Link](id);
EntityModel<User> resource = [Link](user);
[Link](linkTo(methodOn([Link])
.getUserById(id)).withSelfRel());
[Link](linkTo(methodOn([Link])
.getAllUsers()).withRel("users"));
return resource;
}
java
@GetMapping("/users")
public Page<User> getUsers(
@PageableDefault(size = 20, sort = "name") Pageable pageable) {
return [Link](pageable);
}
// URL: /users?page=0&size=10&sort=name,asc
java
@GetMapping("/users/search")
public List<User> searchUsers(
@RequestParam(required = false) String name,
@RequestParam(required = false) String email) {
if (name != null) {
spec = [Link]((root, query, cb) ->
[Link]([Link]("name"), "%" + name + "%"));
}
if (email != null) {
spec = [Link]((root, query, cb) ->
[Link]([Link]("email"), email));
}
return [Link](spec);
}
Key Interfaces:
Repository (marker)
CrudRepository (basic CRUD)
PagingAndSortingRepository (pagination)
JpaRepository (JPA-specific, recommended)
java
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = [Link])
private Long id;
@Temporal([Link])
private Date birthDate;
@Enumerated([Link])
private UserRole role;
@CreationTimestamp
private LocalDateTime createdAt;
@UpdateTimestamp
private LocalDateTime updatedAt;
5.3 Relationships
One-to-One:
java
@Entity
public class User {
@Id
@GeneratedValue
private Long id;
@OneToOne(cascade = [Link])
@JoinColumn(name = "address_id", referencedColumnName = "id")
private Address address;
}
@Entity
public class Address {
@Id
@GeneratedValue
private Long id;
@OneToOne(mappedBy = "address")
private User user;
}
One-to-Many / Many-to-One:
java
@Entity
public class Department {
@Id
@GeneratedValue
private Long id;
@Entity
public class Employee {
@Id
@GeneratedValue
private Long id;
@ManyToOne
@JoinColumn(name = "department_id")
private Department department;
}
Many-to-Many:
java
@Entity
public class Student {
@Id
@GeneratedValue
private Long id;
@ManyToMany
@JoinTable(
name = "student_course",
joinColumns = @JoinColumn(name = "student_id"),
inverseJoinColumns = @JoinColumn(name = "course_id")
)
private Set<Course> courses;
}
@Entity
public class Course {
@Id
@GeneratedValue
private Long id;
@ManyToMany(mappedBy = "courses")
private Set<Student> students;
}
java
public interface UserRepository extends JpaRepository<User, Long> {
// Query derivation
List<User> findByName(String name);
List<User> findByEmailContaining(String email);
List<User> findByAgeGreaterThan(Integer age);
List<User> findByNameAndEmail(String name, String email);
List<User> findByNameOrEmail(String name, String email);
List<User> findByOrderByNameAsc();
// Custom queries
@Query("SELECT u FROM User u WHERE [Link] = ?1")
Optional<User> findByEmail(String email);
// Native queries
@Query(value = "SELECT * FROM users WHERE email = ?1",
nativeQuery = true)
User findByEmailNative(String email);
// Modifying queries
@Modifying
@Query("UPDATE User u SET [Link] = false WHERE [Link] = ?1")
void deactivateUser(Long id);
// Pagination
Page<User> findByNameContaining(String name, Pageable pageable);
}
java
@Service
public class UserService {
@Transactional
public void transferFunds(Long fromId, Long toId, BigDecimal amount) {
Account from = [Link](fromId)
.orElseThrow();
Account to = [Link](toId)
.orElseThrow();
[Link]([Link]().subtract(amount));
[Link]([Link]().add(amount));
[Link](from);
[Link](to);
}
@Transactional(readOnly = true)
public User getUserById(Long id) {
return [Link](id).orElseThrow();
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void createAuditLog(String action) {
// Always creates new transaction
}
}
Transaction Attributes:
Propagation: REQUIRED, REQUIRES_NEW, NESTED, etc.
Isolation: READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ,
SERIALIZABLE
Timeout: Transaction timeout in seconds
ReadOnly: Optimization for read operations
Rollback: rollbackFor, noRollbackFor
java
// Generates N+1 queries
List<Department> departments = [Link]();
for (Department dept : departments) {
[Link]().size(); // Lazy loading - extra query
}
Solutions:
java
// 1. JOIN FETCH
@Query("SELECT d FROM Department d JOIN FETCH [Link]")
List<Department> findAllWithEmployees();
// 2. @EntityGraph
@EntityGraph(attributePaths = {"employees"})
List<Department> findAll();
// 3. Batch Fetching
@Entity
public class Department {
@OneToMany
@BatchSize(size = 10)
private List<Employee> employees;
}
5.7 Caching
Enable Caching:
java
@SpringBootApplication
@EnableCaching
public class Application { }
Cache Annotations:
java
@Service
public class UserService {
Cache Providers:
Simple (ConcurrentHashMap)
Caffeine
Redis
EhCache
Hazelcast
5.8 Auditing
Enable JPA Auditing:
java
@Configuration
@EnableJpaAuditing
public class JpaConfig { }
Auditable Entity:
java
@Entity
@EntityListeners([Link])
public class User {
@CreatedDate
private LocalDateTime createdDate;
@LastModifiedDate
private LocalDateTime lastModifiedDate;
@CreatedBy
private String createdBy;
@LastModifiedBy
private String lastModifiedBy;
}
AuditorAware Implementation:
java
@Component
public class AuditorAwareImpl implements AuditorAware<String> {
@Override
public Optional<String> getCurrentAuditor() {
// Get current user from security context
return [Link]("system");
}
}
Microservices:
Multiple independent services
Complex infrastructure
Scale services independently
Technology diversity
Team autonomy
Service Boundaries:
Domain-Driven Design (DDD)
Bounded contexts
Business capabilities
Team structure (Conway's Law)
Asynchronous:
Message Queues - RabbitMQ, Amazon SQS
Decoupling
Load leveling
Retry mechanisms
Event Streaming - Kafka, AWS Kinesis
Event sourcing
Real-time processing
Event-driven architecture
java
@Configuration
public class GatewayConfig {
@Bean
public RouteLocator customRoutes(RouteLocatorBuilder builder) {
return [Link]()
.route("user-service", r -> r
.path("/api/users/**")
.filters(f -> f
.stripPrefix(1)
.addRequestHeader("X-Gateway", "true"))
.uri("lb://USER-SERVICE"))
.route("order-service", r -> r
.path("/api/orders/**")
.filters(f -> [Link](1))
.uri("lb://ORDER-SERVICE"))
.build();
}
}
Netflix Eureka:
Eureka Server:
java
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication { }
[Link]:
yaml
server:
port: 8761
eureka:
client:
register-with-eureka: false
fetch-registry: false
Eureka Client:
java
@SpringBootApplication
@EnableDiscoveryClient
public class UserServiceApplication { }
[Link]:
yaml
spring:
application:
name: user-service
eureka:
client:
service-url:
defaultZone: [Link]
instance:
prefer-ip-address: true
java
@Configuration
public class LoadBalancerConfig {
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
@Service
public class OrderService {
@Autowired
private RestTemplate restTemplate;
java
@FeignClient(name = "user-service")
public interface UserClient {
@GetMapping("/users/{id}")
User getUserById(@PathVariable Long id);
@PostMapping("/users")
User createUser(@RequestBody User user);
}
@Service
public class OrderService {
@Autowired
private UserClient userClient;
States:
Closed: Normal operation
Open: Service down, reject requests
Half-Open: Test if service recovered
Implementation:
java
@Service
public class UserService {
Configuration:
yaml
resilience4j:
circuitbreaker:
instances:
userService:
sliding-window-size: 10
failure-rate-threshold: 50
wait-duration-in-open-state: 10000
permitted-number-of-calls-in-half-open-state: 3
xml
<dependency>
<groupId>[Link]</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<dependency>
<groupId>[Link]</groupId>
<artifactId>spring-cloud-sleuth-zipkin</artifactId>
</dependency>
Configuration:
yaml
spring:
sleuth:
sampler:
probability: 1.0 # Sample 100% of requests
zipkin:
base-url: [Link]
Features:
Trace ID: Unique ID across all services
Span ID: ID for each operation
Correlation: Track request flow
Performance analysis
java
@SpringBootApplication
@EnableConfigServer
public class ConfigServerApplication { }
[Link]:
yaml
server:
port: 8888
spring:
cloud:
config:
server:
git:
uri: [Link]
default-label: main
Config Client:
yaml
spring:
application:
name: user-service
config:
import: optional:configserver:[Link]
Refresh Configuration:
@RefreshScope annotation
POST to /actuator/refresh
Spring Cloud Bus for broadcasting
Saga Pattern:
java
// Orchestration-based Saga
@Service
public class OrderSagaOrchestrator {
} catch (Exception e) {
// Compensating transactions
[Link]([Link]());
[Link]([Link]());
throw new OrderCreationException(e);
}
}
}
Repository Structure:
config-repo/
├── [Link] (default)
├── [Link]
├── [Link]
├── [Link]
└── [Link]
Property Hierarchy:
1. [Link] (lowest priority)
2. application-{profile}.yml
3. {service-name}.yml
4. {service-name}-{profile}.yml (highest priority)
Route Configuration:
yaml
spring:
cloud:
gateway:
routes:
- id: user-service
uri: lb://USER-SERVICE
predicates:
- Path=/api/users/**
filters:
- StripPrefix=1
- AddRequestHeader=X-Request-From, Gateway
- id: order-service
uri: lb://ORDER-SERVICE
predicates:
- Path=/api/orders/**
- Method=GET,POST
filters:
- StripPrefix=1
- name: CircuitBreaker
args:
name: orderServiceCB
fallbackUri: forward:/fallback/orders
Custom Filter:
java
@Component
public class CustomFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange,
GatewayFilterChain chain) {
ServerHttpRequest request = [Link]();
[Link]("Request: " + [Link]());
@Override
public int getOrder() {
return -1; // Highest priority
}
}
Producer:
java
@Service
public class OrderEventProducer {
@Autowired
private StreamBridge streamBridge;
java
@Component
public class OrderEventConsumer {
@Bean
public Consumer<OrderEvent> processOrder() {
return event -> {
[Link]("Received: " + event);
// Process order event
};
}
}
Configuration:
yaml
spring:
cloud:
stream:
bindings:
processOrder-in-0:
destination: orders
group: order-service
order-out-0:
destination: orders
kafka:
binder:
brokers: localhost:9092
Setup:
yaml
spring:
rabbitmq:
host: localhost
port: 5672
bash
java
@Configuration
public class FeignConfig {
@Bean
public RequestInterceptor requestInterceptor() {
return template -> {
[Link]("X-Custom-Header", "value");
};
}
@Bean
public [Link] feignLoggerLevel() {
return [Link];
}
}
Error Decoder:
java
public class CustomErrorDecoder implements ErrorDecoder {
@Override
public Exception decode(String methodKey, Response response) {
if ([Link]() == 404) {
return new ResourceNotFoundException("Resource not found");
}
return new Exception("Generic error");
}
}
Fallback:
java
@Component
public class UserClientFallback implements UserClient {
@Override
public User getUser(Long id) {
return new User(id, "Default", "default@[Link]");
}
}
java
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http)
throws Exception {
http
.csrf().disable()
.authorizeHttpRequests(auth -> auth
.requestMatchers("/public/**").permitAll()
.requestMatchers("/admin/**").hasRole("ADMIN")
.requestMatchers("/api/**").authenticated()
.anyRequest().authenticated()
)
.httpBasic()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll();
return [Link]();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
java
@Service
public class CustomUserDetailsService implements UserDetailsService {
@Autowired
private UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
return [Link]
.builder()
.username([Link]())
.password([Link]())
.roles([Link]().toArray(new String[0]))
.build();
}
}
JWT Utility:
java
@Component
public class JwtUtil {
JWT Filter:
java
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
@Autowired
private JwtUtil jwtUtil;
@Autowired
private CustomUserDetailsService userDetailsService;
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain)
throws ServletException, IOException {
UserDetails userDetails =
[Link](username);
if ([Link](token, userDetails)) {
UsernamePasswordAuthenticationToken authToken =
new UsernamePasswordAuthenticationToken(
userDetails, null, [Link]());
[Link](
new WebAuthenticationDetailsSource()
.buildDetails(request));
[Link]()
.setAuthentication(authToken);
}
}
}
[Link](request, response);
}
}
Authentication Controller:
java
@RestController
@RequestMapping("/api/auth")
public class AuthController {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private JwtUtil jwtUtil;
@Autowired
private CustomUserDetailsService userDetailsService;
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody LoginRequest request) {
try {
[Link](
new UsernamePasswordAuthenticationToken(
[Link](),
[Link]()
)
);
} catch (BadCredentialsException e) {
throw new BadCredentialsException("Invalid credentials");
}
UserDetails userDetails =
[Link]([Link]());
String token = [Link](userDetails);
yaml
spring:
security:
oauth2:
client:
registration:
google:
client-id: your-client-id
client-secret: your-client-secret
scope: profile, email
github:
client-id: your-client-id
client-secret: your-client-secret
Security Config:
java
@Configuration
public class OAuth2SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http)
throws Exception {
http
.authorizeHttpRequests(auth -> auth
.anyRequest().authenticated()
)
.oauth2Login()
.userInfoEndpoint()
.userService(customOAuth2UserService());
return [Link]();
}
}
java
@Configuration
@EnableGlobalMethodSecurity(
prePostEnabled = true,
securedEnabled = true,
jsr250Enabled = true
)
public class MethodSecurityConfig { }
Annotations:
java
@Service
public class UserService {
@PreAuthorize("hasRole('ADMIN')")
public void deleteUser(Long id) {
[Link](id);
}
@PreAuthorize("hasAuthority('WRITE_PRIVILEGE')")
public User updateUser(User user) {
return [Link](user);
}
@PreAuthorize("#username == [Link]")
public User getUser(String username) {
return [Link](username);
}
@PostAuthorize("[Link] == [Link]")
public Document getDocument(Long id) {
return [Link](id);
}
@Secured({"ROLE_ADMIN", "ROLE_MANAGER"})
public void approveDocument(Long id) {
// Logic
}
}
java
@Configuration
public class CorsConfig {
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
[Link](
[Link]("[Link] "[Link]
[Link](
[Link]("GET", "POST", "PUT", "DELETE", "OPTIONS"));
[Link]([Link]("*"));
[Link](true);
UrlBasedCorsConfigurationSource source =
new UrlBasedCorsConfigurationSource();
[Link]("/**", configuration);
return source;
}
}
java
[Link]()
.csrfTokenRepository([Link]());
java
xml
<dependency>
<groupId>[Link]</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
</dependency>
Configuration:
yaml
spring:
ai:
openai:
api-key: ${OPENAI_API_KEY}
chat:
options:
model: gpt-4
temperature: 0.7
java
@Service
public class AiChatService {
@Autowired
private ChatClient chatClient;
Streaming Response:
java
java
@Service
public class PromptService {
Include:
- Key features
- Benefits
- Target audience
return [Link]([Link]())
.getResult().getOutput().getContent();
}
}
System Messages:
java
java
@Service
public class EmbeddingService {
@Autowired
private EmbeddingClient embeddingClient;
java
@Configuration
public class VectorStoreConfig {
@Bean
public VectorStore vectorStore(EmbeddingClient embeddingClient) {
return new SimpleVectorStore(embeddingClient);
}
}
@Service
public class DocumentService {
@Autowired
private VectorStore vectorStore;
Implementation:
java
@Service
public class RagService {
@Autowired
private VectorStore vectorStore;
@Autowired
private ChatClient chatClient;
Question: %s
// 4. Get AI response
return [Link](new Prompt(promptText))
.getResult().getOutput().getContent();
}
}
java
@Configuration
public class FunctionConfig {
@Bean
@Description("Get current weather for a location")
public Function<WeatherRequest, WeatherResponse> getWeather() {
return request -> {
// Call weather API
return new WeatherResponse(
[Link](),
72,
"Sunny"
);
};
}
}
Use Functions:
java
@Service
public class FunctionCallingService {
@Autowired
private ChatClient chatClient;
return [Link]().getOutput().getContent();
}
}
9.8 Document Processing
Load and Process Documents:
java
@Service
public class DocumentProcessor {
@Autowired
private VectorStore vectorStore;
java
public record Product(String name, String category, double price) {}
@Service
public class OutputParserService {
@Autowired
private ChatClient chatClient;
%s
""".formatted(text, [Link]());
return [Link](
[Link]().getOutput().getContent()
);
}
}
java
@RestController
@RequestMapping("/api/ai")
public class AiController {
@Autowired
private AiChatService chatService;
@Autowired
private RagService ragService;
@PostMapping("/chat")
public ResponseEntity<String> chat(@RequestBody ChatRequest request) {
String response = [Link]([Link]());
return [Link](response);
}
@PostMapping("/chat/stream")
public Flux<String> chatStream(@RequestBody ChatRequest request) {
return [Link]([Link]());
}
@PostMapping("/ask")
public ResponseEntity<String> askQuestion(
@RequestBody QuestionRequest request) {
String answer = [Link]([Link]());
return [Link](answer);
}
@PostMapping("/documents")
public ResponseEntity<Void> uploadDocument(
@RequestParam("file") MultipartFile file) {
// Process and store document
return [Link]().build();
}
}
@SpringBootTest
public class UserServiceTest {
@Mock
private UserRepository userRepository;
@InjectMocks
private UserService userService;
@BeforeEach
void setUp() {
[Link](this);
}
@Test
void testGetUserById_Success() {
// Arrange
User user = new User(1L, "John", "john@[Link]");
when([Link](1L))
.thenReturn([Link](user));
// Act
User result = [Link](1L);
// Assert
assertNotNull(result);
assertEquals("John", [Link]());
verify(userRepository, times(1)).findById(1L);
}
@Test
void testGetUserById_NotFound() {
// Arrange
when([Link](999L))
.thenReturn([Link]());
java
@DataJpaTest
public class UserRepositoryTest {
@Autowired
private UserRepository userRepository;
@Autowired
private TestEntityManager entityManager;
@Test
void testFindByEmail() {
// Arrange
User user = new User("John", "john@[Link]");
[Link](user);
[Link]();
// Act
Optional<User> found = [Link]("john@[Link]");
// Assert
assertTrue([Link]());
assertEquals("John", [Link]().getName());
}
}
Controller Tests:
java
@WebMvcTest([Link])
public class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private UserService userService;
@Test
void testGetAllUsers() throws Exception {
// Arrange
List<User> users = [Link](
new User(1L, "John", "john@[Link]"),
new User(2L, "Jane", "jane@[Link]")
);
when([Link]()).thenReturn(users);
@Test
void testCreateUser() throws Exception {
// Arrange
User user = new User(null, "John", "john@[Link]");
User saved = new User(1L, "John", "john@[Link]");
when([Link](any([Link]))).thenReturn(saved);
@SpringBootTest
@Testcontainers
public class UserServiceIntegrationTest {
@Container
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>(
"postgres:15-alpine"
);
@DynamicPropertySource
static void configureProperties(DynamicPropertyRegistry registry) {
[Link]("[Link]", postgres::getJdbcUrl);
[Link]("[Link]", postgres::getUsername);
[Link]("[Link]", postgres::getPassword);
}
@Autowired
private UserService userService;
@Test
void testUserCreation() {
User user = new User("John", "john@[Link]");
User saved = [Link](user);
assertNotNull([Link]());
assertEquals("John", [Link]());
}
}
Project Structure:
[Link]/
├── controller/
├── service/
│ ├── impl/
├── repository/
├── model/
│ ├── entity/
│ ├── dto/
├── exception/
├── config/
├── security/
└── util/
java
// Mapper
@Component
public class UserMapper {
public UserDTO toDTO(User user) {
return new UserDTO([Link](), [Link]());
}
}
Error Handling:
java
// Custom exception hierarchy
public class BusinessException extends RuntimeException {
private final ErrorCode errorCode;
}
Logging:
java
@Service
@Slf4j
public class UserService {
try {
User user = [Link](id)
.orElseThrow(() -> new ResourceNotFoundException("User not found"));
[Link]("Successfully retrieved user: {}", id);
return user;
} catch (Exception e) {
[Link]("Error fetching user: {}", id, e);
throw e;
}
}
}
Performance Optimization:
Use pagination for large datasets
Implement caching strategically
Optimize database queries (avoid N+1)
Use connection pooling
Implement rate limiting
Use async processing for heavy tasks
11. DEPLOYMENT & DEVOPS {#deployment-devops}
11.1 Docker
Dockerfile:
dockerfile
FROM openjdk:17-jdk-slim
WORKDIR /app
COPY target/[Link] [Link]
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "[Link]"]
Multi-stage Build:
dockerfile
# Build stage
FROM maven:3.8-openjdk-17 AS build
WORKDIR /app
COPY [Link] .
COPY src ./src
RUN mvn clean package -DskipTests
# Run stage
FROM openjdk:17-jdk-slim
WORKDIR /app
COPY --from=build /app/target/[Link] [Link]
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "[Link]"]
Docker Compose:
yaml
version: '3.8'
services:
app:
build: .
ports:
- "8080:8080"
environment:
- SPRING_DATASOURCE_URL=jdbc:postgresql://db:5432/mydb
- SPRING_DATASOURCE_USERNAME=postgres
- SPRING_DATASOURCE_PASSWORD=secret
depends_on:
- db
- redis
db:
image: postgres:15-alpine
environment:
- POSTGRES_DB=mydb
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=secret
volumes:
- postgres-data:/var/lib/postgresql/data
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
postgres-data:
11.2 Kubernetes
Deployment:
yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
replicas: 3
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: myapp:1.0.0
ports:
- containerPort: 8080
env:
- name: SPRING_PROFILES_ACTIVE
value: "prod"
livenessProbe:
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
Service:
yaml
apiVersion: v1
kind: Service
metadata:
name: myapp-service
spec:
selector:
app: myapp
ports:
- protocol: TCP
port: 80
targetPort: 8080
type: LoadBalancer
ConfigMap:
yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: myapp-config
data:
[Link]: |
[Link]=8080
[Link]=jdbc:postgresql://postgres:5432/mydb
yaml
name: CI/CD Pipeline
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
yaml
# [Link]
management:
endpoints:
web:
exposure:
include: prometheus,health,info,metrics
metrics:
export:
prometheus:
enabled: true
xml
<root level="INFO">
<appender-ref ref="JSON"/>
</root>
</configuration>
ADDITIONAL RESOURCES
Recommended Learning Path
1. Java Fundamentals (if needed)
2. Spring Core - IoC, DI, AOP
3. Spring Boot - Auto-configuration, Starters
4. REST APIs - Controllers, DTOs, Validation
5. Database - JPA, Hibernate, Transactions
6. Testing - Unit, Integration tests
7. Microservices - Architecture, patterns
8. Spring Cloud - Gateway, Config, Discovery
9. Security - Authentication, Authorization, JWT
10. Spring AI - Chat, Embeddings, RAG
11. DevOps - Docker, Kubernetes, CI/CD
Practice Projects
1. Blog API - CRUD, authentication, comments
2. E-commerce - Products, cart, orders, payments
3. Task Management - Projects, tasks, assignments
4. Social Media - Posts, likes, follows, feeds
5. Chat Application - Real-time messaging, WebSocket
6. AI Chatbot - Spring AI, RAG, document search
Key Takeaways
Spring Boot simplifies Spring development
Microservices enable scalability and flexibility
Proper testing ensures reliability
Security is not optional
Spring AI makes AI integration straightforward
DevOps practices are essential for production
QUICK REFERENCE
Common Annotations
@SpringBootApplication - Main application class
@RestController - REST controller
@Service - Service layer
@Repository - Data access layer
@Component - Generic component
@Autowired - Dependency injection
@GetMapping - HTTP GET endpoint
@PostMapping - HTTP POST endpoint
@PutMapping - HTTP PUT endpoint
@DeleteMapping - HTTP DELETE endpoint
@PathVariable - Extract path variable
@RequestParam - Extract query parameter
@RequestBody - Parse request body
@Valid - Enable validation
@Transactional - Transaction boundary
@Cacheable - Enable caching
@Async - Asynchronous execution
Useful Commands
bash
# Build project
mvn clean package
# Run application
mvn spring-boot:run
# Run tests
mvn test
# Docker Compose
docker-compose up -d
# Kubernetes
kubectl apply -f [Link]
kubectl get pods
kubectl logs <pod-name>
End of Notes