ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [스프링] 의존성 주입2
    Spring 2022. 10. 3. 10:05

     

    들어가기 전에

     

    • 강한 결합 : 객체 내부에서 다른 객체를 생성하는 것은 강한 결합도를 갖는다 즉, A클래스 내부에서 B라는 객체를 직접 생성하고 있다면 B객체를 C객체로 바꾸고 싶은 경우 A클래스를 수정해야하는 방식이기에 이런 경우를 강한 결합도를 갖는다라고 일컫는다.
    • 느슨한 결합 : 객체를 주입 받는다는 것은 외부에서 생성된 객체를 인터페이스를 통해 넘겨받는것이다. 이렇게하면 객체간 결합도를 낮출 수 있고, 런타임시에 의존관계가 결정되기에 유연한 구조를 갖을 수 있다. SOLID원칙에서 O에 해당하는 Open-Close Principle을 지키기 위해 디자인패턴 중 전략패턴을 사용하게 되는데, 생성자 주입을 사용하게되면 전략패턴을 사용하게 된다.

    6. 스프링 DI 설정

    • 스프링은 의존성 주입을 지원하는 조립기이다.
    package config;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    import spring.ChangePasswordService;
    import spring.MemberDao;
    import spring.MemberRegisterService;
    
    @Configurtion // -> 스프링 설정 클래스로 사용하려면 붙여줘야함
    public class AppCtx {
    
        @Bean // -> 스프링 빈 
        public MemberDao memberDao() {
    			return new MemberDao();
    		}
    	
    		@Bean
    		public MemberRegisterService memberRegSvc() {
    			return new MemberRegisterService(memberDao());
    		}
    
    		@Bean
    		public ChangePasswordService changePwdSvc() {
    			ChangePasswordService pwdSvc = new ChangePasswordServie();
    			pwdSvc.setMemberDao(memberDao); // -> Setter 메서드를 통해 memberDao 객체 주입
    			return pwdSvc;
    		}
    
    }

     

    • 어노테이션 @Bean이 메서드에 붙어있으면 해당 메서드의 이름으로 한 개의 빈 객체를 생성한다. 여러 메서드에 붙어있다면 각각의 이름으로 빈 객체가 생성된다.

    • 왜 의존성 주입을 해야하는가??
      1. 코드의 재사용성을 높이려고
      2. 객체간 결합도를 낮추면서 유연한 코드를 작성하려고
      3. 객체간 종속성을 줄이거나 없애려고
      4. 코드의 테스트를 쉽게 하려고

    • 의존성 주입의 방법
      1. 생성자를 통한 주입
      2. 수정자를 통한 주입
      3. 필드 주입

    방식1. 생성자를 통한 주입

    public class MemberRegisterService {
    	private MemberDao memberDao;
    
    	public MemberRegisterService(MemberDao memberDao) { //-> 생성자를 통해 DI 주입
    		this.memberDao = memberDao; //-> 주입받은 Object를 필드에 할당
    	}
    
    	public Long regist(RegisterRequest req) {
    		//-> 주입받은 객체의 메서드 사용
    		Member member = memberDao.selectByEmail(req.getEmail());
    		...
    		memberDao.insert(newMember);
    		return newMember.getId();
    	}
    
    }

     

    • 생성자 주입을 통해 생기는 이점
      1. 컴파일시 빈 객체를 먼저 생성하지 않고, 먼저 어디서 사용하는지 전부 찾은다음 생성하기에 순환참조를 사전에 방지한다.
      2. final 키워드를 사용할 수 있다. 따라서 클래스 내부에 객체를 바꿔치기 할 수 없어 객체에 대한 불변성이 보장된다.
      3. 의존관계에 대한 내용을 외부로 노출시켜 컴파일시 오류를 잡아낼 수 있다.
      4. Null을 주입하지 않는 한 NullPointerException(NPE)는 발생하지 않는다.

     

     

    방식2. 수정자를 통한 주입

    public class MemberRegisterService {
    		private MemberDao memberDao;
    		
    		public Long regist(RegisterRequest req) {
    			Member member = memberDao.selectByEmail(req.getEmail());
    			...
    			memberDao.insert(newMember);
    			return newMember.getId();
    		}
    
    		public void setMemberDao(MemberDao memberDao) {
    				this.memberDao = memberDao;
    		}
    
    }
    • Setter 메서드는 다음과 같은 규칙을 따라 작성한다.
      1. 매서드 이름은 set으로 시작한다
      2. set다음 문자는 대문자이다
      3. 파라미터는 1개이다
      4. 리턴 타입은 void여야 한다

    • 수정자 주입을 통해 생기는 이점
      1. 런타임시 주입되어 낮은 결합도를 갖게한다.
      2. setter메서드를 통해 어떤 의존 객체가 주입되는지 파악하기 쉽다.

    • 수정자 주입이 갖는 단점
      1. 주입이 필요한 객체가 주입되지 않아도 얼마든지 객체가 생성될 수 있다. 즉, NPE가 발생할 수 있다.

     

    방식3. 필드 주입

    public class MemberRegisterService {
    	
    	@Autowired
    	MemberDao memberDao;
    
    	public Long regist(RegisterRequest req) {
    		Member member = memberDao.selectByEmail(req.getEmail());
    		...
    		memberDao.insert(newMember);
    		return newMember.getId();
    	}
    
    }
    • 필드 주입이 갖는 이점
      1. @Autowired 어노테이션만 적어주면 스프링 설정 클래스의 @Bean메서드에서 의존 주입을 위한 코드를 생성하지 않아도되서 사용이 간편하다.

    • 필드 주입의 단점
      1. 빈 객체를 먼저 생성한 뒤에 의존성을 주입하기에 빈 객체를 생성하는 시점에서 순환참조가 발생하는지 알 방법이 없다.
      2. 스프링 설정 파일을 읽고 모든 빈이 설정되어야 빈 객체가 주입되므로 의존 주입 컨테이너와 결합이 매우 강하게 되어 외부에서 접근이 불가하다.

     

    7. @Configuration 설정 클래스의 @Bean설정과 싱글톤 패턴

    @Configuration
    public class AppCtx {
    		@Bean
    		public MemberDao memberDao() {
    				return new MemberDao();
    		}
    
    		@Bean
    		public MemberRegisterService memberRegSvc() {
    				return new MemberRegisterService(memberDao());
    		}
    
    		@Bean
    		public ChangePasswordService changePwdSvc() {
    				ChangePasswordService pwdSvc = new ChangePasswordService();
    				pwdSvc.setMemberDao(memberDao());
    				return pwdSvc;
    		}
    
    }
    • 위의 코드를 보다 갑자기 드는 의문

    Q : memberRegSvc메서드와 changePwdSvc메서드가 호출 될 때마다 memberDao()메서드를 실행하면 매번 새로운 객체를 생성하여 리턴하는데 서로 다른 객체를 사용하는 것이 아닐까?

    A : 스프링 컨테이서는 @Bean어노테이션이 붙은 메서드에 대해 오직 한 개의 객체만 생성된다. 스프링 컨테이너가 생성한 빈은 싱글톤 패턴을 따르므로 여러 메서드에서 호출될 때 memberDao()메서드는 최초에 한번 생성한 객체를 보관했다가 이후 동일한 객체를 리턴한다.

     

     

    8. 두 개 이상의 설정 파일 사용하기 (실습이라 패스)

    9. getBean() 메서드 사용

    빈 인스턴스는 BeanUtils클래스에서 생성된다.AnnotationConfigApplicationContext객체를 생성할 때, finishBeanFactoryInitialization(beanFactory);에서 이미 getBean()메서드를 호출하여 인스턴스가 생성되고 스프링 컨테이너의 싱글톤으로 등록이된다.

    10. 주입 대상 객체를 모두 빈 객체로 설정해야 하는가?

    객체를 스프링 빈으로 등록할 때와 하지 않을 때의 차이는 스프링 컨테이너가 객체를 관리하냐의 여부이므로 스프링 컨테이너가 제공하는 관리 기능은 필요없고 getBean()메서드로 구할 필요가 없다면 굳이 빈 객체로 등록할 필요는 없다.

    'Spring' 카테고리의 다른 글

    [스프링] Aspect Oriented Programming (AOP을 이용한 로깅파일 구성)  (0) 2023.08.05
    SOLID 원칙  (0) 2023.02.21
    MVC모델의 정의, 장점  (0) 2023.02.08
    [스프링] 컴포넌트 스캔  (0) 2022.10.03
    [스프링] 의존성 주입1  (0) 2022.10.03

    댓글

Designed by Par3k