Contents
発生時のログ
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 0 of constructor in com.springhack.help.controller.LoginController required a bean of type 'com.springhack.help.service.LoginService' that could not be found.
Action:
Consider defining a bean of type 'com.springhack.help.service.LoginService' in your configuration.
parameter 0 of constructor in required a bean of type that could not be found.の発生理由
@RestController
@RequestMapping("/")
public class LoginController {
private final LoginService loginService;
public LoginController(LoginService loginService) {
this.loginService = loginService;
}
@GetMapping
public String get() {
String login = loginService.login("foo","bar");
return login;
}
}
コンストラクターでDIしたいインスタンスが、起動時に見つからない場合に発生します。
ここでは7業目でLoginServiceのインスタンスを受け取って、フィールドloginServiceにセットしようとしています。しかし、何らかの理由(後述)でLoginServiceのインスタンスが生成されなかかったため、本エラーが発生しました。
解決策 DIしたいインスタンスが生成されるようにする
人によってなぜDIしたいインスタンスが生成されなかったかは異なりますが、典型例を示します。
下記はNG例です。
@Service
public interface LoginService {
String login(String mail, String password);
}
public class LoginServiceImpl implements LoginService {
public String login(String mail, String password) {
return "OK";
}
}
上記は、サービスをインターフェースと実装クラスに分けて使用している良い例です。
しかし、@Serviceがインターフェースの方についており、実装クラスにはついていないため、bootRunした時に、LoginServiceインターフェースの実装クラスが作成されませんでした。
その結果、LoginControllerのコンストラクタでLoginServiceをDIしようとしても、インスタンス化されておらず、DI対象がないためエラーが発生します。
修正するには、下記のように実装クラスに@Serviceをつける必要があります。
public interface LoginService {
String login(String mail, String password);
}
@Service
public class LoginServiceImpl implements LoginService {
public String login(String mail, String password) {
return "OK";
}
}
Tips.1 インターフェースと実装クラスのどっちに@Serviceをつける?
現場ではインターフェースと実装クラス、どっちに@Serviceをつけるべき?と整備されていない現場もありますが、正解は実装クラスのみつけるです。
実装クラスにもインターフェースにも両方につけても動きますが、インターフェースはあくまでインターフェースであって、インスタンス化されるべきは実装クラスの方です。
Tip2. @ComponentでもOKです
また、サービス層ではないインテグレーション層の部品、主にAccessorを作るときにも同じことが起きますので、@Componentをつけて実装クラスがインスタンス化されるようにしてください。