Inversion of Control
어떤 객체가 사용하는 의존 객체를 직접 만들어 사용하는게 아니라, 주입 받아 사용하는 방법
스프링 IoC 컨테이너 (BeanFactory)
빈
ApplicationContext
빈으로 등록하는 방법
ClassPathXmlApplicationContext(XML)을 이용해 등록하는 방법
resource 폴더안에 application.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.jiho.springdemo.BookService">
<property name="bookRepository" ref="bookRepository"/>
</bean>
<bean id="bookRepository"
class="me.jiho.springdemo.BookRepository"/>
</beans>
AnnotationConfigApplicationContext(JAVA)를 이용해 등록하는 방법
해당 클래스가 있는 패키지 안에 클래스를 만든다. 그 후 AnnotationConfigApplicationContext를 이용해 해당 클래스를 넣어준다.
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ApplicaitonConfig {
@Bean
public BookRepository bookRepository() {
return new BookRepository();
}
@Bean
public BookService bookService() {
BookService bookService = new BookService(bookRepository());
return bookService;
}
}
public class SpringdemoApplication {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(ApplicaitonConfig.class);
SpringApplication.run(SpringdemoApplication.class, args);
}
}
컴포넌트 스캔
위의 방법대로 하면 번거롭다. 따라서 원하는 클래스에 @Component를 붙이고 컴포넌트 스캔 과정을 통해 빈으로 등록해 준다.
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>"
xmlns:context="<http://www.springframework.org/schema/context>"
xsi:schemaLocation="<http://www.springframework.org/schema/beans> <http://www.springframework.org/schema/beans/spring-beans.xsd> <http://www.springframework.org/schema/context> <https://www.springframework.org/schema/context/spring-context.xsd>">
<context:component-scan base-package="me.jiho.springdemo"/>
</beans>
Java에서 설정하는 방법
@ComponentScan annotation을 이용하여 basePackegeClasses가 위치한 곳부터 ConponentScan을 한다.
@Configuration
@ComponentScan(basePackageClasses = SpringdemoApplication.class)
public class ApplicaitonConfig {
}
스프링 부트의 경우
@SpringBootApplication이라는 annotation이 @ComponentScan과 @Configuration을 모두 포함하여 아래 파일 자체가 Config파일이 된다. 번거롭게 Config 파일을 만들 필요가 없다.
@SpringBootApplication
public class SpringdemoApplication {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(ApplicaitonConfig.class);
SpringApplication.run(SpringdemoApplication.class, args);
}
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
//...
}
@Autowired
@Autowired를 사용 할 수 있는 위치
생성자를 이용
@Controller
class OwnerController {
private final OwnerRepository owners;
private VisitRepository visits;
@Autowired
public OwnerController(OwnerRepository clinicService,
VisitRepository visits) {
this.owners = clinicService;
this.visits = visits;
}
}
필드를 이용
@Controller
class OwnerController {
@Autowired
private final OwnerRepository owners;
@Autowired
private VisitRepository visits;
}
Setter를 이용
@Controller
class OwnerController {
@Autowired
public void setVisits(VisitRepository visits) {
this.visits = visits;
}
private VisitRepository visits;
}
같은 타입의 빈이 여러개인 경우
public interface BookRepository {
}
import org.springframework.stereotype.Repository;
@Repository
public class MyBookRepository implements BookRepository{
}
import org.springframework.stereotype.Repository;
@Repository
public class YourBookRepository implements BookRepository{
}
@Primary를 이용
@Primary annotaion이 붙은 클래스를 주입해준다.
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Repository;
@Repository
@Primary
public class MyBookRepository implements BookRepository{
}
@Qualifier을 이용
주입하고 싶은 빈의 이름을 @Qualifier에 넣어준다.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
@Service
public class BookService {
BookRepository bookRepository;
@Autowired
public BookService(@Qualifier("myBookRepository") BookRepository bookRepository) {
this.bookRepository = bookRepository;
}
}
해당 타입의 빈 모두 받기
리스트 타입으로 해당 타입의 빈 모두 받기
@Service
public class BookService {
List<BookRepository> bookRepository;
@Autowired
public BookService( List<BookRepository> bookRepository) {
this.bookRepository = bookRepository;
}
}
필드의 이름을 빈의 이름과 똑같이 만들기
필드의 이름과 동일한 빈을 주입해 준다. 권장하지 않음
@Service
public class BookService {
BookRepository myBookRepository;
@Autowired
public BookService( BookRepository bookRepository) {
this.myBookRepository = bookRepository;
}
}
동작원리
AutowierdAnnotaionBeanPostProcessor가 Initializing 전에 빈을 찾아서 주입을 해준다.
BeanFactory → BeanPostProcessor → AutowiredAnnotationBeanPostProcessor
스캔 위치 설정
컴포넌트 스캔이 붙어있는 패키지 밖에는 스캔하지 않는다.
@Filter