More Related Content
Similar to 今さら聞けないDiとspring (15)
今さら聞けないDiとspring
- 2. 自己紹介
• 土岐 孝平
• Springを使用したシステム開発の支援
• JavaやSpringの研修の講師
• 書籍の執筆
2
[改訂新版]Spring入門
- 9. その①:使う側が自分で用意する
• 使う側のプログラムが長くなる
– 本質ではない処理が入り込む
• 依存するオブジェクトの設定を変更したり、具象クラスを変更する場合は
、使う側のプログラムを修正する必要がある
9
public class FooDao {
private DataSource dataSource;
public FooDao() {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/foo");
dataSource.setUsername("foo");
dataSource.setPassword("foo123");
dataSource.setMaxActive(50);
dataSource.setMaxIdle(50);
this.dataSource = dataSource;
}
public Foo findFoo() throws Exception {
Connection con = dataSource.getConnection();
con.prepareStatement("select * from foo ...");
・・・
「用意する」
部分
「使う」部分
public class Main {
public static void main(String[] args) {
FooDao fooDao = new FooDao();
Foo foo = fooDao.findFoo();
・・・
【実行するときの例】
- 10. 依存するオブジェクトを変更した例
• テストが難しい
– テストの度に修正が必要
– 修正漏れが発生する可能性があり危険
10
public class FooDao {
private DataSource dataSource;
public FooDao() {
EmbeddedDatabase dataSource =
new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2).build();
this.dataSource = dataSource;
}
public Foo findFoo() throws Exception {
Connection con = dataSource.getConnection();
con.prepareStatement("select * from foo ...");
・・・
プログラムの
修正が必要
- 11. その②:使う側とは別のところで用意する
• 使う側のプログラムがシンプルになる
– 本質の処理に注力できる
• 依存するオブジェクトの設定を変更したり、具象クラスを変更する場合に
、使う側のプログラムを修正する必要が無い
11
public class FooDao {
private DataSource dataSource;
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
public Foo findFoo() throws Exception {
Connection con = dataSource.getConnection();
con.prepareStatement("select * from foo ...");
・・・
public class Main {
public static void main(String[] args) {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/foo");
dataSource.setUsername("foo");
dataSource.setPassword("foo123");
dataSource.setMaxActive(50);
dataSource.setMaxIdle(50);
FooDao fooDao = new FooDao();
fooDao.setDataSource(dataSource);
Foo foo = fooDao.findFoo();
・・・
「用意する」
部分
「使う」部分
- 12. 依存するオブジェクトを変更した例
• テスト時に、使う側のプログラムを修正する必要が無い
12
public class FooDao {
private DataSource dataSource;
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
public Foo findFoo() throws Exception {
Connection con = dataSource.getConnection();
con.prepareStatement("select * from foo ...");
・・・
public class MainForTest {
public static void main(String[] args) {
EmbeddedDatabase dataSource =
new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2).build();
FooDao fooDao = new FooDao();
fooDao.setDataSource(dataSource);
Foo foo = fooDao.findFoo();
・・・
「用意する」
部分
修正不要 「用意する」部分を
変更
- 14. DIの「I」とは?
• Injection:注入
• 依存するオブジェクトを注入する
14
public class FooDao {
private DataSource dataSource;
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
public Foo findFoo() throws Exception {
Connection con = dataSource.getConnection();
con.prepareStatement("select * from foo ...");
・・・
public class Main {
public static void main(String[] args) {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/foo");
dataSource.setUsername("foo");
dataSource.setPassword("foo123");
dataSource.setMaxActive(50);
dataSource.setMaxIdle(50);
FooDao fooDao = new FooDao();
fooDao.setDataSource(dataSource);
Foo foo = fooDao.findFoo();
・・・ 「注入する」部分
- 17. 留意点
• すべてのオブジェクトをDIする訳ではない
• メソッド呼出の度に作成するようなオブジェクト(フィールドで
保持できないオブジェクト)はDIに適さない
– 例:Entityのオブジェクト
17
public class FooDao {
private DataSource dataSource;
・・・
public Foo findFoo() throws Exception {
Connection con = dataSource.getConnection();
PreparedStatement ps = con.prepareStatement("select * from foo ...");
・・・
ResultSet rs = ps.executeQuery();
Foo foo = null;
while(rs.next()) {
foo = new Foo();
foo.setName(rs.getString("name"));
foo.setAge(rs.getInt("age"));
}
・・・
return foo;
}
メソッド呼出の度に作成するオブ
ジェクト
- 20. DIを適用してる?
20
public class DataSourceFactory {
private static DataSource dataSource;
static {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/foo");
dataSource.setUsername("foo");
dataSource.setPassword("foo123");
dataSource.setMaxTotal(50);
dataSource.setMaxIdle(50);
dataSource.setMinIdle(10);
DataSourceFactory.dataSource = dataSource;
}
public static DataSource getDataSource() {
return dataSource;
}
}
public class FooDao {
private DataSource dataSource;
public FooDao() {
this.dataSource =
DataSourceFactory.getDataSource();
}
public Foo findFoo() throws Exception {
Connection con = dataSource.getConnection();
con.prepareStatement("select * from foo ...");
・・・
public class Main {
public static void main(String[] args) {
FooDao fooDao = new FooDao();
Foo foo = fooDao.findFoo();
System.out.println(foo);
}
}
- 21. DIを適用してる?
21
public class DataSourceFactory {
private static DataSource dataSource;
static {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/foo");
dataSource.setUsername("foo");
dataSource.setPassword("foo123");
dataSource.setMaxTotal(50);
dataSource.setMaxIdle(50);
dataSource.setMinIdle(10);
DataSourceFactory.dataSource = dataSource;
}
public static DataSource getDataSource() {
return dataSource;
}
}
public class FooDao {
private DataSource dataSource;
public FooDao() {
this.dataSource =
DataSourceFactory.getDataSource();
}
public Foo findFoo() throws Exception {
Connection con = dataSource.getConnection();
con.prepareStatement("select * from foo ...");
・・・
public class Main {
public static void main(String[] args) {
FooDao fooDao = new FooDao();
Foo foo = fooDao.findFoo();
System.out.println(foo);
}
}
答え:適用していません
依存するオブジェクトを
自分で取得しています。
DIの場合、取得するのはなく、
「注入」してもらいます。
- 22. DIを適用してる?その2
22
public class ObjectFactory {
private static FooDao fooDao;
static {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/foo");
dataSource.setUsername("foo");
dataSource.setPassword("foo123");
dataSource.setMaxTotal(50);
dataSource.setMaxIdle(50);
dataSource.setMinIdle(10);
fooDao = new FooDao();
fooDao.setDataSource(dataSource);
}
public static FooDao getFooDao() {
return fooDao;
}
}
public class FooDao {
private DataSource dataSource;
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
public Foo findFoo() throws Exception {
Connection con = dataSource.getConnection();
con.prepareStatement("select * from foo ...");
・・・
public class Main {
public static void main(String[] args) {
FooDao fooDao = ObjectFactory.getFooDao();
Foo foo = fooDao.findFoo();
System.out.println(foo);
}
}
- 23. DIを適用してる?その2
23
public class ObjectFactory {
private static FooDao fooDao;
static {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/foo");
dataSource.setUsername("foo");
dataSource.setPassword("foo123");
dataSource.setMaxTotal(50);
dataSource.setMaxIdle(50);
dataSource.setMinIdle(10);
fooDao = new FooDao();
fooDao.setDataSource(dataSource);
}
public static FooDao getFooDao() {
return fooDao;
}
}
public class FooDao {
private DataSource dataSource;
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
public Foo findFoo() throws Exception {
Connection con = dataSource.getConnection();
con.prepareStatement("select * from foo ...");
・・・
public class Main {
public static void main(String[] args) {
FooDao fooDao = ObjectFactory.getFooDao();
Foo foo = fooDao.findFoo();
System.out.println(foo);
}
}
答え:適用しています
依存するオブジェクトを、
自分で用意してないし、
取得もしていません。
「注入」してもらってます。
- 26. Springの用語
• Bean
– SpringのDIで作られたオブジェクトのこと
• コンフィグレーション
– Beanの定義情報。どのクラスのBeanを用意するか?どのBeanを注入する
か?などを定義する
• DIコンテナ
– Beanの入れ物。コンフィグレーションに従ってBeanを作成し、IDを割り振って
管理する。IDや型を指定してBeanを取得することも可能。
26
DIコンテナ
コンフィグレー
ション Bean Bean
ID:foo ID:bar
- 28. XML
• Springの誕生時からサポートされている
28
package sample;
public class FooService {
private FooDao fooDao;
public void setFooDao(FooDao fooDao) {
this.fooDao = fooDao;
}
・・・
}
package sample;
public class FooDao {
・・・
}
<bean id="fooService" class="sample.FooService">
<property name="fooDao" ref="fooDao"/>
</bean>
<bean id="fooDao" class="sample.FooDao">
</bean>
DIコンテナ
FooService FooDao
ID:fooService ID:fooDao
XML
public class Main {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("sample/foo.xml");
FooService fooService = ctx.getBean(FooService.class);
・・・
}
}
【参考】実行の例
- 29. JavaConfig
• Spring3.0からサポート
29
package sample;
public class FooService {
private FooDao fooDao;
public void setFooDao(FooDao fooDao) {
this.fooDao = fooDao;
}
・・・
}
package sample;
public class FooDao {
・・・
}
@Configuration
public class FooConfig {
@Bean
public FooService fooService() {
FooService fooService = new FooService();
fooService.setFooDao(fooDao());
return fooService;
}
@Bean
public FooDao fooDao() {
return new FooDao();
}
}
DIコンテナ
FooService FooDao
ID:fooService ID:fooDao
・Bean IDは、メソッド名の「fooDao」となる
JavaConfig
- 30. アノテーション
• Spring2.5からサポート
30
package sample;
@Service
public class FooService {
@Autowired
private FooDao fooDao;
・・・
}
package sample;
@Repository
public class FooDao {
・・・
}
<context:component-scan base-package=“sample"/> DIコンテナ
FooService FooDao
ID:fooService ID:fooDao
@Configuration
@ComponentScan(basePackages="sample")
public class FooConfig {
}
もしくは
・Bean IDは、クラス名の先頭文字を小文字にし
た「fooDao」となる。
・value属性値で明示的に指定することも可能
例)@Repository(“fooDaoCustom”)
XML
JavaConfig
- 35. コンフィグレーションの手段の使い分け2/2
35
package sample;
@Service
public class FooService {
@Autowired
private FooDao fooDao;
public Foo getFoo() {
Foo foo = fooDao.findFoo();
・・・
package sample;
@Repository
public class FooDao {
@Autowired
private DataSource dataSource;
public Foo findFoo() {
Connection con = dataSource.getConnection();
con.prepareStatement("select * from foo ...");
・・・
package sample;
@Controller
public class FooController {
@Autowired
private FooService fooService;
@RequestMapping("/showFoo")
public String showFoo() {
Foo foo = fooService.getFoo();
・・・
@Configuration
@ComponentScan(basePackages="sample")
public class AppConfig {
@Bean
public DataSource dataSource() {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/foo");
dataSource.setUsername("foo");
dataSource.setPassword("foo123");
dataSource.setMaxActive(50);
dataSource.setMaxIdle(50);
dataSource.setMinIdle(10);
return dataSource;
}
}
Controller Service Dao DataSource
- 38. JUnit実行時
38
public class FooServiceTest {
private FooService fooService;
@Before
public void setup() {
ApplicationContext ctx = new AnnotationConfigApplicationContext(FooTestConfig.class);
fooService = ctx.getBean(FooService.class);
}
@Test
public void testGetFoo() {
Foo foo = fooService.getFoo();
assertNotNull(foo);
}
}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=FooTestConfig.class)
public class FooServiceTest {
@Autowired
private FooService fooService;
@Test
public void testGetFoo() {
Foo foo = fooService.getFoo();
assertNotNull(foo);
}
}より簡潔な書き方
- 40. Spring Boot実行時
40
package sample;
@SpringBootApplication
public class FooApplication {
public static void main(String[] args) {
SpringApplication.run(FooApplication.class, args);
}
}
指定したコンフィグレーションのファ
イル(自分自身)を読込んでDIコンテ
ナが作成される。合わせて、Tomcat
も起動する。
@SpringBootApplicationには、
@Configration、@ComponentScanが
含まれている。basePackagesの指定
が無いため、アノテーションを付加し
たクラス(FooApplication)のパッケー
ジ(sample)がベースパッケージとなり、
配下のクラス(サブパッケージも含む)
がコンポーネントスキャンされる。
@SpringBootApplication
FooApplication
sample
@Controller
FooController
@Service
FooService
@Repository
FooDao
@Configuration
AppConfig
Tomcat
DIコンテナ
Controller Service Dao DataSource
- 45. 実行結果はどうなる?
45
@Configuration
@ComponentScan(basePackages=“sample")
public class FooConfig {
}
public interface FooDao {
public void foo();
}
package sample;
@Repository
public class FooDaoA implements FooDao {
public void foo() {
System.out.print("A");
}
}
package sample;
@Repository
public class FooDaoB implements FooDao {
public void foo() {
System.out.print(“B");
}
}
package sample;
@Service
public class FooService {
@Autowired
private FooDao fooDao;
public void execute() {
fooDao.foo();
}
}
public class Main {
public static void main(String[] args) {
ApplicationContext ctx =
new AnnotationConfigApplicationContext(FooConfig.class);
FooService fooService = ctx.getBean(FooService.class);
fooService.execute();
}
}
(A) “A”と表示される
(B) “B”と表示される
(C) “AB”と表示される
(D) 例外が発生する
- 46. 実行結果はどうなる?
46
@Configuration
@ComponentScan(basePackages=“sample")
public class FooConfig {
}
public interface FooDao {
public void foo();
}
package sample;
@Repository
public class FooDaoA implements FooDao {
public void foo() {
System.out.print("A");
}
}
package sample;
@Repository
public class FooDaoB implements FooDao {
public void foo() {
System.out.print(“B");
}
}
package sample;
@Service
public class FooService {
@Autowired
private FooDao fooDao;
public void execute() {
fooDao.foo();
}
}
public class Main {
public static void main(String[] args) {
ApplicationContext ctx =
new AnnotationConfigApplicationContext(FooConfig.class);
FooService fooService = ctx.getBean(FooService.class);
fooService.execute();
}
}
(A) “A”と表示される
(B) “B”と表示される
(C) “AB”と表示される
(D) 例外が発生する
FooDaoを実装したBeanが
2つ存在するため、どれを
注入すればよいか分から
ない