@Autowired
를 이용한 의존 자동 주입에 대해 알아보자 자동 주입: 의존 대상을 설정 코드에서 직접 주입하지 않고 스프링이 자동으로 의존하는 빈 객체를 주입해주는 기능
자동 주입 방법: 의존을 주입할 대상에 @Autowired 애노테이션을 붙여서 스프링이 자동으로 빈을 주입하도록 한다.
필드, 메서드,생성자,세터 등에 주입할 수 있다.
import org.springframework.beans.factory.annotation.Autowired;
public class ChangePasswordService {
@Autowired
private MemberDao memberDao;
public void changePassword(String userId, String newPassword) {
memberDao.updatePassword(userId, newPassword);
}
}
설명: 이렇게 하면
ChangePasswordService
가 생성될 때, 스프링이 자동으로MemberDao
객체를memberDao
필드에 주입한다.
= memberDao가 주입되지 않으면?
ChangePasswordService
의memberDao
필드가null
-> 암호 변경 기능을 실행 시NullPointerException
가 나옴
@Autowired
애노테이션을 사용했지만 일치하는 빈이 없는 경우,스프링은 예외를 발생시킴.MemberDao
타입의 빈이 스프링 컨테이너에 등록되지 않았기 때문이다.
따라서 암호 변경 기능이 정상 작동함 =
@Autowired
애노테이션을 붙인 필드에 실제MemberDao
타입의 빈 객체가 잘 들어감
자동 주입 가능한 빈이 두 개 이상이면 자동 주입할 빈을 지정할 수 있는 방법이 필요 -> @Qualifier 애노테이션을 사용하여 여러 빈 중에서 특정 빈을 선택할 수 있다.
1. 빈 설정 메서드에서 @Qualifier 사용 @Bean 애노테이션을 사용하여 빈을 등록할 때, @Qualifier를 통해 빈의 이름을 명시할 수 있다. 예제:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.beans.factory.annotation.Qualifier;
@Configuration
public class AppCtx {
@Bean
@Qualifier("memberDao1")
public MemberDao memberDao1() {
return new MemberDao1();
}
@Bean
@Qualifier("memberDao2")
public MemberDao memberDao2() {
return new MemberDao2();
}
}
이 설정에서는 @Qualifier를 사용하여 빈의 이름을 지정하고, 이 이름으로 빈을 구분합니다.
2. 의존성 주입에서 @Qualifier 사용 @Autowired와 함께 사용되는 @Qualifier는 의존성 주입 시 특정 빈을 선택하는 데 사용된다. 필드 주입, 메서드 주입, 생성자 주입에서 모두 가능하다.
public class ChangePasswordService {
@Autowired
@Qualifier("memberDao1")
private MemberDao memberDao;
public void changePassword(String userId, String newPassword) {
memberDao.updatePassword(userId, newPassword);
}
}
-메서드 주입: 생성자 또는 세터 메서드의 파라미터에 @Qualifier를 사용하여 빈을 주입가능
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
public class ChangePasswordService {
private final MemberDao memberDao;
@Autowired
public ChangePasswordService(@Qualifier("myMemberDao") MemberDao memberDao) {
this.memberDao = memberDao;
}
public void changePassword(String userId, String newPassword) {
memberDao.updatePassword(userId, newPassword);
}
}
빈 설정 메서드에서의 사용은 빈을 등록할 때 이름을 지정하는 것이고, 의존성 주입에서의 사용은 이미 등록된 여러 빈 중에서 어떤 것을 주입할지 명시적으로 선택하는 것
별도의 @Qualifier 애노테이션을 사용하지 않으면 기본 한정자로는 빈 이름이 사용 따라서 같은 타입의 빈이 여러 개면 @Qualifier 애노테이션을 써서 한정자를 따로 명시해주자
상속 관계에서 발생할 수 있는 문제점:
MemberPrinter를 상속한 MemberSummaryPrinter클래스가 있다고 하자.
@Configuration
public class AppConfig {
@Bean
public MemberPrinter memberPrinter1() {
return new MemberPrinter();
}
@Bean
public MemberSummaryPrinter memberPrinter2() {
return new MemberSummaryPrinter();
}
}
public class MemberService {
@Autowired
private MemberPrinter memberPrinter;
}
겉으로는 다른 타입이여서 문제 없을 거 같지만 @Autowired를 사용하면, 실제로는 MemberSummaryPrinter클래스는 MemberPrinter의 자식클래스여서 MemberPrinter에 할당될 수 있어서 스프링이 두 개의 빈 중 어느 것을 주입해야할 지 모호해지는 문제가 발생
두 가지 해결 방법:
- @Qualifier 사용하기
public class MemberService {
@Autowired
@Qualifier("memberPrinter1")
private MemberPrinter memberPrinter;
}
- 상속 구조 활용하기
@Configuration
public class AppConfig {
@Bean
public MemberSummaryPrinter memberSummaryPrinter() {
return new MemberSummaryPrinter();
}
}
MemberSummaryPrinter 타입은 하나만 존재하므로 MemberSummaryPrinter 빈을 자동 주입 받도록 코드를 수정하면 자동 주입 대상이 두 개여서 발생하는 문제를 해결할 수있음.
5.1 생성자 초기화와 필수 여부 지정 방식 동작 이해
기본값 (required=true): 빈이 필수로 주입되어야 하며, 빈이 없으면 예외 발생.
빈이 없어도 되는 경우(빈 주입이 선택적인 경우) 1.required=false : 이렇게 쓰면 빈이 없어도 애플리케이션이 정상 작동한다. 대신 빈이 없으면 해당 메서드는 호출되지 않음
@Autowired(required=false)
private MyDependency myDependency;
2.@Nullable:빈이 있으면 빈 전달하고, 빈이 없을 경우 필드에 null이 할당됨. 상 메서드는 호출됨
import javax.annotation.Nullable;
@Autowired
@Nullable
private MyDependency myDependency;
자동 주입 대상 타입이 Optional인 경우: 일치하는 빈 있으면 해당 빈 값 갖는 Optional을 인자로 전달 없으면 값 없는 Optional을 인자로 전달
@Autowired
public void setDateFormatter(Optional</DateTimeFormatter>formatterOpt){
if(formatterOpt.isPresent()){
this.dateTimeFormatter = formatterOpt.get();
} else{
this.dateTimeFormatter = null;
}
}
자동 주입: @Autowired를 사용하여 스프링이 빈을 자동으로 주입한다. 명시적 주입: 수동으로 빈을 설정하거나 XML 파일을 사용하여 빈을 정의한다.
만약 명시적 주입과 자동 주입 둘다 써져있으면 자동주입으로 처리된다고 한다. 둘다 쓰면 나중에 오류 찾기 힘들다고 한다. 일부 자동주입 적용이 어려운 코드를 제외하고는 그냥 자동주입을 쓰라고 한다.
컴포넌트 스캔은
@Component
애노테이션을 사용하여 스프링이 자동으로 빈을 검색하여 등록하는 기능이다.
import org.springframework.stereotype.Component;
@Component("muComponent")
public class MyComponent {
}
@Component를 붙인 클래스는 스캔 대상이 된다. @Component 애노테이션에 값을 주면 그 값을 빈 이름으로 사용한다.
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan(basePackages = "spring")
public class AppConfig {
}
@Component 애노테이션이 붙인 클래스를 스캔해서 스프링 빈으로 등록하려면 설정클래스에 @ComponentScan 애노테이션을 적용해야한다. basePackages 속성으로 지정한 패키지 내의 빈들만 스캔하여 등록한다.
excludeFilters 속성을 사용하면 특정 대상을 자동등록 대상에서 제외할 수있다.
예시로 보자 1.애노테이션 필터 특정 애노테이션이 붙은 타입을 제외함
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
@Configuration
@ComponentScan(
basePackages = "com.example",
excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = CustomAnnotation.class)
)
public class AppConfig {
}
2.타입 필터 특정 타입 제외
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
@Configuration
@ComponentScan(
basePackages = "com.example",
excludeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = MyService.class)
)
public class AppConfig {
}
3.두 개 이상의 필터 설정
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
@Configuration
@ComponentScan(
basePackages = "com.example",
excludeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = CustomAnnotation.class),
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = MyService.class)
}
)
public class AppConfig {
}
4.정규표현식 필터: spring으로 시작하고 Dao로 끝나는 특 패턴을 사용하여 클래스를 스캔에서 제외함
@ComponentScan(basePackages = {"spring"},
excludeFilters = @Fiter(type = FilterType.REGEX,pattern = "spring\\..*Dao"))
4.1 기본 스캔 대상 @Component, @Service, @Repository, @Controller 등 스프링의 스테레오타입 애노테이션이 붙은 클래스들
5.1 빈 이름 충돌 같은 이름의 빈이 여러 개 등록되면 충돌이 발생할 수 있다. 이 경우 빈 이름을 명확히 하여 문제를 해결한다. 예시: 두 개의 @Component 빈이 같은 이름을 가진 경우, 스프링은 어떤 빈을 사용할지 결정하지 못할 수 있다. 빈 이름을 명시적으로 설정하여 문제를 해결한다.
5.2 수동 등록한 빈과 충돌 XML 설정 파일에서 수동 등록한 빈과 @Component로 등록된 빈이 충돌할 수 있다. 이름이 같은 경우는 수동으로 이름을 등록한 쪽이 살아남는다.. 이름이 다른 경우는 둘 다 생성되니까 잘 표시해서 잘 골라쓰자.