
@Autowired = “이 타입의 Bean 좀 넣어줘”
- 스프링 컨테이너 안에서 타입 기준으로 Bean을 찾아서 주입해 준다.
BookService입장에서 보면
- “나 BookDAO 필요해요”
- 그러면 컨테이너가
BookDAO타입 Bean을 찾아 넣어주는 구조.
예시 구조:
public interface BookDAO { ... }
@Repository("bookDAO")
public class BookDAOImpl implements BookDAO { ... }
@Service("bookServiceField")
public class BookService {
@Autowired
private BookDAO bookDAO; // 여기로 BookDAOImpl이 들어옴
public List<BookDTO> selectAllBooks() { ... }
}
포인트
new BookDAOImpl() 이런 코드가 서비스에 없어야 스프링 DI를 제대로 쓰는 것. BookService에서BookDAO를 사용하기 위해 BookDAOImple() 을 여기서 new 한다면, 결합도가 높아지는 것.@Autowired 주입 방법 3가지@Service("bookServiceField")
public class BookService {
@Autowired
private /*final*/BookDAO bookDAO;
...
}
BookDao 타입 의 bean 객체를 이 프로퍼티에 자동으로 추가한다.
장점
단점
→ 실무에서는 점점 덜 쓰는 추세, 하지만 학습용 예제나 단순 서비스에서 많이 본다.
@Service("bookServiceConstructor")
public class BookService {
private final BookDAO bookDAO;
@Autowired // 생성자가 하나면 생략 가능
public BookService(BookDAO bookDAO) {
this.bookDAO = bookDAO;
}
public List<BookDto> selectAllBooks() {
return bookDao.selectBookList();
}
public BookDto selectBookById(int sequence) {
return bookDao.selectOneBook(sequence);
}
}
장점이 매우 많다:
new BookService(new FakeBookDAO());
이런 식으로 단위 테스트 가능@Autowired 는 생성자가 하나면 spring4.3 버전 이후로 생략해도 자동으로 어노테이션을 주입해준다. 단, 생성자가 1개 이상이라면 명시적으로 작성해야 한다.→ 스프링 공식 문서와 요즘 코드 스타일은 “생성자 주입 + final 필드” 패턴을 가장 많이 쓴다.
실행파일 예시
public static void main(String[] args) {
ApplicationContext context =
new AnnotationConfigApplicationContext("com.ohgiraffers.section01");
String[] beanDefinitionNames = context.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println("beanDefinitionName = " + beanDefinitionName);
}
// IoC 컨테이너 확인
BookService bookService = context.getBean("bookServiceConstructor", BookService.class);
System.out.println("bookService = " + bookService.selectBookById(1));
List<BookDto> books = bookService.selectAllBooks();
for (BookDto book : books) {
System.out.println(book);
}
}
IoC 컨테이너 없이 코드를 테스트할 때는 아래와 같이도 간단하게 사용한다.
/* IoC 컨테이너 없이 */
BookService bookService2 = new BookService(new BookDaoImpl());
System.out.println("bookService2 = " + bookService2.selectBookById(1));
@Service("bookServiceSetter")
public class BookService {
private BookDAO bookDAO;
@Autowired
public void setBookDAO(BookDAO bookDAO) {
this.bookDAO = bookDAO;
}
}
특징:
@Autowired(required= true/false)@Autowired(required = true/false) 는
스프링이 이 의존성을 반드시 주입해야 하는지 여부를 설정하는 옵션이다.
아주 간단하게 정리하면:
required = true (기본값)“무조건 이 타입의 Bean 있어야 해. 없으면 앱 실행하면 안 돼.”
- 스프링이 빈을 못 찾으면 애플리케이션 실행 자체가 실패한다.
- 즉, 스프링이 주입 필수 의존성이라고 판단한다.
예:
@Autowired(required = true)
private BookDAO bookDAO;
BookDAO 타입 Bean이 없으면:
NoSuchBeanDefinitionException
발생하고 프로그램이 안 뜬다.
required = false의미 : bean 이 없어도 무방. 있으면 넣고, 없으면 null로 설정”
@Autowired(required = false)
private BookDAO bookDAO;
이 경우:
그래서 다음 같은 설계가 가능하다:
bookDAO.selectAllBooks(); // bookDAO null이면 NullPointerException 발생
그래서 선택적 의존성에 보통 아래 방식이 많이 쓰인다.
@Autowired(required = false)
private Optional<BookDAO> bookDAO;
또는
스프링 부트 + 현대 스프링에서는 아래 조합이 더 추천됨:
required = true → 빈이 반드시 있어야 한다(없으면 에러)required = false → 빈이 없어도 된다(없으면 null로 둠)여러 bean 중 하나를 고르는 Annotation
Pokemon 예제를 기준으로 보면 쉽게 이해된다.
public interface Pokemon { void attack(); }
@Component
public class Charmander implements Pokemon { ... }
@Component
public class Pikachu implements Pokemon { ... }
@Component
public class Squirtle implements Pokemon { ... }
지금 Pokemon 타입 Bean이 3개다.
@Service
public class PokemonService {
@Autowired
private Pokemon pokemon; // 어느 걸 넣어야 할까?
}
이러면 에러가 난다.
“Pokemon 타입 Bean이 3개인데 하나만 고를 수가 없다.”
이때 쓰는 게 @Primary 와 @Qualifier.
@Component
@Primary
public class Charmander implements Pokemon { ... }
Pokemon 타입을 주입해야 할 때Charmander가 기본 선택@Service("pokemonServicePrimary")
public class PokemonService {
private final Pokemon pokemon;
@Autowired
public PokemonService(Pokemon pokemon) {
this.pokemon = pokemon; // 자동으로 Charmander
}
}
규칙:
@Service("pokemonServiceQualifier")
public class PokemonService {
@Autowired
@Qualifier("pikachu")
private Pokemon pokemon; // pikachu Bean을 넣어라
public void pokemonAttack() {
pokemon.attack();
}
}
@Qualifier("빈이름") 으로 정확히 Bean을 지정할 수 있다. 여러 개의 빈 객체 중 특정 빈 객체를 이름으로 지정하는 어노테이션 (@Primary 보다 우선시 된다.)결과
피카츄 ⚡⚡⚡ 백만 볼트 ⚡⚡⚡
→ @Primary 가 Charmender에 붙어 있지만 @Qualifier 를 “pikachu” 로 지정하여, pokemon 객체에 pikachu가 들어가 있다.
@Autowired
public PokemonService(@Qualifier("squirtle") Pokemon pokemon) {
this.pokemon = pokemon; // Squirtle 주입
}
우선순위:
Collectionmain
public static void main(String[] args) {
ApplicationContext context =
new AnnotationConfigApplicationContext("com.ohgiraffers.section02");
String[] beanDefinitionNames = context.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println("beanDefinitionName = " + beanDefinitionName);
}
/* 1. List */
System.out.println("========== LIST =============");
// 해당 타입 전부를 싹 긁어오는거임
PokemonServiceList pokemonService = context.getBean("pokemonServiceCollectionList", PokemonServiceList.class);
pokemonService.pokemonAttacks();
/* 2. Map */
System.out.println("============ MAP ==============");
PokemonServiceMap pokemonServiceMap = context.getBean("pokemonServiceCollectionMap", PokemonServiceMap.class);
pokemonServiceMap.pokemonAttacks();
}
@Service("pokemonServiceCollectionList")
public class PokemonServiceList {
private final List<Pokemon> pokemonList;
@Autowired
public PokemonServiceList(List<Pokemon> pokemonList) {
this.pokemonList = pokemonList;
}
public void pokemonAttack() {
pokemonList.forEach(Pokemon::attack);
}
}
Pokemon 타입 Bean들을 전부 찾아 List에 넣어준다.main
/* 1. List */
System.out.println("========== LIST =============");
// 해당 타입 전부를 싹 긁어오는거임
PokemonServiceList pokemonService = context.getBean("pokemonServiceCollectionList", PokemonServiceList.class);
pokemonService.pokemonAttacks();
@Service("pokemonServiceCollectionMap")
public class PokemonServiceMap {
private Map<String, Pokemon> pokemons;
public PokemonServiceMap(Map<String, Pokemon> pokemons) {
this.pokemons = pokemons;
}
public void pokemonAttacks() {
pokemons.forEach((name, v) -> {
System.out.println("Pokemon : " + name);
System.out.print("공격 : ");
v.attack();
});
}
}
이 패턴은 “플러그인 여러 개, 전략 패턴 여러 개”를 한 번에 주입하고 싶을 때 유용하다.
main
/* 2. Map */
System.out.println("============ MAP ==============");
PokemonServiceMap pokemonServiceMap = context.getBean("pokemonServiceCollectionMap", PokemonServiceMap.class);
pokemonServiceMap.pokemonAttacks();
mainpublic static void main(String[] args) {
ApplicationContext context =
new AnnotationConfigApplicationContext("com.ohgiraffers.section02");
String[] beanDefinitionNames = context.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println("beanDefinitionName = " + beanDefinitionName);
}
/* 1. List */
System.out.println("========== LIST =============");
// 해당 타입 전부를 싹 긁어오는거임
PokemonServiceList pokemonService = context.getBean("pokemonServiceCollectionList", PokemonServiceList.class);
pokemonService.pokemonAttacks();
/* 2. Map */
System.out.println("============ MAP ==============");
PokemonServiceMap pokemonServiceMap = context.getBean("pokemonServiceCollectionMap", PokemonServiceMap.class);
pokemonServiceMap.pokemonAttacks();
}
결과
========== LIST =============
파이리 불꽃 공격 🔥🔥
피카츄 ⚡⚡⚡ 백만 볼트 ⚡⚡⚡
꼬부기 물대표 공격 🚿🚿
============ MAP ==============
Pokemon : charmander
공격 : 파이리 불꽃 공격 🔥🔥
Pokemon : pikachu
공격 : 피카츄 ⚡⚡⚡ 백만 볼트 ⚡⚡⚡
Pokemon : sqiuretle
공격 : 꼬부기 물대표 공격 🚿🚿
@Service("pokemonServiceResource")
public class PokemonService {
@Resource(name = "pikachu")
private Pokemon pokemon;
public void pokemonAttack() {
pokemon.attack();
}
}
특징:
@Resource : 자바 진영에서 제공하는 DI Annotation
@Autowired와 달리 name 속성 값으로 의존성 주입을 할 수 있고 필드 주입, 세터 주입만 가능하다.
name 으로 의존성을 주입한다.name 속성을 쓰면: 이름 → (없으면) 타입 순으로 찾는다.name 없이 쓰면: 필드 이름 → 타입 순으로 찾는 형태로 동작@Resource
private List<Pokemon> pokemonList; // 타입으로도 주입 가능
List<Pokemon> 타입이라면, name 속성을 생략하면 3개의 bean이 담긴다. 규칙은 위에와 동일하다.주의:
스프링만 쓸 거면 보통 @Autowired + @Qualifier 조합을 더 자주 사용한다.
@Service("pokemonServiceInject")
public class PokemonService {
@Inject
@Named("pikachu")
private Pokemon pokemon;
public void pokemonAttack() {
pokemon.attack();
}
}
@Named("빈이름") 으로 선택결과
꼬부기 물대표 공격 🚿🚿
사용 가능한 위치:
예: 생성자 주입
@Service("pokemonServiceInject")
public class PokemonService {
private final Pokemon pokemon;
@Inject
public PokemonService(@Named("pikachu") Pokemon pokemon) {
this.pokemon = pokemon;
}
}
@Autowired = 타입으로 의존성 주입@Primary = 타입 여러 개일 때 “기본값”@Qualifier = 그 중에서도 “이름으로 정확히 지정”@Resource = 이름 우선(자바 표준)@Inject = 타입 우선(자바 표준)