Springインターフェースを用いたユーザー行動のイベントリスナー実装

基本概念

イベント監視機構はオブザーバーパターンとして理解でき,データ発信者(イベントソース)とデータ受信者(リスナー)で構成される.Javaではイベントオブジェクトはjava.util.EventObjectを継承し,イベントリスナーはjava.util.EventListenerを実装する.EventObjectはデフォルトコンストラクタを持たず,イベント発生源を追跡するためのsourceパラメータが必要となる.

Springのイベントモデル

SpringのイベントオブジェクトはApplicationEventで,EventObjectを継承している.主要な実装は以下の通り:

public abstract class ApplicationEvent extends EventObject {
    public ApplicationEvent(Object source) {
        super(source);
    }
}

イベントリスナーはApplicationListenerインターフェースを実装する:

public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
    void handleEvent(E event);
}

実装手法

Springでイベント監視を実装する主な方法:

  • インターフェースベース:ApplicationListenerの実装
  • アノテーションベース:@EventListenerの利用

本記事ではインターフェースベースのアプローチに焦点を当てる.

実装例

1. カスタムイベントの定義

public class UserActivityEvent extends ApplicationEvent {
    private final UserActionRecord actionRecord;
    
    public UserActivityEvent(UserActionRecord actionRecord) {
        super(actionRecord);
        this.actionRecord = actionRecord;
    }
    
    public UserActionRecord getActionRecord() {
        return actionRecord;
    }
}

2. ユーザー行動データモデル

public class UserActionRecord implements Serializable {
    private Integer userId;
    private String loginId;
    private String displayName;
    private String activityDetail;
    private LocalDateTime timestamp;
    
    // コンストラクタとアクセサメソッド
    public UserActionRecord(Integer userId, String loginId, 
                           String displayName, LocalDateTime timestamp) {
        this.userId = userId;
        this.loginId = loginId;
        this.displayName = displayName;
        this.timestamp = timestamp;
    }
}

3. イベントリスナーの登録

@Component
public class ActivityTracker implements ApplicationListener<UserActivityEvent> {
    
    private final ActivityLogService logService;
    
    @Autowired
    public ActivityTracker(ActivityLogService logService) {
        this.logService = logService;
    }
    
    @Override
    public void handleEvent(UserActivityEvent event) {
        logService.recordAction(event.getActionRecord());
    }
}

4. イベント発行の実装

@Component
public class LogoutHandler extends SimpleUrlLogoutSuccessHandler {
    
    private final ApplicationEventPublisher publisher;
    private final UserAccountService accountService;
    
    @Autowired
    public LogoutHandler(ApplicationEventPublisher publisher, 
                        UserAccountService accountService) {
        this.publisher = publisher;
        this.accountService = accountService;
    }
    
    @Override
    public void onLogoutSuccess(HttpServletRequest request, 
                               HttpServletResponse response, 
                               Authentication auth) {
        UserDetails userDetails = (UserDetails) auth.getPrincipal();
        UserAccount account = accountService.findByLoginId(userDetails.getUsername());
        
        UserActionRecord action = new UserActionRecord(
            account.getId(), 
            account.getLoginId(),
            account.getDisplayName(),
            LocalDateTime.now()
        );
        action.setActivityDetail(account.getLoginId() + " がシステムからログアウトしました");
        
        publisher.publishEvent(new UserActivityEvent(action));
        ResponseUtil.sendSuccess(response);
    }
}

タグ: Spring Framework イベント駆動設計 ApplicationListener Java EE Observer Pattern

5月17日 17:20 投稿