본문 바로가기

개인적으로 공부한 것을 정리해 놓은 블로그입니다 틀린 것이 있으면 댓글 부탁 드립니다!


SPRING FRAMEWORK

Spring core 스터디 3 - IoC 컨테이너 와 Application context

반응형

 dependency들을 부품이라고 한다면 xml 혹은 annotation은 그 부품에 대한 주문서 ,  IoC 컨테이너는 그 부품들이 조립되는 컨테이너라고 할 수 있다 . 

 스프링은 xml/annotation에 입력되 있는 내용대로 객체를 생성하고 그 객체들을 담아 관리하는 것이 IoC 컨테이너의 역할이다 

 

그렇다면 IoC란 무슨 뜻일까 . 

 

이전의 DI에서 설명했던 일체형과 조립형을 다시보자

 

A->B->C->D

  

A,B,C,D는 생성될 때 순서대로 다음 객체를 dependency로 갖게 된다고 가정하면

B를 부품으로 갖는 A를 만들면 B -> C-> D -> 순으로  객체가 생성된다. 

일체형 프로그램의 구조이다

 

D->C->B->A  

  

반면에 외부에서 의존객체를 주입하는 조립형 프로그램은 위와 같이 반대로 생성된다. 

위와 같이 역순으로 의존객체가 생성되어 조립된다

  스프링의 IoC 컨테이너도 위와 같이 작동한다. IoC는 Inversion of Control (역순) 컨테이너를 의미한다. 

 

 

Dependency를 직접 주입해보기 (스프링 없이)  

 

public class Program {
    public static void main(String[] args) {
        Exam exam = new NewlecExam();
        // InlineExamConsole에 exam을 주입하고 있다 DI
        // InlineExamConsole을 GridExamConsole으로 바꾸고 싶다면
        // 외부 설정이 필요하고 설정을 통해 위와 같은 작업을 해주는 것이
        // 스프링 프레임 워크이다.
        //ExamConsole console = new InlineExamConsole(exam);
        //아래 두 줄의 코드를 스프링이 대신해준다.
        //스프링에게 xml로 지시해줘야 한다.
        
        //객체를 직접 생성하고 injection 함
        ExamConsole console = new GridExamConsole();
        console.setExam(exam);
        console.print();
    }
}

 

위의 프로그램에서는 console은 exam을 의존객채로서 setter 메서드를 통해 주입받고 있다. 

 

Spring을 통한 DI

 

  eclipse와 같은 IDE에서 Spring STS를 설치하면  파일 생성시 형식 목록에 

 

 Spring 관련의 파일들을 생성할 수 있다. 

 

<?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">


</beans>

 

Spring Bean Configuration File을 선택하면 위와 같이 스프링 설정 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">
	<!-- new NewlecExam()의 생성과 조립을 스프링에게 맞긴다  -->
	<!-- id에는 담을 변수명  class에는 담을 객체를 패키지명까지적어준다
		 같은 이름의 클래스가 존재할 수 있기 떄문에
	-->
	<bean id="exam" class="src.spring.di.entity.NewlecExam"></bean>
	
	<!-- ExamConsole console = new GridExamConsole(); -->
	<bean id="console" class="src.spring.di.ui.GridExamConsole">
		<!-- 위의 두 객체의 결합 -->
		<!-- console.setExam(exam); -->
		<!-- property의 name 에는 Setter 메서드를 적어준다 
		     하지만 setExam의 set은 지우고 첫글자는 소문자로 아래와 같이 적도록 지정되있다. -->
		<!-- value형일 경우 value에 ref형일경우 ref에 참조값을 적어준다. -->
		<!-- exam 함수의 setExam을 부르는 것과 마찬가지이다 -->
		<property name="exam"  ref="exam"/>
	</bean>
	
	
</beans>

 

 위의  xml 파일은 Program 클래스에서 생성한 console과 exam의

생성과 조합을 스프링에게 위임해 처리할 수 있도록 지시서를 작성한 것이다.

 

<beans> 안에 스프링에게 관리르 위임할 객체들을 

<bean>을 통해 지정한다  bean의 속성인 id는 해당 객체를 담을 변수명을 적어주고 class에는 해당 클래스의 위치를

패키지 명을 포함하여 적어준다 (같은 이름의 클래스가 존재할 수 있기 떄문)

 

 조합은 <property>로 지정한다 위의 코드에서는 console에서 exam을 주입받고 있기 떄문에 

console <bean>안에 <property>로 exam을 지정해 준다. 

<property>의 속성인 name은 지정할 클래스의 Setter메서드를 불러준다. 하지만 setExam으로 적어주지 않고 

set을 뗀 Exam에서 첫글자를 소문자로 하는 형식 exma으로 기입하도록 지정되어 있다. 

해당 값이 레퍼런스타입인지 벨류타입인지에 따라  value 혹은 ref에 setter메서드의 파라미터로 들어갈 bean을 기입해주면 된다. 위에서 exam은 레퍼런스타입이기 때문에 ref="exam"으로 지정했다. 

 

 

Application Context

 

 위의 xml 지시서를 읽어들이고 실질적으로 객체의 생성과 조립을 담당하는 인터페이스이다

xml을 읽어들이는 방식에 따라  구현 클래스가 다른데 대표적으로 xml의 어플리케이션 루트로 부터 classpath를 통해  xml을 읽는 ClassPathXmlApplicationContext가 있다.

 

그 외에는 

 FileSystemXmlApplicationContext - 파일 시스템 경로로부터 xml을 찾음

 XmlWebApplicationContext - xml을 web에 올려두고 web의 url을 통해 xml을 찾음

 AnnotationConfigApllicationContext - 컴포넌트 스캔을 통해  설정 파일을 찾음

가 있다 

 

Application Context 생성과 configuration.xml에 지정된 bean들 불러와 사용해보기

  main 메서드에서 직접 ApplicationContext를 구현하는 클래스들을 생성하여  configuration.xml을 읽어오고 그 안에 있는 bean들을 사용해보자 

 

//spring에 객체생성과 injection을 맡김
 //xml을 읽어 들이고 해당 객제들을 생성, 조립하여 IoC 컨테이너에 담아 넣는다.
 ApplicationContext context = new ClassPathXmlApplicationContext("src/spring/di/setting.xml");

ClassPathXmlApplicationContext를 통해 xml의 위치를 지정해줬다 해당 객체는 xml을 읽고 bean으로 지정된 객체를 생성하고 조립한 후 IoC컨테이너에 담을 것이다.

 

IoC 컨테이너에 담긴 Bean 사용하는 2가지 방법이 있다

 

//1.bean의 id를 통해서 불러옴
 ExamConsole consoleWithId = (ExamConsole) context.getBean("console");
 //2.baen의 class를 통해 불러옴
 ExamConsole consoleWithClass = context.getBean(ExamConsole.class);

 

id를 통해 불러올 경우 기본으로 Object 타입으로 받아오기 때문에 캐스팅을 해줘야 하는 불편함이 있어 클래스타입으로 불러오는 2번 방법을 많이 사용한다 .

 

  

위의 xml 파일에는 GridExamConsole로 클래스가 지정되 있다

참고로 GridExamConsole의 구성은 아래와 같다.

 

public class GridExamConsole implements ExamConsole {
    private Exam exam;
    
    public GridExamConsole() {}
    
    public GridExamConsole(Exam exam) {
        this.exam = exam;
    }

    @Override
    public void print() {
        System.out.println("total  |  avg");
        System.out.printf("%3d    |  %3.2f\n",exam.total(),exam.avg());
    }

	@Override
	public void setExam(Exam exam) {
		this.exam = exam;
	}
}

consoleWithClass 변수에 해당 객체가 담겼고 print() 메서드를 사용해보면

결과는 아래와 같다.

 

total  |  avg
  0    |  0.00

메인메서드에 GridExamConsole과 exam을 생성,조립하지 않았는데 잘 작동되었다. 

이것이 스프링의 역할이다 . 

 

만약 GridExamConsole을  InlineExamConsole으로 바꾸고 싶다 하면  xml에 해당 bean이 참조하는 class를 바꿔주면 된다.

 위의 xml 파일에서 아래에 해당하는 부분만 아래와 같이 바꾸고 print메서드를 사용해봤다.

 

<bean id="console" class="src.spring.di.ui.InlineExamConsole">


//결과값
//total is 0, avg is 0.000000

 

소스코드 변경없이 외부 xml 파일 수정만으로

InlineExamConsole에 메소드 내용대로 결과 값이 잘 바뀌어서 출력되었다. 

 

Value 타입의 DI

 위에서 xml에서는 bean에서 property를 ref를 통해 exam 클래스를 주입하였다.

이번에는 value 타입을 주입 해보자

 

<?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">
	
	<!-- value 타입 DI -->
	<bean id="exam" class="src.spring.di.entity.NewlecExam">
		<property name = "kor" value="10"/>
		<property name = "eng" value="10"/>
		<property name = "math" value="10"/>
		<property name = "com" value="10"/>
	</bean>
	
	<bean id="console" class="src.spring.di.ui.InlineExamConsole">
		<property name="exam"  ref="exam"/>
	</bean>
</beans>

위처럼 property로 value 타입의 값을 줄 수도 있다 . ref 타입과 마찬가지로 name에서는 해당 값의 setter를 불러오기 때문에  해당 값의 setter가 존재해야한다. 

 

또한 아래와 같이 생성자를 통해서도 값을 지정할 수 있다.  

 

<?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">
	
	<!-- 값 타입 DI -->
	<bean id="exam" class="src.spring.di.entity.NewlecExam">
    	<!--생성자가 여러개인 경우 타입을 지정해 줄 수도 있다 -->
		<constructor-arg name = "kor" value="10" type="int"/> 
		<constructor-arg name = "eng" value="10"/>
		<constructor-arg name = "math" value="10"/>
		<constructor-arg name = "com" value="10"/>
	</bean>
	
	<bean id="console" class="src.spring.di.ui.InlineExamConsole">
		<property name="exam"  ref="exam"/>
	</bean>
</beans>

 

 

네임스페이스를 사용하려면 위와 같이 namespace를 지정한다. p는 property 지정을 위해 사용하는 namespace다 

네임스페이스를 사용하면  아래와 같이 bean의 속성으로 p:프로퍼티이름 ="값" 형식으로 property를 지정할 수 있다.

 

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:p="http://www.springframework.org/schema/p"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
	
	<!-- 값 타입 DI -->
	<bean id="exam" class="src.spring.di.entity.NewlecExam" p:kor="10" p:eng="20"
				p:com="30" p:math="40">
	</bean>
	
	<bean id="console" class="src.spring.di.ui.InlineExamConsole">
		<property name="exam"  ref="exam"/>
	</bean>
</beans>

 

 

컬랙션의 DI

 

 자바에서 제공하는 컬랙션으로 지정하여 list 형태로 빈을 정의 할 수도 있다.

 

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:p="http://www.springframework.org/schema/p"
	xmlns:util="http://www.springframework.org/schema/util"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.3.xsd">

	<bean id="exam" class="src.spring.di.entity.NewlecExam">
		<property name = "kor" value="10"/>
		<property name = "eng" value="10"/>
		<property name = "math" value="10"/>
		<property name = "com" value="10"/>
	</bean>
	
	<!-- ExamConsole console = new GridExamConsole(); -->
	<bean id="console" class="src.spring.di.ui.InlineExamConsole">
		<property name="exam"  ref="exam"/>
	</bean>
	
	<!--1. ArrayList로 빈 지정 bean 여러개를 list로 관리 가능 -->
	<bean id="exams" class="java.util.ArrayList">
		<constructor-arg>
			<list>
				<bean class="src.spring.di.entity.NewlecExam" p:com="1" p:kor="2" p:math="3" p:eng="4"/>
				<ref bean="exam"/>
			</list>
		</constructor-arg>
	</bean>
	
	<!--2. 위의 방식을 namespace을 사용할 수도 있다. -->
	
	<util:list id="exams" list-class="java.util.ArrayList">
		<bean class="src.spring.di.entity.NewlecExam" p:com="1" p:kor="2" p:math="3" p:eng="4"/>
		<ref bean="exam"/>
	</util:list>
	
	
</beans>

 

 컬랙션으로 타입을 지정하여 받을 수 있다.  property는 setter 함수 일때 사용할 수 있는 것이기 떄문에  1번과 같이
constructor-arg를 사용하여 리스트 생성자로 리스트를 전달할 수 있다. ArrayList의 생성자에 넘겨줄 List는 <list> 안에 정의해 주면된다. 

 아니면 2번처럼 name 스페이스를 사용할 수도 있다.

 

xml의 namespaces를 보면 util이 있다 util: 네임스페이스를 사용하여 컬랙션을 생성하도록 도와준다.

1번 보다 훨씬 간결하다.

반응형