일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 | 31 |
- maven
- javaprogramming
- progressive web app
- 서브쿼리
- 자바스크립트
- javascript
- HTML
- sqldeveloper
- 스프링
- framework
- 자바프로그래밍
- web
- 국비지원
- js
- mybatis
- 프로그레시브웹앱
- CSS
- PWA
- 생활코딩
- tdd
- sql
- Oracle
- 메이븐
- TodayILearned
- TIL
- 프레임워크
- SpringMVC
- 오라클
- JavaScript 내장객체
- springaop
- Today
- Total
1cm
자바 프로그래밍_Day_101_스프링 개요 본문
2022. 01. 10
스프링 개요
Spring Framework란?
자바 플랫폼을 위한 오픈소스 애플리케이션 프레임워크로 간단하게 스프링(Spring)이라고도 불림. 동적인 웹 사이트를 개발하기 위한 여러 가지 서비스를 제공하고 있으며, 대한민국 공공기관의 웹서비스 개발시 사용을 권장하고 있는 전자정부 표준프레임워크의 기반 기술로써 쓰이고 있음
Spring Framework의 특징
DI (Dependency Injection) 의존성 주입 |
설정 파일이나 어노테이션을 통해 객체간의 의존 관계를 설정하여 개발자가 직접 의존하는 객체를 생성할 필요가 없다. |
Spring AOP (Aspect Oriented Programming) 관점 지향 프로그래밍 |
트랜잭션, 로깅, 보안 등 여러 모듈, 여러 계층에서 공통으로 필요로 하는 기능의 경우 해당 기능들을 분리하여 관리한다. |
POJO (Plain Old Java Object) |
일반적인 J2EE 프레임워크에 비해 특정 라이브러리를 사용할 필요가 없어 개발이 쉬우며, 기존 라이브러리의 지원이 용이하다. * 쉽게 말하면 오래된 방식의 자바 오브젝트라는 뜻 |
IOC (Inversion of Control) 제어 반전 |
컨트롤의 제어권이 개발자가 아니라 프레임워크에 있다는 뜻으로 객체의 생성부터 모든 생명주기의 관리까지 프레임워크가 주도하고 있다. 객체를 생성하고, 직접 호출하는 프로그램이 아니라, 만들어둔 자원을 호출해서 사용한다. |
Spring JDBC | Mybatis나 Hibernate 등의 데이터베이스를 처리하는 영속성 프레임워크와 연결할 수 있는 인터페이스를 제공한다. |
Spring MVC | MVC 디자인 패턴을 통해 웹 어플리케이션의 Model, View, Controller 사이의 의존 관계를 DI 컨테이너에서 관리하여 개발자가 아닌 서버가 객체들을 관리하는 웹 애플리케이션을 추구할 수 있다. |
PSA (Portable Service Abstraction) |
스프링은 다른 여러 모듈을 사용함에 있어 별도의 추상화 레이어를 제공한다. 예를 들어 JPA를 사용할 때에서 Spring JPA를 사용하여 추상화하므로 실제 구현에 있어서Hibernate를 사용하든 EclipseLink를 사용하든 개발자는 이 모듈의 의존 없이 프로그램에집중할 수 있다. |
* Spring AOP 관련 '횡단관심사' 참고 이미지
Spring의 구성 모듈
- Data 접근 계층
: JDBC나 데이터베이스에 연결하는 모듈로, Data 트랜잭션에 해당하는 기능을 담당하여 영속성 프레임워크의 연결을 담당한다.
- Web 계층 (MVC / Remoting)
: Spring Framework에서 Servlet, Struts 등 웹 구현 기술과의 연결점을 Spring MVC 구성으로 지원하기 위해 제공되는 모듈 계층이다. 또한 스프링의 리모팅 기술로 RMI, Hessian, Burlap, JAX-WS, HTTP 호출자 그리고 REST API 모듈을 제공한다.
- AOP 계층
: Spring에서 각 흐름 간 공통의 코드를 한 쪽으로 빼내어 필요한 시점에 해당 코드를 첨부하게 하기 위해 지원하는 계층으로, 별도의 proxy를 두어 동작한다. 이를 통해 객체간의 결합도를 낮출 수 있다.
- Core Container
: Spring의 핵심 부분이라고 할 수 있으며 모든 스프링 관련 모듈은 이 Core Container 기반으로 구축된다. Springdml rmsrksdl ehlsms IoC((Inversion of Control) 또는 DI) 기능을 지원하는 영역을 담당하고 있다. BeanFactory를 기반으로 Bean 클래스들을 제어할 수 있는 기능을 지원한다.
Spring 모듈 정리
모듈명 | 내 용 |
spring-beans | 스프링 컨테이너를 이용해서 객체를 생성하는 기본기능을 제공 |
spring-context | 객체생성, 라이프 사이클 처리, 스키마 확장 등의 기능을 제공 |
spring-aop | AOP기능을 제공 |
spring-web | REST 클라이언트 데이터 변환 처리, 서블릿 필터, 파일 업로드 지원 등 웹 개발에 필요한 기반 기능을 제공 |
spring-webmvc | 스프링 기반의 MVC 프레임워크, 웹 애플리케이션을 개발하는데 필요한 컨트롤러, 뷰 구현을 제공 |
spring-websocket | 스프링 MVC에서 웹 소켓 연동을 처리할 수 있도록 제공 |
spring-oxm | XML과 자바 객체간의 매핑을 처리하기 위한 API 제공 |
spring-tx | 트랜잭션 처리를 위한 추상 레이어를 제공 |
spring-jdbc | JDBC 프로그래밍을 보다 쉽게 할 수 있는 템플릿 제공 |
spring-orm | Hibernate, JPA, Mybatis 등과의 연동을 지원 |
spring-jms | JMS 서버와 메시지를 쉽게 주고 받을 수 있도록 하기 위한 템플릿 |
spring-context-support | 스케쥴링, 메일발송, 캐시연동, 벨로시티 등 부가 기능을 제공 |
Spring의 동작 구조 - Spring 애플리케이션
Spring IOC (Inversion of Control)
- IoC(제어 반전) 란?
IoC란, Inversion of Control의 약자로 프로그램을 구동하는데 필요한 객체에 대한 생성, 변경 등의 관리를 프로그램을 개발하는 사람이 아닌 프로그램을 구동하는 컨테이너에서 직접 관리하는 것을 말한다.
스프링은 IoC 구조를 통해 구동 시 필요한 객체의 생성부터 생명주기까지 해당 객체에 대한 관리를 직접 수행함
왜 이렇게 제어를 하지? -> 객체들 간의 결합도를 낮추기 위해.
IoC 컨테이너
스프링에서 관리하는 객체를 'Bean(빈)'이라고 하고, 해당 빈들을 관리한다는 의미로 컨테이너를 'Bean Factory'라고 한다.
최상위 Bean Factory를 확장하여 만들어진게 ApplicationContext라고 보면 된다.
IoC 컨테이너의 역할
1. 객체의 생명주기와 의존성을 관리
2. VO (DTO / POJO) 객체의 생성, 초기화, 소멸 등의 처리를 담당
3. 개발자가 직접 객체를 생성할 수 있지만 해당 권한을 컨테이너에 맡김으로써 소스 코드 구현의 시간 단축 가능
IoC 컨테이너와 Bean 객체
Bean 빈 |
- 스프링이 IoC 방식으로 관리하는 Class - 스프링이 직접 생성과 제어를 담당하는 객체 |
Bean Factory 빈 팩토리 |
- 스프링의 IoC를 담당하는 핵심 컨테이너 - Bean을 등록, 생성, 조회, 반환하는 기능을 담당 |
ApplicationContext 애플리케이션 컨텍스트 |
- BeanFactory를 확장한 IoC 컨테이너 - Bean을 등록하고 관리하는 기능은 BeanFactory와 동일하지만 스프링이 제공하는 각종 부가 서비스를 추가로 제공함 |
GenericXmlApplicationContext | - ApplicationContext를 구현한 Class - 일반적으로 XML 형태의 문서를 읽어 컨테이너 역할을 수행 |
Configuration metadata 설정 메타 정보 |
- ApplicationContext 또는 BeanFactory가 IoC를 적용하기 위해 사용하는 설정 정보 - 설정 메타 정보는 IoC 컨테이너에 의해 관리되는 Bean 객체를 생성하고 구성할 때 사용 |
Spring DI - DI(의존성 주입)의 정의와 장점
DI란, Dependency Injection의 약자로 IoC 구현의 핵심 기술이라고 할 수 있다.
사용하는 객체를 직접 생성하여 만드는 것이 아니라 컨테이너가 빈의 설정 정보를 읽어와 자동으로 해당 객체에 연결하는 것을 의미한다. 이렇게 의존성을 주입 받게 되면 이후 해당 객체를 수정해야 할 상황이 발생했을 때 소스 코드의 수정을 최소화 할 수 있다.
DI의 장점
1. 개발자가 작성해야 할 코드가 단순해진다.
2. 각 객체간의 종속 관계(결합도)를 해소할 수 있다.
* 객체간의 종속 관계 (결합도)? : 한 클래스에서 필드 객체를 생성할 때 발생하는 두 객체간의 관계를 말하며, 각 객체간의 내용이 수정이 될 경우 영향을 미치는 정도를 나타낸다.
-> 하나의 클래스가 변경되면 연결되어있는 다른 클래스들도 변경되어야 하는 상황 발생 -> 그런 경우가 많아질 수록 결합도가 높다고 표현함. -> 결합도가 높을 경우 테스트, 재활용 어려움 -> 결합도를 낮추기 위해 DI를 사용한다.
Spriing DI(Dependency Injection) 종류
- Setter 메소드를 통한 의존성 주입
: 의존성을 주입 받는 Setter 메소드를 만들고, 이를 통해 의존성을 주입
: Setter 메소드를 통해 의존 관계가 있는 Bean을 주입하려면 <property> 태그를 사용한다.
- 생성자를 통한 의존성 주입
: 필요한 의존성을 포함하는 클래스에 생성자를 만들고, 이를 통해 의존성을 주입
: Constructor를 통해 의존 관계가 있는 Bean을 주입하려면 <constructor-arg> 태그를 사용한다.
- 메소드를 통한 의존성 주입
: 의존성을 입력받는 일반 메소드를 만들고 이를 통해 의존성을 주입
실습
- 자주 쓰는 생성(new) 메뉴들만 볼 수 있게 설정하기(mac 기준)
window -> perspective -> customize Perspective
shortcuts 메뉴에서 설정할 수 있다.
그리고 file-> new 들어가면 자주 쓰는 것들만 세팅완료
Spring Legacy Project 생성
Spring MVC Project -> 파일명 02_SpringDI -> Next -> 프로젝트의 베이스 패키지라고 생각하면 된다.(도메인의 역순)
com.kh.프로젝트명으로 생성
아래의 구조처럼 프로젝트가 생성된다.
프로젝트 전체 구조
main 폴더 내 구조
웹 컨테이너 설정에 필요한 xml 파일들 -> web.xml
spring -> spring 설정파일들
WEB-INF -> views 들어가있는 이유 : WEB-INF 폴더는 주소를 통해 직접 접근 불가능함. -> 모든 요청이 Controller 역할이 받고 -> controller에서 해당하는 view에 forwarding 시키는 구조 때문
resources 폴더가 두 개인 이유
-> webapp-> resources : 웹에 필요한 js, css, 이미지 파일들이 위치
-> main -> resources : 프로젝트 설정에 필요한 설정 파일들이 위치
webapp 폴더 내의 구조
프로젝트 생성후 pom.xml에서 자바와 스프링프레임워크에 대한 설정 변경
java-version은 11로, spring framework에 대한 버전 정보는 1. 홈페이지에서 알 수 있는데 GA가 테스트까지 완료된 버전이라고 생각하면 된다.
2. mvnrepository에서 spring 검색 후 버전 확인 가능
버전 설정 후 maven-> project update
최신 버전으로 변경된 것을 확인할 수 있다.
Spring 내의 version을 직접적으로 수정해주지 않고, properties 내의 버전명만 수정해줘도 자동으로 변경된다.
그 다음 Servlet 버전 수정 -> mvnrepository에서 확인
pom.xml에 붙여넣어줌
sevlet jsp도 수정
4개 추가해줬다.
<!-- JSTL -->
<dependency>
<groupId>org.apache.taglibs</groupId>
<artifactId>taglibs-standard-impl</artifactId>
<version>1.2.5</version>
</dependency>
<dependency>
<groupId>org.apache.taglibs</groupId>
<artifactId>taglibs-standard-spec</artifactId>
<version>1.2.5</version>
</dependency>
<dependency>
<groupId>org.apache.taglibs</groupId>
<artifactId>taglibs-standard-jstlel</artifactId>
<version>1.2.5</version>
</dependency>
<dependency>
<groupId>org.apache.taglibs</groupId>
<artifactId>taglibs-standard-compat</artifactId>
<version>1.2.5</version>
</dependency>
maven-eclipse-plugin의 version을 2.10로 수정해줬다.
version, source, target 수정
그리고 run on server 하면 jar파일을 못 찾는다고 500에러가 뜬다. -> 버그인듯..?
-> WEB-INF -> lib 폴더 생성 후 이전 MVC2때 썼던 taglibs 파일들 4개를 가지고 와서 복붙해준다.
그리고 다시 run on server 해주면 정상출력된다.
근데 인코딩이 안돼있네? -> web.xml파일에 필터 등록만 해주면 됨
<!--
UTF-8 인코딩 필터 등록
: 필터를 직접 만들어서 등록해도 되지만, 스프링에서 인코딩 필터를 제공하고 있기 때문에 web.xml에 필터를 등록해 주기만 하면 된다.
-->
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
인코딩 성공
화면은 WEB-INF -> home.jsp를 사용자가 WEB-INF에 직접 접근이 불가능 하기 때문에 java-> com-> di-> homeController.java에서 @RequestMapping에 지정해놓은 메소드를 통해 접근하여 출력해준다.
"home"를 찾아서 출력해줌
-> 서블릿 역할을 함.
기존 01_Mybatis에서 작성했었던 lombok, test 라이브러리를 02_SpringDI -> pom.xml에 복붙
<!-- lombok 라이브러리 -->
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
<!-- scope : 라이브러리가 실행될 시점 설정 / provided : 컴파일할 때만 사용되며, 배포시에는 사용되지 않음 -->
<scope>provided</scope>
</dependency>
<!-- Test 관련 라이브러리 -->
<!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-api -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.8.2</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-params -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>5.8.2</version>
<scope>test</scope>
</dependency>
<!-- 테스트를 더 효율적으로 검증할 수 있는 관련 라이브러러 -->
<!-- https://mvnrepository.com/artifact/org.assertj/assertj-core -->
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.21.0</version>
<scope>test</scope>
</dependency>
Maven Dependencies에 추가된 것 까지 확인
DI, AOP는 TDD방식으로 진행
1. 종속관계를 만들어준다.
)) 사람 -> 애완동물이 있다 -> 애완동물을 들고 다니는 관계를 객체지향으로 만들어 볼 것
-> Owner -> pet 관계
1-1) Owner 클래스 생성
Owner -> command+1 눌러서 JUnit test case 생성
test밑에 동일한 경로로 OwnerTest 클래스 생성됨
테스트가 가능한 환경인지 테스트
Owner 객체를 만들어서 문제 없는지 테스트
Owner.java로 돌아와서 아래처럼 작성해줌
package com.kh.di.owner;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class Owner {
private String name;
private int age;
private Dog dog;
}
Dog에서 발생한 오류 해결을 위해 pet클래스 생성해주고 아래 코드처럼 작성해준다.
package com.kh.di.pet;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class Dog {
private String name;
public String bark() {
return "멍멍!";
}
}
이제 new Owner에 정보 입력 후
Owner안에 있는 Dog에 대한 확인도 가능
Owner객체에서 Dog객체를 참조하고 있는 형태로 만들었다.
-> 이런 관계를 종속관계라고 한다.
-> 주인이 잘 키우고 있었는데 고양이가 키우고 싶다.? -> pet을 고양이로 바꿔야 함 -> Owner는 현재 코드 상으로 보면 강아지 밖에 가질 수 없음
왜냐? -> Owner.java안에 Dog밖에 없기 때문 -> 고양이를 만들어주고 싶다면 고양이 객체를 추가 시켜줘야 함
-> pet 폴더 내에 cat클래스를 만들어준다.
-> 그리고 Owner객체를 사용하는 쪽에서 고양이를 만들어서 넘겨줘야 한다.
-> 에러 발생
왜?
-> Owner는 강아지만 받을 수 있기 때문이다
-> Dog대신 Cat으로 바꿔줘야 함
-> 그리고 getDog을 Cat으로도 바꿔줘야 에러가 사라짐
-> 테스트 시 통과함
1. Owner에게 고양이 객체만 만들어서 넘겨주면 문제가 없을 거라 생각하고 new Cat("나비")를 작성해 주었으나, 참조관계에 있는 코드, 실행하는 테스트 코드 등 연관된 객체들간에 수정이 생겼다. -> 객체들 간의 수정이 많이, 자주 발생하는 것을 객체들간의 결합도가 높다고 얘기한다.
결합도가 높으면 높을수록? -> 코드 재사용 어렵고, 추가 에러 발생 확률, 유지보수 어려움의 단점들이 생김
그럼 결합도를 낮춰주면 된다. -> 다형성, 생성자 주입을 통해 결합도를 낮춰줬다. -> 하나의 타입으로 여러 타입의 객체를 담을 수 있게 구현하는 것 -> 상속, 인터페이스 구현 등을 이용한다. (하나의 클래스를 수정 시 다른 클래스에서의 수정을 최소화하기 위함)
다형성을 통해 결합도를 낮춰주기
pet 폴더에 Pet 인터페이스 생성
package com.kh.di.pet;
public interface Pet {
// 추상 메소드 생성 (Cat, Dog 공통적으로 들어가는 메소드를 추상화 해줌)
public String bark();
}
추상메소드 생성 후
Cat, Dog클래스에 Pet을 상속 시켜주고, 공통적인 특성들인 bark를 @Override를 통해 추가해준다.
그러면 Owner에서 Pet을 통해서 원하는 pet으로 가지고 올 수 있다.
-> 객체간의 결합도를 낮춰줬다.
-> 실제로 실행하는 부분에서는 코드를 직접 수정해줘야함 (OwnerTest.java) -> 객체들간의 결합도를 완전히 제거 할 수는 없고, 최대한 느슨하게 만들어줄 수는 있다.
-> 기존 java에서 했던 방법
그럼 Spring에서는 어떻게? -> DI 활용
-> 객체들의 관계를 프레임워크에게 알려줘야 한다.
1. xml을 통한 설정
2. 자바 설정 파일을 통해 설정
3. 어노테이션을 통해 설정
Spring DI - XML 방식으로 bean 생성 -> bean들끼리 연결시켜주기
Spring XML 방식?
: Spring 컨테이너 구동 시 Spring의 환경 설정 관련된 xml 파일을 불러오는데, 이 파일에 bean, aop, transaction 등 여러 사항을 다 작성하여 구동하는 방식이다.
-> XML 파일에 객체들에 대한 정보, 관계설정 정보들이 들어있는 환경 설정 파일이라고 생각하면 된다.
Spring XML 기본 설정 - 구조, 주요 태그
XML 구조 : <beans> 태그를 최상위 태그로 하여 <beans> 태그 안에 다양한 태그로 값을 설정할 수 있다.
XML 주요 태그
태그명 | 내용 | 속성 |
<beans> | xml 파일의 최상위 태그로 여러가지 namespace를 설정 | namespace: p, c, aop, tx, mvc 등 |
<bean> | 스프링에서 사용할 POJO 객체를 등록할 때 사용 | Id, class, scope |
<import> | 설정 파일을 불러오는 태그 | resource |
* namespace : 설정을 간편하게 도와주는 기능
Spring XML 태그 - <bean>
<bean> : POJO 객체를 컨테이너에 등록하여 컨테이너가 사용할 수 있게 만드는 태그
작성법 : <bean id | name="명칭" class="클래스 풀네임" [기타 옵션] />
* 기본적으로 id를 많이 사용하고, 클래스는 해당 클래스를 포함한 풀네임을 입력해줘야 한다.
<bean> 속성
속성 | 속성명 | 내용 |
기본 속성 | id [="String"] | 객체 생성 시 사용하는 변수라고 보면 된다. 명명 규칙 : 낙타 표기법 사용 / 자바 식별자 작성 규칙 적용 |
name [="String"] | 자바 식별자 작성 규칙을 따르지 않음 / 특수 기호 포함 시 사용 | |
class [="클래스 풀네임"] | POJO 객체를 지정 / 패키지를 포함한 클래스명 작성 | |
기타 속성 | init-method [="메소드명"] | 객체 생성 후 초기화 하거나 실행되어야 할 기능이 있는 경우 |
destroy-method [="메소드명"] | 객체 삭제 되기 전에 실행되어야 할 기능이 있는 경우 | |
lazy-init [="true | false"] | 객체가 즉시 로딩되지 않고 사용 시 로딩 선택(true) | |
scope [="설정값"] | 객체 생성 방식을 지정 |
* scope 설정 값
- singleton : spring 컨테이너 내에 단 하나의 객체만 생성(default)
- prototype : 다수의 객체가 존재할 수 있음 / 객체가 필요할 때마다 새로 생성되어 줌
<bean> 내부 태그
- <constructor-arg>
: <bean>으로 지정된 객체가 생성될 때 default 생성자가 아닌 매개변수가 있는 생성자로 생성하여 초기값을 대입 (단, 일치하는 매개변수가 있는 생성자가 있어야 한다.)
# 객체의 생성자와 매핑된다. 변수 선언 순서대로 대입
- <property>
: <bean>으로 지정된 객체의 필드에 값을 대입할 때 사용
<bean>으로 지정된 객체의 필드로 있는 collection에 값을 대입할 때 사용, 초기값을 대입(단, 일치하는 setter 메소드가 있어야 함)
# 객체의 setter 메소드와 매핑됨 (명칭 일치해야 함)
Spring XML 방식 장/단점
- XML 단독 사용 : 모든 Bean을 명시적으로 XML에 등록하는 방법
장점
- 생성되는 모든 Bean을 XML에서 확인할 수 있다. -> 유지보수 용이
- 프로젝트를 운영하는 입장에서 관리의 편의성이 높다.
단점
- Bean의 개수가 많아지면 XML파일을 관리하기 어렵다.
- 여러 개발자가 같은 설정 파일을 수정하면 설정에 충돌이 발생할 수 있다.
- DI에 필요한 적절한 setter 메소드 또는 생성자가 코드 내부에 반드시 존재해야 된다.
Spring을 통해 XML로 객체들의 관계 설정 후 가져오는 실습
> root-context.xml
Owner 생성
-> root-context.xml의 bean에서 발생하는 오류는 기본생성자가 없어서 발생하는 오류 -> Owner.java -> @NoArgsConstructor 추가시켜줌으로써 해결
고양이 객체도 만들어준다. -> cat.java에 @NoArgsConstructor 추가해줌
pet 부분의 ref로 bean끼리 이어준다. 이런 과정을 의존성 주입(DI)라고 한다.
-> 설정 파일을 작성한다고 해서 스프링이 바로 객체로 만들어 주지 않음
-> 스프링에 있는 애플리케이션 컨텍스트가 읽어가서 이 내용대로 bean을 만든 다음 관리하게 됨
애플리케이션 컨텍스트를 통해 객체를 가져오기
// 애플리케이션 컨텍스트를 통해 객체를 가져오기
@Test
public void contextTest() {
// new GenericXmlApplicationContext(xml 파일의 위치 지정");
GenericXmlApplicationContext context =
new GenericXmlApplicationContext("WEB-INF/spring/root-context.xml");
// object로 가져온 후 형변환
// Owner owner = (Owner) context.getBean("owner");
// 두 번째 매개값에 bean을 가져올 때 변환해서 가져올 타입 명시
Owner owner = context.getBean("owner", Owner.class);
}
ApplicationContext를 통해 객체를 가져올 때 두 가지 방법이 있는데,
1. Object로 가져온 뒤 형변환
2. 두 번째 매개값에 bean을 가져올 때 변환해서 가져올 타입 명시
assertThat으로 테스트 진행 시 오류 발생 -> 클래스 패스 상의 있는 xml 파일이어야 테스트 진행이 가능하므로 추가 시켜준다.
(Object owner -> Owner owner로 수정해줌)
클래스패스에 webapp을 추가시켜줬다.
그러면 오류가 사라지고 테스트 진행도 통과함
콘솔로도 확인
root-context.xml 파일을 resource로 복사해준다 -> build path내의 webapp도 지워줌
그리고 main/resources/spring밑으로 root-context.xml 파일을 옮겨주고, webapp 밑에 있던 root-context.xml에서 작성했던 bean들은 지워준다.
그리고 contextTest에서 xml 위치를 WEB-INF를 지워준다. -> 클래스패스 상에 있기 때문
// 애플리케이션 컨텍스트를 통해 객체를 가져오기
@Test
public void contextTest() {
// 스프링의 애플리케이션 컨텍스트를 활용하여 객체 간의 결합을 더욱 느슨하게 만들어준다.
// new GenericXmlApplicationContext(클래스패스 상의 xml 파일의 위치 지정");
GenericXmlApplicationContext context =
new GenericXmlApplicationContext("spring/root-context.xml");
// object로 가져온 후 형변환
// Owner owner = (Owner) context.getBean("owner");
// 두 번째 매개값에 bean을 가져올 때 변환해서 가져올 타입 명시
Owner owner = context.getBean("owner", Owner.class);
System.out.println(owner);
assertThat(owner).isNotNull();
assertThat(owner.getPet()).isNotNull();
}
고양이를 강아지로 바꿔보기
기본 생성자를 통해 만들어줄 수 있도록 @NoArgsConstructor 추가
-> setter를 통해 이름을 주입시켜주고 강아지를 owner에 연결해주는 작업을 진행할 것임
root-context.xml에 id값이 dog인 bean 추가 -> 애플리케이션 컨텍스트가 bean을 보고 Dog 클래스의 기본생성자를 통해 객체를 만들어준다.
<!--
Pet pet = new Dog();
pet.setName("댕댕이");
-->
<bean id="dog" class="com.kh.di.pet.Dog">
<property name="name" value="댕댕이"/>
</bean>
그리고 constructor-arg의 ref를 cat에서 dog으로 바꿔주면
pet이 cat에서 dog로 바뀐걸 확인할 수 있다.
실습 전체 코드
> OwnerTest.java
package com.kh.di.owner;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.context.support.GenericXmlApplicationContext;
import com.kh.di.pet.Cat;
import com.kh.di.pet.Dog;
class OwnerTest {
@Test
@Disabled
public void nothing() {
}
@Test
public void create() {
// 기존에 자바 애플리케이션에서는 다형성과 생성자 주입을 통해 객체간의 결합을 느슨하게 만들어 준다.
Owner owner = new Owner("홍길동", 20, new Cat("나비"));
assertThat(owner).isNotNull();
assertThat(owner.getPet()).isNotNull();
}
// 애플리케이션 컨텍스트를 통해 객체를 가져오기
@Test
public void contextTest() {
// 스프링의 애플리케이션 컨텍스트를 활용하여 객체 간의 결합을 더욱 느슨하게 만들어준다.
// new GenericXmlApplicationContext(클래스패스 상의 xml 파일의 위치 지정");
GenericXmlApplicationContext context =
new GenericXmlApplicationContext("spring/root-context.xml");
// object로 가져온 후 형변환
// Owner owner = (Owner) context.getBean("owner");
// 두 번째 매개값에 bean을 가져올 때 변환해서 가져올 타입 명시
Owner owner = context.getBean("owner", Owner.class);
System.out.println(owner);
assertThat(owner).isNotNull();
assertThat(owner.getPet()).isNotNull();
}
}
> root-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- Root Context: defines shared resources visible to all other web components -->
<!--
기존 방식 :
Owner owner = new Owner("홍길동", 20, new Cat("나비"));
-->
<bean id="owner" class="com.kh.di.owner.Owner">
<constructor-arg name="name" value="홍길동" />
<constructor-arg name="age" value="20" />
<constructor-arg name="pet" ref="dog" />
</bean>
<!--
Pet pet = new Cat("나비")
-->
<bean id="cat" class="com.kh.di.pet.Cat">
<constructor-arg name="name" value="나비"/>
</bean>
<!--
Pet pet = new Dog();
pet.setName("댕댕이");
-->
<bean id="dog" class="com.kh.di.pet.Dog">
<property name="name" value="댕댕이"/>
</bean>
</beans>
> homecontroller.java
package com.kh.di;
import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
/**
* Handles requests for the application home page.
*/
@Controller
public class HomeController {
private static final Logger logger = LoggerFactory.getLogger(HomeController.class);
/**
* Simply selects the home view to render by returning its name.
*/
@RequestMapping(value = "/", method = RequestMethod.GET)
public String home(Locale locale, Model model) {
logger.info("Welcome home! The client locale is {}.", locale);
Date date = new Date();
DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale);
String formattedDate = dateFormat.format(date);
model.addAttribute("serverTime", formattedDate );
return "home";
}
}
> dog.java
package com.kh.di.pet;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Dog implements Pet{
private String name;
@Override
public String bark() {
return "멍멍!";
}
}
> cat.java
package com.kh.di.pet;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Cat implements Pet{
private String name;
@Override
public String bark() {
return "야옹~";
}
}
> Owner.java
package com.kh.di.owner;
import com.kh.di.pet.Cat;
import com.kh.di.pet.Dog;
import com.kh.di.pet.Pet;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Owner {
private String name;
private int age;
private Pet pet;
}
'국비지원_Java > Java Programming_2' 카테고리의 다른 글
자바 프로그래밍_Day_103_Spring DI (0) | 2022.01.27 |
---|---|
자바 프로그래밍_Day_102_인터페이스 구현 평가 (0) | 2022.01.21 |
자바 프로그래밍_Day_100_Framework : MyBatis 정리 / log4j (0) | 2022.01.19 |
자바 프로그래밍_Day_99_MyBatis 게시글 등록 / 수정 / 삭제 (0) | 2022.01.16 |
자바 프로그래밍_Day_98_Framework : MyBatis 동적 쿼리 / 게시글 조회 (0) | 2022.01.12 |