SPRING

DI (Dependency Injection) / 의존 객체 생성 방법

nang. 2019. 3. 18. 02:49
반응형
SMALL

- Dependency(의존) Injection(주입) 

--> DI


* 의존? (Dependency)

: A객체랑 B객체가 있는데 A객체가 B객체의 어떤 메소드를 호출하고 값을 리턴한다치면

( A ----> B )

===> A에서 B에 있는걸 불러 ===> A는 B가 필요해... B한테 의존할거야... 너 없으면 안돼...

===> A는 B에 의존한다.


* 주입? (Injection)

: 의존이랑 비슷한데 의존이랑 반대야

===> A에서 B에 있는걸 불러 ===> B가 A에 들어가... B를 A에 주입할거야...

===> B를 A에 주입한다.

===> A는 주입 받는다.


★ A는 B에 의존 = B를 A에 주입

★ 불려지는게 B 의존 객체 (의존할 객체) -----> 지존

★ 불러서 쓰는게 A 의존하는 객체 




* 객체 외부에 존재하는 assembler(container) (= spring)가 런타임시에 객체들 사이의 의존 관계를 파악하고 의존 객체를 객체에 주입

* 컨테이너가 해줌

* 근데 객체 정의, 의존 관계 설정은 개발자가

* 객체들 간의 의존 관계는 XML 파일이나 annotation을 통해 설정 됨

* 객체들 간의 결합도(coupling)를 낮춤 ---> 테스트 및 수정 용이, 재사용성 증가

* 응집도(cohesion) ↑ 결합도(coupling) ↓ ---> 응고





★ 인터페이스 https://mdown.blog.me/221316632143

- 인터페이스는 객체가 될 수 없어! 클래스가 아니니까! 객체가 가져야하는 특징, 특성 같은거야! 인터페이스를 지키는 객체!

: ArticleDao는 인터페이스지 객체가 아니야

: ArticleDao를 상속 받아서 구현한게 객체

: MysqlArticleDao / OracleArticleDao

: 상속 받았으니까 ArticleDao에 있는 메소드 insert() / selectById()가 결국 MysqlArticleDao / OracleArticleDao 클래스에 있는거야






< 의존 객체 생성 방법 >


① 의존하는 객체를 직접 생성

public class WriteArticleService {

private ArticleDao articleDao = new MysqlArticleDao();

...

}


* private 인터페이스타입 변수 = new 인터페이스구현객체(); 



단점)

- 결합(coupling)이 너무 타이트 (결합도는 낮아야함)

- 의존 클래스(의존 객체=불려지는거=MysqlArticleDao)를 다른 클래스로 변경하려면? 힘든 수정이 필요

- MysqlArticleDao() ---> OracleArticleDao()

- 여러 클래스들도 의존하고 있으면 다 하나하나 수정해야 함

- 클래스마다 개발자가 다르면? 진도가 다르면?

- 단위 테스트를 위해서 코드 수정이 필요해짐






② Factory 클래스 이용 (==> 간접적 생성)

public class WriteArticleService {
private ArticleDaoFactory factory = ArticleDaoFactory.newInstance();
private ArticleDao articleDao = factory.newArticleDao();
...
}

* Factory클래스를 따로 만들고 거기에 있는 DAO(newInstance)를 호출해서 사용 * ArticleDaoFactory 클래스 안에 newInstance() 메소드, 그 안에 newArticleDao() 메소드





장점) - 수정 필요 시 Factory 클래스만 수정하면 됨 단점) - 객체마다 Factory 클래스를 구현해야 함 - Factory 객체에 대한 타이트한 의존 관계 형성















③ Dependency Injection 이용 (Assembler(Spring)가 의존 객체 생성 후 의존하는 객체에게 전달)

★ 생성자 : 내가 만약 객체를 생성할 때 마다 그 객체에 어떠한 기능을 주고 싶거나, 변수를 초기화하고 싶다면 생성자 이용 ★ this : 멤버변수, 나를 부른 놈, 자신을 호출한 객체, 생성자를 부른 객체의 변수 ★ super : 부모 클래스의 생성자를 쓰겠다, 그냥 상속받은 클래스에서 초기화하면 되지 왜? 왜냐면 내 변수는 내가 책임질게 네 변수는 네가 책임져! 이런 의미란다 코드가 꼬일 수 있기에 ★ 오버라이딩 : 부모 클래스의 메소드를 자식 클래스에서 재정의 하는 것, 클래스이름 및 매개변수 다 같아야 함 ★ 오버로딩 : 생성자가 대표적 오버로딩인데 같은 클래스에서 같은 이름의 메소드들 정의, 클래스이름은 같아야하고 매개변수는 달라도 됨 ★ 다형성 : 왼쪽 변이 같거나 더 커야 함. Employee(상위클래스) m = new Manager(하위클래스)(); 이면 왼쪽변이 크니까 가능한데 대신 Employee 클래스에 있는 변수만 사용 가능. Manager() 객체라고 해서 Manager()에 있는 변수를 사용할 수 있는게 아니야.


public class WriteArticleService { //의존하는 객체
private ArticleDao articleDao; //필드(참조변수) 선언
public WriteArticleService(ArticleDao articleDao) {
//생성자를 통해 의존 객체(MysqlArticleDao 등)를 전달받도록 구현

this.articleDao = articleDao; //의존 객체 주입(DI)
}

public void writeArticle(Article article) {
articleDao.insert(article); //의존 객체 호출
//이 articleDao는 바뀐 값. 주입된 값. 주입된 객체의 insert메소드 호출
// 즉 MysqlArticleDao의 내용들이 담긴 것이 여기로 와서 주입된거야(이건 밑 코드가 먼저 실행)
// insert해라~ 처리는 이 클래스에서 하는 것
}
...
}





* 위 코드는 생성자 사용 방법임. 생성자를 정의 해야함

--> 생성자로 의존 객체를 주입하는 것

* 다른 방법으로는 setter 메소드를 정의 하는 방법이 있음

--> setter 메소드로 의존 객체 주입하는 것



장점)

- 의존 객체를 직접 생성하거나 찾기 위한 코드가 불필요

- new 안씀

- 의존 객체 주입

- 독립적 구현

- 그래서 loose coupling임



단점)

- 의존 객체가 위 코드에는 안나와있어서 구체적으로 Mysql..인지 Oracle..인지는 알 수 없음




public class MysqlArticleDao implements ArticleDao { //인터페이스인 ArticleDao를 구체적으로 구현한 클래스
private Map<Integer, Article> mysqlDB = new HashMap<>();
//Map 타입의 mysqlDB는 HashMap 객체이다. 메모리에 저장한다.

@Override
public void insert(Article article) {
System.out.println("MysqlArticleDao.insert() 실행");
mysqlDB.put(article.getId(), article);
//Map 타입에 따라서 Article 객체를 Map에 저장
//--> Article 타입의 article이 저장되는거니까Article 객체가 저장되는거지.
System.out.println(" --> Article " + article.getId() + " was inserted into Mysql.");
}
// 결과
// MysqlArticleDao.insert() 실행
// --> Article 23 was inserted into Mysql.

@Override
public Article selectById(int id) {
System.out.println("MysqlArticleDao.selectById() 실행");
System.out.println(" --> Article " + id + " was selected from Mysql.");
return mysqlDB.get(id); //Map에서 Article 객체를 검색
}
}






public class Assembler { // 스프링에선 구현 필요는 없다.
private ArticleDao articleDao;
private WriteArticleService writeService;
private ReadArticleService readService;

public Assembler() {
articleDao = new MysqlArticleDao();
// ArticleDao를 구현한 객체 생성
// 즉 articleDao에는 MysqlArticleDao의 내용들이 담긴거야
writeService = new WriteArticleService(articleDao);
// WriteArticleService 객체 생성
// ArticleDao를 구현한 객체를 전달(생성자 만들었으니까 거기로)
// 즉 이 writeService에는 MysqlArticleDao 내용이랑 WrtieArticleService의 insert 해라~ 까지 담겨있는거야
readService = new ReadArticleService(articleDao);
}

public WriteArticleService getWriteArticleService() { return writeService; --> WriteArticleService 객체임 }
public ReadArticleService getReadArticleService() { return readService; }
}








public class MainForAssembler { // 메인 테스트 프로그램
public static void main(String[] args) {
Assembler assembler = new Assembler();
// Assembler 객체 생성

WriteArticleService writeService = assembler.getWriteArticleService();
// Assembler로부터 WriteArticleService 타입의 객체를 가져옴
// 그러면 즉 이 writeService에는 MysqlArticleDao 내용이랑 WrtieArticleService의 insert 해라~ 까지 담겨있는거야
// 근데 아직 insert안함 insert가 있는 메소드는 WriteArticleService의 writeArticle(Article article) 메소드 이니까
// 이 .writeArticle() 가 호출 되어야 그 안에 있는 insert가 실행되는 것!

Article article = new Article(101, "This is a new article ...");
// Article 객체 생성
System.out.println(article + " was created.");

writeService.writeArticle(article);
// WrtieArticleService 클래스를 호출하게 되는거니까 이제 여기서 insert() 실행 됨
System.out.println();

ReadArticleService readService = assembler.getReadArticleService();
Article result = readService.readArticle(101);
// 얘는 객체 안만들고 값만 가져오는거니까...
System.out.println(result + " was read.");
}
}





반응형
LIST

'SPRING' 카테고리의 다른 글

DI / 의존 관계 설정 방법 2 (Annotation 이용 방법)  (0) 2019.03.18
DI / 의존 관계 설정 방법 1 (XML 이용 방법)  (0) 2019.03.18
Spring Web MVC framework  (0) 2019.03.16
spring Components  (0) 2019.03.15
what is Spring framework?  (0) 2019.03.15