TIL(2022.04.08)

조지성·2022년 4월 8일
0

TIL

목록 보기
55/78
post-thumbnail

Chap04. 의존 자동 주입

@Autowired 애노테이션 필수 여부

  • 자동 주입할 대상이 필수가 아닌경우 3가지 방법을 이용해서 처리할 수 있다.
  • 메서드와 필드에 둘 다 적용 가능하다.
  1. @Autowired 애노테이션의 required 속성을 false로 설정
  • 매칭되는 빈이 없어도 익셉션이 발생하지 않으며 자동 주입을 수행하지 않는다.
	@Autowired(required = false) // 메서드의 파라미터를 자동주입 , 자동 주입할 대상이 필수가 아니다
	public void setDateTimeFormatter(DateTimeFormatter dateTimeFormatter) {
		this.dateTimeFormatter = dateTimeFormatter;
	}
  1. 자바8의 Optional을 사용
  • 자동 주입 대상 타입이 Optional인 경우,
    • 일치하는 빈이 존재하지 않으면 값이 없는 Optional을 인자로 전달(예외발생x)
    • 일치하는 빈이 존재하면 해당 빈을 값으로 갖는 Optional을 인자로 전달
	public void setDateTimeFormatter(Optional<DateTimeFormatter> formatterOpt) {
		if(formatterOpt.isPresent()) {
			this.dateTimeFormatter = formatterOpt.get();
		}else {
			this.dateTimeFormatter = null;
		}
	}
  1. @Nullable 애노테이션 사용
  • 의존 주입 대상 파라미터에 @Nullable 을 붙이면 세터 메서드를 호출할 때 자동 주입할 빈이 존재하면 해당 빈을 인자로 전달하고, 존재하지 않으면 인자로 null을 전달
  • @Autowired(require = false)와 다른점은 자동 주입할 빈이 존재하지 않아도 메서드가 호출된다는점이다.
	public void setDateTimeFormatter(@Nullable DateTimeFormatter dateTimeFormatter) {
		this.dateTimeFormatter = dateTimeFormatter;
	}

생성자 초기화와 필수 여부 지정 방식 동작 이해

  • @Autowired(required = false)는 일치하는 빈이 없으면 값 할당 자체를 하지 않음
  • @Nullable 일치하는 빈이 없을 때 null 값을 할당
package spring;

import java.time.format.DateTimeFormatter;
import java.util.Optional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.lang.Nullable;

public class MemberPrinter {
	
	private DateTimeFormatter dateTimeFormatter;
	
	public MemberPrinter() {
		dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy년 MM월 dd일");
	}
	
    //@Autowired(required = false)인 경우 setDateTimeFormatte에 할당되지 않기 때문에 생성자에 설정한 형식 대로 출력
    // @Nullable인 경우 null인 전달되므로 밑에 방식대로 출력
	public void print(Member member) {
		if(dateTimeFormatter == null) {
			System.out.printf(
					"회원 정보: 아이디=%d, 이메일=%s, 이름=%s, 등록일=%tF\n",
					member.getId(),
					member.getEmail(),
					member.getName(),
					member.getRegisterDateTime()
			);
		}else {
			System.out.printf(
					"회원 정보: 아이디=%d, 이메일=%s, 이름=%s, 등록일=%s\n",
					member.getId(),
					member.getEmail(),
					member.getName(),
					dateTimeFormatter.format(member.getRegisterDateTime())
			);
		}
	}
	
	@Autowired(required = false) 
	public void setDateTimeFormatter(DateTimeFormatter dateTimeFormatter) {
		this.dateTimeFormatter = dateTimeFormatter;
	}
	
	
//	public void setDateTimeFormatter(@Nullable DateTimeFormatter dateTimeFormatter) {
//		this.dateTimeFormatter = dateTimeFormatter;
//	}
	
}

자동 주입과 명시적 의존 주입 관의 관계

  • 설정 클래스에서 의존을 주입했는데 자동 주입 대상이면 어떻게 될까?
    • 설정 클래스에서 세터 메서드를 통해 의존을 주입해도 해당 세터 메서드에 @Autowired 가 붙어있으면 자동 주입을 통해 일치하는 빈을 주입
    • @Autowired 애노테이션을 사용했다면 설정 클래스에서 객체를 주입하기 보다는 스프링이 제공하는 자동 주입 기능을 사용하는 편이 낫다.
	@Bean
	@Qualifier("printer")
	public MemberPrinter memberPrinter1() {
		return new MemberPrinter();
	}
	
	@Bean//MemberPrinter의 하위클래스 빈 생성
	@Qualifier("summaryPrinter")
	public MemberSummarayPrinter memberPrinter2() {
		return new MemberSummarayPrinter();
	}
    
    	@Bean
	public MemberInfoPrinter infoPrinter() {
		MemberInfoPrinter infoPrinter = new MemberInfoPrinter();
		//memberPrinter2 빈을 주입
		infoPrinter.setMemberPrinter(memberPrinter2());
		return infoPrinter;
	}
	@Autowired
	@Qualifier("printer") //memberPrinter1()의 빈을 주입
	public void setMemberPrinter(MemberPrinter memberPrinter) {
		this.memberPrinter = memberPrinter;
	}

chap05.컴포넌트 스캔

  • 컴포넌트 스캔은 스프링이 직접 클래스를 검색해서 빈으로 등록해주는 기능
  • 설정 클래스에 빈으로 등록하지 않아도 원하는 클래스를 빈으로 등록할 수 있으므로 컴포넌트 스캔 기능을 사용하면 설정 코드가 크게 줄어든다.

@Component 애노테이션으로 스캔 대상 지정

  • 스프링이 검색해서 빈으로 등록할 수 있으려면 클래스에 @Component 애노테이션을 붙여야함
  • @Component 애노테이션은 해당 클래스를 스캔 대상으로 표시
  • @Component에 값을 주지않으면 클래스 이름의 첫 글자를 소문자로 바꾼 이름을 빈 이름으로 사용
  • @Component에 값을 주면 그 값을 빈 이름으로 사용
@Component
public class MemberDao {
	private static long nextId = 0;
}

@Component("infoPrinter")
public class MemberInfoPrinter {
}

@ComponentScan 애노테이션으로 스캔 설정

  • @Component를 스프링 빈으로 적용하려면 설정 클래스에 @ComponentScan 애노테이션을 적용해야 한다.
  • @ComponentScan을 통해서 스프링 컨테이너가 자동으로 빈으로 등록시켜주기 때문에 설정 클래스 코드가 많이 줄어든다
  • @ComponentScan의 basePackages 속성값은 스캔 대상 패키지 목록을 지정
    • 지정한 패키지 안에 @Component 애노테이션이 붙은 클래스의 객체를 생성해서 빈으로 등록
package config;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

import spring.ChangePasswordService;
import spring.MemberDao;
import spring.MemberInfoPrinter;
import spring.MemberListPrinter;
import spring.MemberPrinter;
import spring.MemberRegisterService;
import spring.MemberSummarayPrinter;
import spring.VersionPrinter;

@Configuration
@ComponentScan(basePackages = {"spring"}) //spring 패캐지에 있는 @Component를 빈으로 등록
public class AppCtx {

	@Bean
	@Qualifier("printer")
	public MemberPrinter memberPrinter1() {
		return new MemberPrinter();
	}
	
	@Bean//MemberPrinter의 하위클래스 빈 생성
	@Qualifier("summaryPrinter")
	public MemberSummarayPrinter memberPrinter2() {
		return new MemberSummarayPrinter();
	}
	
	@Bean
	public VersionPrinter versionPrinter() {
		VersionPrinter versionPrinter = new VersionPrinter();
		versionPrinter.setMajorVersion(5);
		versionPrinter.setMinorVersion(0);
		return versionPrinter;
	}
}

스캔 대상에서 제외하거나 포함하기

  • excludeFilters 속성을 사용하면 스캔할 때 특정 대상을 자동 등록 대상에서 제외할 수 있다
  • FilterType.REGEX
    • 정규표현식을 사용해서 제외 대상을 지정
  • pattern
    • FilterType에 적용할 값 설정
    • String[ ] 타입이므로 배열을 이용해서 한 개 이상 지정할 수 있다.
@Configuration
@ComponentScan(
		basePackages = {"spring"} 
		, excludeFilters = @Filter(type = FilterType.REGEX , pattern = "spring\\..*Dao")
) 
public class AppCtx {
  • FilterType.AspectJ
    • AspectJ 패턴을 통해서 제외 대상을 지정
    • AspectJ 패턴이 동작하려면 aspectjweaver 모듈을 pom.xml에 추가해야함
@Configuration
@ComponentScan(
		basePackages = {"spring"} 
		, excludeFilters = @Filter(type = FilterType.AspectJ , pattern = "spring.*Dao")
) 
public class AppCtx {
  • 특정 애노테이션을 붙인 타입을 대상에서 제외 할수 있다.
@Configuration
@ComponentScan(
		basePackages = {"spring"} 
		, excludeFilters = @Filter(type = FilterType.ANNOTATION, classes ={no.class , product.class} )
) 
public class AppCtx {
  • 특정 타입이나 그 하위 타입을 컴포넌트 스캔 대상에서 제외
@Configuration
@ComponentScan(
		basePackages = {"spring"} 
		, excludeFilters = @Filter(type = FilterType.ASSIGNABLE_TYPE, classes ={MemberDao.class , product.class} )
) 
public class AppCtx {
  • 필터가 두개 이상인 경우
@Configuration
@ComponentScan(
		basePackages = {"spring"} 
		, excludeFilters = {
        	@Filter(type = FilterType.ASSIGNABLE_TYPE, classes ={MemberDao.class , product.class} ) ,
            @Filter(type = FilterType.AspectJ , pattern = "spring.*Dao")
        }
) 
public class AppCtx {

기본 스캔 대상

  • 다음 어노테이션을 붙인 클래스가 컴포넌트 스캔 대상에 포함된다
    • @Component
    • @Controller
    • @Service
    • @Repository
    • @AspectJ
    • @Configuration
  • @AspectJ를 제외한 나머지 어노테이션은 실제로는 @Component 어노테이션에 대한 특수 어노테이션.....

컴포넌트 스캔에 따른 충돌 처리

빈 이름 충돌

  • 각각 다른 패키지에서 @Componet로 같은 이름의 빈을 설정하면 오류가 발생
  • 컴포넌트 스캔과정에서 서로 다른 타입인데 같은 빈 이름을 사용하는 경우가 있다면 둘 중 하나에 명시적으로 빈 이름을 지정해서 충돌을 피해야한다.

수동 등록한 빈과 충돌

  • 스캔할 때 사용하는 빈 이름(@Component)과 수동 등록한 빈 이름이 같은 경우 수동 등록한 빈이 우선 적용한다.따라서 빈은 하나만 존재
  • 스캔할 때 사용하는 빈 이름(@Component)과 수동 등록한 빈 이름이 다른 경우에는 @Qualifer를 통해서 알맞은 빈을 선택해야 한다. 이때 빈은 두개 다 존재한다.
profile
초보 개발자의 성장기💻

0개의 댓글