Springの注解、AOP、内部構造とMyBatis統合までの実践ガイド

自動DIによる依存性管理の進化

SpringのIoCコンテナは、Bean間の依存関係を自動的に解決する「自動ワイヤリング」機能を提供しており、設定の冗長性を大幅に削減できます。XMLベースの定義から始まり、アノテーション、JavaConfigへと進化することで、開発効率と保守性が向上します。

XMLでの自動注入

Bean定義においてautowire属性を使用することで、プロパティ名または型に基づいて自動的に他のBeanが注入されます。

<bean id="cat" class="example.Cat"/>
<bean id="dog" class="example.Dog"/>
<bean id="person" class="example.Person" autowire="byType"/>
  • byName: プロパティ名とBean IDが一致するものを検索。
  • byType: 同一型のBeanが1つだけ存在する必要がある。

アノテーションによる依存性の解決

アノテーション方式は現在の主流であり、@Autowired@Resourceを利用してクラス内に直接設定を記述できます。

public class Person {
    @Autowired
    private Cat cat;

    @Resource(name = "dog")
    private Dog dog;
}

XMLではコンポーネントスキャンを有効化する必要があります:

<context:annotation-config/>
<context:component-scan base-package="example"/>

@Autowired vs @Resource

項目 @Autowired (Spring) @Resource (JSR-250)
解決順序 型 → 名前 名前 → 型
フレームワーク依存 低(標準API)
推奨度

アノテーションによる完全なコンポーネント管理

XMLから完全に脱却し、Javaコード上でBeanを定義する方法です。主なステップは以下の通りです。

  1. @ComponentScanでスキャン対象パッケージを指定。
  2. 各層に応じたアノテーションを使用:
    - @Repository: DAO層
    - @Service: サービス層
    - @Controller: コントローラ層
  3. @Valueでプリミティブ値を注入。
  4. @Scope("prototype")などでスコープを制御。

最適な戦略:ハイブリッドアプローチ

  • XML/JavaConfig: 共通設定(データソース、トランザクションマネージャなど)
  • アノテーション: 業務ロジックを持つクラスの管理

Javaによる設定 (JavaConfig)

Spring 3.0以降、XML不要の純粋なJava設定が可能になりました。@Configurationクラス内で@Beanメソッドを定義します。

@Configuration
@ComponentScan("example.service")
public class AppConfig {

    @Bean
    public DataSource dataSource() {
        DriverManagerDataSource ds = new DriverManagerDataSource();
        ds.setDriverClassName("com.mysql.jdbc.Driver");
        ds.setUrl("jdbc:mysql://localhost:3306/test");
        ds.setUsername("root");
        ds.setPassword("pass");
        return ds;
    }
}

テスト時にはAnnotationConfigApplicationContextを使用してコンテキストを読み込みます。

動的プロキシとAOPの基盤

AOPの背後にあるのは動的プロキシパターンです。静的プロキシでは代理クラスの数が増大する問題があり、動的プロキシによりこれを解消します。

JDK動的プロキシの実装

public class LoggingHandler implements InvocationHandler {
    private Object target;

    public LoggingHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("メソッド開始: " + method.getName());
        try {
            return method.invoke(target, args);
        } finally {
            System.out.println("メソッド終了: " + method.getName());
        }
    }
}

利用側:

UserServiceImpl userService = new UserServiceImpl();
UserService proxy = (UserService) Proxy.newProxyInstance(
    userService.getClass().getClassLoader(),
    userService.getClass().getInterfaces(),
    new LoggingHandler(userService)
);

アスペクト指向プログラミング(AOP)

AOPは横断的関心事をモジュール化し、ビジネスロジックから分離する技術です。ログ出力、トランザクション管理などが代表例です。

主要概念

  • アスペクト: 横断的関心事のモジュール(例: ログ用クラス)
  • 通知(Advice): 実行される処理内容(前処理、後処理など)
  • ポイントカット: どのメソッドに適用するかを指定
  • 結合点(Join Point): 処理が挿入可能な位置(通常はメソッド呼び出し)

アノテーションによるAOPの実現

@Aspect
@Component
public class LoggingAspect {

    @Before("execution(* example.service.*.*(..))")
    public void logBefore(JoinPoint jp) {
        System.out.println("呼び出し: " + jp.getSignature().getName());
    }

    @Around("@annotation(Timed)")
    public Object measureTime(ProceedingJoinPoint pjp) throws Throwable {
        long start = System.currentTimeMillis();
        Object result = pjp.proceed();
        System.out.println(pjp.getSignature() + " 実行時間: " + (System.currentTimeMillis() - start) + "ms");
        return result;
    }
}

XML設定:

<aop:aspectj-autoproxy/>

MyBatisとの統合

SpringとMyBatisを連携させることで、DAOのインスタンス管理やトランザクション制御を一元化できます。

必要な依存関係

<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>2.0.7</version>
</dependency>

SpringによるMyBatisコンポーネント管理

  • SqlSessionFactoryBean: Spring管理下でファクトリを生成
  • SqlSessionTemplate: スレッドセーフなセッションラッパー
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    <property name="mapperLocations" value="classpath:mappers/*.xml"/>
</bean>

<bean id="userMapper" class="example.mapper.UserMapperImpl">
    <property name="sqlSessionTemplate" ref="sqlSession"/>
</bean>

宣言的トランザクション管理

業務コードにトランザクション処理を埋め込むのではなく、設定によって自動的に制御します。

XMLでの宣言的トランザクション

<bean id="transactionManager"
      class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>

<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="save*" propagation="REQUIRED"/>
        <tx:method name="delete*" propagation="REQUIRED"/>
        <tx:method name="*" read-only="true"/>
    </tx:attributes>
</tx:advice>

<aop:config>
    <aop:pointcut id="serviceMethods" expression="execution(* example.service.*.*(..))"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethods"/>
</aop:config>

アノテーションによる簡略化

@Transactionalを付加するだけで済みます:

@Service
public class UserService {
    
    @Transactional
    public void registerUser(User user) {
        userDao.insert(user);
        sendWelcomeEmail(user); // 失敗時はロールバック
    }
}

設定ファイルには以下を追加:

<tx:annotation-driven/>

Springのこれらの機能は、疎結合で再利用可能なアプリケーション構築を可能にし、特に大規模システムにおいてその真価を発揮します。基礎となるDI/AOPの理解は、Spring Boot時代においても不可欠です。

タグ: Spring AOP JavaConfig MyBatis トランザクション管理

7月11日 20:06 投稿