- 스프링 개요
- 프로젝트 생성
- 의존 객체
- 설정 및 구현
- 연결
스프링 프레임워크는 주요 기능으로 DI
AOP
MVC
JDBC
등을 제공한다.
스프링 프레임워크에서 제공하고 있는 모듈로는 다음과 같은 것들이 있다.
spring-core
: 스프링의 핵심인 DI 와 IoC 를 제공
spring-aop
: AOP 구현 기능 제공
spring-jdbc
: 데이터베이스를 적은 양의 코드로 쉽게 다룰 수 있는 기능 제공
spring-tx
: 스프링에서 제공하는 트랜잭션 관련 기능 제공
spring-webmvc
: 스프링에서 제공하는 컨트롤러 뷰 모델을 이용한 MVC 구현 기능 제공
스프링에서 제공하고 있는 모듈을 사용하려면 모듈에 대한 의존설정을 개발 프로젝트에 XML 파일 등을 이용해 개발자가 직접 하면 된다.
스프링 컨테이너는 IoC 컨테이너 또는 DI 컨테이너라고도 한다.
스프링에서 객체를 생성하고 조립하며 컨테이너를 통해 생성된 객체를 Bean
이라고 한다.
XML 문서 등을 통해 객체 생성 및 속성 데이터 작성
➡️ 스프링 컨테이너에서 객체 생성 및 조립
➡️ 자바 문서 등 개발 문서에서 애플리케이션을 구현
maven 은 라이브러리를 연결해주고 빌드를 위한 플랫폼으로 메이븐을 이용해 프로젝트를 생성한다. 이후 필요한 모듈을 가져오기 위한 메이븐 설정파일인 pom.xml
을 작성해 필요한 라이브러리만 다운로드해 사용할 수 있다.
모든 스프링 프로젝트의 기본 구조는 다음과 같다.
pjt
: 스프링 프로젝트 root
pjt/src/main/java
: 앞으로 만들어지는 자바 파일들이 관리되는 폴더
pjt/src/main/resources
: 자원을 관리하는 폴더로 스프링 설정 파일인 xml 또는 프로퍼티 파일 등이 관리됨
기본적인 스프링 프로젝트 생성 방법은 다음과 같다.
testPjt/pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>spring4</groupId>
<artifactId>testPjt</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.1.0.RELEASE</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>utf-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
</project>
필요한 모듈을 가져오기 위한 메이븐 설정파일을 생성한다.
testPjt/src/main/resources/applicationContext.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
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="tWalk" class="testPjt.TransportationWalk"/> // 스프링 컨테이너에 객체 생성
</beans>
위와 같이 스프링 설정파일을 작성한다.
testPjt/src/main/java/MainClass.java
public class MainClass {
public static void main(String[] args) {
// TransportationWalk transportationWalk = new TransportationWalk();
// transportationWalk.move();
GenericXmlApplicationContext ctx = new GenericXmlApplicationContext("classpath:applicationContext.xml"); // 컨테이너 생성
TransportationWalk transportationWalk = ctx.getBean("tWalk", TransportationWalk.class); // 컨테이너로부터 bean 접근
transportationWalk.move();
ctx.close(); // 리소스 반환
}
}
컨테이너를 생성하고 객체 빈에 접근한다.
큰 객체 안에 또 다른 작은 객체가 있는 경우 의존 주입 DI(Dependency Injection)
된 상태라고 한다.
예를 들어 로봇 장난감 객체 안 배터리 객체를 의존 주입된 상태로 볼 수 있다.
public StudentAssembler() {
studentDao = new StudentDao();
registerService = new StudentRegisterService(studentDao);
modifyService = new StudentModifyService(studentDao);
deleteService = new StudentDeleteService(studentDao);
selectService = new StudentSelectService(studentDao);
allSelectService = new StudentAllSelectService(studentDao);
}
위 코드에서 각각의 등록 · 수정 · 삭제 · 검색 서비스는 studentDao 객체에 의존하고 있다.
<bean id="studentDao" class="ems.member.dao.StudentDao" ></bean>
<bean id="registerService" class="ems.member.service.StudentRegisterService">
<constructor-arg ref="studentDao"></constructor-arg>
</bean>
<bean id="modifyService" class="ems.member.service.StudentModifyService">
<constructor-arg ref="studentDao"></constructor-arg>
</bean>
<bean id="deleteService" class="ems.member.service.StudentDeleteService">
<constructor-arg ref="studentDao"></constructor-arg>
</bean>
<bean id="selectService" class="ems.member.service.StudentSelectService">
<constructor-arg ref="studentDao"></constructor-arg>
</bean>
<bean id="allSelectService" class="ems.member.service.StudentAllSelectService">
<constructor-arg ref="studentDao"></constructor-arg>
</bean>
앞에서 작성한 자바 코드를 다음과 같이 스프링 컨테이너를 이용해 작성할 수 있다.
public StudentRegisterService(StudentDao studentDao) {
this.studentDao = studentDao;
}
위 자바 코드를 아래와 같은 스프링 코드로 변환할 수 있다.
<bean id="studentDao" class="ems.member.dao.StudentDao"></bean>
<bean id="registerService" class="ems.member.service.StudentRegisterService">
<constructor-arg ref="studentDao"></constructor-arg>
</bean>
public void setJdbcUrl(String jdbcUrl) {
this.jdbcUrl = jdbcUrl;
}
public void setUserId(String userId) {
this.userId = userId;
}
public void setUserPw(String userPw) {
this.userPw = userPw;
}
위 자바 코드를 아래와 같은 스프링 코드로 변환할 수 있다.
<bean id="dataBaseConnectionInfoDev" class="ems.member.DataBaseConnectionInfo">
<property name="jdbcUrl" value="jdbc:oracle:thin:@localhost:1521:xe"/>
<property name="userId" value="scott"/>
<property name="userPw" value="tiger"/>
</bean>
프로퍼티 네임 생성 규칙은 set 을 제거한 뒤 첫 글자를 소문자로 변경한다.
public void setDevelopers(List<String> developers) {
this.developers = developers;
}
List 타입의 경우 다음과 같은 스프링 코드로 변환할 수 있다.
<property name="developers">
<list>
<value>Cheney.</value>
<value>Eloy.</value>
<value>Jasper</value>
<value>Dillon.</value>
<value>Kian.</value>
</list>
</property>
리스트는 값이 여러 개이므로 list 태그 안에 value 속성을 이용해 데이터를 주입한다.
public void setAdministrators(Map<String, String> administrators) {
this.administrators = administrators;
}
Map 타입의 경우 다음과 같은 스프링 코드로 변환할 수 있다.
<property name="administrators">
<map>
<entry>
<key>
<value>Cheney<value>
</key>
<value>cheney@springPjt.org<value>
</entry>
<entry>
<key>
<value>Jasper<value>
</key>
<value>jasper@springPjt.org<value>
</entry>
</map>
</property>
map 과 entry 태그 안에 key 와 value 태그를 이용해 데이터를 주입한다.
스프링 설정 파일을 효율적으로 관리하기 위해 설정 파일을 분리할 수 있으며 일반적으로 기능별로 분리한다.
// 각각의 분리된 파일을 모두 가져오기
String[] appCtxs = {"classpath:appCtx1.xml", "classpath:appCtx2.xml", "classpath:appCtx3.xml"};
GenericXmlApplicationContext ctx = new GenericXmlApplicationContext(appCtxs);
// 하나의 xml 파일에 모두 import 한 뒤 해당 파일만 가져오기
GenericXmlApplicationContext ctx = new GenericXmlApplicationContext("classpath:appCtxImport.xml");
스프링 컨테이너에서 생성된 빈 객체의 경우 동일한 타입에 대해서는 기본적으로 객체가 한 개만 생성되며 getBean()
메소드로 호출될 때 동일한 객체가 반환된다.
싱글톤과 반대 개념으로 getBean()
메소드를 호출할 때마다 새로운 객체가 생성되며 개발자가 스프링 설정 파일에서 빈 객체를 정의할 때 scope 속성을 별도 명시한다.
<bean id="dependencyBean" class="scope.ex.DependencyBean" scope="prototype">
<constructor-arg ref="injectionBean">
<property name="injectionBean" ref="injectionBean"/>
</bean>
<constructor-org>
또는 <property>
태그로 의존 대상 객체를 명시하지 않아도 스프링 컨테이너가 자동으로 필요한 의존 대상 객체를 찾아서 주입해 주는 기능이다. 어노테이션을 이용할 때는 우선 스프링 설정 파일에 <context:annotation-config>
태그를 입력 후 빈 객체를 생성해야 한다.
주입하려고 하는 객체의 타입이 일치하는 객체를 자동으로 주입한다. Autowired 어노테이션은 생성자 뿐만 아니라 프로퍼티나 메서드에도 사용할 수도 있으며 이 때는 반드시 디폴트 생성자를 명시해야 한다.
주입하려고 하는 객체의 이름이 일치하는 객체를 자동으로 주입한다. Resource 어노테이션은 생성자에는 사용하지 못하고 프로퍼티나 메서드에 사용 가능하며 반드시 디폴트 생성자를 명시해야 한다.
Autowired 와 거의 유사하다. 차이점은 Autowired 의 경우 required 속성을 이용해 의존대상 객체가 없어도 익셉션을 피할 수 있지만 Inject 는 required 속성을 지원하지 않는다.
동일한 타입의 객체가 두 개 이상인 경우 스프링 컨테이너는 자동 주입 대상 객체를 판단하지 못해서 Exception 을 발생시킨다.
<bean id="wordDao1" class="com.word.dao.WordDao">
<qualifier value="usedDao"/> <!-- usedDao 객체를 주입 -->
</bean>
<bean id="wordDao2" class="com.word.dao.WordDao"/>
<bean id="wordDao3" class="com.word.dao.WordDao"/>
따라서 qualifier 태그를 사용해 주입할 객체를 지정해준다.
@Autowired
@Qualifier("usedDao")
private WordDao wordDao;
빈 객체의 생명주기는 스프링 컨테이너의 생명주기와 같다.
즉 스프링 컨테이너가 초기화 될 때 빈 객체가 생성 및 주입되며 스프링 컨테이너가 종료될 때 빈 객체 또한 소멸된다.
빈 객체를 생성 및 소멸시킬 때 특정 작업을 하기 위한 방법으로 다음 두 가지가 있다.
public class BookRegisterService implements InitializingBean, DisposableBean {
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("bean 객체 생성");
}
@Override
public void destroy() throws Exception {
System.out.println("bean 객체 소멸");
}
}
위와 같이 InitializingBean 과 DisposableBean 인터페이스를 구현할 수 있다.
<bean id="bookRegisterService" class="com.brms.book.service.BookRegisterService"
init-method="initMethod" destroy-method="destroyMethod"/>
속성값을 스프링 설정파일의 빈 객체를 생성하는 코드에 넣어 사용한다.
public class BookRegisterService implements InitializingBean, DisposableBean {
public void initMethod() {
System.out.println("BookRegisterService 빈(Bean)객체 생성 단계");
}
public void destroyMethod() {
System.out.println("BookRegisterService 빈(Bean)객체 소멸 단계");
}
}
이 때 스프링 설정파일에서 지정한 속성값과 똑같은 메서드를 해당 빈에 만들어 사용한다.
xml 을 이용한 스프링 설정 파일 생성을 자바 파일로 생성할 수 있다.
@Configuration // 스프링 설정 파일
public class MemberConfig {
// 기본 객체 생성
@Bean // 빈 객체 생성
public StudentDao studentDao() {
return new StudentDao();
}
// 생성자를 이용한 객체 생성
@Bean
public StudentRegisterService registerService() {
return new StudentRegisterService(studentDao());
}
// 프로퍼티를 이용한 객체 생성
@Bean
public DataBaseConnectionInfo dataBaseConnectionInfoDev() {
DataBaseConnectionInfo infoDev = new DataBaseConnectionInfo();
infoDev.setJdbcUrl("jdbc:oracle:thin:@localhost:1521:xe");
infoDev.setUserId("scott");
infoDev.setUserPw("tiger");
return infoDev;
}
@Bean
public EMSInformationService informationService() {
EMSInformationService info = new EMSInformationService();
// List 타입 객체 생성
ArrayList<String> developers = new ArrayList<String>();
developers.add("Cheney.");
developers.add("Eloy.");
developers.add("Jasper.");
developers.add("Dillon.");
developers.add("Kian.");
info.setDevelopers(developers);
// Map 타입 객체 생성
Map<String, String> administrators = new HashMap<String, String>();
administrators.put("Cheney", "cheney@springPjt.org");
administrators.put("Jasper", "jasper@springPjt.org");
info.setAdministrators(administrators);
return info;
}
}
컨테이너 생성 방법은 다음과 같다.
// xml 을 이용한 컨테이너 생성
GenericXmlApplicationContext ctx = new GenericXmlApplicationContext("classpath:applicationContext.xml");
// 어노테이션을 이용한 컨테이너 생성
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(MemberConfig.class);
웹 프로그래밍을 구축하기 위한 설계 Model 1 과 Model 2 구조는 다음과 같다.
Model 1 은 클라이언트가 Request 요청을 하면 웹 어플리케이션 서버인 was 는 요청을 처리하고 데이터베이스에 접근해 얻은 결과를 가공해 클라이언트에게 다시 html 코드로 Response 응답한다.
즉 Model 1 은 html 파일 안에 java 코드나 각종 태그들이 존재하며 빠른 개발이 장점이지만 유지보수가 어렵다.
Model 2 는 Model 1 을 보완한 방식으로 먼저 클라이언트의 요청을 Controller 가 받아서 많은 Service 중 어떤 Service 에게 다음 작업을 요청할지 컨트롤한다. 이후 Service 는 DAO 를 통해 데이터베이스에 접근한 뒤 Model 객체로 데이터베이스와 통신한다.
최종적으로 얻어온 값을 Controller 는 View 객체를 생성해 클라이언트에게 응답한다.
대부분의 웹 프로그래밍 개발에서 사용하는 방식으로 MVC 를 기본으로 하며 유지보수가 용이하다.
🎈 스프링 MVC 프레임워크 설계 구조
클라이언트의 요청을 DispatcherServlet
객체가 받음
DispatcherServlet
은 많은 Controller 중 가장 적합한 Controller 를 찾아주는 HandlerMapping
에게 요청
DispatcherServlet
은 해당 Controller 에서 가장 적합한 메서드를 찾아주는 HandlerAdapter
에게 요청 후 처리 결과를 Model 데이터로 가져옴
DispatcherServlet
은 뷰에 해당하는 ViewResolver
에게 Model 과 View 정보를 갖고 있는 Controller
로부터 그 View 에 해당하는 가장 적합한 JSP 문서를 찾아줄 것을 요청
ViewResolver
는 가장 적합한 JSP 페이지 View
를 선택해 응답 생성
최종적으로 클라이언트에게 JSP 응답
스프링 프로젝트의 전체적인 구조는 다음과 같다.
java 폴더
java 파일들이 위치한다. 주로 패키지로 묶어서 관리하며 웹 애플리케이션에서 사용되는 ControllerㆍServiceㆍDao 객체들이 위치한다.
webapp 폴더
스프링 설정파일ㆍjsp 파일ㆍhtml css js 파일 등 웹과 관련된 파일들이 위치한다.
resources 폴더
jsp 파일을 제외한 html css js 파일 등이 위치한다.
spring 폴더
스프링 컨테이너를 생성하기 위한 스프링 설정파일
servlet-context.xml
이 위치한다.
views 폴더
View 로 사용되며 응답에 필요한 jsp 파일이 위치한다.
web.xml
파일웹 설정 파일로 DispatcherServlet 을 등록하는 코드가 있으며 DispatcherServlet 의 초기화 데이터로 스프링 설정파일인
servlet-context.xml
을 등록한다.
pom.xml
파일전체 프로젝트를 빌드하거나 메인 레파지토리에서 프로젝트에 필요한 라이브러리를 내려받기 위한 메이븐 설정 파일이다.
sts 를 이용하지 않는 경우 다음과 같이 기본적인 폴더를 생성한 뒤 프로젝트를 import 하여 프로젝트를 생성할 수 있다.
C:\spring\pjt\springStudy\src
C:\spring\pjt\springStudy\src\main
C:\spring\pjt\springStudy\src\main\java
C:\spring\pjt\springStudy\src\main\webapp
C:\spring\pjt\springStudy\src\main\webapp\resources
C:\spring\pjt\springStudy\src\main\webapp\WEB-INF
C:\spring\pjt\springStudy\src\main\webapp\WEB-INF\spring
C:\spring\pjt\springStudy\src\main\webapp\WEB-INF\views