ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Spring(1) - Ioc와 DI컨테이너 작성
    Back-end/Spring 2021. 6. 24. 13:11
    반응형

    이번에는 직접 Spring프로젝트를 구현해볼려고 한다.

    Spring프로젝트는 다른 프레임워크와 다르게 무슨 파일이 어떻게 작동하는지에 대한 원리를 이해하기도 많이 어렵다.

    부족한 부분이 있을 수도 있으면 댓글을 달아주길 바란다.

     

    첫 번째로 Ioc(Inversion of control)에 대한 개념을 설명하겠다.

     

    해석을 하면 "제어의 역전"이다. 이 뜻은 무슨 뜻이냐면 원래는 개발자가 주도로 객체를 생성하고 해야하는데 그것을

    프레임워크인 "Spring"이 담당하고 있다라고 이해하면 된다. 자세히는 Container부분이 담당한다.

     

     

    그러면 IoC컨테이너에 대해서 알아보자.

    IoC컨테이너는 주로 객체 생성 및 Bean객체 라이프 사이클을 관리한다.  Bean객체란 IoC컨테이너에 의해 생성되고 관리되는 객체를 일컫는 말이다.

    IoC컨테이너에서 사용하는 핵심적인 Class 2가지가 있는데 ApplicationContext, BeanFactory이다.

     

    1. BeanFactory

    자바의 팩토리 디자인 패턴을 구현한 것. Bean을 생성 및 분배. getBean()이용.

     

    2. ApplicationContext

    BeanFactory를 상속 받은 인터페이스. BeanFactory보다 훨씬 많은 기능을 가짐.

    AOP, 메세지처리, 이벤트 처리 등의 기능을 제공.

    @ContextConfiguration(locations="classpath:config/beans.xml")
    ........
    ApplicationContext context;
    Hello hello = context.getBean("hello");

    위의 예시는 beans.xml파일에 있는 빈 객체들을 생성하고 그것을 getBean을 통해서 빈 들을 가져와서 활용한다.

    결과적으로 AppicationContext를 사용하면 그 곳에 Bean객체들이 다 만들어진다는 뜻이다.

     

    그리고 IoC컨테이너를 구현하기 위한 방법으로는 Dependency Lookup, Dependency Injection이 있다.

    Dependency Lookup은 컨테이너가 제공하는 API를 이용해 Bean을 찾는 것이다. 특성은 종속성이 높다.

    Dependency Injection은 컨테이너가 빈 설정을 바탕으로 자동으로 연결해준다. 종속성이 낮아진다.

    DI같은 경우는 XML파일 및 Annotation을 이용하여 Bean을 등록해준다.

    위의 Setter Injection, Constructor Injection을 살펴보자.

     

    1. Setter Injection : Property태그를 이용.

    	<bean id="hello" class="myspring.di.xml.Hello">
    		<property name="name" value="Spring"/>
    		<property name="printer" ref="printer"/>
    	</bean>

    위와 같이 value와 ref로 값을 지정해준다. value는 단순 값을 지정. ref는 객체 주입 시 사용한다.

     

    2. Constructor Injection : Constructor-arg태그를 이용.

    	<bean id="hello2" class="myspring.di.xml.Hello">
    		<constructor-arg index="0" value="${myname}"></constructor-arg>
    		<constructor-arg index="1" ref="${myprinter}"></constructor-arg>
    	</bean>

    Constructor은 주로 많은 인자 수를 받을 때 이용한다. index로 value,ref를 제어한다.

     

     

    위의 개념들을 익히고 이제 실제로 DI컨테이너를 구현해보자.

    순서는 PoJo Class작성 -> XML 설정 -> DI 클래스 작성이다 .

     

     

    1. PoJo(Plain Java Old Project)만들기

    진짜 용어들이 다양한 것 같다. PoJo란 Java Beans를 의미한다. 그 중에서도 "순수한 자바 객체"를 의미한다.

    즉, 어떠한 인터페이스에도 상속받지 않는 것. 예시를 살펴보자.

    public class Hello {
    	private String name;
    	private Printer printer;
        
    	public Hello(String name, Printer printer) {
    		super();
    		this.name = name;
    		this.printer = printer;
    	}
    	
        public String getName() {
        	return name;
        }
        
    	public void setName(String name) {
    		this.name = name;
    	}
    
    	public Printer getPrinter() {
        	return printer;
        }
        
    	public void setPrinter(Printer printer) {
    		this.printer = printer;
    	}
    }

    위와 같이 set,get method가 분명하게 있어야 한다.

    이렇게 PoJo 클래스를 생성하였다. 이제 XML파일을 설정하는 방법을 알아보자.

     

    2. 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"
    	xmlns:context="http://www.springframework.org/schema/context"
    	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
    		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
    
    <context:property-placeholder location="classpath:config/value.properties"/>
    
    	<bean id="hello2" class="myspring.di.xml.Hello">
    		<constructor-arg index="0" value="${myname}"></constructor-arg>
    		<constructor-arg index="1" ref="${myprinter}"></constructor-arg>
    		<property name="names">
    			<list>
    				<value>${value1}</value>
    				<value>${value2}</value>
    				<value>${value3}</value>
    			</list>
    		</property>
    	</bean>
    
    	<bean id="hello" class="myspring.di.xml.Hello">
    		<!-- setName(message) -->
    		<property name="name" value="Spring"/>
    		<!-- setPrinter(Printer) -->
    		<property name="printer" ref="printer"/>
    	</bean>
    	
    	<bean id="printer" class="myspring.di.xml.StringPrinter"/>
    	<bean id="consolePrinter" class="myspring.di.xml.ConsolePrint"/>
    </beans>

    위의 코드는 XML파일에 Beans을 등록한 것이다. 하나하나 살펴보자.

    일단 Beans밖에 있는 코드들은 파일을 생성할 경우 자동으로 생성되기 때문에 스킵하겠다.

    첫 번째 <context:property-placeholder>태그이다. 이것의 역할은 Beans들의 Properties를 외부에서 따로 관리해준다.

    왜냐하면 자주 바뀔 수 있는 데이터들이 있기 때문이다. 첫 번째 bean태그를 보면 "${}"표시가 있다.

    이것은 value.properties파일에서 가져온 값들로 대체된다.

     

    두 번째로는 bean에 대한 것을 살펴보자. 위의 개념 중 setter injectionconstructor injection을 둘 다 구현해봤다.

    그리고 list태그는 따로 다음에 설명하겠다. 간단하게 컬렉션 타입으로 값들을 모아놓은 변수이다.

     

    이렇게 Beans를 설정했다. 그리고 최종적으로 Beans을 생성해 줄 수 있는 BeanFactory를 만들어보자.

     

    3. DI/IoC컨테이너 생성

     

    컨테이너를 생성하기 위한 두 가지 방법을 설명하겠다.

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations="classpath:config/beans.xml")
    public class HelloBeanSpring {
    	@Autowired
    	ApplicationContext context;
        
    }

    첫 번째는 어노테이션을 이용하여 클래스에서 직접적으로 Beans을 얻어오는 것이다.

    위와 같이 RunWith와 ContextConfiguration Annotation을 사용한다.

    그리고 Autowired로 의존성 주입을 한다.

     

     

    	public static void main(String[] args) {
    		ApplicationContext context = new GenericXmlApplicationContext("config/beans.xml");
    		Hello hello = (Hello) context.getBean("hello");
    		System.out.println(hello.sayHello());
    }

    두 번째로는 GenericXmlApplicationContext로 Beans이 설정되어있는 Xml파일을 읽어 Beans 생성하는 것이다.

    두 번째 방법은 첫 번째보다 불편하고 코드가 길어지기 때문에 첫 번째 방법을 사용하겠다.

     

    반응형

    댓글

Designed by Tistory.