teratail header banner
teratail header banner
質問するログイン新規登録

回答編集履歴

2

追記

2019/06/04 13:00

投稿

rubytomato
rubytomato

スコア1752

answer CHANGED
@@ -180,4 +180,128 @@
180
180
  > RecordManage.java(3)で作成したコンストラクタを受け取るためのもの
181
181
 
182
182
  ここの”コンストラクタを受け取る”とはどういう意味なのかちょっと分かりませんが、AddressBookControllerクラスにコンストラクタを追加したのは、SpringにRecordManagerのインスタンスを注入してもらうためです。(コンストラクタインジェクションを使って)
183
- 上記で触れていますが、RecordManagerクラスにComponentアノテーションを付加したことによりSpringがインスタンスを生成、管理してくれています。
183
+ 上記で触れていますが、RecordManagerクラスにComponentアノテーションを付加したことによりSpringがインスタンスを生成、管理してくれています。
184
+
185
+ **2019/06/04 追記**
186
+
187
+ > 質問の意図としては、jdbcTemplateがRecordManagerクラスに注入され、更にrecordManagerがAddressBookControllerクラスに注入されるという具合に値(オブジェクト?)が引き継がれていくのでしょうか、ということでした。
188
+
189
+ "引き継がれていくのでしょうか"という表現は多少違和感(聞きなれない表現なので)がありますが、概ねそのような理解で良いかと思います。
190
+
191
+ > コンストラクラインジェクションと普通の引数ありのコンストラクタの見極めはどのように行うのでしょうか。(記述方法としては同じになると思うのですが・・・)
192
+ > デフォルトコンストラクタが見当たらないのもなぜなのかわかりません。これもSpringが勝手にやってくれているということなのでしょうか。
193
+
194
+ クラスにコンストラクタを1つも定義しなかった場合、暗黙的に引数無し、処理なしのコンストラクタが定義されます。これがいわゆるデフォルトコンストラクタと呼ばれるものです。
195
+ この動きはJavaの言語仕様でSpringは関係ありません。
196
+
197
+ 以下の1)と2)は同じコードと言えます。コンストラクタを1つも定義しなかった場合、1)には2)のような引数無し、処理なしのコンストラクタが(ソースコード上は見えませんが)追加されます。
198
+
199
+ 1)
200
+ ```java
201
+ @Controller
202
+ public class AddressBookController {
203
+
204
+ // コンストラクタを明示的に定義しなかった場合、ソースコード上には見えないが
205
+ // 引数無し、処理なしのデフォルトコンストラクタが暗黙的に定義される
206
+
207
+ }
208
+ ```
209
+
210
+ 2)
211
+ ```
212
+ @Controller
213
+ public class AddressBookController {
214
+
215
+ // デフォルトコンストラクタ
216
+ public AddressBookController() {
217
+ }
218
+
219
+ }
220
+ ```
221
+
222
+ ただし、3)のようにクラスに明示的にコンストラクタを追加すると、引数無し、処理なしのデフォルトコンストラクタは追加されません。これもJavaの言語仕様です。
223
+ コンストラクタが1つだけの場合、Springはそのコンストラクタを使ってコントローラクラスのインスタンスを生成します。
224
+
225
+ 3)
226
+ ```
227
+ @Controller
228
+ public class AddressBookController {
229
+
230
+ private RecordManager recordManager;
231
+
232
+ // RecordManagerがSpringの管理下にある場合、管理しているインスタンスを引数に渡す
233
+ public AddressBookController(RecordManager recordManager) {
234
+ this.recordManager = recordManager;
235
+ }
236
+
237
+ }
238
+ ```
239
+
240
+ 次に明示的にデフォルトコンストラクタを定義し、コンストラクタを2つにした場合どうなるかというと
241
+ Springはデフォルトコンストラクタを使用してコントローラクラスのインスタンスを生成するので、4)の例で言えばrecordManagerフィールドはnullになります。
242
+
243
+ 4)
244
+ ```
245
+ @Controller
246
+ public class AddressBookController {
247
+
248
+ private RecordManager recordManager;
249
+
250
+ public AddressBookController() {
251
+ }
252
+
253
+ public AddressBookController(RecordManager recordManager) {
254
+ this.recordManager = recordManager;
255
+ }
256
+
257
+ }
258
+ ```
259
+
260
+ 引数有りのコンストラクタを使ってほしい場合は以下のようにコンストラクタにAutowiredアノテーションを付加します。
261
+ SpringはAutowiredアノテーションが付いたコンストラクタを使ってコントローラクラスのインスタンスを生成、その際にRecordManagerのインスタンスを引数に与えます。
262
+
263
+ 5)
264
+ ```
265
+ @Controller
266
+ public class AddressBookController {
267
+
268
+ private RecordManager recordManager;
269
+
270
+ public AddressBookController() {
271
+ }
272
+
273
+ @Autowired
274
+ public AddressBookController(RecordManager recordManager) {
275
+ this.recordManager = recordManager;
276
+ }
277
+
278
+ }
279
+ ```
280
+
281
+ 引数の数が違うコンストラクタを定義し、そこにもAutowiredアノテーションを付加した場合どうなるかですが
282
+ コンストラクタにつけられるAutowiredアノテーションは1つまでなのでエラーになります。
283
+
284
+ 6)
285
+ ```
286
+ @Controller
287
+ public class AddressBookController {
288
+
289
+ private RecordManager recordManager;
290
+
291
+ public AddressBookController() {
292
+ }
293
+
294
+ // これはエラー
295
+ @Autowired
296
+ public AddressBookController(RecordManager recordManager) {
297
+ this.recordManager = recordManager;
298
+ }
299
+
300
+ // これもエラー
301
+ @Autowired
302
+ public AddressBookController(RecordManager recordManager, String dummyParam) {
303
+ this.recordManager = recordManager;
304
+ }
305
+
306
+ }
307
+ ```

1

追記

2019/06/04 13:00

投稿

rubytomato
rubytomato

スコア1752

answer CHANGED
@@ -81,5 +81,103 @@
81
81
 
82
82
  ```
83
83
 
84
+ 上記の内容で動作すると思うのですが、実際に環境を構築して動かした訳ではないので保証はできません。このコードを参考にして動かしたときに別の新しいエラーがでましたら、質問内容に追記ください。
84
85
 
86
+ **2019/06/01追記**
87
+
88
+ > ①RecordManage.java(1)において、@Componentを記載することnewせずともRecordManageというインスタンスが生成されるという理解で合っておりますでしょうか。
89
+
90
+ そのご理解で問題ないと思いますが、すこし補足しますとRecordManagerのインスタンスを使うには、誰かがnewしなければなりません。
91
+ その誰かを、Componentアノテーションを付加することでSpringに任せています。
92
+
93
+ Springは、アプリケーション起動時にComponentアノテーションが付加されたクラスを探し出しインスタンスを生成し管理します。
94
+ なお、Componentアノテーション以外にも下記のアノテーションが付加されたクラスが同様にインスタンス生成されます。
95
+ (他にもありますが、ここでは省略します。)
96
+
97
+ @Controller
98
+ @Service
99
+ @Repository
100
+
101
+ Controllerアノテーションは、コントローラクラスに付加されているのをご覧になっていると思いますが、コントローラクラスのインスタンス生成もSpringが行っています。
102
+
103
+ ```
104
+ @Controller
105
+ @RequestMapping(value = "/book")
106
+ public class AddressBookController {
107
+
108
+ //...省略...
109
+
110
+ }
111
+ ```
112
+
113
+ Springは自分が生成したインスタンスを管理していて、そのインスタンスが必要な場面がくるとそこへ注入(Injection)します。
114
+ 必要な場所とは、ご存知の通りAutowiredアノテーションが付いているフィールドやメソッドなどです。
115
+
116
+ Springがサポートする注入の方法は3つあります。
117
+
118
+ 1. フィールドインジェクション
119
+
120
+ フィールドインジェクションを呼ばれる注入方法です。記述するコード量が少なくて済むのでこの書き方が多いように思います。
121
+
122
+ ```
123
+ @Autowired
124
+ private JdbcTemplate jdbcTemplate;
125
+ ```
126
+
127
+ 2. セッターインジェクション
128
+
129
+ クラスを作成したときにフィールドにゲッター(getXXXX)/セッター(setXXXX)メソッドを書いたことがあるかと思いますが、このセッターメソッドを使う方法です。
130
+ Autowiredはセッターメソッドに付けます。
131
+ なお、私の経験上ですがセッターインジェクションが使われているコードはあまり見たことがありません。
132
+
133
+ ```
134
+ private JdbcTemplate jdbcTemplate;
135
+
136
+ @Autowired
137
+ public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
138
+ this.jdbcTemplate = jdbcTemplate
139
+ }
140
+ ```
141
+
142
+ 3. コンストラクタインジェクション
143
+
144
+ 名前の通りクラスのコンストラクタを使った注入方法です。
145
+ コンストラクタが1つだけの場合はAutowiredアノテーションを省略することができます。が、書いてもエラーではありません。
146
+
147
+ ```
148
+ private JdbcTemplate jdbcTemplate;
149
+
150
+ // @Autowired 付けなくてもよい
151
+ public RecordManager(JdbcTemplate jdbcTemplate) {
152
+ this.jdbcTemplate = jdbcTemplate;
153
+ }
154
+ ```
155
+
156
+ 上記のとおり、Component(やController,Service, Repository)とAutowiredアノテーションを利用することで、インスタンスの生成と注入を任せることができますが、1点気を付けなければならない点があります。
157
+ それは、Springは自身が生成したインスタンスに対してのみインスタンスの注入を行うということです。
158
+ このご質問のRecordManagerクラスのJdbcTemplateフィールドがnullになってしまう理由は、RecordManagerのインスタンス生成をSpringが担当していなかったからということになります。
159
+
160
+ ざっくりとした説明になりましたが、この機能はSpring Frameworkの重要な機能の1つでDI (Dependency Injection) / "依存性の注入"と呼ばれています。ネットで検索すれば詳しい記事がたくさんみつかるとおもいますので探して読んでみてください。
161
+ あるいは書籍で知識を仕入れるのも最初のころはいいかもしれません。
162
+
163
+
164
+ > ②RecordManage.java(3)において、コンストラクタインジェクションにする意図。jdbcTemplateを動的に注入して・・・ということだと考えていますが、コンストラクタインジェクションを行わない場合、どのようなことが起こりえるのでしょうか。(テストのしやすさとかの問題だけ?)
165
+
166
+ こちらの意味は、コンストラクタインジェクションとフィールドインジェクションの違い(メリット/デメリット)は何か?という風に解釈しましたが、その前提で進めます。
167
+
168
+ 上記で触れていますが、注入(Injection)方法には3つあります。これらは場面によって使い分けるものと私自身は考えていますが、Springの開発チームは
169
+ ”つねにコンストラクタインジェクションを使う”ことをお勧めする(Recommend)というメッセージを出しています。
170
+
171
+ このようなメッセージを出す背景には、コンストラクタインジェクションのメリットが大きいからだと思いますが、テストコードが書きやすくなるのもその1つだと思います。
172
+ 他にもありますが、私自身上手くご説明できないので、"spring コンストラクタインジェクション フィールドインジェクション"などのキーワードで探してみてください。すぐに詳しく解説してくれているページがすぐに見つかると思います。
173
+
174
+ もし探したページをいくつか読んでも腑に落ちないということであれば、Spring Frameworkはかなり大きなプロジェクトなので前提となる知識が不足しているのかもしれません。
175
+ そんなときは先にJavaでコードを書く時のベストプラクティスを習得するという方向に向かうのもいいと思います。(並行して簡単なSpringを使ったアプリケーションを書き続けるのもなお効果があるとおもいます)
176
+
177
+
178
+ > ③AddressBookController.java(2)において、コンストラクタを追加していますがこれはRecordManage.java(3)で作成したコンストラクタを受け取るためのものという理解で合っておりますでしょうか。
179
+
180
+ > RecordManage.java(3)で作成したコンストラクタを受け取るためのもの
181
+
182
+ ここの”コンストラクタを受け取る”とはどういう意味なのかちょっと分かりませんが、AddressBookControllerクラスにコンストラクタを追加したのは、SpringにRecordManagerのインスタンスを注入してもらうためです。(コンストラクタインジェクションを使って)
85
- 上記の内容動作ると思うのですが、実際環境を構築して動かした訳ではないので保証はできません。このコ参考にて動かしたと別の新しいエラーでましたら質問内容に追記ださい。
183
+ 上記で触れていますが、RecordManagerクラスComponentアノテション付加したとによりSpringインスタンスを生成管理してれてます