Setter를 통한 의존성 주입을 실습해보도록 하겠습니다.
DataBaseInfo 클래스 생성 후 필드와 set,get메서드를 만듭니다.
public class DataBaseInfo {//DB에서 필요한 정보들을 저장하는 객체
private String url;//DB가 서버 어디에 위치해있는지에 대한 url, 서버의 주소
private String uid;//사용자 계정명
private String upw;//사용자 비밀번호
//private -> 생성자 혹은 setter로 초기화
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUid() {
return uid;
}
public void setUid(String uid) {
this.uid = uid;
}
public String getUpw() {
return upw;
}
public void setUpw(String upw) {
this.upw = upw;
}
}
MainClass 생성후 DataBase클래스에 대한 객체를 생성하고 객체를통해 각 필드에 대한 set메서드를 호출하여 필드의 값을 초기화 합니다.
import org.springframework.context.support.GenericXmlApplicationContext;
public class MainClass {
public static void main(String[] args) {
DataBaseInfo dbInfo = new DataBaseInfo();//DB정보에 대한 객체 생성
dbInfo.setUid("spring1"); //아이디 주입
dbInfo.setUpw("sss111"); //비밀번호 주입
dbInfo.setUrl("jdbc:mysql://localhost:3306/spring");
}
}
이런방식의 코드는 데이터베이스 정보가 변경되었을 때 정보수정이 조금 힘들수 있지 않을까 생각해봅니다.
만약 DB정보가 변경되었을때 DB관리자가 이곳에 있는 설정을 변경해야한다면, 이때에 DB관리자가 만약 자바언어를 모른다면 서버코드를 건드리는것 보다는, 또 서버에 내부핵심코드를 건드리기 보다는 설정파일을 변경시켜 유지보수를 좀 더 쉽게할 수 있습니다.
db-config.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="db1" class="com.spring.basic.ex02.DataBaseInfo">
<!-- setter 주입 -->
<property name="url" value="jdbc:mysql://localhost:3306/spring"/>
<property name="uid" value="spring1"/>
<property name="upw" value="sss111"/>
</bean>
</beans>
bean id를 부여해주고 DataBaseInfo클래스를 지정하여 bean을 선언해 줍니다.
setter() 메서드로 주입하는방법으로 bean태그안에 <property>태그를 사용합니다
property태그의 name속성에는 해당 클래스의 필드명을 작성합니다.
편의상 필드명 이라고 했지만 정확하게는 예를들어 setUrl() 메서드는 url멤버변수에 대한 setter메서드 입니다.
자바의 관례상으로 setter가 동작하게되는 메서드의 동일한 이름의 필드명에 접근합니다.
value속성에는 저장할 value값을 각각 적어줍니다.
Main클래스에서 get메서드 호출후 출력!
import org.springframework.context.support.GenericXmlApplicationContext;
public class MainClass {
public static void main(String[] args) {
GenericXmlApplicationContext ct =
new GenericXmlApplicationContext("classpath:db-config.xml");
DataBaseInfo db1 = ct.getBean("db1",DataBaseInfo.class);
System.out.println("URL : "+db1.getUrl());
System.out.println("UID : "+db1.getUid());
System.out.println("UPW : "+db1.getUpw());
}
}
이렇게 구현하게되면 자바를 모르는사람도 xml파일에서 쉽게 수정할수 있게 됩니다.
서버는 가만히있고, 로직은 가만히있고 설정파일만 건드려 수정이 가능해 집니다
IoC라는 방식이 xml같은 파일을 제어해서 서버로직을 변경 할 수있습다.
파일명과 로직들을 고정시킨상태로 설정파일의 변경을통해 서버로직을 교체 할 수 있게됩니다.
우리가 만약에 URL이 다른 데이터베이스를 두개를 써야될때도 두번째 Bean을 등록하면 됩니다.
xml설정파일에 두번째 Bean을 등록합니다.
<?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="db1" class="com.spring.basic.ex02.DataBaseInfo">
<!-- setter 주입 -->
<property name="url" value="jdbc:mysql://localhost:3306/spring"/>
<property name="uid" value="spring1"/>
<property name="upw" value="sss111"/>
</bean>
<!-- 두번째 빈 등록 -->
<bean id="db2" class="com.spring.basic.ex02.DataBaseInfo">
<!-- setter 주입 -->
<property name="url" value="jdbc:mysql://localhost:3306/practice"/>
<property name="uid" value="root"/>
<property name="upw" value="mysql"/>
</bean>
</beans>
아이디만 바꿔주고 똑같이 주입하게 되면 두번째 객체를 얻어낼 수 있습니다.
메인클래스에서 두번째 객체를 꺼내는 코드를 구현 한 후 출력해줍니다
import org.springframework.context.support.GenericXmlApplicationContext;
public class MainClass {
public static void main(String[] args) {
GenericXmlApplicationContext ct =
new GenericXmlApplicationContext("classpath:db-config.xml");
DataBaseInfo db1 = ct.getBean("db1",DataBaseInfo.class);
System.out.println("URL : "+db1.getUrl());
System.out.println("UID : "+db1.getUid());
System.out.println("UPW : "+db1.getUpw());
System.out.println("=================================================");
//두번째 객체 꺼내기.
DataBaseInfo db2 = ct.getBean("db2",DataBaseInfo.class);
System.out.println("URL : "+db2.getUrl());
System.out.println("UID : "+db2.getUid());
System.out.println("UPW : "+db2.getUpw());
}
}
DataBaseInfo의 두번째 객체를 만들어준 후 getBean()으로 아이디값만 제어해서 받아주면 두번째 DB정보도 얻어 올 수 있습니다.
사실 getBean() 메서드는 원초적인 방식입니다. 이걸 자동으로 처리해주는 기능이 스프링에 존재한답니다^^ㅋㅋ
다음으로는 MemberDAO라는 클래스를 만들어보겠습니다
public class MemberDAO {//DataAccessObject
private DataBaseInfo dbInfo;
public void setDbInfo(DataBaseInfo dbInfo) {
this.dbInfo = dbInfo;
}
public void showDBInfo() {
System.out.println("URL : "+dbInfo.getUrl());
System.out.println("UID : "+dbInfo.getUid());
System.out.println("UPW : "+dbInfo.getUpw());
}
}
DAO에서는 DataAccess를 해야하는데 엑세스를 하기 위해서는 url,uid,upw 같은 정보들이 필요하므로 DataBaseInfo클래스와 의존관계를 만듭니다
이어서 setter()메서드를 생성해준 후 DB에대한 정보를 출력해주는 메서드 showDBInfo()를 만들어줍니다
만약 우리가 멤버DAO 객체를 생성할 때, 실수로 setter메서드를 호출하지 않아서 필드값, DbInfo의 객체, 데이터객체가 안들어왔다면 NullPointerException 에러를 띄우게 됩니다
메인메서드에서 MemberDAO클래스의 객체 dao를 생성한 후 해당 클래스의 메서드를 호출합니다.
import org.springframework.context.support.GenericXmlApplicationContext;
public class MainClass {
public static void main(String[] args) {
GenericXmlApplicationContext ct =
new GenericXmlApplicationContext("classpath:db-config.xml");
DataBaseInfo db1 = ct.getBean("db1",DataBaseInfo.class);
System.out.println("=================================================");
MemberDAO dao = new MemberDAO();
dao.showDBInfo();
}
}
실수로 setter()메서드를 빼먹으면
setter메서드를 호출하지않아 NullPointerError발생됩니다.
여기서 말하는 setter()메서드호출이란 dbInfo.set() 혹은 xml에서 설정한 setter를 말한다.
현재 이곳에서 showDBInfo()를 호출하면 DAO내부에서는 set메서드가 작동된적이 없어서 DAO에 선언된 필드 dbInfo의 값이 null인 상태입니다. 즉, 비어있는 객체상태 이므로 NullPointerException에러를 띄우게됩니다
다시 메인메서드에서 dao객체의 set메서드를 호출합니다
import org.springframework.context.support.GenericXmlApplicationContext;
public class MainClass {
public static void main(String[] args) {
GenericXmlApplicationContext ct =
new GenericXmlApplicationContext("classpath:db-config.xml");
DataBaseInfo db1 = ct.getBean("db1",DataBaseInfo.class);
System.out.println("=================================================");
MemberDAO dao = new MemberDAO();
dao.setDbInfo(db2);
dao.showDBInfo();
}
}
우리가 만약 MemberDAO클래스에 IoC를통해 제어의역전으로 bean에 setter()주입을 등록해 놓았다면 이런 불상사가 발생하지 않는다는 것입니다.
xml설정파일에 setter주입.
<?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="db2" class="com.spring.basic.ex02.DataBaseInfo">
<property name="url" value="jdbc:mysql://localhost:3306/practice"/>
<property name="uid" value="root"/>
<property name="upw" value="mysql"/>
</bean>
<bean id = "dao" class="com.spring.basic.ex02.MemberDAO">
<property name="dbInfo" ref="db2"/> <!-- db2에대한 객체를 dbInfo메서드에 주입-->
</bean>
</beans>
dao객체는 DataBaseInfo와 의존적 관계입니다.
dao객체가 존재하는 MemberDAO클래스에 선언된 필드를 name에 선언해 주며 (사용할 setter메서드에 해당하는 필드명) ref는 DAO에 의존적 관계인 클래스의 빈 아이디를 씁니다.
bean아이디를 따라가 보면 class에 해당클래스가 존재하는 경로와 가리키는 클래스명이 작성되있습니다
bean이라는것은 해당 클래스를 객체화 시켜줍니다. 즉, ref에 bean id를 적게되면 해당 클래스의 객체를 주입 시켜주는것 입니다. set메서드에 객체를 주입한다고 볼수있습니다.
db2에대한 객체를 dbInfo메서드에 주입합니다.
메인메서드에서 다시 코드를 구현하고 실행 해 보도록 하겠습니다.
import org.springframework.context.support.GenericXmlApplicationContext;
public class MainClass {
public static void main(String[] args) {
GenericXmlApplicationContext ct =
new GenericXmlApplicationContext("classpath:db-config.xml");
DataBaseInfo db1 = ct.getBean("db1",DataBaseInfo.class);
System.out.println("=================================================");
MemberDAO dao = ct.getBean("dao",MemberDAO.class);
dao2.showDBInfo();
}
}
이때 dao객체에는 xml에서 setter로 주입시켰기 때문에 자바단에서 따로 set을 호출하지 않습니다
그렇다면 xml로 setter메서드를 만들었으니 자바VO 클래스에setter메서드는 지워도 돌아갈까요?
이 궁금증에 대한 실행 결과는 오류를띄웁니다
클래스의 setter()메서드와 bean으로 등록된 객체가 함께 맞물려서 돌아가나 봅니다.
그러나 내부적으로 코드를 확인하지 못하는 지금상황의경우에는 xml에 설정된 이름을 통해 등록된 값이 해당 setter메서드의 매개변수에 접근한다고 이해할수도 있지만 생각이 짧았습니다.
그냥 위에서 주석처리 한 setter()메서드를 호출해서 직접 값을넣는코드작업을 xml 에 객체 의존주입을 통해서 설정한 것 같습니다. 아니 그게 맞습니다. 때문에 DataBaseInfo클래스에 선언된 필드에 대한 set()메서드를 삭제하면 당연하게도 작동이 되지 않습니다.
이제는 가볍게 지난시간에 작성했던 Restaurant클래스를 열어 해당 클래스의 생성자 코드를 막아두고
setter()메서드를 통한 의존성주입 코드를 구현해 보겠습니다.
package com.spring.basic.ex01;
public class Restaurant {
private Chef chef;
/*
public Restaurant(Chef chef) {
System.out.println("레스토랑이 생성됨!");
this.chef=chef;
}*/
//생성자 주석처리 후 set메서드 생성
public void setChef(Chef chef) {
this.chef = chef;
}
public void orderDinner() {//저녁식사 주문 기능 - 저녁식사를 주문하면 세프가 요리를하기 시작한다.
System.out.println("저녁식사를 주문합니다.");
chef.cook();
}
}
레스토랑 클래스에 생성자를 차단하고 Setter()메서드를 작성했습니다.
test-config.xml파일에서 생성자를 대신하여 setter를통해
의존성관계와 의존객체를 주입하도록 코드를 작성하겠습니다
<?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="test" class="com.spring.basic.SpringTest" />
<!-- Day01_3 Hotel객체와 의존객체들의 빈 등록 및 의존성 주입 설정 -->
<bean id="chef" class="com.spring.basic.ex01.Chef"/>
<bean id="res" class="com.spring.basic.ex01.Restaurant">
<property name="chef" ref="chef" />
<!-- <constructor-arg ref="chef"/> -->
</bean>
<bean id="hotel" class="com.spring.basic.ex01.Hotel">
<constructor-arg ref="res" />
</bean>
</beans>
constructor-arg태그를 주석처리 하고 property태그로 setter()주입을 등록합니다.
정상적으로 작동하는것을 확인할 수 있습니다.
'SpringFramework > BASIC' 카테고리의 다른 글
자동 스캔 명령태그 / 자동 의존성 주입 어노테이션 context:annotation-config @Autowired/@Qulifier/@Resource/@Inject (0) | 2020.08.29 |
---|---|
싱글톤타입/프로토타입 Bean객체의 범위 (0) | 2020.08.29 |
DI 의존성 주입 - 생성자 (0) | 2020.08.29 |
Maven프로젝트 생성 및 pom.xml 정리 및 설정 Maven의 개념 - Maven Repository/Maven폴더구조 (0) | 2020.08.28 |
스프링 프레임워크의 기본 개념 정리 (1) | 2020.08.28 |