-
DI 방법 3가지Spring/Spring 기본 지식 2020. 3. 12. 23:27반응형
Srping 4.3부터 어떠한 클래스에 생성자가 1개뿐이고, 그 생성자를 주입받는 래퍼런스 변수들이 Bean으로 등록되어있다면 그 빈을 자동으로 주입하도록 되어있다.
스프링에서의 DI방법 3가지
Field Injection
@Controller public class HomeController { private static final Logger logger = LoggerFactory.getLogger(HomeController.class); @Autowired private TestService1 service1; @RequestMapping(value = "/", method = RequestMethod.GET) public String home(Locale locale, Model model) { service1.test1(); return "home"; } }
@Service public class TestService1 { @Autowired TestService2 service2; public void test1() { service2.test2(); } }
@Service public class TestService2 { @Autowired TestService1 service1; public void test2() { service1.test1(); } }
Field Injection의 단점
순환참조
- 위와같이 @Autowired를 사용하는 것이다. 위의 단점은 Bean객체들이 서로를 바라보게 된다면 위와 같은 오류(StackOverflowError)가 발생이 된다. 이게 바로 서로를 참조하게 되는 순환참조이다
- 순환참조는 객체 생성시점에는 오류를 잡을 수 없고 객체생성 후 로직상에서 잡을 수 밖에 없다.
DI 컨네이너와 결합이 매우 강하게되어 외부에서 사용하기 어려워진다.
- DI 프레임 워크의 핵심 아이디어 중 하나는 ‘관리되는 클래스가 DI 컨테이너에 의존하지 않아야 한다’ 이다. 즉, 모든 필수 종속성을 전달하면 독립적으로 인스턴스화 할 수있는 단순한 POJO 여야한다. 이렇게 하면 DI 컨테이너를 시작하지 않고 단위 테스트에서 이를 인스턴스화하고 별도로 테스트 할 수 있다. 하지만 필드 주입의 경우, 스프링 설정 파일을 읽고 모든 Bean 설정이 되어야 테스트를 할 수 있다.
의존 관계가 보이지 않는다.
- DI 컨테이너를 사용하면 클래스가 스스로 종속성을 관리 할 필요가 없음을 의미한다. 의존성을 얻기 위한 책임을 더 이상 클래스가 하지 않는다. DI 컨테이너 또는 단위 테스트의 경우, mock 객체를 직접 만드는 등과 같은 다른 무언가가 책임진다. 그래서 클래스가 더 이상 종속성을 가져올 책임이 없으면, 수정자 또는 생성자를 사용하여 클래스 의존관계를 명확하게 보여줘야 한다. 그러나 필드 주입은 클래스 의존관계가 명확하지 않다. 그래서 필수 또는 불변 프로퍼티 DI 라면 생성자 주입, 선택적이고 가변 프로퍼티 DI라면 수정자 메소드 주입을 사용해서 보다 정확하게 의존 관계를 설명해야 한다.
Setter Based Injection(수정자를 통한 주입)
@Controller public class HomeController { private static final Logger logger = LoggerFactory.getLogger(HomeController.class); private TestService1 service1; @Autowired public void setTestService1(TestService1 service) { this.service1 = service; } @RequestMapping(value = "/", method = RequestMethod.GET) public String home(Locale locale, Model model) { service1.test1(); return "home"; } }
@Service public class TestService1 { private TestService2 service2; @Autowired public void setTestService2(TestService2 service) { this.service2 = service; } public void test1() { service2.test2(); } }
@Service public class TestService2 { private TestService1 service1; @Autowired public void setTestService1(TestService1 service) { this.service1 = service; } public void test2() { service1.test1(); } }
Field Injection의 단점
순환참조
- 위와같이 @Autowired를 사용하는 것이다. 위의 단점은 Bean객체들이 서로를 바라보게 된다면 위와 같은 오류(StackOverflowError)가 발생이 된다. 이게 바로 서로를 참조하게 되는 순환참조이다
- 순환참조는 객체 생성시점에는 오류를 잡을 수 없고 객체생성 후 로직상에서 잡을 수 밖에 없다.
Constructor based Injection(생성자 주입)
@Service public class TestService1 { private TestService2 service2; public TestService1(TestService2 service) { this.service2 = service; } public void test1() { service2.test2(); } }
@Service public class TestService2 { private TestService1 service1; public TestService2(TestService1 service) { this.service1 = service; } public void test2() { service1.test1(); } }
RROR: org.springframework.web.servlet.DispatcherServlet - Context initialization failed org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'homeController': Unsatisfied dependency expressed through field 'service1'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'testService1' defined in file [C:\spring\worksapce\test\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\wtpwebapps\study\WEB-INF\classes\kr\co\study\service\TestService1.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'testService2' defined in file [C:\spring\worksapce\test\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\wtpwebapps\study\WEB-INF\classes\kr\co\study\service\TestService2.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'testService1': Requested bean is currently in creation: Is there an unresolvable circular reference? at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:592) at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88) at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:370) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1219) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:551) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482) at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197) at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:754) at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:866) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:542) at org.springframework.web.servlet.FrameworkServlet.configureAndRefreshWebApplicationContext(FrameworkServlet.java:668) at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:634) at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:682) at org.springframework.web.servlet.FrameworkServlet.initWebApplicationContext(FrameworkServlet.java:553) at org.springframework.web.servlet.FrameworkServlet.initServletBean(FrameworkServlet.java:494) at org.springframework.web.servlet.HttpServletBean.init(HttpServletBean.java:136) at javax.servlet.GenericServlet.init(GenericServlet.java:158) at org.apache.catalina.core.StandardWrapper.initServlet(StandardWrapper.java:1134) at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1089) at org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java:983) at org.apache.catalina.core.StandardContext.loadOnStartup(StandardContext.java:4871) at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5180) at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183) at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1384) at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1374) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at org.apache.tomcat.util.threads.InlineExecutorService.execute(InlineExecutorService.java:75) at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:134) at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:909) at org.apache.catalina.core.StandardHost.startInternal(StandardHost.java:841) at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183) at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1384) at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1374) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at org.apache.tomcat.util.threads.InlineExecutorService.execute(InlineExecutorService.java:75) at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:134) at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:909) at org.apache.catalina.core.StandardEngine.startInternal(StandardEngine.java:262) at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183) at org.apache.catalina.core.StandardService.startInternal(StandardService.java:421) at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183) at org.apache.catalina.core.StandardServer.startInternal(StandardServer.java:930) at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183) at org.apache.catalina.startup.Catalina.start(Catalina.java:633) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:344) at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:475)
Constructor based Injection 장점
순환참조 방지
- 객체 생성 시점에서 발견하여 오류를 발생시킨다.
오늘 본 강의가 10분 정도인데 DI 3가지를 직접 가동시켜 이에 따른 결과를 공부해 보았다.
공부했던 것이지만 다시 공부하니 기억이 새록새록 났다.
나중에도 까먹지 않게 공부해야겠다.
반응형'Spring > Spring 기본 지식' 카테고리의 다른 글
Spring postgresql mybatis jdbc 기본 연결테스트 (0) 2020.03.23 서블릿 컨테이너와 스프링 컨테이너 (0) 2020.03.19 Spring Bean Life Cycle (0) 2020.03.15 spring IoC의 용어 정리 (0) 2020.02.11 1. spring 사용 이유 (0) 2020.02.06