2020-3-26 다시 시작하는 스프링(2) - 빈주입
수동으로 빈등록하고 주입하기
XML방식
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="bookService"
class="me.jimmy.springmvc.book.BookService">
<property name="bootRepository" ref="bootRepository"></property>
</bean>
<bean id="bootRepository" class="me.jimmy.springmvc.book.BookRepository"/>
</beans>
// 다른방법
<context:component-scan base-package="me.jimmy.springmvc"></context:comp> // anto
- component-scan을 이용해서 base-package이하의 컴포넌트들을 스캔해서 빈으로 등록한다.
- 어노테이션기반으로 빈을 등록하고 설정하는 부분은 스프링2.5부터 가능.
Java annotation방식
@Configuration
public class ApplicationConfig {
@Bean
public BookRepository bookRepository() {
return new BookRepository();
}
@Bean
public BookService bookService() {
BookService bookService = new BookService();
bookService.setBookRepository(bookRepository());
return bookService;
}
}
public class SpringmvcApplication {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfig.class);
String [] beans = context.getBeanDefinitionNames();
System.out.println(Arrays.toString(beans));
}
}
//Another
@Configuration
@ComponentScan(basePackages = "me.") // 방법1
@ComponentScan(basePackageClasses = SpringmvcApplication.class) // 방법2
public class ApplicationConfig {
}
- BookService에서 setter를 썼기때문에 Autowired가 가능했다.
- 자바 어노테이션 방식 역시 xml component-scan처럼 @ComponentScan을 활용할 수 있다. @ComponentScan의 속성에는 basePackage와 basePacakgeClasses가 있지만 basePacakgeClasses가 더 type-safe하다.
- basepackageClasses를 활용하면 클래스에 해당하는 주변 패키지들을 다 스캔한다.
- 위의 방법이 현재 스프링부트에서 사용하는 방법에 가장 근접하다.
현재의방식
@SpringBootApplication
public class SpringmvcApplication {
public static void main(String[] args) {
SpringApplication.run(SpringmvcApplication.class, args);
}
}
- xml, java 어노테이션 방식으로 ApplicationContext를 만들고 주입을 하다가 현재는 @SpringBootApplication만 붙이면 알아서 다 된다.
- @SpringBootApplication 내부를 보면 @ComponentsScan, @SpringBootConfiguration 등이 이미 붙어있다.
@Autowired
- @Autowired는 생성자를 통해 주입받는 방법과 필드 주입, 세터에서 주입받는 방식이 있다.
@Service
public class BookService {
@Autowired // 필드주입
private BookRepository bookRepository;
@Autowired // 생성자 주입
public BookService(BookRepository bookRepository) {
this.bookRepository = bookRepository;
}
@Autowired(required = false) // setter주입
public BookService setBookRepository(BookRepository bookRepository) {
this.bookRepository = bookRepository;
return this;
}
}
- required는 기본적으로 true이다. 안필요하면 false로 둔다.
- 생성자를 통해 주입할때는 주입되는 빈이 개입이 되서 반드시 있어야한다. setter나 필드 주입을 사용할때는 required를 통해 해당 빈이 없이도 빈이 생성될 수 있게해줄 수 있다.
경우의수
-
해당 타입의 빈이 없는 경우
-
해당 타입의 빈이 한 개인 경우
-
해당 타입의 빈이 여러개인 경우
- 빈 이름으로 시도
- 같은 이름의 빈 찾으면 해당 빈 사용
- 같은 이름으로 못찾으면 실패
- 빈 이름으로 시도
@Repository
public class JimmyBookRepository implements BookRepository {
}
@Repository
@Primary // sol1
public class MyBookRepository implements BookRepository {
}
@Service
public class BookService {
@Autowired
@Qualifier("jimmyBookRepository") // sol2
private BookRepository bookRepository;
}
// Field bookRepository in me.jimmy.springmvc.book.BookService required a single bean, but 2 were found => error message
// Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed => solution
@Component
public class BookRunner implements ApplicationRunner {
@Autowired
private BookService bookService;
@Override
public void run(ApplicationArguments args) throws Exception {
bookService.printBook();
}
}
// solution1 : class me.jimmy.springmvc.book.MyBookRepository$$EnhancerBySpringCGLIB$$593a097f
// solution2 : class me.jimmy.springmvc.book.JimmyBookRepository$$EnhancerBySpringCGLIB$$bc20518d
- BookService에서는 BookRepository를 주입못해준다. 이유는 BookRepository에 해당하는 bean이 1개 이상이기 때문이다.
- 방법1 : @Primary를 붙인다. 붙이고나서 ApplicationRunner를 구현해서 돌려보면 @Primay를 붙인 MyBookRepository 클래스가 주입된것을 확인할 수 있다.
- 방법2 : @Qualifier(“className”) 소문자로 시작되는 className을 붙여서 한다. 이때는 @Primary보다 우선순위가 높다.
- 방법3 : service에서 List
types를 받는다.. - @Autowired는 타입과 이름을 같이 본다.
- @Primary를 가장 추천.
동작원리
- BeanPostProcessor참조 : 새로 만든 빈 인스턴스를 수정할 수 있는 라이프 사이클 인터페이스
Written on March 26, 2020