自動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を定義する方法です。主なステップは以下の通りです。
@ComponentScanでスキャン対象パッケージを指定。- 各層に応じたアノテーションを使用:
-@Repository: DAO層
-@Service: サービス層
-@Controller: コントローラ層 @Valueでプリミティブ値を注入。@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時代においても不可欠です。