반응형
01. DTO클래스
Student클래스
package xyz.itwill05.di;
//학생정보를 저장하기 위한 클래스 - VO 클래스(DTO 클래스)
public class Student {
private int num;
public String name;
public String email;
public Student() {
System.out.println("### Student 클래스의 기본 생성자 호출 ###");
}
public Student(int num) {
super();
this.num = num;
System.out.println("### Student 클래스의 매개변수(학번)가 선언된 생성자 호출 ###");
}
/*
//Spring Bean Configuration File에서 매개변수가 있는 생성자로 객체를 만들기 위해 constructor-arg 엘리먼트 사용 시,
//스프링에서는 전달값을 기본적으로 문자열(String 객체)로 전달하기 때문에
//아래의 생성자를 만들 경우 num값을 설정하는 생성자는 dead code가 되어버리니 주의!!
//즉, 오버로드로 메소드를 선언해도 문자열(String 객체)로 선언된 생성자만 계속 이용하게 됨!!
public Student(String name) {
super();
this.name = name;
System.out.println("### Student 클래스의 매개변수(이름)가 선언된 생성자 호출 ###");
}
*/
public Student(int num, String name) {
super();
this.num = num;
this.name = name;
System.out.println("### Student 클래스의 매개변수(학번,이름)가 선언된 생성자 호출 ###");
}
public Student(int num, String name, String email) {
super();
this.num = num;
this.name = name;
this.email = email;
System.out.println("### Student 클래스의 매개변수(학번,이름,이메일)가 선언된 생성자 호출 ###");
}
public int getNum() {
return num;
}
public void setNum(int num) {
System.out.println("*** Student 클래스의 setNum(int num) 메소드 호출 ***");
this.num = num;
}
public String getName() {
return name;
}
public void setName(String name) {
System.out.println("*** Student 클래스의 setName(String name) 메소드 호출 ***");
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
System.out.println("*** Student 클래스의 setEmail(String email) 메소드 호출 ***");
this.email = email;
}
//객체에 저장된 필드값을 확인하기 위해 오버라이드 선언
@Override
public String toString() {
return "학번= "+num+", 이름 = "+name+", 이메일 = "+email;
}
}
02. DAO클래스 - 포함(의존) : 부
(부모) StudentDAO인터페이스
package xyz.itwill05.di;
import java.util.List;
//학생정보를 처리하는 DAO 클래스가 반드시 상속받아야 하는 인터페이스
// => 객체 간의 결합도를 낮추어 유지보수의 효율성 증가
public interface StudentDAO {
int insertStudent(Student student);
int updateStudent(Student student);
int deleteStudent(int num);
Student selectStudent(int num);
List<Student> selectStudentList();
}
(자식) StudentJdbcDAO클래스
package xyz.itwill05.di;
import java.util.List;
//DAO 클래스 : 저장매체(File,DBMS, 등)에 대한 행 삽입, 변경, 삭제, 검색 기능을 제공하는 클래스
//=> 저장매체의 종류 또는 방법에 따라 DAO 클래스 변경 가능
//=> DAO 클래스가 변경되어도 DAO 클래스와 관계가 있는 클래스(Service 클래스)에 영향을 최소화
//하기 위해 인터페이스를 상속받아 작성 - 결합도를 낮춰 유지보수의 효율성 증가
public class StudentJdbcDAO implements StudentDAO{
public StudentJdbcDAO() {
System.out.println("### StudentJdbcDAO 클래스의 기본 생성자 호출 ###");
}
@Override
public int insertStudent(Student student) {
System.out.println("*** StudentJdbcDAO 클래스의 insertStudent(Student student) 메소드 호출 ***");
return 0;
}
@Override
public int updateStudent(Student student) {
System.out.println("*** StudentJdbcDAO 클래스의 updateStudent(Student student) 메소드 호출 ***");
return 0;
}
@Override
public int deleteStudent(int num) {
System.out.println("*** StudentJdbcDAO 클래스의 deleteStudent(int num) 메소드 호출 ***");
return 0;
}
@Override
public Student selectStudent(int num) {
System.out.println("*** StudentJdbcDAO 클래스의 selectStudent(int num) 메소드 호출 ***");
return null;
}
@Override
public List<Student> selectStudentList() {
System.out.println("*** StudentJdbcDAO 클래스의 selectStudentList() 메소드 호출 ***");
return null;
}
}
(자식) StudentMybtisDAO클래스
package xyz.itwill05.di;
import java.util.List;
public class StudentMybtisDAO implements StudentDAO{
public StudentMybtisDAO() {
System.out.println("### StudentMybtisDAO 클래스의 기본 생성자 호출 ###");
}
@Override
public int insertStudent(Student student) {
System.out.println("*** StudentMybtisDAO 클래스의 insertStudent(Student student) 메소드 호출 ***");
return 0;
}
@Override
public int updateStudent(Student student) {
System.out.println("*** StudentMybtisDAO 클래스의 updateStudent(Student student) 메소드 호출 ***");
return 0;
}
@Override
public int deleteStudent(int num) {
System.out.println("*** StudentMybtisDAO 클래스의 deleteStudent(int num) 메소드 호출 ***");
return 0;
}
@Override
public Student selectStudent(int num) {
System.out.println("*** StudentMybtisDAO 클래스의 selectStudent(int num) 메소드 호출 ***");
return null;
}
@Override
public List<Student> selectStudentList() {
System.out.println("*** StudentMybtisDAO 클래스의 selectStudentList() 메소드 호출 ***");
return null;
}
}
03. Service클래스 - 포함(의존) : 주
(부모) StudentService인터페이스
package xyz.itwill05.di;
import java.util.List;
//학생정보를 처리하는 Service 클래스가 반드시 상속받아야 되는 인터페이스
public interface StudentService {
void addStudent(Student student);
void modifyStudent(Student student);
void removeStudent(int num);
Student getStudent(int num);
List<Student> getStudentList();
}
(자식) StudentServiceImpl클래스
package xyz.itwill05.di;
import java.util.List;
//Service 클래스 ( = 컴퍼넌트 )
// => 프로그램 실행에 필요한 데이터 처리 기능을 "모듈화"하여 제공하는 클래스 - 컴퍼넌트
// => Service 클래스의 메소드는 다수의 DAO 클래스의 메소드를 호출하여 데이터 처리 기능 제공 - 모듈화
// => DAO 클래스(보조-도구)는 Service 클래스(주-main)와 포함관계(의존관계)로 설정되도록 작성
// => Service 클래스가 변경되어도 Service 클래스와 관계가 있는 클래스(모델 클래스)에 영향을 최소화
//하기 위해 반드시 인터페이스 상속받아 작성 - 결합도를 낮춰 유지보수의 효율성 증가
public class StudentServiceImpl implements StudentService{
//StudentJdbcDAO 클래스로 필드 선언
//StudentJdbcDAO 객체만을 저장하기 위한 필드
// => 필드에 StudentJdbcDAO 객체를 저장해야만
//"포함"관계(Association)가 성립 - 의존관계는 아님
// => StudentServiceImpl 클래스의 메소드에서 StudentJdbcDAO 객체의 메소드 호출 가능
//문제점) DAO 클래스가 변경될 경우 Service 클래스의 필드 및 메소드도 변경해야함
//=> 결합도가 높아 유지보수의 효율성 낮음(Service 클래스의 메소드도 계속 수정해야함!!!)
//해결법) DAO 클래스가 반드시 상속받아야 되는 인터페이스로 필드 선언
// => 필드에는 인터페이스를 상속받은 모든 DAO 클래스의 객체 저장 가능
//private StudentJdbcDAO studentJdbcDAO;
public StudentServiceImpl() {
System.out.println("### StudentServiceImpl 클래스의 기본 생성자 호출 ###");
}
public StudentServiceImpl(StudentDAO studentDAO) {
super();
System.out.println("### StudentServiceImpl 클래스의 매개변수가 선언된 생성자 호출 ###");
}
public StudentDAO getStudentDAO() {
return studentDAO;
}
public void setStudentDAO(StudentDAO studentDAO) {
System.out.println("*** StudentServiceImpl 클래스의 setStudentDAO(StudentDAO studentDAO) 메소드 호출 ***");
this.studentDAO = studentDAO;
}
@Override
public void addStudent(Student student) {
System.out.println("*** StudentServiceImpl 클래스의 addStudent(Student student) 메소드 호출 ***");
studentDAO.insertStudent(student);
}
@Override
public void modifyStudent(Student student) {
System.out.println("*** StudentServiceImpl 클래스의 modifyStudent(Student student) 메소드 호출 ***");
studentDAO.updateStudent(student);
}
@Override
public void removeStudent(int num) {
System.out.println("*** StudentServiceImpl 클래스의 removeStudent(int num) 메소드 호출 ***");
studentDAO.deleteStudent(num);
}
@Override
public Student getStudent(int num) {
System.out.println("*** StudentServiceImpl 클래스의 getStudent(int num) 메소드 호출 ***");
studentDAO.selectStudent(num);
return null;
}
@Override
public List<Student> getStudentList() {
System.out.println("*** StudentServiceImpl 클래스의 getStudentList() 메소드 호출 ***");
studentDAO.selectStudentList();
return null;
}
}
04. 💖Spring Bean Configuration File
[05-1_di.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>">
[student1] 기본생성자로 기본값으로 [초기값 저장]
<!-- Spring Bean으로 등록된 클래스의 기본 생성자를 이용하여 객체 생성 -->
<!-- => 객체의 필드에는 기본값(숫자형:0, 논리형:false, 참조형:null) 자동 저장 -->
<bean class="xyz.itwill05.di.Student" id="student1"/>
<!-- Spring Bean Injection :
스프링 컨테이너에 의해 Spring Bean Configuration File에
등록된 클래스로 객체(Spring Bean) 생성 시
필드에 원하는 값(Value Injection) 또는 객체(Dependecy Injection)를
저장되도록 설정 -->
<!-- => 생성자(Constructor Injection) 또는 Setter 메소드(Setter Injection)를 이용하여 값 또는 객체를 필드에 저장 -->
1) 값 주입
🍒[student2] Contructor Injection : 매개변수가 있는 생성자로 [초기값 저장]
<!-- Contructor Injection -->
<!-- Spring Bean으로 등록된 클래스의 매개변수가 선언된 생성자를 이용하여 객체 생성 -->
<!-- => bean 엘리먼트의 하위 엘리먼트를 사용하여 생성자 매개변수에 값을 전달하여 필드값으로 저장 -->
<!-- => Contructor Injection : 생성자를 이용하여 필드 초기화 작업 실행 -->
<bean class="xyz.itwill05.di.Student" id="student2">
<!-- constructor-arg : Spring Bean으로 등록된 클래스의 생성자 매개변수에 값(객체)을 전달하기 위한 엘리먼트 -->
<!-- => 엘리먼트의 갯수만큼 매개변수가 선언된 생성자를 반드시 작성 -->
<!-- value 속성 : 매개변수에 전달하기 위한 값을 속성값으로 설정 -->
<!-- => spring Bean에 등록된 클래스가 객체로 생성될 때 객체 필드에 전달값 저장 -->
<!-- => Value Injection(값 주입) : 객체의 필드에 값이 저장되도록 초기화 작업 실행 -->
<!-- => 전달값은 기본적으로 문자열(String 객체)로 전달 - 매개변수의 자료형에 의해 자동 형변환 -->
<!-- => 매개변수의 자료형에 의해 자동 형변환될 경우 NumberFormatException 발생 가능 -->
<constructor-arg value="1000"/><!-- 전제조건 : 매개변수가 1개짜리 생성자 존재 / 문자열 1000이 전달되면 자동형변환되어 int로 저장될 것임 -->
<!--<constructor-arg value="홍길동"/>--> <!-- NumberFormatException 발생 -->
</bean>
🍒[student3] Contructor Injection : 매개변수가 있는 생성자로 순서 지정해 [초기값 저장]
<!-- constructor-arg의 작성순서에 의해 매개변수에 값(객체)이 전달되어 객체 초기화 -->
<!--
<bean class="xyz.itwill05.di.Student" id="student3">
<constructor-arg value="2000"/>
<constructor-arg value="홍길동"/>
<constructor-arg value="abc@itwill.xyz"/>
</bean>
-->
<bean class="xyz.itwill05.di.Student" id="student3">
<!-- index 속성 : 생성자 매개변수에 값(객체)을 전달하기 위한 순서를 속성값으로 설정 -->
<!-- => Index 속성값은 0부터 1씩 증가되는 정수값 사용 -->
<constructor-arg value="홍길동" index="1"/>
<constructor-arg value="abc@itwill.xyz" index="2"/>
<constructor-arg value="2000" index="0"/>
</bean>
🍒[student4] Setter Injection : Setter 메소드로 [초기값 저장]
<!-- Setter Injection -->
<!-- 클래스의 기본 생성자를 이용하여 객체 생성 - 객체 필드에는 기본값 저장 -->
<!-- => 하위 엘리먼트를 사용하여 Setter 메소드를 호출해 필드값 변경 - Setter Injection -->
<bean class="xyz.itwill05.di.Student" id="student4">
<!-- property : 객체의 Setter 메소드를 호출하여 필드값을 변경하는 엘리먼트 -->
<!-- name 속성 : 필드값을 변경하기 위한 필드명을 속성값으로 설정 - 자동 완성 기능 사용 -->
<!-- => name 속성값으로 설정된 필드에 대한 Setter 메소드를 호출하여 필드값 변경 -->
<!-- -> 필드에 대한 Setter 메소드가 없거나 잘못 선언된 경우 예외 발생 -->
<!-- value 속성 : 필드에 저장될 값을 속성값으로 설정 - 값 주입(Value Injection) -->
<property name="num" value="3000"/>
<property name="name" value="임꺽정"/>
<property name="email" value="xyz@itwill.xyz"/>
</bean>
🍒[student5] Contructor Injection & Setter Injection
<!-- Constructor Injection & Setter Injection -->
<!-- 생성자(Constructor Injection)와 Setter 메소드(Setter Injection)를 같이 사용하여 객체 초기화 작업 가능 - 값 주입(Value Injection) -->
<bean class="xyz.itwill05.di.Student" id="student5">
<constructor-arg value="4000"/>
<constructor-arg value="전우치"/>
<property name="email" value="opq@itwill.xyz"/>
</bean>
🍒[student6] properties파일 이용해 [초기값 저장]
<!-- PropertyPlaceholderConfigurer 클래스 : Properties 파일을 제공받아 파일에 설정된 값을
Spring Bean Configuration File에서 사용할 수 있도록 제공하는 클래스 : deprecated 되어있음-->
<!-- => locations 필드에 Propertis 파일의 경로를 전달하여 저장 -->
<!-- => Propertis 파일에 의해 제공되는 값은 Spring Bean Configuration File에서 ${이름}으로 사용 가능 -->
<!--
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations" value="xyz/itwill05/di/student.properties"/>
</bean>
-->
<!-- Spring 5.2 이상에서는 PropertySourcesPlaceholderConfigurer 클래스를 사용하여
Properties 파일이 제공받아 Spring Bean Configuration File에서 사용할 수 있도록 변경 -->
<bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
<property name="locations" value="xyz/itwill05/di/student.properties"/>
</bean>
<!-- Propertis 파일에 의해 제공되는 값을 사용하여 객체 필드 초기화 작업 -->
<bean class="xyz.itwill05.di.Student" id="student6">
<property name="num" value="${num}"/>
<property name="name" value="${name}"/>
<property name="email" value="${email}"/>
</bean>
<!-- => Propertis 파일을 사용하는 이유 - 실제로 Spring 컨테이너가 읽는 파일도 아니지만, 이렇게 쓰는 이유는???? -->
<!-- => XML은 직관적이라 좋긴하지만 가독성이 떨어짐 (즉, 써야하는 엘리먼트와 속성들이 너무 많음)
반면, Propertis 파일은 가독성이 좋고, ${이름}으로 XML 파일에서 사용하므로 환경 설정에 필요한 값들이 중복되지 않음
그리고 값 변경이 필요할 때 Propertis 파일만 변경하면 되므로 유지보수의 효율성 증가 -->
2) 객체 주입(의존성 주입)
🍑 객체 등록
<!-- DI : 스프링은 XML 파일에서 직접 의존 관계를 설정해주며, 변경, 삭제 등 관리 가능함 -->
<!-- StudentDAO 인터페이스를 상속받은 자식 클래스를 Spring Bean으로 등록 -->
<bean class="xyz.itwill05.di.StudentJdbcDAO" id="studentJdbcDAO"/>
<bean class="xyz.itwill05.di.StudentMybtisDAO" id="studentMybtisDAO"/>
🍑 Constructor Injection : 의존성 주입 방법1
<!-- StudentService 인터페이스를 상속받은 자식클래스를 Spring Bean으로 등록 -->
<!-- => 클래스의 기본 생성자를 이용하여 객체 생성 - 객체 필드에는 기본값 저장 -->
<!-- 문제점) StudentServiceImpl 클래스로 생성된 객체의 studnetDAO 필드에는 [null]이 저장되어
StudentServiceImpl 클래스의 메소드에서 studnetDAO 필드로 StudentDAO 클래스의 메소드를
호출하면 NullPointerExcetion 발생 - 🍑의존관계 미성립 -->
<!-- 해결법) StudentServiceImpl 클래스의 객체 필드에 StudentDAO 인터페이스를 상속받은
자식클래스의 객체가 저장되도록 설정 - 🍑의존관계 성립 -->
<!-- <bean class="xyz.itwill05.di.StudentServiceImpl" id="studentServiceImpl"/> -->
<!-- StudentServiceImpl 클래스의 매개변수가 선언된 생성자를 이용하여 객체 생성 -->
<!-- => 생성자 매개변수에 StudentDAO 인터페이스를 상속받은 자식클래스의 객체를 전달하여 StudentDAO 필드에
필드에 저장 => 🍑Constructor Injection -->
<!-- constructor-arg 엘리먼트를 사용하여 StudentServiceImpl 클래스의 객체 필드에
StudentDAO 인터페이스를 상속받은 자식클래스의 객체 저장 - 🍑의존관계 성립 -->
<!-- ref 속성 : 스프링 컨테이너로 관리되는 Spring Bean의 식별자를 속성값으로 설정 - 자동완성 기능 이용 -->
<!-- => 스프링 컨테이너로 관리되는 Spring Bean을 객체 필드에 저장 - 의존성 주입(DI : Dependency Injection) -->
<!-- => 직접 값을 주입하는 것이 아닌 의존성 주입! -->
<!--
<bean class="xyz.itwill05.di.StudentServiceImpl" id="studentServiceImpl">
<constructor-arg ref="studentJdbcDAO"/>
</bean>
-->
🍑 Setter Injection : 의존성 주입 방법2
<!-- StudentServiceImpl 클래스의 기본 생성자를 이용하여 객체를 생성-->
<!-- => Setter 메소드를 호출하여 StudentDAO 인터페이스를 상속받은 자식클래스의 객체를
필드에 저장 => 🍑Setter Injection -->
<!-- property 엘리먼트를 사용하여 StudentDAO 클래스의 객체 필드에 StudentDAO
인터페이스를 상속받은 자식클래스의 객체 저장 - 🍑의존관계 성립 -->
<!--
<bean class="xyz.itwill05.di.StudentServiceImpl" id="studentServiceImpl">
<property name="studentDAO" ref="studentJdbcDAO"/>
</bean>
-->
🍑 의존관계 변경
<!-- 기존에 사용하던 StudentJdbcDAO 클래스 대신 새롭게 작성한 StudentMybtisDAO 클래스로
의존관계를 변경하고자 할 경우 ref 속성값만 변경 -->
<!-- => 기존 클래스 대신 새로운 클래스로 변경해도 관계가 설정된 클래스를 변경하지 않아도
Spring Bean Configuration File만 수정해도 관계 변경 - 유지보수의 효율성 증가 -->
<bean class="xyz.itwill05.di.StudentServiceImpl" id="studentServiceImpl">
<property name="studentDAO" ref="studentMybtisDAO"/>
</bean>
</beans>
05. properties 파일
student.properties
#Student Properties
num=5000
name=\\\\uC77C\\\\uC9C0\\\\uB9E4
email=il@itwill.xyz
06. 요청처리
StudentApp.java
package xyz.itwill05.di;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class StudentApp {
public static void main(String[] args) {
System.out.println("============= Spring Container 초기화 전 =============");
ApplicationContext context = new ClassPathXmlApplicationContext("05-1_di.xml");
System.out.println("============= Spring Container 초기화 후 =============");
🍒값을 주입한 객체 호출
Student student1 = context.getBean("student1",Student.class);
//참조변수 출력 시 Student 클래스의 toString() 메소드 자동 호출 - 객체의 필드값 확인
System.out.println(student1);
System.out.println("======================================================");
Student student2 = context.getBean("student2",Student.class);
System.out.println(student2);
System.out.println("======================================================");
Student student3 = context.getBean("student3",Student.class);
System.out.println(student3);
System.out.println("======================================================");
Student student4 = context.getBean("student4",Student.class);
System.out.println(student4);
System.out.println("======================================================");
Student student5 = context.getBean("student5",Student.class);
System.out.println(student5);
System.out.println("======================================================");
Student student6 = context.getBean("student6",Student.class);
System.out.println(student6);
System.out.println("======================================================");
🍑의존성(객체)을 주입한 객체 호출
//프로그램 실행에 필요한 데이타 처리 기능은 Service 클래스의 메소드를 호출하여 사용
// => 스프링 컨테이너에게 Service 클래스의 객체를 제공받아 메소드 호출
//StudentServiceImpl service = context.getBean("studentServiceImpl",StudentServiceImpl.class);
//클래스로 참조변수를 생성하여 객체를 반환받아 저장하는 것보다는 인터페이스로 참조변수를
//생성하여 객체를 저장하는 것이 유지보수의 효율성 증가
// => 인터페이스로 반환받기 위한 객체의 형변환 가능
StudentService service = context.getBean("studentServiceImpl",StudentService.class);
service.addStudent(student1);
service.modifyStudent(student1);
service.removeStudent(1000);
service.getStudent(1000);
service.getStudentList();
System.out.println("======================================================");
((ClassPathXmlApplicationContext)context).close();
}
}
반응형