ㅁ 스프링 특징 2
ㅁ DI (Dependency Injection)
- 의존성 주입.
- IoC와 연관되어 있는 기술이다.
- 개발자가 직접 객체를 생성하지 않고 스프링 컨테이너가 관리하고 있는 Bean 객체를 자동으로 주입하는 개념.
ㅁ 클래스 생성
- com.br.spring.di에 Phone 클래스 생성
ㅁ
- 사실 스프링 레거시 프로젝트를 만드는 순간 ~이 자동으로 만들어진다.
- src/main/webapp/WEB-INF/spring에 root-copntext.xml
- 커피콩모양이 빈이다. s는 스프링이다. s와 커피콩이 같이 있으면 전에 만들었떤 appCtx.xml처럼 spring bean configuraiton file이다.
ㅁ root-context.xml
- 이 파일은 서버 시작과 동시에 이 xml파일은 자동으로 바로 읽혀진다.
- 직접 컨테이터 객체를 만들 필요가 없다.
- 서버 우클릭 - add and remove로 지금 작업중인 프로젝트를 올린다.
- ~ web.xml파일이다.
ㅁ web.xml
- 서버를 스타트하는 순간 web.xml 파일이 읽혀지는데 여기에 root-context.xml 파일이 적혀있어서 얘도 실행된다.
- 열어보면 기존의 동적 웹 프로젝트의 web.xml 파일과는 많이 다르다.
- <context-param> 태그와 <listener> 태그는 세트다. 둘 다 스프링 컨테이너와 관련된 태그다.
- ContextLoaderListener는 스프링 컨테이너를 생성해주는 역할의 클래스다.
- contextConfigLocation는 스프링 컨테이너를 생성할 때 읽어들일 파일의 경로다.
- 스프링 컨테이너는 Spring에서 애플리케이션의 객체(Bean)를 생성하고 관리하는 시스템을 뜻한다.
- root-context.xml에 등록되어있는 빈들이 어쩌구
※ <context-param> + <listener>
- ContextLoaderListener 클래스에 의해서 스프링 컨테이너 환경설정이 작성되어있는 root-context.xml 파일이 제일 먼저 읽혀지면서 Spring Container가 생성된다.
(1) 서버 start시 제일 먼저 web.xml 파일이 실행된다.
(2) web.xml 파일에 작성되어 있는 <context-param>, <listener> 태그에 의해서 root-context.xml 파일이 제일 먼저 읽혀진다. 이를 pre-loading이라고도 한다.
(3) root-context.xml (Spring Bean Configuration File)에 작성된 빈들이 스프링 컨테이너에 등록된다.
ㅁ 다시 root-context.xml 파일
ㅁ 클래스 생성
- com.br.spring.di에 PhoneController1 클래스 생성
- ~메소드 하나가 기존에 만들었다 서블릿 클래스 하나다.
- 프린트문 넣고 정상적으로 작동하는지 테스트한다.
ㅁ 서버 스타트
- 스프링부터 서버를 start할 때 읽어들이는 문서가 엄청 많다. 그 중 하나라도 문제가 있으면 서버 start가 안 된다.
ㅁ 오류
- public class PhoneController1 위에 @Controller라는 annotation이 있어야 한다.
- 클래스 위에 @Controller 어노테이션을 붙이고 재실행한다.
응답 페이지는 지금 없어서 이렇게 나오면 정상적으로 된거다.
ㅁ Container에 등록된 Bean을 가져오는 방법
(1) 가장 근본적인 방법은 빈들이 담겨있는 Spring 컨테이너 객체로부터 getBean 메소드로 가져오는 방법이다.
Spring Bean Configuration File(xml) 파일이나 @Configuration이 붙은 클래스파일을 AbstractApplicationContext형 변수에 담고 getBean 메소드로 꺼내서 쓴다.
(2) 새로운 방법은 DI 관련 어노테이션을 이용하는 방법이다.
- @Inject : 등록된 Bean들 중 타입(class)이 일치하는 Bean 객체를 주입해주는 어노테이션
- @Resource : 등록된 Bean들 중 이름(id)이 일치하는 Bean 객체를 주입해주는 어노테이션
이름으로 찾으려면 @Qualifier("이름")를 직접 명시해야 한다.
- @Autowired : 등록된 Bean들 중 타입(class)이 일치하는 Bean 객체를 주입해주는 어노테이션
@Autowired는 @Qualifier이 내장되어있어서 타입이 여러개일 경우 이름으로 자동으로 찾아진다.
- 실무에서는 거진 @Autowired를 사용한다.
ㅁ @Autowired 사용방법
(1) 필드로 생성된 객체 주입
- 필드마다 매번 필드 위에 @Autowired 작성
(2) 메소드로 생성된 객체 주입
- 생성자의 매개변수에 스프링 컨테이너가 관리하는 Bean을 전달받아 주입.
- @Autowired 생략불가능
- 통상 setter 메소드의 형태로 작성. 이렇게 작성하지 않아도 @Autowired 붙어있으면 주입된다.
(3) 생성자로 생성된 객체 주입
- 생성자의 매개변수에 스프링 컨테이너가 관리하는 Bean을 전달받아 주입.
- 필드를 초기화하는 매개변수 생성자 작성 후 해당 생성자 상단에 @Autowired 어노테이션 기술
- @Autowired 어노테이션 생략가능.
- 실무에서 자주 쓰는 방법은 생성자를 이용하는 방법이다.
필드, 메소드는 각각 10개면 어노테이션을 10번 다 써야 한다. @Autowired 생략도 가능하고.
@Controller
public class PhoneController1 {
private Phone b;
// @Autowired 생략 가능
public PhoneController1(Phone b) { // 매개변수에 생성된 객체 주입
this.b = b;
}
@RequestMapping("/test2") // "localhost:8888/spring/test2" 라는 url 요청시 실행되는 메소드
public void diTest2() {
System.out.println(b);
}
}
@Controller
public class PhoneController1 {
private Phone a;
private Phone b;
// @Autowired 생략 가능
public PhoneController1(Phone phone1, Phone phone2) { // 매개변수에 생성된 객체 주입
this.a = phone1;
this.b = phone2;
}
@RequestMapping("/test2") // "localhost:8888/spring/test2" 라는 url 요청시 실행되는 메소드
public void diTest2() {
System.out.println(a);
System.out.println(b);
}
}
- 같은 타입의 빈 객체가 여러개인 경우에는 @Autrowired가 타입(class) 다음에 이름(id)로 찾는다.
ㅁ 회사에선 그냥 new로 생성해서 쓸 때도 있다. 굳이 빈 등록하는게 더 번거로워서. 이런다고 크게 문제가 되지는 않는다.
ㅁ PhoneController1 수정
- http://localhost:8888/spring/test1, http://localhost:8888/spring/test2, http://localhost:8888/spring/test3를 각각 요청한 결과이다.
- 404뜨는 건 정상이다.
- 메소드 위의 @RequestMapping는 메소드당 1개여야만 한다. 같으면 오류남.
==============================================================================
ㅁ 지금까지는 동일한 타입의 bean(Phone)이 하나였다.
ㅁ root-context.xml
- phone2 빈을 만든다. 동일한 타입의 bean(Phone 객체)이 2개가 되었다.
ㅁ 서버를 스타트해본다.
- 서버를 스타트함과 동시에 오류가 발생한다.
- 마지막 오류를 보면 phone 객체가 2개가 ~
- @Autowired가 type으로 bean을 찾기 때문이다.
- 동일한 타입의 bean이 여러개 등록되어 있을 경우 @Autowired는 기본적으로 타입으로 빈을 탐색한다.
- 동일한 타입의 빈이 여러개일 경우 오류를 유발시킨다.
- 단, 해당 필드명을 내가 주입받고자하는 빈의 이름으로 작성시 이름으로 빈을 찾아서 주입한다.
- @Autrowired는 탐색순서가 기본적으로 먼저 타입(class)으로 찾고, 그 다음 이름(id)으로찾는다.
이름에 해당하는 것도 존재하지 않으면 오류가 발생한다.
ㅁ PhoneController1 수정
- http://localhost:8888/spring/test1, http://localhost:8888/spring/test2, http://localhost:8888/spring/test3를 각각 요청한 결과이다.
ㅁ
- 컨트롤러 측에 필요한 필드는 사실 dto 필드가 아니라 서비스다.
==========================================================================================
ㅁ com.br.spring.di에 PhoneController2 클래스 생성
- 위에 @Controller를 붙인다.
ㅁ com.br.spring.di에 PhoneService 인터페이스 생성
- 이렇게 서비스를 인터페이스, 그 인터페이스를 구현하는 클래스로 나눈 이유는
웹용 서비스 클래스와 모바일용 서비스 클래스를 다르게 ~
ㅁ com.br.spring.di에 PhoneServiceWebImpl 구현 클래스 생성
ㅁ com.br.spring.di에 PhoneServiceMobileImpl 구현 클래스 생성
ㅁ PhoneController2 클래스 수정
- 스프링 사용 전에는 매번 서비스 객체를 생성해서 호출했다.
private PhoneService pService = new PhoneServiceWebImpl(); -- 웹 개발 당시
private PhoneService pService = new PhoneServiceMobileImpl(); -- 모바일 개발 당시
- 그런데 개발자가 직접 new로 객체 생성시 결합도가 높은 문제가 발생한다.
- 결합도(= 종속관계) : 연관관계에서의 클래스들 간에 각 클래스 수정시 서로에게 영향을 미치는 정도.
※ 연관관계의 클래스
public class A {
private B b = new B();
}
public class B { }
- A 클래스에서 B 클래스를 필드로 사용할 경우 연관과계가 형성된다.
- B 클래스가 변경(클래스명, 생성자, 메소드 수정 등)될 경우 A 클래스도 일부 수정해야되는 상황이 발생한다.
- 이런 상황을 두 객체간의 결합도가 높다, 강하다라고 표현한다.
- new로 생성하면 PhoneServiceWebImpl 클래스명이 변경되는 순간 이 클래스가 쓰이던
PhoneController2에 와서 ~도 수정해야할 수 있다.
- ~ B만 수정하면 된다. A 클래스는 수정하지 않아도 된다.
※ 스프링의 IoC + DI를 활용해서 해결 가능
(1) 개발에 사용할 서비스 클래스를 빈으로 등록 (xml 방식, 자바 방식, MVC용 어노테이션)
- 빈으로 등록하는 방법이 사실 하나 더 있다. 나중에 배울건데 MVC용 어노테이션
(2) DI를 통해 해당 객체를 주입받아 사용 (@Autowired)
- PhoneServiceWebImpl 의 위에 @Service를 붙이면 빈 등록이 끝난다.
- 여기에만 해본다.
- 이름을 바꿔도 상관없다.
- 사용할 객체의 클래스가 변경돼도 해당 객체를 참조하는 클래스에서 수정할 필요가 없다. 결합도가 낮아진 것이다.
ㅁ PhoneServiceWebImpl의 @Service는 주석처리하고 PhoneServiceMobileImpl 클래스의 위에 @Serivce를 붙인다.
- 이것도 잘 된다.
=============================
ㅁ IoC + DI의 장점
- 개발에 사용할 서비스 클래스를 빈으로 등록(xml 방식, 자바 방식, MVC용 어노테이션)하고,
DI관련 어노테이션(@Autowired 등)을 통해 해당 객체를 주입받아 사용한다.
(1) 메모리를 보다 효율적으로 사용할 수 있다.
- 직접 new로 생성할 경우 매번 메모리 영역에 새로 생성된다.
자주 사용될 객체, 동시에 다수가 사용할 객체라면 그만큼 객체가 생성되었다가 소멸됨을 반복하며 메모리를 빈번하게 사용한다.
- 스프링의 IoC와 DI 개념이 적용되면 스프링 컨테이너가 해당 객체를 딱 한번만 생성해서 가지고 있다가,
필요할 때마다 해당 객체를 자동으로 주입해준다. (싱글톤 개념이 들어가 있음)
(2) 클래스간의 결합도를 해소할 수 있음 (결합도를 낮출 수 있음)
- 결합도가 높을 때 발생되는 문제 -> 특정 클래스 수정시 해당 클래스를 사용하고 있는 다른 클래스도 수정해야됨.
ex) MemberServiceImpl 클래스의 이름이 바뀌는 경우 new로 직접 생성한 경우에는 직접 new로 생성했던 구문을 전부 바꿔야 한다.
- 스프링의 IoC와 DI 개념이 적용되면 결합도가 낮아져서 소스코드의 수정을 최소화 할 수 있다.
ex) MemberServiceImpl 클래스 이름만 바꾸면 된다.
ㅁ 개발할 때 결합도는 낮고 응집도는 높게 코딩하는 것이 좋다.
'Spring' 카테고리의 다른 글
[Spring] MVC (2) resources 등록 (0) | 2024.10.16 |
---|---|
[Spring] MVC (1) (0) | 2024.10.16 |
[Spring] IOC(제어의 역전) (0) | 2024.10.14 |
[Spring] 첫번째 프로젝트 생성, pom.xml (2) | 2024.10.14 |
[Spring] 스프링 기초 (0) | 2024.10.14 |