Java Spring Boot: Building Microservices
Spring Boot has revolutionized Java development by providing a convention-over-configuration approach to building enterprise applications. Let's explore how to create microservices with Spring Boot.
Why Spring Boot?
Spring Boot offers:
- Auto-configuration: Minimal setup required
- Embedded servers: No need for external Tomcat/Jetty
- Production-ready features: Health checks, metrics, monitoring
- Microservices support: Easy to build and deploy independently
Project Setup
Create a new Spring Boot project using Spring Initializr:
# Using Spring CLI
spring init --dependencies=web,jpa,h2,actuator my-microservice
cd my-microservice
Or add dependencies to your pom.xml
:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
Creating a RESTful Service
Let's build a simple Product microservice:
// Product.java - Entity class
@Entity
@Table(name = "products")
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String name;
@Column(nullable = false)
private BigDecimal price;
private String description;
// Constructors, getters, and setters
public Product() {}
public Product(String name, BigDecimal price, String description) {
this.name = name;
this.price = price;
this.description = description;
}
// Getters and setters...
}
Repository Layer
Create a JPA repository:
// ProductRepository.java
@Repository
public interface ProductRepository extends JpaRepository<Product, Long> {
List<Product> findByNameContainingIgnoreCase(String name);
List<Product> findByPriceBetween(BigDecimal minPrice, BigDecimal maxPrice);
@Query("SELECT p FROM Product p WHERE p.price < :maxPrice")
List<Product> findCheapProducts(@Param("maxPrice") BigDecimal maxPrice);
}
Service Layer
Implement business logic:
// ProductService.java
@Service
@Transactional
public class ProductService {
private final ProductRepository productRepository;
public ProductService(ProductRepository productRepository) {
this.productRepository = productRepository;
}
public List<Product> getAllProducts() {
return productRepository.findAll();
}
public Optional<Product> getProductById(Long id) {
return productRepository.findById(id);
}
public Product createProduct(Product product) {
return productRepository.save(product);
}
public Product updateProduct(Long id, Product productDetails) {
return productRepository.findById(id)
.map(product -> {
product.setName(productDetails.getName());
product.setPrice(productDetails.getPrice());
product.setDescription(productDetails.getDescription());
return productRepository.save(product);
})
.orElseThrow(() -> new ProductNotFoundException("Product not found with id: " + id));
}
public void deleteProduct(Long id) {
productRepository.deleteById(id);
}
}
REST Controller
Create RESTful endpoints:
// ProductController.java
@RestController
@RequestMapping("/api/products")
@CrossOrigin(origins = "*")
public class ProductController {
private final ProductService productService;
public ProductController(ProductService productService) {
this.productService = productService;
}
@GetMapping
public ResponseEntity<List<Product>> getAllProducts() {
List<Product> products = productService.getAllProducts();
return ResponseEntity.ok(products);
}
@GetMapping("/{id}")
public ResponseEntity<Product> getProductById(@PathVariable Long id) {
return productService.getProductById(id)
.map(product -> ResponseEntity.ok(product))
.orElse(ResponseEntity.notFound().build());
}
@PostMapping
public ResponseEntity<Product> createProduct(@Valid @RequestBody Product product) {
Product savedProduct = productService.createProduct(product);
return ResponseEntity.status(HttpStatus.CREATED).body(savedProduct);
}
@PutMapping("/{id}")
public ResponseEntity<Product> updateProduct(@PathVariable Long id,
@Valid @RequestBody Product product) {
try {
Product updatedProduct = productService.updateProduct(id, product);
return ResponseEntity.ok(updatedProduct);
} catch (ProductNotFoundException e) {
return ResponseEntity.notFound().build();
}
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteProduct(@PathVariable Long id) {
productService.deleteProduct(id);
return ResponseEntity.noContent().build();
}
}
Configuration
Configure your application properties:
# application.properties
server.port=8080
spring.application.name=product-service
# Database configuration
spring.datasource.url=jdbc:h2:mem:productdb
spring.datasource.driver-class-name=org.h2.Driver
spring.h2.console.enabled=true
# JPA configuration
spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.show-sql=true
# Actuator endpoints
management.endpoints.web.exposure.include=health,info,metrics
Exception Handling
Add global exception handling:
// GlobalExceptionHandler.java
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(ProductNotFoundException.class)
public ResponseEntity<ErrorResponse> handleProductNotFound(ProductNotFoundException ex) {
ErrorResponse error = new ErrorResponse("PRODUCT_NOT_FOUND", ex.getMessage());
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ErrorResponse> handleValidationException(MethodArgumentNotValidException ex) {
ErrorResponse error = new ErrorResponse("VALIDATION_ERROR", "Invalid input data");
return ResponseEntity.badRequest().body(error);
}
}
Running the Application
Start your Spring Boot application:
// Application.java
@SpringBootApplication
public class ProductServiceApplication {
public static void main(String[] args) {
SpringApplication.run(ProductServiceApplication.class, args);
}
}
Testing Your Microservice
Test your endpoints:
# Get all products
curl http://localhost:8080/api/products
# Create a product
curl -X POST http://localhost:8080/api/products \
-H "Content-Type: application/json" \
-d '{"name":"Laptop","price":999.99,"description":"Gaming laptop"}'
# Health check
curl http://localhost:8080/actuator/health
Next Steps
To build production-ready microservices, consider:
- Service Discovery: Eureka, Consul
- API Gateway: Spring Cloud Gateway
- Configuration Management: Spring Cloud Config
- Circuit Breakers: Hystrix, Resilience4j
- Distributed Tracing: Zipkin, Jaeger
- Containerization: Docker, Kubernetes
Spring Boot provides an excellent foundation for building scalable, maintainable microservices!