Getting Started with Spring Boot
To develop a web application using Spring Boot that returns "hello world" when accessing /hello, follow these steps:
- Create a Spring Boot project with web dependencies
- Define a controller class with a method and appropriate annotations
- Run and test the application
Request Handler Implementation
package com.example.demo.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class GreetingController {
@RequestMapping("/greeting")
public String greet() {
System.out.println("Hello from Spring Boot");
return "hello world";
}
}
Parameter Handling
Simple Parameters
When parameter names match method parameter names, they are automatically mapped and type conversion is performed:
@RequestMapping("/simple-params")
public String processSimpleParams(String name, int age) {
System.out.println("Received parameters: " + name + ", " + age);
return "Parameters received successfully";
}
If parameter names don't match, use @RequestParam for mapping:
@RequestMapping("/mapped-params")
public String processMappedParams(@RequestParam(name="username") String name, Integer age) {
System.out.println("Mapped parameters: " + name + ", " + age);
return "Mapped parameters: " + name + ", " + age;
}
Entity Parameters
For simple POJOs, request parameters with matching property names are automatically mapped:
@RequestMapping("/entity-param")
public String processEntityParam(User user) {
System.out.println(user);
return "Entity processed successfully";
}
public class User {
private String name;
private Integer age;
// Getters and setters
}
Complex Entity Objects
Nested POJOs can be handled using dot notation in parameter names:
@RequestMapping("/complex-entity")
public String processComplexEntity(User user) {
System.out.println(user);
return "Complex entity processed: " + user;
}
public class User {
private String name;
private Integer age;
private Address address;
// Getters and setters
}
public class Address {
private String province;
private String city;
// Getters and setters
}
Array and Collection Parameters
For arrays, parameter names must match and multiple values should be provided:
@RequestMapping("/array-param")
public String processArrayParam(String[] hobbies) {
for (String hobby : hobbies) {
System.out.print(hobby + " ");
}
return "Array processed";
}
For collections, @RequestParam is required:
@RequestMapping("/collection-param")
public String processCollectionParam(@RequestParam List<String> interests) {
for (String interest : interests) {
System.out.print(interest + " ");
}
return "Collection processed";
}
Date Parameters
Use @DateTimeFormat for date parameter conversion:
@RequestMapping("/date-param")
public String processDateParam(@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") LocalDateTime timestamp) {
System.out.println("Received timestamp: " + timestamp);
return "Date processed: " + timestamp;
}
JSON Parameters
For JSON data, use @RequestBody to map to POJOs:
@RequestMapping("/-param")
public String processJsonParam(@RequestBody User user) {
System.out.println(user);
return user.toString();
}
Path Parameters
Extract parameters directly from the URL path using @PathVariable:
@RequestMapping("/path/{id}")
public String processPathParam(@PathVariable Integer id) {
System.out.println("ID received: " + id);
return "Processed ID: " + id;
}
Multiple parameters:
@RequestMapping("/path/{userId}/{userName}")
public String processMultiplePathParams(@PathVariable Integer userId, @PathVariable String userName) {
System.out.println("User ID: " + userId + ", Name: " + userName);
return "Processed user: ID=" + userId + ", Name=" + userName;
}
Response Handling
@ResponseBody Annotation
The @ResponseBody annotation (included in @RestController) converts return values to JSON responses:
@RequestMapping("/users")
public List<User> getAllUsers() {
User user1 = new User("Alice", 25, new Address("CA", "San Francisco"));
User user2 = new User("Bob", 30, new Address("NY", "New York"));
User user3 = new User("Charlie", 28, new Address("TX", "Austin"));
List<User> users = new ArrayList<>();
users.add(user1);
users.add(user2);
users.add(user3);
return users;
}
Unified Response Format
Implement a consistent response structure:
public class ApiResponse {
private Integer status;
private String message;
private Object data;
public ApiResponse() {}
public ApiResponse(Integer status, String message, Object data) {
this.status = status;
this.message = message;
this.data = data;
}
// Getters and setters
public static ApiResponse success(Object data) {
return new ApiResponse(200, "Success", data);
}
public static ApiResponse success() {
return new ApiResponse(200, "Success", null);
}
public static ApiResponse error(String message) {
return new ApiResponse(500, message, null);
}
}
Layered Architecture
Three-Layer Architecture
- Controller Layer: Handles HTTP requests, processes input, and returns responses
- Service Layer: Implements business logic and rules
- Data Access Layer (DAO): Manages database operations and persistence
Inversion of Control (IOC) and Dependency Injection
IOC transfers object creation control from the program to an external container. Dependency Injection provides resources to the application at runtime.
Bean Management
Declare beans using annotations:
| Annotation | Description | Usage |
|---|---|---|
| @Component | Basic annotation for declaring beans | General purpose components |
| @Controller | @Component derivative for controllers | Web controller classes |
| @Service | @Component derivative for services | Business logic classes |
| @Repository | @Component derivative for data access | Data access classes |
Dependency Injection
Use @Autowired for dependency injection:
@RestController
public class UserController {
@Autowired
private UserService userService;
@RequestMapping("/users")
public ApiResponse getAllUsers() {
List<User> users = userService.retrieveUsers();
return ApiResponse.success(users);
}
}
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
public List<User> retrieveUsers() {
List<User> users = userDao.fetchUsers();
// Additional business logic
return users;
}
}
@Repository
public class UserDaoImpl implements UserDao {
@Override
public List<User> fetchUsers() {
// Data access logic
return Arrays.asList(
new User("Alice", 25, new Address("CA", "San Francisco")),
new User("Bob", 30, new Address("NY", "New York"))
);
}
}
HTTP Request Handling
GET Requests
Implement GET requests using @RequestMapping or @GetMapping:
@RestController
public class DepartmentController {
// Using @RequestMapping
@RequestMapping(value = "/departments", method = RequestMethod.GET)
public ApiResponse getDepartments() {
// Implementation
return ApiResponse.success();
}
// Using @GetMapping
@GetMapping("/departments")
public ApiResponse listDepartments() {
// Implementation
return ApiResponse.success();
}
}
Spring Boot Configuration
Configuration Options
Spring Boot supports multiple configuration formats:
application.properties
server.port=8080
server.address=127.0.0.1
application.yml
server:
port: 8080
address: 127.0.0.1
YAML Syntax Rules
- Case-sensitive
- Values must be separated by spaces
- Use spaces for indentation (no tabs)
- Same-level elements must be left-aligned
- # indicates comments
Configuration Properties
Inject configuration values directly into class properties:
@Data
@Component
@ConfigurationProperties(prefix="storage.cloud")
public class CloudStorageConfig {
private String endpoint;
private String accessKey;
private String secretKey;
private String bucketName;
}
@Value vs @ConfigurationProperties
- @Value: Injects individual configuration values
- @ConfigurationProperties: Batches multiple configuration values into a bean
Session Management
Session Tracking Technologies
Cookies
@GetMapping("/set-cookie")
public ApiResponse setCookie(HttpServletResponse response) {
response.addCookie(new Cookie("user_token", "abc123"));
return ApiResponse.success();
}
@GetMapping("/get-cookie")
public ApiResponse getCookie(HttpServletRequest request) {
Cookie[] cookies = request.getCookies();
String tokenValue = "";
for (Cookie cookie : cookies) {
if ("user_token".equals(cookie.getName())) {
tokenValue = cookie.getValue();
}
}
return ApiResponse.success(tokenValue);
}
HTTP Sessions
@GetMapping("/set-session")
public ApiResponse setSession(HttpSession session) {
session.setAttribute("currentUser", "admin");
return ApiResponse.success("Session set");
}
@GetMapping("/get-session")
public ApiResponse getSession(HttpServletRequest request) {
HttpSession session = request.getSession();
String user = (String) session.getAttribute("currentUser");
return ApiResponse.success(user);
}
JWT Tokens
JWT (JSON Web Tokens) provide a secure way to transmit information:
@GetMapping("/generate-token")
public ApiResponse generateToken() {
Map claims = new HashMap<>();
claims.put("userId", 123);
claims.put("username", "testuser");
String jwt = Jwts.builder()
.signWith(SignatureAlgorithm.HS256, "secret-key")
.setClaims(claims)
.setExpiration(new Date(System.currentTimeMillis() + 3600000))
.compact();
return ApiResponse.success(jwt);
}
Exception Handling
Global Exception Handler
Implement a centralized exception handler:
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
public ApiResponse handleException(Exception ex) {
ex.printStackTrace();
return ApiResponse.error("An error occurred: " + ex.getMessage());
}
@ExceptionHandler(ResourceNotFoundException.class)
public ApiResponse handleResourceNotFound(ResourceNotFoundException ex) {
return ApiResponse.error(ex.getMessage());
}
}
Transaction Management
@Transactional Annotation
Use @Transactional to ensure atomic operations:
@Service
public class DepartmentServiceImpl implements DepartmentService {
@Autowired
private DepartmentMapper departmentMapper;
@Autowired
private EmployeeMapper employeeMapper;
@Transactional
@Override
public void deleteDepartment(Integer id) {
departmentMapper.delete(id);
employeeMapper.deleteByDepartmentId(id);
}
}
Transaction Properties
Rollback Behavior
@Transactional(rollbackFor = Exception.class)
public void performOperation() {
// Operations that should rollback on any exception
}
Propagation Behavior
| Value | Description |
|---|---|
| REQUIRED | Requires transaction, joins if exists |
| REQUIRES_NEW | Creates new transaction regardless |
| SUPPORTS | Supports transaction, runs without if none |
| NOT_SUPPORTED | Does not support transaction, suspends if exists |
Aspect-Oriented Programming (AOP)
AOP Fundamentals
AOP allows cross-cutting concerns to be modularized:
Implementing AOP
@Component
@Aspect
@Slf4j
public class ExecutionTimeAspect {
@Around("execution(* com.example.demo.service.*.*(..))")
public Object measureExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
Object result = joinPoint.proceed();
long duration = System.currentTimeMillis() - start;
log.info("Method {} executed in {} ms",
joinPoint.getSignature().getName(), duration);
return result;
}
}
Advice Types
- @Around: Advice around the method execution
- @Before: Advice before method execution
- @After: Advice after method execution
- @AfterReturning: Advice after successful method execution
- @AfterThrowing: Advice after method throws exception
Pointcut Expressions
// Method execution in any service package
@Around("execution(* com.example.service.*.*(..))")
// Method execution in specific class
@Around("execution(* com.example.service.OrderService.*(..))")
// Method with specific annotation
@Around("@annotation(com.example.annotation.Loggable)")
Spring Boot Principles
Configuration Priority
Spring Boot configuration sources in priority order:
- Command line arguments
- Java system properties
- application.properties
- application.yml
- application.yaml
Bean Scopes
| Scope | Description |
|---|---|
| singleton | Single instance per Spring container (default) |
| prototype | New instance each time requested |
| request | Single instance per HTTP request |
| session | Single instance per HTTP session |
Conditional Configuration
Use @Conditional for conditional bean registration:
@Bean
@ConditionalOnClass(name = "com.example.SomeClass")
public SomeBean someBean() {
return new SomeBean();
}
@Bean
@ConditionalOnMissingBean
public Gson gson(GsonBuilder gsonBuilder) {
return gsonBuilder.create();
}