Springフレームワークの核心:IoCコンテナと依存性注入の基礎ガイド

IoC(制御の反転)の概念と実装

IoC(Inversion of Control)は、オブジェクトの生成および管理の制御を開発者からフレームワーク(コンテナ)に移譲する設計思想です。従来の開発では、呼び出し元が依存するオブジェクトを直接インスタンス化(new)していましたが、Springではこれをコンテナが行います。

まず、基本的な依存関係を定義します。ここでは「ユーザー情報のログ出力」を例にします。

// データアクセス層のインターフェース
public interface Logger {
    void log(String message);
}

// 実装クラス
public class ConsoleLogger implements Logger {
    @Override
    public void log(String message) {
        System.out.println("[LOG] " + message);
    }
}

次に、ビジネスロジック層でこのLoggerを使用します。従来の方法では、サービスクラス内でnew ConsoleLogger()としていましたが、SpringではXML設定でBeanを定義し、コンテナから取得します。

<!-- applicationContext.xml -->
<bean id="consoleLogger" class="com.example.demo.ConsoleLogger"/>

実行コードは以下のようになります。

public class MainApp {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Logger logger = (Logger) context.getBean("consoleLogger");
        logger.log("System started");
    }
}

DI(依存性注入)の実装パターン

DI(Dependency Injection)は、オブジェクト間の依存関係をコンテナが設定する仕組みです。主要な注入方法には以下があります。

1. Setter注入

Setterメソッドを経由して依存オブジェクトを注入します。最も一般的な方法の一つです。

public class UserActionService {
    private Logger logger;

    // Setterメソッド
    public void setLogger(Logger logger) {
        this.logger = logger;
    }

    public void executeAction(String user) {
        logger.log("User action performed by: " + user);
    }
}
<bean id="userActionService" class="com.example.demo.UserActionService">
    <!-- nameはプロパティ名、refは注入するBeanのID -->
    <property name="logger" ref="consoleLogger"/>
</bean>

また、単純型(String, intなど)の注入も可能です。

public class ConsoleLogger implements Logger {
    private String prefix;

    public void setPrefix(String prefix) {
        this.prefix = prefix;
    }
    // ...
}
<bean id="consoleLogger" class="com.example.demo.ConsoleLogger">
    <property name="prefix" value="DEBUG"/>
</bean>

2. コンストラクタ注入

コンストラクタの引数経由で依存関係を解決します。依存関係が必須である場合に推奨されます。

public class UserActionService {
    private final Logger logger;

    public UserActionService(Logger logger) {
        this.logger = logger;
    }
    // ...
}
<bean id="userActionService" class="com.example.demo.UserActionService">
    <constructor-arg ref="consoleLogger"/>
</bean>

引数の型が曖昧な場合は、indextype属性を使用して明示的に指定できます。

3. 自動ワイヤリング(Autowiring)

autowire属性を使用することで、XMLの記述を省略できます。型(byType)または名前(byName)による自動結合が可能です。

<bean id="userActionService" class="com.example.demo.UserActionService" autowire="byType"/>

Beanのスコープとライフサイクル

Beanのデフォルトスコープは「Singleton(単例)」であり、コンテナから取得するたびに同じインスタンスが返されます。一方、「Prototype(プロトタイプ)」を指定すると、取得のたびに新しいインスタンスが生成されます。

<bean id="prototypeService" class="com.example.demo.UserActionService" scope="prototype"/>

Singletonはステートレスなサービス(Service, Dao, Util)に適し、Prototypeは状態を持つオブジェクトに適しています。

Beanのライフサイクル(初期化処理と破棄処理)を管理するには、init-methodおよびdestroy-methodを使用します。破棄メソッドを実行するには、コンテナを正常に閉じる必要があります。

<bean id="lifecycleBean" class="com.example.demo.LifecycleBean" 
      init-method="initialize" destroy-method="cleanup"/>
// コンテナのクローズ処理
((ClassPathXmlApplicationContext) context).close();

または、Spring標準のインターフェースInitializingBeanおよびDisposableBeanを実装する方法もあります。

外部ライブラリのBean管理

開発者が作成したクラスだけでなく、Druidなどのサードパーティ製ライブラリのクラスもBeanとして管理できます。通常、propertyタグを使用して接続設定を注入します。

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3306/mydb"/>
    <property name="username" value="admin"/>
    <property name="password" value="secret"/>
</bean>

設定値を外部ファイル(jdbc.properties)に分離する場合は、context名前空間を使用してプロパティファイルをロードし、${...}プレースホルダーで参照します。

<context:property-placeholder location="classpath:jdbc.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="url" value="${jdbc.url}"/>
    <!-- 他のプロパティも同様にプレースホルダーを使用 -->
</bean>

アノテーションによる開発

XML設定に代わり、アノテーションを使用することでより簡潔に記述できます。

コンポーネントスキャンとBean定義

@Component@Service@Repository@Controllerをクラスに付与することで、Beanとして登録されます。以下は設定クラスの例です。

@Configuration
@ComponentScan("com.example") // スキャン対象のパッケージ
public class AppConfig {
    // 設定クラス本体は空で可
}
@Service
public class UserActionService {
    // ...
}

依存性注入のアノテーション

@Autowiredを使用して、型に基づき自動的に依存オブジェクトが注入されます。

@Service
public class UserActionService {
    @Autowired
    private Logger logger;
    // ...
}

同一型のBeanが複数ある場合、@QualifierでBean名を指定して解決します。また、単純な値は@Valueで注入可能です。

@Value("${app.name}")
private String appName;

サードパーティ製Beanのアノテーション設定

@Beanアノテーションをメソッドに付与し、設定クラス内でインスタンスを生成・返却します。

@Configuration
public class DatabaseConfig {
    
    @Bean
    public DataSource dataSource() {
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName("com.mysql.jdbc.Driver");
        ds.setUrl("jdbc:mysql://localhost:3306/mydb");
        ds.setUsername("admin");
        ds.setPassword("secret");
        return ds;
    }
}

設定を複数のクラスに分割する場合は、@Importを使用してインポートします。

@Configuration
@Import(DatabaseConfig.class)
@ComponentScan("com.example")
public class AppConfig {
}

5月19日 10:06 投稿