Spring Bootとは?初心者でもわかる基礎知識
Spring Bootは、本格的な Spring アプリケーションを素早く効率的に開発するためのフレームワークです。従来のSpring Frameworkの複雑な設定を大幅に簡略化し、「設定より規約」(Convention over Configuration)の原則に基づいて、開発者が本質的なビジネスロジックに集中できる環境を提供します。
従来のSpring Frameworkとの決定的な違い
Spring FrameworkとSpring Bootの主な違いは以下の通りです:
項目 | Spring Framework | Spring Boot |
---|---|---|
設定方法 | XML設定やJava設定が必要 | 自動設定が基本(設定不要) |
アプリケーション起動 | 外部のアプリケーションサーバーが必要 | 組み込みサーバー(Tomcatなど)を内包 |
依存関係管理 | 手動での設定が必要 | スターターPOMによる自動管理 |
デプロイ | WAR形式が基本 | 実行可能なJARファイルとして配布可能 |
開発速度 | 設定に時間がかかる | 最小限の設定で素早く開発可能 |
従来のSpring Frameworkでは、以下のような設定が必要でした:
// 従来のSpring Framework設定例 @Configuration public class WebConfig { @Bean public ViewResolver viewResolver() { InternalResourceViewResolver resolver = new InternalResourceViewResolver(); resolver.setPrefix("/WEB-INF/views/"); resolver.setSuffix(".jsp"); return resolver; } @Bean public DataSource dataSource() { DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setDriverClassName("com.mysql.jdbc.Driver"); dataSource.setUrl("jdbc:mysql://localhost:3306/db_name"); dataSource.setUsername("username"); dataSource.setPassword("password"); return dataSource; } }
一方、Spring Bootでは多くの設定が自動化されています:
// Spring Bootでの最小構成 @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
なぜ今Spring Bootが選ばれているのか
- 開発生産性の大幅な向上
- スターターPOMによる依存関係の自動管理
- 自動設定による設定作業の削減
- 組み込みサーバーによる即時実行環境の提供
- マイクロサービスアーキテクチャとの親和性
- 軽量で独立したサービスの開発が容易
- クラウドネイティブアプリケーションの開発に最適
- コンテナ化が容易で、Kubernetesなどとの統合が簡単
- 充実したエコシステム
- 豊富な組み込み機能(セキュリティ、モニタリング等)
- アクチュエーター機能による運用管理の容易さ
- 多様なクラウドプラットフォームとの連携
Spring Bootが解決する3つの開発課題
- 複雑な設定管理の課題
// 従来の複雑なデータベース設定の代わりに spring.datasource.url=jdbc:mysql://localhost:3306/db_name spring.datasource.username=username spring.datasource.password=password
- アプリケーション展開の課題
# 実行可能JARファイルの作成と実行が簡単 ./mvnw package java -jar target/myapplication-0.0.1-SNAPSHOT.jar
- 開発環境の標準化の課題
<!-- 必要な依存関係を一括で管理 --> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.2.0</version> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies>
これらの特徴により、Spring Bootは現代のJava開発における事実上の標準フレームワークとして確立されています。次世代のクラウドネイティブアプリケーション開発において、Spring Bootは不可欠なツールとなっています。
【2024年版】Spring Boot開発環境の構築手順
Spring Bootの開発環境を効率的に構築し、スムーズな開発をスタートするための手順を詳しく解説します。2024年の最新バージョンに対応した環境構築方法を紹介します。
JDKとIDEのインストールとセットアップ
- JDK(Java Development Kit)のインストール
- Spring Boot 3.x系は Java 17以上が必要
- 推奨:Amazon Corretto 17 または Eclipse Temurin 17
# macOSの場合(Homebrewを使用) brew install --cask temurin17 # Windowsの場合 # Eclipse Temurinの公式サイトからインストーラーをダウンロード # https://adoptium.net/
環境変数の設定(Windows):
setx JAVA_HOME "C:\Program Files\Eclipse Adoptium\jdk-17.x.x" setx PATH "%PATH%;%JAVA_HOME%\bin"
- IDEのインストールと設定
推奨IDE:IntelliJ IDEA または Spring Tool Suite(STS)
IntelliJ IDEAの場合:
- Ultimate版:Spring Boot開発に最適な機能が標準搭載
- Community版:追加プラグインで対応可能
Spring Tool Suiteの場合:
# macOSの場合 brew install --cask springtoolsuite # Windowsの場合は公式サイトからダウンロード # https://spring.io/tools
Spring Initializrを使った最速プロジェクト作成
- Webインターフェースの利用
https://start.spring.io/ にアクセスし、以下の項目を設定:
Project: Maven Language: Java Spring Boot: 3.2.x Project Metadata: Group: com.example Artifact: demo Name: demo Description: Demo project for Spring Boot Package name: com.example.demo Packaging: Jar Java: 17
- IDEからの直接利用
IntelliJ IDEAの場合:
1. File → New → Project 2. Spring Initializr を選択 3. 必要な依存関係を追加: - Spring Web - Spring Data JPA - Spring Security(必要な場合) - Lombok - DevTools
- CLIからの利用
# Spring Boot CLIのインストール(macOS) brew tap spring-io/tap brew install spring-boot # プロジェクト作成 spring init --dependencies=web,data-jpa,security my-project
依存関係の効率的な管理方法
- pom.xmlの基本構成
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.2.0</version> </parent> <groupId>com.example</groupId> <artifactId>demo</artifactId> <version>0.0.1-SNAPSHOT</version> <properties> <java.version>17</java.version> <!-- カスタムプロパティの定義 --> <mysql.version>8.0.33</mysql.version> <lombok.version>1.18.30</lombok.version> </properties> <dependencies> <!-- 必要な依存関係を追加 --> </dependencies> </project>
- よく使用される依存関係の追加
<dependencies> <!-- Web アプリケーション用 --> <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>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <!-- テスト用 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
- バージョン管理のベストプラクティス
- spring-boot-starter-parentを使用してバージョン管理を一元化
- 依存関係の衝突を防ぐためのバージョン指定
- BOMを使用した依存関係バージョンの管理
<dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
開発環境構築後のプロジェクト動作確認:
# プロジェクトのビルド ./mvnw clean install # アプリケーションの起動 ./mvnw spring-boot:run
これで基本的な開発環境の構築は完了です。次のステップでは、この環境を使って実際のアプリケーション開発に進むことができます。
Spring Bootによるサンプルアプリケーション開発
実践的なSpring Bootアプリケーション開発の手順を、具体的なコード例と共に解説します。ここでは書籍管理システムを例に、基本的なCRUD操作を実装していきます。
RESTful APIの実装手順と注意点
- エンティティクラスの作成
@Entity @Table(name = "books") @Data @NoArgsConstructor @AllArgsConstructor public class Book { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @NotBlank(message = "タイトルは必須です") private String title; @NotBlank(message = "著者は必須です") private String author; @Column(name = "published_date") private LocalDate publishedDate; @DecimalMin(value = "0.0", message = "価格は0円以上である必要があります") private BigDecimal price; }
- リポジトリの実装
@Repository public interface BookRepository extends JpaRepository<Book, Long> { List<Book> findByAuthor(String author); Optional<Book> findByTitleAndAuthor(String title, String author); // カスタムクエリの例 @Query("SELECT b FROM Book b WHERE b.price <= :maxPrice") List<Book> findBooksWithinPriceRange(@Param("maxPrice") BigDecimal maxPrice); }
- サービス層の実装
@Service @Transactional @Slf4j public class BookService { private final BookRepository bookRepository; @Autowired public BookService(BookRepository bookRepository) { this.bookRepository = bookRepository; } public Book createBook(Book book) { log.info("Creating new book: {}", book.getTitle()); validateBook(book); return bookRepository.save(book); } public List<Book> getAllBooks() { return bookRepository.findAll(); } public Book updateBook(Long id, Book bookDetails) { return bookRepository.findById(id) .map(existingBook -> { BeanUtils.copyProperties(bookDetails, existingBook, "id"); return bookRepository.save(existingBook); }) .orElseThrow(() -> new ResourceNotFoundException("Book not found with id: " + id)); } private void validateBook(Book book) { if (bookRepository.findByTitleAndAuthor(book.getTitle(), book.getAuthor()).isPresent()) { throw new DuplicateResourceException("Book already exists"); } } }
- コントローラーの実装
@RestController @RequestMapping("/api/books") @Validated public class BookController { private final BookService bookService; @Autowired public BookController(BookService bookService) { this.bookService = bookService; } @PostMapping @ResponseStatus(HttpStatus.CREATED) public ResponseEntity<Book> createBook(@Valid @RequestBody Book book) { Book createdBook = bookService.createBook(book); return ResponseEntity.created(URI.create("/api/books/" + createdBook.getId())) .body(createdBook); } @GetMapping public List<Book> getAllBooks() { return bookService.getAllBooks(); } @GetMapping("/{id}") public ResponseEntity<Book> getBookById(@PathVariable Long id) { return bookService.getBookById(id) .map(ResponseEntity::ok) .orElse(ResponseEntity.notFound().build()); } @ExceptionHandler(ConstraintViolationException.class) @ResponseStatus(HttpStatus.BAD_REQUEST) public Map<String, String> handleValidationExceptions(ConstraintViolationException ex) { Map<String, String> errors = new HashMap<>(); ex.getConstraintViolations().forEach(violation -> { errors.put(violation.getPropertyPath().toString(), violation.getMessage()); }); return errors; } }
データベース連携のベストプラクティス
- アプリケーションプロパティの設定
spring: datasource: url: jdbc:mysql://localhost:3306/bookdb?useSSL=false username: ${DB_USERNAME} password: ${DB_PASSWORD} hikari: maximum-pool-size: 10 minimum-idle: 5 idle-timeout: 300000 jpa: hibernate: ddl-auto: update properties: hibernate: dialect: org.hibernate.dialect.MySQLDialect format_sql: true show-sql: true
- トランザクション管理の実装
@Service @Transactional(readOnly = true) // 読み取り専用をデフォルトに public class BookTransactionService { @Transactional // 書き込み操作用に個別指定 public void transferBook(Long fromId, Long toId) { Book sourceBook = bookRepository.findById(fromId) .orElseThrow(() -> new ResourceNotFoundException("Source book not found")); Book targetBook = bookRepository.findById(toId) .orElseThrow(() -> new ResourceNotFoundException("Target book not found")); // トランザクション内での処理 sourceBook.setAvailable(false); targetBook.setAvailable(true); bookRepository.save(sourceBook); bookRepository.save(targetBook); } }
セキュリティ設定の実装方法
- Spring Securityの基本設定
@Configuration @EnableWebSecurity public class SecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .csrf(csrf -> csrf.disable()) .authorizeHttpRequests(auth -> auth .requestMatchers("/api/public/**").permitAll() .requestMatchers("/api/books/**").authenticated() .anyRequest().authenticated() ) .sessionManagement(session -> session .sessionCreationPolicy(SessionCreationPolicy.STATELESS) ) .oauth2ResourceServer(oauth2 -> oauth2 .jwt(jwt -> jwt.decoder(jwtDecoder())) ); return http.build(); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } }
- JWT認証の実装
@Service @Slf4j public class JwtTokenProvider { @Value("${jwt.secret}") private String jwtSecret; @Value("${jwt.expiration}") private int jwtExpiration; public String generateToken(Authentication authentication) { UserDetails userDetails = (UserDetails) authentication.getPrincipal(); return Jwts.builder() .setSubject(userDetails.getUsername()) .setIssuedAt(new Date()) .setExpiration(new Date((new Date()).getTime() + jwtExpiration)) .signWith(SignatureAlgorithm.HS512, jwtSecret) .compact(); } public boolean validateToken(String token) { try { Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(token); return true; } catch (SignatureException ex) { log.error("Invalid JWT signature"); } catch (MalformedJwtException ex) { log.error("Invalid JWT token"); } catch (ExpiredJwtException ex) { log.error("Expired JWT token"); } return false; } }
- CORSの設定
@Configuration public class CorsConfig { @Bean public WebMvcConfigurer corsConfigurer() { return new WebMvcConfigurer() { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/api/**") .allowedOrigins("http://localhost:3000") .allowedMethods("GET", "POST", "PUT", "DELETE") .allowedHeaders("*") .allowCredentials(true) .maxAge(3600); } }; } }
このサンプルアプリケーションは、実践的なSpring Boot開発の基礎となる要素を含んでいます。実際の開発では、これらのコードを基盤として、要件に応じたカスタマイズや機能の追加を行っていくことになります。
実践的なSpring Boot開発テクニック
Spring Bootの高度な機能を活用し、より堅牢なアプリケーションを開発するための実践的なテクニックを解説します。
自動設定(Auto-configuration)の活用方法
- カスタム自動設定の作成
@Configuration @AutoConfigureAfter(DataSourceAutoConfiguration.class) public class CustomDataSourceConfig { @Bean @ConditionalOnMissingBean public DataSourceHealthIndicator dataSourceHealthIndicator(DataSource dataSource) { return new DataSourceHealthIndicator(dataSource); } @Bean @ConditionalOnProperty(prefix = "app.datasource", name = "enable-metrics", havingValue = "true") public DataSourceMetricsCollector dataSourceMetricsCollector(DataSource dataSource) { return new DataSourceMetricsCollector(dataSource); } }
- 条件付き設定の実装
@Configuration @ConditionalOnClass(name = "org.springframework.data.redis.connection.RedisConnectionFactory") public class RedisConfig { @Bean @ConditionalOnMissingBean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(connectionFactory); template.setKeySerializer(new StringRedisSerializer()); template.setValueSerializer(new GenericJackson2JsonRedisSerializer()); return template; } }
- 自動設定のデバッグ
# application.properties debug=true logging.level.org.springframework.boot.autoconfigure=DEBUG
プロファイル管理による環境分離の実現
- プロファイル別の設定ファイル
# application-development.yml spring: datasource: url: jdbc:h2:mem:devdb username: sa password: jpa: show-sql: true # application-production.yml spring: datasource: url: jdbc:mysql://prod-server:3306/proddb username: ${PROD_DB_USER} password: ${PROD_DB_PASS} jpa: show-sql: false
- プロファイル別のBean定義
@Configuration public class ProfileSpecificConfig { @Bean @Profile("development") public EmailService mockEmailService() { return new MockEmailService(); } @Bean @Profile("production") public EmailService smtpEmailService( @Value("${smtp.host}") String host, @Value("${smtp.port}") int port) { return new SmtpEmailService(host, port); } }
- プロファイルの動的切り替え
@Component @Slf4j public class ProfileManager { private final Environment environment; @Autowired public ProfileManager(Environment environment) { this.environment = environment; } public void logActiveProfiles() { for (String profile : environment.getActiveProfiles()) { log.info("Currently active profile: {}", profile); } } }
ログ設定とモニタリングの実装
- Logback設定の実装
<!-- logback-spring.xml --> <?xml version="1.0" encoding="UTF-8"?> <configuration> <springProperty scope="context" name="appName" source="spring.application.name"/> <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern> %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n </pattern> </encoder> </appender> <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>logs/${appName}.log</file> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>logs/${appName}-%d{yyyy-MM-dd}.log</fileNamePattern> <maxHistory>30</maxHistory> </rollingPolicy> <encoder> <pattern> %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n </pattern> </encoder> </appender> <root level="INFO"> <appender-ref ref="CONSOLE"/> <appender-ref ref="FILE"/> </root> <logger name="com.example.myapp" level="DEBUG"/> </configuration>
- Actuatorの設定とカスタマイズ
# application.yml management: endpoints: web: exposure: include: health,metrics,info,prometheus endpoint: health: show-details: always metrics: tags: application: ${spring.application.name}
@Component public class CustomHealthIndicator implements HealthIndicator { @Override public Health health() { try { // カスタムヘルスチェックロジック checkExternalService(); return Health.up() .withDetail("externalService", "OK") .build(); } catch (Exception e) { return Health.down() .withDetail("error", e.getMessage()) .build(); } } }
- メトリクス収集の実装
@Component @Slf4j public class CustomMetricsCollector { private final MeterRegistry meterRegistry; public CustomMetricsCollector(MeterRegistry meterRegistry) { this.meterRegistry = meterRegistry; } @Scheduled(fixedRate = 60000) public void collectMetrics() { // カスタムメトリクスの収集 Gauge.builder("app.business.metric", this, this::calculateBusinessMetric) .tag("type", "business") .description("A business metric") .register(meterRegistry); } @Timed(value = "app.processing.time", description = "Time taken to process") public void processBusinessLogic() { // ビジネスロジックの実行 } }
- 分散トレーシングの設定
# application.yml spring: sleuth: sampler: probability: 1.0 zipkin: base-url: http://zipkin:9411
@RestController @Slf4j public class TracedController { @Autowired private Tracer tracer; @GetMapping("/traced-endpoint") public ResponseEntity<?> tracedEndpoint() { Span span = tracer.currentSpan(); span.tag("custom.tag", "value"); log.info("Processing traced endpoint"); return ResponseEntity.ok().build(); } }
これらの実践的なテクニックを適切に組み合わせることで、より保守性が高く、運用しやすいアプリケーションを開発することができます。特に本番環境での運用を見据えた場合、ログ管理とモニタリングの重要性は非常に高くなります。
Spring Bootアプリケーションの本番デプロイ
Spring Bootアプリケーションを本番環境にデプロイする際の手順と、運用管理のベストプラクティスを解説します。
コンテナ化とDockerfileの作成手順
- マルチステージビルドを活用したDockerfile
# ビルドステージ FROM eclipse-temurin:17-jdk AS builder WORKDIR /app COPY . . RUN ./mvnw clean package -DskipTests # 実行ステージ FROM eclipse-temurin:17-jre WORKDIR /app COPY --from=builder /app/target/*.jar app.jar # ヘルスチェック設定 HEALTHCHECK --interval=30s --timeout=3s \ CMD curl -f http://localhost:8080/actuator/health || exit 1 # 環境変数の設定 ENV JAVA_OPTS="-Xms512m -Xmx1024m" EXPOSE 8080 ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]
- Docker Composeによる環境構築
# docker-compose.yml version: '3.8' services: app: build: . ports: - "8080:8080" environment: - SPRING_PROFILES_ACTIVE=production - SPRING_DATASOURCE_URL=jdbc:mysql://db:3306/myapp - SPRING_DATASOURCE_USERNAME=${DB_USER} - SPRING_DATASOURCE_PASSWORD=${DB_PASS} depends_on: - db networks: - app-network db: image: mysql:8.0 environment: - MYSQL_DATABASE=myapp - MYSQL_USER=${DB_USER} - MYSQL_PASSWORD=${DB_PASS} - MYSQL_ROOT_PASSWORD=${DB_ROOT_PASS} volumes: - db-data:/var/lib/mysql networks: - app-network networks: app-network: driver: bridge volumes: db-data:
クラウドプラットフォームへのデプロイ方法
- AWS Elastic Beanstalkへのデプロイ
# .elasticbeanstalk/config.yml branch-defaults: main: environment: myapp-production environment-defaults: myapp-production: branch: null repository: null global: application_name: myapp default_ec2_keyname: null default_platform: Docker running on 64bit Amazon Linux 2 default_region: ap-northeast-1 sc: git
デプロイコマンド:
# Elastic Beanstalk CLIのインストール pip install awsebcli # 初期化とデプロイ eb init eb create myapp-production eb deploy
- Google Cloud Runへのデプロイ
# プロジェクトの設定 gcloud config set project myapp-project # Dockerイメージのビルドとプッシュ gcloud builds submit --tag gcr.io/myapp-project/myapp # Cloud Runへのデプロイ gcloud run deploy myapp \ --image gcr.io/myapp-project/myapp \ --platform managed \ --region asia-northeast1 \ --allow-unauthenticated \ --set-env-vars="SPRING_PROFILES_ACTIVE=production"
- Azure App Serviceへのデプロイ
# azure-pipeline.yml trigger: - main pool: vmImage: 'ubuntu-latest' steps: - task: Docker@2 inputs: containerRegistry: 'myAzureContainerRegistry' repository: 'myapp' command: 'buildAndPush' Dockerfile: '**/Dockerfile' tags: | $(Build.BuildId) latest - task: AzureWebAppContainer@1 inputs: azureSubscription: 'myAzureSubscription' appName: 'myapp' containers: 'myazurecontainerregistry.azurecr.io/myapp:$(Build.BuildId)'
本番環境での性能監視とトラブルシューティング
- Prometheusによるメトリクス監視
# prometheus.yml global: scrape_interval: 15s scrape_configs: - job_name: 'spring-boot-app' metrics_path: '/actuator/prometheus' static_configs: - targets: ['app:8080']
- Grafanaダッシュボードの設定
{ "dashboard": { "id": null, "title": "Spring Boot Metrics", "panels": [ { "title": "JVM Memory Usage", "type": "graph", "datasource": "Prometheus", "targets": [ { "expr": "jvm_memory_used_bytes", "legendFormat": "{{area}}" } ] }, { "title": "HTTP Request Duration", "type": "graph", "datasource": "Prometheus", "targets": [ { "expr": "http_server_requests_seconds_count", "legendFormat": "{{status}}" } ] } ] } }
- ログ集約とアラート設定
# fluentd.conf <source> @type forward port 24224 bind 0.0.0.0 </source> <match app.**> @type elasticsearch host elasticsearch port 9200 logstash_format true logstash_prefix app-logs flush_interval 5s </match>
- パフォーマンスチューニングの設定例
# application-production.yml server: tomcat: max-threads: 200 min-spare-threads: 20 accept-count: 100 spring: datasource: hikari: maximum-pool-size: 20 minimum-idle: 5 idle-timeout: 300000 jpa: properties: hibernate: jdbc: batch_size: 50 order_inserts: true order_updates: true generate_statistics: true logging: pattern: console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n" file: name: /var/log/myapp/application.log level: root: INFO org.springframework.web: WARN com.example.myapp: INFO
これらの設定と手順を適切に実装することで、安定した本番環境の運用が可能になります。特に、モニタリングとログ収集の仕組みを整備することで、問題発生時の早期発見と対応が容易になります。