아래 글은 토비의 스프링을 읽고 공부하였습니다.
목차
1. DI란
1) 의존관계
2) 모델링 시점의 의존관계 vs 런타임 시점의 의존관계
3) 제3의 존재
4) 의존관계 검색과 주입
5) 의존관계 주입 방법 3가지
2. XML을 이용한 DI
1) DI 설정정보를 만드는 XML
2) XML을 이용하는 애플리케이션 컨텍스트와 Datasource
1. DI
DI는 의존관계 주입으로, IoC와 동떨어진 개념은 아니다. DI는 오브젝트 레퍼런스를 외부로부터 제공(주입)받고 이를 통해 여타 오브젝트와 동적으로 의존관계가 만들어지는 것이 핵심이다. 우리가 보통 알고있는 가장 대표적인 DI는 @Autowired 어노테이션일 것이다.
1. 의존관계
객체를 직접 생성하는 것이 아니라 외부에서 생성한 후 주입하는 방식이다. 왼쪽 방법은 A 객체에서 B, C 객체를 New 생성자를 이용해 직접 생성하는 방법이고, 오른쪽 방법은 외부에서 생성한 B, C 객체를 setter() 방식을 통해 사용한다. 즉 IoC 컨테이너에서 만들어진 B와 C 객체를 가져오는 것이다.
2. 모델링 시점의 의존관계 VS 런타임 시점의 의존관계
위에서 봤던 두 종류의 의존관계는 모델링 / 런타임 시점으로 나누어서 설명할 수 있다.
#모델링 시점의 의존관계
public class A {
private B b = new B(); // 모델링 시점의 의존 관계
}
#런타임 시점의 의존관계
@Component
public class A {
private B b; // 런타임 시점의 의존 관계
@Autowired
public A(B b) {
this.b = b;
}
}
# 런타임 시점의 의존관계의 또 다른 예시 - 생성자
public class UserDao {
ConnectionMaker connectionMaker;
// DI (Dependency Injection) 를 이용한 방법
public UserDao(ConnectionMaker connectionMaker) {
this.connectionMaker = connectionMaker;
}
}
모델링 시점의 의존관계는 컴파일 시점에 의존관계가 결정되며, 위와 같이 코드 상에서 의존관계가 성립된다. 반면 런타임 시점의 의존관계는 런타임 시점에 동적으로 의존관계가 결정되며 스프링 컨테이너는 외부에서 빈을 생성하고 관리하며 컴포넌트들 간의 의존성을 설정한다. 예를 들어 @Autowired 어노테이션은 클래스 A가 클래스 B에 런타임 시점에 의존하는 것을 가능하게 한다.
3. 제3의 존재
DI의 핵심은 설계 시점에서는 알지 못했던 두 오브젝트의 관계를 맺도록 도와주는 제 3의 존재가 있다는 것이다. 제 3의 존재는 바로 관계설정 책임을 가진 코드를 분리해서 만들어진 오브젝트라고 볼 수 있다. DaoFactory, 애플리케이션 컨텍스트, 빈 팩토리, IoC 컨테이너 등이 모두 외부에서 오브젝트 사이의 런타임 관계를 맺어주는 책임을 지닌 제 3의 존재라고 볼 수 있다.
4. 의존관계 검색과 주입
스프링이 제공하는 IoC 방식에는 의존관계를 맺는 방법이 외부로부터의 주입이 아니라 스스로 검색해서 가져오는 의존관계 검색이 있다. 의존관계 검색은 자신이 필요로 하는 의존 오브젝트를 능동적으로 찾는다. 예를 들어 스태틱 메소드인 main()에서는 DI를 이용해 오브젝트를 주입받을 방법이 없기 때문에 사용자 요청을 받을 때마다 main() 메소드와 같은 서블릿에서 스프링 컨테이너에 담긴 오브젝트를 사용하려면 한 번은 의존관계 검색 방식을 사용해 오브젝트를 가져와야 한다. 이런 서블릿은 스프링이 미리 만들어 제공한다.!
검색과 주입의 가장 큰 차이는 '주입' 방식에서는 주입을 원하는 오브젝트는 먼저 자기 자신이 컨테이너가 관리하는 빈이 되어야 한다는 것이다. 아래에서도 DI 방식에서는 UserService가 UserRepository를 주입받을 때 UserService에 있는 어노테이션 @Service가 이 UserService도 컨테이너가 관리하는 빈이 되었다는 걸 의미한다.
# 의존관계 검색
public class DependencyLookupExample {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
// 의존성 검색을 통해 Bean을 가져옴
UserService userService = context.getBean(UserService.class);
// UserService의 메서드 호출
userService.processUser("123");
}
}
# 의존관계 주입
@Service
public class UserService {
private UserRepository userRepository;
@Autowired // 생성자 주입을 통해 UserRepository 의존성을 주입
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
// UserService의 메서드 구현
public void processUser(String userId) {
// userRepository를 사용하여 작업 수행
}
}
5. 의존관계 주입 방법 3가지
- 생성자를 이용한 주입 ✔️ 권장!
- 수정관계 메소드(setter)을 이용한 주입 -> 의존관계가 주입될 수 있을 때 사용한다.
- 필드주입(@Autowired) -> 가장 편한 방법 같지만 외부에서 수정이 불가능하다는 치명적인 단점을 갖는다.
- 일반메소드 사용 (일반적으로 사용하지 않는다)
2. XML을 이용한 DI
요즘에는 Spring에서 자체적으로 모두 해결해주지만 많은 레거시 프로젝트에서는 아직 XML을 사용하는 곳들이 있으니 알아두자.
1. DI 설정정보를 만드는 XML
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframwork.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-3.0.xsd">
<bean id="connectionMaker" class="springbook.user.dao.NConnectionMaker" />
<bean id="userDao" class="springbook.user.dao.UserDao">
<property name="ConnectionMaker" ref="connectionMaker" />
</bean>
</beans>
쉽게 말해 @Configuration, @Bean이 붙은 자바 클래스로 만든 설정과 내용이 동일하다. 이 설정 파일을 통해 빈 객체를 스프링 컨테이너에서 관리하고 DI를 수행할 수 있다.
<bean> 스프링 컨테이너에 등록할 빈을 정의. 각 빈은 고유한 id 속성을 가진다.
<property> userDao.setConnectionMaker(connectionMaker); 를 의미한다. 즉 userDao 안에 connectionMaker를 주입한다.
2. XML을 이용하는 애플리케이션 컨텍스트과 dataSource
<!-- global properties setting -->
<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close">
<constructor-arg>
<bean class="com.zaxxer.hikari.HikariConfig">
<constructor-arg>
<props>
<prop key="jdbcUrl">${url}</prop>
<prop key="username">${username}</prop>
<prop key="password">${password}</prop>
</props>
</constructor-arg>
<property name="driverClassName" value="${driver}"/>
<property name="minimumIdle" value="5" />
<property name="maximumPoolSize" value="10" />
<property name="connectionTestQuery" value="select 1 from sys.dual" />
<property name="connectionTimeout" value="300000" />
</bean>
</constructor-arg>
</bean>
빈의 생성과 관계설정 같은 제어를 담당하는 IoC 오브젝트를 빈 팩토리(Bean Factory), 또는 애플리케이션 컨텍스트(application context)라고 부른다.
dataSource() 메소드에서처럼 DB 연결정보를 넣는 설정을 만들 수 있다.
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.example.MyService;
public class MyApp {
public static void main(String[] args) {
// XML 설정 파일을 사용하여 애플리케이션 컨텍스트를 로드
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
}
}
이렇게 xml파일을 가져와 application context를 로드할 수 있다. 물론 지금은 springboot에서 제공해주는 여러 툴들로 이 과정이 모두 생략된다!
'BackEnd > JAVA\SPRING' 카테고리의 다른 글
[SPRING] enum 타입, 역할과 책임의 분리 (0) | 2023.10.02 |
---|---|
[SPRING] DI, 템플릿과 콜백 (0) | 2023.09.22 |
[SPRING] IOC (0) | 2023.09.03 |
[SPRING] DAO의 분리와 확장 (5) | 2023.09.03 |
[OPEN SOURCE] FOSSLIGHT 오픈소스 기여 (0) | 2023.08.21 |