Spring Boot
Javaエンタープライズアプリケーション開発の事実上の標準フレームワーク。自動設定と豊富なエコシステムにより高速開発を実現。
GitHub概要
spring-projects/spring-boot
Spring Boot helps you to create Spring-powered, production-grade applications and services with absolute minimum fuss.
スター78,081
ウォッチ3,343
フォーク41,398
作成日:2012年10月19日
言語:Java
ライセンス:Apache License 2.0
トピックス
frameworkjavaspringspring-boot
スター履歴
データ取得日時: 2025/8/13 01:43
フレームワーク
Spring Boot
概要
Spring Bootは、Javaエコシステムにおける最も人気の高いWebアプリケーションフレームワークです。
詳細
Spring Boot(スプリング ブート)は、Spring Frameworkをベースにした、迅速かつ簡単にスタンドアロンで本番級のSpringベースアプリケーションを作成するためのフレームワークです。2014年にリリースされて以来、Javaのデファクトスタンダードとして確固たる地位を築いています。自動設定(Auto-configuration)機能により、従来のSpringで必要だった複雑なXML設定を大幅に削減し、最小限のコードでWebアプリケーションを構築できます。組み込まれたTomcatやJettyサーバーにより、単一のJARファイルとして実行可能なアプリケーションを作成でき、デプロイメントが簡素化されています。依存性注入(DI)とアスペクト指向プログラミング(AOP)により、保守性と拡張性の高いアプリケーション開発が可能です。現在ではマイクロサービスアーキテクチャやクラウドネイティブアプリケーション開発の分野で広く使用されており、Spring Cloud、Spring Securityなどの豊富なエコシステムと組み合わせて企業システムの構築に活用されています。
メリット・デメリット
メリット
- 自動設定: 複雑な設定を自動化し、開発効率を大幅に向上
- 組み込みサーバー: TomcatやJettyが内蔵されており、簡単にWebアプリケーションを実行
- スターター依存関係: 必要な機能をまとめてパッケージ化し、依存関係管理を簡素化
- 本番対応機能: Actuatorによる監視・管理機能で運用を支援
- 豊富なエコシステム: Spring Cloud、Spring Securityなど多彩な関連技術
- マイクロサービス対応: 軽量で高速起動のためマイクロサービスに最適
- 企業サポート: VMware Tanzuによる長期サポートと継続的開発
- 高い市場価値: 企業システムでの需要が高く、平均単価67万円(2024年調査)
デメリット
- 学習コスト: Springエコシステム全体の理解が必要で習得に時間が必要
- メモリ使用量: フレームワークのオーバーヘッドでメモリ消費が多い傾向
- 起動時間: DIコンテナ初期化やAOP処理により起動時間が長くなることがある
- 過剰な機能: シンプルなアプリケーションには機能が過多になる場合がある
- ブラックボックス化: 自動設定により内部動作が見えにくくなる問題
- Java依存: Java言語とJVMに依存するため他言語では使用不可
主要リンク
- Spring Boot公式サイト
- Spring Boot公式ドキュメント
- Spring Initializr
- Spring Boot GitHub
- Spring Guides
- Spring Boot Actuator
書き方の例
Hello World(基本的なWebアプリケーション)
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@SpringBootApplication
@RestController
public class DemoApplication {
@GetMapping("/")
public String hello() {
return "Hello, Spring Boot!";
}
@GetMapping("/api/status")
public String status() {
return "Application is running!";
}
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
REST API開発
package com.example.demo.controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.http.ResponseEntity;
import java.util.List;
import java.util.ArrayList;
@RestController
@RequestMapping("/api/users")
public class UserController {
private List<User> users = new ArrayList<>();
@GetMapping
public List<User> getAllUsers() {
return users;
}
@GetMapping("/{id}")
public ResponseEntity<User> getUserById(@PathVariable Long id) {
User user = users.stream()
.filter(u -> u.getId().equals(id))
.findFirst()
.orElse(null);
if (user != null) {
return ResponseEntity.ok(user);
} else {
return ResponseEntity.notFound().build();
}
}
@PostMapping
public ResponseEntity<User> createUser(@RequestBody User user) {
user.setId((long) (users.size() + 1));
users.add(user);
return ResponseEntity.ok(user);
}
@PutMapping("/{id}")
public ResponseEntity<User> updateUser(@PathVariable Long id, @RequestBody User updatedUser) {
User user = users.stream()
.filter(u -> u.getId().equals(id))
.findFirst()
.orElse(null);
if (user != null) {
user.setName(updatedUser.getName());
user.setEmail(updatedUser.getEmail());
return ResponseEntity.ok(user);
} else {
return ResponseEntity.notFound().build();
}
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
users.removeIf(u -> u.getId().equals(id));
return ResponseEntity.noContent().build();
}
}
// User エンティティクラス
class User {
private Long id;
private String name;
private String email;
// コンストラクタ、ゲッター、セッター
public User() {}
public User(String name, String email) {
this.name = name;
this.email = email;
}
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
}
データベース操作(Spring Data JPA)
package com.example.demo.entity;
import jakarta.persistence.*;
@Entity
@Table(name = "products")
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String name;
@Column
private String description;
@Column(nullable = false)
private Double price;
// コンストラクタ、ゲッター、セッター
public Product() {}
public Product(String name, String description, Double price) {
this.name = name;
this.description = description;
this.price = price;
}
// ゲッター・セッターは省略
}
// Repository インターフェース
package com.example.demo.repository;
import com.example.demo.entity.Product;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import java.util.List;
public interface ProductRepository extends JpaRepository<Product, Long> {
List<Product> findByNameContaining(String name);
List<Product> findByPriceBetween(Double minPrice, Double maxPrice);
@Query("SELECT p FROM Product p WHERE p.price > :price")
List<Product> findExpensiveProducts(@Param("price") Double price);
}
// Service クラス
package com.example.demo.service;
import com.example.demo.entity.Product;
import com.example.demo.repository.ProductRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;
@Service
public class ProductService {
@Autowired
private ProductRepository productRepository;
public List<Product> getAllProducts() {
return productRepository.findAll();
}
public Optional<Product> getProductById(Long id) {
return productRepository.findById(id);
}
public Product saveProduct(Product product) {
return productRepository.save(product);
}
public void deleteProduct(Long id) {
productRepository.deleteById(id);
}
public List<Product> searchProducts(String keyword) {
return productRepository.findByNameContaining(keyword);
}
}
依存性注入とBean管理
package com.example.demo.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
@Configuration
public class AppConfig {
@Bean
@Primary
public EmailService emailService() {
return new EmailServiceImpl();
}
@Bean
public SmsService smsService() {
return new SmsServiceImpl();
}
}
// サービスインターフェース
package com.example.demo.service;
public interface NotificationService {
void sendNotification(String message, String recipient);
}
// Email実装
package com.example.demo.service.impl;
import com.example.demo.service.NotificationService;
import org.springframework.stereotype.Service;
@Service
public class EmailServiceImpl implements NotificationService {
@Override
public void sendNotification(String message, String recipient) {
System.out.println("Email sent to " + recipient + ": " + message);
}
}
// SMS実装
@Service
public class SmsServiceImpl implements NotificationService {
@Override
public void sendNotification(String message, String recipient) {
System.out.println("SMS sent to " + recipient + ": " + message);
}
}
// 依存性注入の使用例
package com.example.demo.controller;
import com.example.demo.service.NotificationService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/notifications")
public class NotificationController {
@Autowired
private NotificationService emailService; // @Primaryが注入される
@Autowired
@Qualifier("smsServiceImpl")
private NotificationService smsService;
@PostMapping("/email")
public String sendEmail(@RequestParam String message, @RequestParam String recipient) {
emailService.sendNotification(message, recipient);
return "Email notification sent";
}
@PostMapping("/sms")
public String sendSms(@RequestParam String message, @RequestParam String recipient) {
smsService.sendNotification(message, recipient);
return "SMS notification sent";
}
}
アノテーションベース設定
package com.example.demo.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConfigurationProperties(prefix = "app")
public class ApplicationProperties {
private String name;
private String version;
private Database database = new Database();
// ゲッター・セッター
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getVersion() { return version; }
public void setVersion(String version) { this.version = version; }
public Database getDatabase() { return database; }
public void setDatabase(Database database) { this.database = database; }
public static class Database {
private String host;
private int port;
private String name;
// ゲッター・セッター
public String getHost() { return host; }
public void setHost(String host) { this.host = host; }
public int getPort() { return port; }
public void setPort(int port) { this.port = port; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
}
}
// application.properties
/*
app.name=My Spring Boot App
app.version=1.0.0
app.database.host=localhost
app.database.port=5432
app.database.name=mydb
*/
// 設定値を使用するコンポーネント
package com.example.demo.component;
import com.example.demo.config.ApplicationProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import jakarta.annotation.PostConstruct;
@Component
public class AppInfoComponent {
@Autowired
private ApplicationProperties appProperties;
@PostConstruct
public void printAppInfo() {
System.out.println("Application Name: " + appProperties.getName());
System.out.println("Version: " + appProperties.getVersion());
System.out.println("Database: " + appProperties.getDatabase().getHost() +
":" + appProperties.getDatabase().getPort());
}
}
テスト(JUnitとMockito)
package com.example.demo.controller;
import com.example.demo.service.ProductService;
import com.example.demo.entity.Product;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import java.util.Arrays;
import java.util.Optional;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
@WebMvcTest(ProductController.class)
public class ProductControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private ProductService productService;
@Autowired
private ObjectMapper objectMapper;
@Test
public void testGetAllProducts() throws Exception {
// Given
Product product1 = new Product("Product 1", "Description 1", 100.0);
Product product2 = new Product("Product 2", "Description 2", 200.0);
when(productService.getAllProducts()).thenReturn(Arrays.asList(product1, product2));
// When & Then
mockMvc.perform(get("/api/products"))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(jsonPath("$").isArray())
.andExpect(jsonPath("$.length()").value(2))
.andExpect(jsonPath("$[0].name").value("Product 1"))
.andExpect(jsonPath("$[1].name").value("Product 2"));
verify(productService).getAllProducts();
}
@Test
public void testGetProductById() throws Exception {
// Given
Product product = new Product("Test Product", "Test Description", 150.0);
when(productService.getProductById(1L)).thenReturn(Optional.of(product));
// When & Then
mockMvc.perform(get("/api/products/1"))
.andExpect(status().isOk())
.andExpected(jsonPath("$.name").value("Test Product"))
.andExpected(jsonPath("$.price").value(150.0));
verify(productService).getProductById(1L);
}
@Test
public void testCreateProduct() throws Exception {
// Given
Product newProduct = new Product("New Product", "New Description", 300.0);
Product savedProduct = new Product("New Product", "New Description", 300.0);
savedProduct.setId(1L);
when(productService.saveProduct(any(Product.class))).thenReturn(savedProduct);
// When & Then
mockMvc.perform(post("/api/products")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(newProduct)))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.id").value(1))
.andExpect(jsonPath("$.name").value("New Product"));
verify(productService).saveProduct(any(Product.class));
}
}
// 統合テスト例
package com.example.demo;
import com.example.demo.entity.Product;
import com.example.demo.repository.ProductRepository;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.test.web.server.LocalServerPort;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.TestPropertySource;
import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@TestPropertySource(locations = "classpath:application-test.properties")
public class ProductIntegrationTest {
@LocalServerPort
private int port;
@Autowired
private TestRestTemplate restTemplate;
@Autowired
private ProductRepository productRepository;
@Test
public void testCreateAndRetrieveProduct() {
// Given
Product product = new Product("Integration Test Product", "Test Description", 99.99);
// When - Create product
ResponseEntity<Product> createResponse = restTemplate.postForEntity(
"http://localhost:" + port + "/api/products",
product,
Product.class
);
// Then - Verify creation
assertThat(createResponse.getStatusCode()).isEqualTo(HttpStatus.CREATED);
assertThat(createResponse.getBody().getName()).isEqualTo("Integration Test Product");
// When - Retrieve product
Long productId = createResponse.getBody().getId();
ResponseEntity<Product> getResponse = restTemplate.getForEntity(
"http://localhost:" + port + "/api/products/" + productId,
Product.class
);
// Then - Verify retrieval
assertThat(getResponse.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(getResponse.getBody().getName()).isEqualTo("Integration Test Product");
}
}