Spring

DIで実クラスではなくインターフェースを指定する理由
- 切り替えの融通が聞く(実装A→実装Bにしたい場合、アノテーションでプライムを宣言すれば、実装Bのクラス追加のみで対応できる)
- UTする際にモックで書き換えしやすい。実クラスを指定していた場合むり
- 環境によって(dev,honban)などをプロパティで切り替えて制御できる

いつも忘れるモデルのバインド

spring security 闇
これは Spring Security の「セッション固定攻撃」対策の機能の一つで、認証前にアクセスしようとしていたURLを記憶し、ログイン成功後にそのURLにリダイレクトする仕組みが働いているためです。
この動作を修正するには以下の2つの方法があります:
SecurityConfig で明示的にデフォルトのサクセスURLを設定する:
ログインフォームで明示的にリダイレクト先を指定する:
defaultSuccessUrlの第2引数にtrueを指定することで、常に指定したURLにリダイレクトされるようになり、以前アクセスしていたURLは無視されます。

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.formLogin(login -> login
.loginProcessingUrl("/login")
.loginPage("/login")
.defaultSuccessUrl("/", true) // 第2引数のtrueを追加
.failureUrl("/login?error")
.usernameParameter("accountId")
.passwordParameter("pw")
.permitAll()
)
// ...existing code...
}

<form action="#" th:action="@{/login}" method="post">
<input type="hidden" name="redirect_uri" value="/" />
</form>

Spring DI+ファクトリー

Spring MVCでこのような画面を作る場合、表示専用の値と入力項目を1つのFormクラスにまとめることをお勧めします。
推奨アプローチ:1つのFormクラス
public class UserEditForm {
// 表示専用(ユーザが変更不可)
private String userId;
private String userName;
private LocalDateTime createdAt;
// 入力項目(ユーザが変更可能)
@NotBlank
private String email;
@Size(max = 100)
private String description;
// getter/setter...
}
この方法の利点
画面との対応が明確になります。Thymeleafテンプレートでth:object="${form}"
として、表示項目も入力項目も同じオブジェクトから参照できるため、画面構造と一致します。
コントローラーがシンプルになります。1つのオブジェクトをModelに渡すだけで済み、画面表示時と更新処理時で同じFormクラスを使えます。
保守性が高いです。画面項目の追加や変更時に、関連するすべての項目が1つのクラスにまとまっているため、修正箇所が分散しません。
実装例
@Controller
public class UserController {
@GetMapping("/user/edit/{id}")
public String editForm(@PathVariable String id, Model model) {
UserEditForm form = new UserEditForm();
// DBから値を取得して設定
User user = userService.findById(id);
form.setUserId(user.getId());
form.setUserName(user.getName());
form.setCreatedAt(user.getCreatedAt());
form.setEmail(user.getEmail());
form.setDescription(user.getDescription());
model.addAttribute("form", form);
return "user/edit";
}
@PostMapping("/user/edit")
public String update(@Valid @ModelAttribute UserEditForm form,
BindingResult result) {
if (result.hasErrors()) {
return "user/edit";
}
// 入力項目のみを更新処理に使用
userService.updateUser(form.getUserId(),
form.getEmail(),
form.getDescription());
return "redirect:/user/list";
}
}
この方法により、画面の構造とコードの構造が一致し、開発・保守の両面で効率的になります。