1. 개요
- 토비의 스프링 3주차 스터디
- 1장 오브젝트와 의존관계 (123p ~ 143p)
2. 메서드를 이용한 의존관계 주입
2.1. 수정자 메서드를 통한 주입
지금까지는 UserDao 의존성 주입을 위해 생성자를 사용했으나, 자바 관례에서는 수정자를 사용한다.
수정자란 Setter 메서드로 오브젝트 내의 애트리뷰트 값을 변경하는 것이 목적이며, 입력 값에 대한 검증 작업을 수행할 수도 있다.
public class UserDao{
private ConnectionMaker connectionMaker;
public void setConnectionMaker(ConnectionMaker connectionMaker){
this.connectionMaker = connectionMaker
}
...
}
2.2. 일반 메서드를 통한 주입
수정자 메서드는 관례에 따라 set으로 시작하고 한번에 하나의 파라미터만 가질 수 있다. 이런 제약이 싫다면 여러개의 파라미터를 갖는 일반 메서드를 DI용으로 사용할 수 있으나, 일반적으로 수정자 메서드를 사용한다.
2.3. 수정자 메서드를 선호하는 이유
1) 자바 빈 규약을 준수 (멤버변수에 getter/setter 메서드가 있어야 한다)
2) XML을 사용한 DI 처리 시 수정자 메서드와 연관되는 부분이 있다.
XML을 사용한 DI부분을 보면 위 내용을 이해할 수 있을 것이다.
3. XML을 사용한 DI
의존성 주입을 하는 방법에는 DaoFactory 클래스 처럼 Java 코드 구현하는 방법과 XML 파일로 구현하는 방법이 있다. XML은 단순한 텍스트 파일이기 때문에 다루기 쉽고, 쉽게 이해할 수 있으며, 별도의 빌드 작업이 없다는 것이 장점이 있다.
3.1. XML 설정
XML 설정 시 <beans> 를 루트 엘리먼트로 하며, 하위에 여러 개의 <bean> 태그를 통해 bean들을 정의한다. <beans>를 DaoFactory의 @Configuration, <bean>을 @Bean으로 대응해서 생각하면 된다. <bean> 태그는 id와 class라는 속성을 갖는데 id는 빈의 이름, class는 빈의 클래스 경로를 넣어준다.
<bean id = "connectionMaker" class = "org.example.user.factory.DConnectionMaker"/>
의존관계 설정은 <bean>의 <property> 태그를 사용한다. <property> 태그는 name과 ref라는 속성을 갖는데 name은 빈 오브젝트의 멤버변수 이름을, ref는 수정자 메서드를 통해 주입해줄 빈의 이름이다.
name에 입력하는 값에는 조건이 하나가 붙는데 바로 name에 대한 수정자 메서드가 있어야 한다는 점이다. 이 조건이 붙는 이유는 XML을 통한 DI시 객체 의존 관계 설정을 수정자 메서드로 처리하기 때문이다.
이 부분이 앞서 언급했던 'DI 처리 시 수정자 메서드와 매핑되는 부분'이다.
그리고 name 멤버 변수에 대한 수정자 메서드(setter)가 있어야 한다는 것이 자바 빈 규약 중 하나이다.
이에 따라 UserDao의 생성자 메서드를 통해 의존 객체를 받아오던 방식을 수정자 메서드를 통한 방식으로 변경하였다.
public class UserDao{
private ConnectionMaker connectionMaker;
public void setConnectionMaker(ConnectionMaker connectionMaker){
this.connectionMaker = connectionMaker
}
...
}
이 내용을 바탕으로 DaoFactory를 수정하면 아래와 같으며,
@Configuration
public class DaoFactory {
@Bean
public UserDao userDao(){
UserDao userDao = new UserDao();
userDao.setConnectionMaker(connectionMaker());
return userDao;
}
@Bean
public ConnectionMaker connectionMaker(){
return new DConnectionMaker();
}
}
DaoFactory를 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 = "userDao" class ="org.example.user.dao.UserDao">
<property name="connectionMaker" ref = "connectionMaker"></property>
</bean>
<bean id = "connectionMaker" class = "org.example.user.factory.DConnectionMaker"/>
</beans>
만약 name에 입력한 멤버변수의 수정자 메서드가 없다면, XML을 통한 의존 주입 시 name 부분에서 수정자를 만들라는 메시지와 함께 오류가 발생한다.
3.2. XML을 사용하는 애플리케이션 컨텍스트
이제 애플리케이션 컨텍스트 생성 시 DaoFactory 설정파일을 읽는 방법 대신 XML 설정파일을 읽는 방법으로 변경해야 한다. 방법은 GenericXmlApplicationContext 생성자를 사용하고, 생성자 파라미터로 XML파일의 클래스 패스를 지정하면 된다.
XML 설정파일의 이름은 관례에 따라 applicationContext.xml이라고 만든 후 앞 내용을 참고하여 XML 파일을 작성한다.
작성이 완료되면 main 메서드에서 AnnotationConfigApplicationContext 생성자를 통한 ApplicationContext 부분을 주석처리한 후 GenericXmlApplicationContext으로 변경한다.
public static void main(String[] args) throws SQLException, ClassNotFoundException {
ApplicationContext applicationContext = new GenericXmlApplicationContext("applicationContext.xml");
// ApplicationContext applicationContext = new AnnotationConfigApplicationContext(DaoFactory.class);
UserDao userDao = applicationContext.getBean("userDao", UserDao.class);
User user = new User();
user.setId("test123");
user.setPassword("1234");
user.setName("테스터");
userDao.add(user);
}
XML 설정파일을 통한 DI가 처리되어 테이블에 데이터가 Insert 됨을 확인할 수 있다.
4. DataSource 사용하기
지금은 DB Connection을 ConnectionMaker.makeConection() 메서드를 통해 가져오고 있지만, 자바에서 지원하는 DataSource라는 인터페이스를 사용하여 가져오도록 수정해보자.
DataSource 의 구현체 클래스는 SimpleDriverDataSource을 사용한다. 스프링에서 제공하므로 라이브러리 의존성을 추가해주자.
implementation 'org.springframework:spring-jdbc:3.1.4.RELEASE'
4.1. 사용하지 않는 코드 삭제
ConnectionMaker 대신 DataSource를 사용하고, DConnectionMaker, NConnectionMaker 대신 SimpleDriverDataSource를 사용하기 때문에 ConnectionMaker, DConnectionMaker, NConnectionMaker 클래스를 삭제하자.
4.2. UserDao 코드 수정
DataSource에 대한 멤버변수를 생성하고, XML 설정을 사용하기 위해 DataSource에 대한 수정자 메서드를 생성한다. 기존 사용했던 conectionMaker.makeConnection() 대신 dataSource.getConnection()으로 변경한다.
public class UserDao {
private DataSource dataSource;
public void setDataSource(DataSource dataSource){
this.dataSource = dataSource;
}
public void add(User user) throws SQLException {
Connection c = dataSource.getConnection();
...
}
public User get(String id) throws SQLException{
Connection c = dataSource.getConnection();
...
}
}
4.3. DaoFactory 수정
XML을 통해 DI를 처리하긴 하지만 Java 코드 부분도 한번 수정해보았다. SimpleDriverDataSource의 사용 방법은 다음과 같이 수정자 메서드를 통해 DriverClass, Url, Username, Password를 설정한 후 해당 객체를 DataSource 형으로 리턴하면 된다.
@Configuration
public class DaoFactory {
@Bean
public UserDao userDao(){
UserDao userDao = new UserDao();
userDao.setDataSource(dataSource());
return userDao;
}
@Bean
public DataSource dataSource(){
SimpleDriverDataSource dataSource = new SimpleDriverDataSource();
dataSource.setDriverClass(com.mysql.jdbc.Driver.class);
dataSource.setUrl("jdbc:mysql://localhost/spring");
dataSource.setUsername("DB 접속 ID");
dataSource.setPassword("DB 접속 Password");
return dataSource;
}
}
4.4. XML 파일 수정
XML 파일의 DI 정보를 다음과 같이 수정한다.
<?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 = "dataSource" class ="org.springframework.jdbc.datasource.SimpleDriverDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="url" value = "jdbc:mysql://localhost/spring"></property>
<property name="username" value = "DB 접속 ID"></property>
<property name="password" value = "DB 접속 Password"></property>
</bean>
<bean id = "userDao" class = "org.example.user.dao.UserDao">
<property name = "dataSource" ref = "dataSource"></property>
</bean>
</beans>
여기서 <property> 속성으로 value라는 값이 처음 사용된다. value는 name에 대한 멤버변수의 값을 주입하는 속성이며, 수정자 메서드의 파라미터에 맞게 자동으로 변환하여 주입한다.
예를들어 driverClass의 값을 주입할 때 "com.mysql.jdbc.Driver"라는 문자열을 넣고 있는데, 내부적으로 동작할 때에는setDriverClass 수정자 메서드의 파라미터 자료형에 맞게 문자열을 클래스로 변환하는 중간과정을 거치게 된다. 최종적으로는 문자열이 아닌 클래스 형태로 변환되어 수정자 메서드를 호출하게 된다.
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
5. 회고
XML을 통한 DI 방식을 사용해보았다. 실무에서 담당했던 프로젝트 중 대부분은 XML을 통한 DI가 사용되고 있었는데, 실제로 어떻게 DI되는지는 생각해보지 않았었다.
이번 스터디를 통해 자바 코드에 수정자 메서드를 왜 넣었는지와 값 주입 시 아무 생각없이 문자열을 넣을 수 있었던 이유를 실제 동작 메커니즘과 함께 이해하게 되었다.
최근에는 DI를 담당하는 설정파일 없이 @Autowired나 final을 사용하여 생성자를 통한 DI를 하고 있어 이러한 메커니즘을 굳이 이해할 필요가 있을까라는 생각이 들 수 있으나, 실무에서는 오래된 서비스를 운용할 케이스도 있으므로 충분히 이해하고 넘어가야할 가치가 있는 부분이라고 생각된다.
'공부 > 토비의 스프링 스터디' 카테고리의 다른 글
[토비의 스프링 스터디] 7주차 / 예외 / 예외 처리 방법 및 전략 (0) | 2023.05.25 |
---|---|
[토비의 스프링 스터디] 5주차 / 템플릿 / 직접 DI (0) | 2023.05.16 |
[토비의 스프링 스터디] 4주차 / 테스트 / JUnit 실습 (0) | 2023.05.09 |
[토비의 스프링 스터디] 2주차 / 빈의 스코프 / 싱글톤 / DI / 의존주입 (0) | 2023.04.25 |
[토비의 스프링 스터디] 1주차 / 스프링이란 / 초난감 DAO 예제 / 스프링 IoC (0) | 2023.04.17 |