3.

【Spring】@Beanアノテーションとは

編集
この記事の要点
  • @Beanメソッドの戻り値を Spring コンテナの Bean として登録するアノテーション
  • @Configuration クラス内のメソッドに付与
  • @Component との違い: クラスではなくメソッド単位で Bean 登録
  • サードパーティクラス(ライブラリ)を Bean 化するのに最適
  • 名前指定: @Bean("customName")、デフォルトはメソッド名

 

@Bean の基本

@Configuration
public class AppConfig {

    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();  // 戻り値が Bean になる
    }

    @Bean
    public ObjectMapper objectMapper() {
        return new ObjectMapper()
            .registerModule(new JavaTimeModule())
            .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
    }
}

// 利用
@Service
public class ApiService {
    private final RestTemplate restTemplate;

    public ApiService(RestTemplate restTemplate) {  // 自動注入
        this.restTemplate = restTemplate;
    }
}

@Bean vs @Component

項目@Bean@Component
付与対象メソッドクラス
場所@Configuration クラス内クラス定義
サードパーティクラス○ 使える× ソースを変更できない
初期化ロジック細かく制御可能コンストラクタ + @PostConstruct
条件付き Bean容易@Profile で大まか
使うべき場面外部ライブラリ・複雑な構成自分のサービス・コンポーネント

属性

@Configuration
public class AppConfig {

    // Bean 名指定
    @Bean(name = "myDataSource")
    public DataSource dataSource() { ... }

    // 複数名(エイリアス)
    @Bean({"ds", "primaryDataSource"})
    public DataSource dataSource() { ... }

    // 初期化メソッド + 破棄メソッド
    @Bean(initMethod = "init", destroyMethod = "close")
    public ConnectionPool pool() {
        return new ConnectionPool();
    }

    // 自動 close 無効化
    @Bean(destroyMethod = "")
    public AutoCloseable resource() { ... }

    // Lazy
    @Bean
    @Lazy
    public ExpensiveService expensiveService() { ... }

    // Scope
    @Bean
    @Scope("prototype")
    public TaskWorker taskWorker() { ... }
}

依存性の注入

① メソッド引数で他 Bean を注入

@Configuration
public class AppConfig {

    @Bean
    public DataSource dataSource() {
        return new HikariDataSource(...);
    }

    @Bean
    public JdbcTemplate jdbcTemplate(DataSource dataSource) {  // ← 自動注入
        return new JdbcTemplate(dataSource);
    }

    @Bean
    public UserRepository userRepository(JdbcTemplate jdbc) {
        return new UserRepository(jdbc);
    }
}

② @Configuration 内のメソッド呼び出し

@Configuration  // proxyBeanMethods=true (デフォルト)
public class AppConfig {

    @Bean
    public DataSource dataSource() {
        return new HikariDataSource(...);
    }

    @Bean
    public JdbcTemplate jdbcTemplate() {
        return new JdbcTemplate(dataSource());  // 呼び出し OK
        // CGLib プロキシで同一インスタンス保証
    }
}

// 一方、@Configuration(proxyBeanMethods = false) または @Component なら
// メソッド呼び出しで新インスタンスが返るので注意
@Configuration(proxyBeanMethods = false)
public class AppConfig {
    @Bean
    public JdbcTemplate jdbcTemplate(DataSource dataSource) {  // 必ず引数で受け取る
        return new JdbcTemplate(dataSource);
    }
}

条件付き Bean 登録

@Configuration
public class AppConfig {

    @Bean
    @ConditionalOnProperty(name = "feature.cache.enabled", havingValue = "true")
    public CacheManager cacheManager() {
        return new RedisCacheManager(...);
    }

    @Bean
    @ConditionalOnMissingBean(DataSource.class)  // 他に DataSource Bean がなければ
    public DataSource defaultDataSource() {
        return new EmbeddedDatabaseBuilder().build();
    }

    @Bean
    @ConditionalOnClass(name = "redis.clients.jedis.Jedis")  // Jedis クラスがあれば
    public RedisTemplate redisTemplate() {
        return new RedisTemplate<>();
    }

    @Bean
    @Profile("prod")
    public ProductionService productionService() {
        return new ProductionService();
    }
}

初期化と破棄

方法 1: @Bean の initMethod / destroyMethod

@Bean(initMethod = "start", destroyMethod = "stop")
public BackgroundWorker worker() {
    return new BackgroundWorker();
}

class BackgroundWorker {
    public void start() { /* ... */ }
    public void stop() { /* ... */ }
}

方法 2: メソッド内で初期化

@Bean
public DataSource dataSource() {
    HikariDataSource ds = new HikariDataSource();
    ds.setJdbcUrl("jdbc:postgresql://localhost:5432/mydb");
    ds.setMaximumPoolSize(50);
    ds.setConnectionTimeout(30000);
    return ds;
}

方法 3: @PostConstruct (Bean 自身に書ける場合)

@Bean
public MyService myService() {
    return new MyService();
}

class MyService {
    @PostConstruct
    public void init() { /* ... */ }

    @PreDestroy
    public void cleanup() { /* ... */ }
}

典型的な使用例

① RestTemplate / WebClient

@Configuration
public class HttpClientConfig {

    @Bean
    public RestTemplate restTemplate(RestTemplateBuilder builder) {
        return builder
            .setConnectTimeout(Duration.ofSeconds(5))
            .setReadTimeout(Duration.ofSeconds(30))
            .additionalInterceptors(new LoggingInterceptor())
            .build();
    }

    @Bean
    public WebClient webClient() {
        return WebClient.builder()
            .baseUrl("https://api.example.com")
            .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
            .build();
    }
}

② DataSource / ConnectionPool

@Configuration
public class DatabaseConfig {

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSourceProperties primaryDataSourceProperties() {
        return new DataSourceProperties();
    }

    @Bean
    @Primary
    public DataSource primaryDataSource() {
        return primaryDataSourceProperties()
            .initializeDataSourceBuilder()
            .build();
    }

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.secondary")
    public DataSource secondaryDataSource() {
        return DataSourceBuilder.create().build();
    }
}

③ ObjectMapper / JSON 設定

@Bean
public ObjectMapper objectMapper() {
    return new ObjectMapper()
        .registerModule(new JavaTimeModule())
        .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
        .setSerializationInclusion(JsonInclude.Include.NON_NULL)
        .setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
}

④ TaskExecutor / ThreadPool

@Bean
public TaskExecutor taskExecutor() {
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    executor.setCorePoolSize(10);
    executor.setMaxPoolSize(50);
    executor.setQueueCapacity(100);
    executor.setThreadNamePrefix("task-");
    executor.initialize();
    return executor;
}

関連記事

編集
Post Share
子ページ

子ページはありません

同階層のページ
  1. @After
  2. @Autowired
  3. @Bean
  4. @Before
  5. @Column
  6. @Component
  7. @Configuration
  8. @Controller
  9. @Data
  10. @Entity
  11. @GeneratedValue
  12. @Id
  13. @Modifying
  14. @PathVariable
  15. @PropertySource
  16. @Repository
  17. @RequestBody
  18. @RequestMapping
  19. @ResponseBody
  20. @RestController
  21. @Service
  22. @SpringBootApplication
  23. @Table
  24. @Transactional
  25. @Value