코딩 70 일 / 2022.09.30 / Spring DI, Annotation(어노테이션) 기반 DI
Spring / Springboot
- Model 2 를 빠르게 개발하기 위해 사용하는 프레임워크
Spring
- Maven 으로 라이브러리를 관리 (선택불가)
- 그래서 구조가 Maven 프로젝트와 비슷
- EL 과 JSTL 로 결과를 출력
Springboot
- Maven 또는 Gradle 로 라이브러리 관리 가능
- EL, JSTL, 타임리프로 결과를 출력
+ 타임리프
- 타임리프 사용시 View 페이지가 html 문서가 되어야함, 기존 JSP 태그를 사용하지 못하게됨
Spring Project (Web Application) 구조
Spring Project import / export 할때
- Maven, Spring 프로젝트를 import / export 할때는 war 파일이 아닌 zip 파일을 압축해제해서 import, 복붙해서 output
- 서버에 최종적으로 배포할때는 war 파일로
환경설정 파일 정리
1. Spring 환경설정 파일
- servlet-context.xml
- root-context.xml
2. 프로젝트 환경설정 파일
- web.xml
3. Maven 환경설정 파일
- pom.xml
web.xml 에 들어가는 주요 3가지 내용
1. DispatcherServlet 위치 설정 : <servlet-class> 태그 안에 있다
2. 환경설정 파일 2개 불러오기 : root-context.xml 과 servlet-context.xml 파일
3. 한글 인코딩 (아직 안들어가있다)
root-context.xml
- DB 연동 관련 내용
- 주로 bean 을 만들어서 DB 접속을 처리한다
- 여기서 Setter DI 도 사용함
- beans 루트 엘리먼트 안에 bean 을 추가해서 사용
- 어노테이션 방식 + 직접 bean 객체 생성 방식 둘다 사용함
servlet-context.xml
- View 파일들이 저장된 최상위 디렉토리 위치를 잡음, 그래야 View 파일로 찾아갈 수 있다
- ViewResolve (prefix, suffix)
- ViewResolve 에도 Setter DI 사용함
- 어노테이션 기반으로 처리됨
DI
- 환경설정 파일에서 주로 사용한다 (MyBatis 환경설정 파일, Spring 환경설정 파일)
- Setter DI 를 주로 사용함
DI 에서 주의
- 할당(주입) 할때 다른 bean 의 id 를 사용할때는 value 속성 대신 ref 속성 사용
- 메인 메소드에서 resources 폴더에 저장된 환경설정 파일을 불러올때는 classpath: 를 붙이기
+ SqlSession 와 객체 생성
- SqlSession 은 부모 인터페이스
- SqlSessionTemplate 는 SqlSession 을 상속하는 구현 클래스
- SqlSession 은 객체 생성할 수 없으므로 SqlSessionTemplate 으로 bean 객체를 만들고 업캐스팅 해야함
+ Anotaion 을 이용한 DI
- 나중엔 어노테이션 기반으로 바뀐다
- bean 객체를 만들지 않고 처리 가능
Spring DI 예제 (이어서)
Spring DI 예제 10 (sample 9 와 비슷)
- src/main/java/sample10/ 안의 파일들
- new 연산자로 객체를 직접 생성하지 않음, Spring 의 환경설정 파일에서 객체를 생성함
- 또한 Spring 의 환경설정 파일에서 필드값을 초기화함
- Ex01.java : 메인메소드를 가진 클래스이다, 아래에서 코드 설명
- Vehicle.java : 부모 인터페이스, 추상메소드 rider()
- VehicleImpl.java : Vehicle 인터페이스를 상속하는 구현 클래스, rider() 메소드를 오버라이딩함, rider() 에서 매개변수로 전달된거 출력
- Outputer.java : 인터페이스, 추상메소드 output()
- FileOutputer.java : Output 인터페이스를 상속하는 구현 클래스, output() 메소드를 오버라이딩함
- 이 구현클래스 2개로 bean 객체 따로 만듬
- VehicleImpl.java
package sample10;
public class VehicleImpl implements Vehicle {
private String name;
private String rider;
private Outputer out;
public void setName(String name) {
this.name = name; // name="대박이"
}
public void setRider(String rider) {
this.rider = rider; // rider="비행기"
}
public void setOut(Outputer out) {
this.out = out; // out=out
}
public void rider() {
String msg = name + "(이)가 " + rider + "을(를) 탄다";
System.out.println(msg);
out.output(msg);
}
}
- 필드, setter 메소드, 메소드 로 구성되어있다.
- 프로퍼티에 값을 할당하는 방법 중 Setter 메소드를 사용할 것
- 인터페이스 Outputer 객체 output 의 setter 메소드 setOutput() , 매개변수는 인터페이스 Outputer 형
- setOut() 의 매개변수 자료형은 부모 인터페이스인 Output 이고, 여기 매개변수에 주입되는 값은 bean10.xml 에서 만들어진 자식인 FileOutputer 객체가 주입됨 (업캐스팅)
+ 인터페이스로는 객체 생성을 못하므로 자식 구현 클래스로 객체를 생성해서 업캐스팅 해야함
- 그럼 프로퍼티 out 은 FileOutpter 객체를 받은 것임
- Vehicle의 rider() 메소드를 여기서 오버라이딩
- rider() 안에서 초기화된 필드값을 출력하고, FileOutputer 객체 output 으로 FileOupter 클래스에서 오버라이딩된 메소드 output() 호출하자, 그럼 output() 에서는 파일을 생성해준다
* 잘 이해가 안되면 아래를 읽고 다시 여기 돌아와서 읽어보기
- Outputer.java
package sample10;
public interface Outputer {
void output(String msg);
}
- FileOutputer.java
package sample10;
import java.io.FileWriter;
import java.io.IOException;
public class FileOutputter implements Outputer {
private String fileName;
public void setFileName(String fileName) {
this.fileName = fileName; // fileName="aa.txt"
}
public void output(String msg) {
try {
FileWriter fw = new FileWriter(fileName);
fw.write(msg);
fw.close();
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
}
- 부모 인터페이스 Output 을 상속
- output() 메소드를 메소드 오버라이딩 한다, 파일을 생성하고 매개변수로 받은 msg 를 파일에 쓰고 있음
- FileOutputer 로 bean 객체를 생성하고 그 필드 fileName 에 Setter 메소드인 setFileName() 으로 Setter DI 할 것
- 그리고 그 fileName 으로 파일명을 설정해서 FileWriter 객체 fw 를 생성
- Ex01.java
package sample10;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Ex01 {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("/sample10/beans10.xml");
Vehicle vh = (Vehicle) ac.getBean("vh");
vh.rider();
}
}
- beans10.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="vh" class="sample10.VehicleImpl">
<property name="name" value="대박이"></property>
<property name="rider" value="비행기"></property>
<property name="out" ref="out"></property>
</bean>
<bean id="out" class="sample10.FileOutputter">
<property name="fileName" value="aa.txt"></property>
</bean>
</beans>
- 2개의 bean 객체를 생성하고 있다
첫번째 객체 생성 (beans01.xml 부분)
<bean id="vh" class="sample10.VehicleImpl">
<property name="name" value="대박이"></property>
<property name="rider" value="비행기"></property>
<property name="out" ref="out"></property>
</bean>
- VehicleImpl 클래스로 bean 객체를 생성하고 프로퍼티 name, rider, out 에 Setter 메소드로 값 초기화 (= Setter DI)
- VehicleImple 객체 vh 의 프로퍼티 name 에 "대박이", rider 에 "비행기", out 에 아래에서 생성한 두번째 객체인 FileOutputter 객체 out 을 주입
- 프로퍼티 out 의 자료형은 Outputer 인터페이스, 주입되는 값은 FileOutputter 객체, 즉 업캐스팅
ex) Outputer out = new FileOutputter() 와 같다
- 위에서 이렇게 out 객체를 프로퍼티 out 에 주입해야만 VehicleImpl 객체 vh 에서 VehicleImpl 클래스의 오버라이딩된 메소드 rider() 를 호출하면서, 그 rider() 메소드 안에서 프로퍼티이자 FileOutputter 로 만든 객체 out 으로 오버라이딩 된 메소드 output() 호출 가능
두번째 객체 생성 (beans01.xml 부분)
<bean id="out" class="sample10.FileOutputter">
<property name="fileName" value="aa.txt"></property>
</bean>
- 힙메모리에 새 공간을 생성해서 FileOutputter 객체 out 을 생성한다
- FileOutputter 클래스로 객체 out 을 생성하고 프로퍼티 fileName 에 "aa.txt" 를 Setter DI 로 주입
- 객체 생성 및 주입되는 시점 : beans10.xml 을 메인 메소드에서 읽어올때 메모리에서 자동으로 모든 객체 생성, 모든 주입이 일어남
- Ex01.java 실행시
- 파일 aa.txt 가 생성되었고 원하는 메세지가 작성되었음
Spring DI 예제 11 (DAO, DTO, Service 등도 있다)
- src/main/java/sample11/ 안의 파일들
- 지금까지의 예제와 다른 형태, DAO, DTO, Service 등도 있음
Spring DI 예제 11 구조 & 흐름
- Web Application 에서는 갈땐 Controller-> Service -> DAO, 올땐 DAO -> Service -> Controller
- 현재는 Web Application 이 아닌 Application 이므로 Controller 클래스는 없다, 그 역할을 메인메소드가 하고 있는거임
- 갈때 : main() -> Service -> dao , 돌아올땐 반대로 dao -> Service -> main() 으로 돌아오자
- service 와 dao 폴더에는 인터페이스 하나, 구현 클래스 하나 씩 있다
- Ex01.java : 메인메소드를 가진 클래스이다, Controller 클래스의 역할을 대신한다
- dao/ProductDao.java : 인터페이스, 추상메소드 getProduct()
- dao/ProductDaoImpl.java : ProductDao 를 상속하는 구현클래스, 추상메소드 getProduct() 를 오버라이딩
- service/ProductService.java : 인터페이스, 추상메소드 getProduct()
- service/ProductServiceImpl.java : ProductService 를 상속하는 구현클래스, 추상메소드 getProduct() 를 오버라이딩
- model/Product.java : DTO 클래스, 상품이름 name 필드, 상품가격 price 필드, setter / getter 메소드가 있다, 값을 저장하고 돌려줄때 사용
main() -> Service -> DAO 로 가는 방법
- 먼저 Spring 환경설정 파일 beans11.xml 에서 구현 클래스 두개 ProductServiceImpl, ProductDaoImpl 로 bean 를 만들 것
- main() 메소드에서 getBean 으로 Service 클래스 객체를 가져오고, 그걸로 Service 객체의 메소드 사용 가능
- Service 클래스에서는 DAO 객체를 가져와서, 그걸로 DAO 객체의 메소드 사용 가능
- main() (Controller)에서는 Service 클래스 객체를 구해와야만 Service 의 메소드를 호출 가능
- Service 클래스 객체는 DAO 객체를 구해와야만 DAO 의 메소드를 호출 가능
- 구해온다 = 프로퍼티 자료형이 DAO
- beans11.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="pd" class="sample11.dao.ProductDaoImpl"></bean>
<bean id="ps" class="sample11.service.ProductServiceImpl">
<property name="pd" ref="pd"></property>
</bean>
</beans>
<첫번째 객체>
- ProductDaoImpl 클래스(DAO 클래스)로 객체 pd 생성
- 이 객체는 아래에서 참조할 것
<두번째 객체>
- 이 객체는 main() 에서 getbean("ps") 로 가져올 것
- ProductServiceImpl 클래스(Service 클래스)로 객체 ps 생성
- ProductServiceImpl 클래스의 프로퍼티에 주입을 해야하는데, 그 프로퍼티는 ProductDao 자료형이므로 ProductDaoImpl 클래스로 생성된 객체가 주입되어야함, 그래서 위에서 만든 ProductDaoImpl 객체 pd 를 ref 로 주입함
- ProductServiceImpl.java
package sample11.service;
import sample11.dao.ProductDao;
import sample11.model.Product;
public class ProductServiceImpl implements ProductService {
private ProductDao pd;
public void setPd(ProductDao pd) {
this.pd = pd;
}
public Product getProduct() {
return pd.getProduct("라면");
}
}
- 프로퍼티 ProductDao pd 가 있다
- 이 Service 클래스로 객체를 생성과 주입을 할때 프로퍼티 pd 에는 객체가 들어가야하므로 ref 로 DAO 구현클래스 객체를 넘겨줘야한다
- 이 프로퍼티 자료형을 인터페이스 ProductDao 로 해도 되고, 구현클래스 ProductDaoImpl 로 해도 된다
- 여기선 인터페이스 ProductDao 가 프로퍼티의 자료형이므로 ProductDaoImpl 으로 객체를 만들어서 업캐스팅 해야함
- beans11.xml 에서 이 프로퍼티이자 DAO 객체 pd 에 주입(SetPd() Setter DI 로) 을 할 것 * 위로 가서 beans11.xml 보자
- ProductDaoImpl.java
package sample11.dao;
import sample11.model.Product;
public class ProductDaoImpl implements ProductDao {
public Product getProduct(String name) {
return new Product(name, 2000);
}
}
- Ex01.java
package sample11;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
import sample11.model.Product;
import sample11.service.ProductService;
public class Ex01 {
public static void main(String[] args) {
ApplicationContext ac = new GenericXmlApplicationContext("/sample11/beans11.xml");
ProductService ps = (ProductService) ac.getBean("ps");
Product product = ps.getProduct();
System.out.println(product); // println(product.toString()) 와 같다
}
}
- Web Application 에서는 Controller 클래스가 이 main() 메소드 역할을 한다
- 같은 패키지 안에 beans11.xml 이 있으므로 패키지명부터 해당 파일명까지 써준다, 읽어올때 객체생성되고 DI 까지 완료됨
- main() -> Service 로 넘어간다는 의미 = main() 에서 Service 클래스의 메소드를 호출한다
- main() -> Service 로 넘어가야하므로 beans11.xml 에서 만들어진 Service 객체인 "ps" 를 구해온다
- Service 객체를 구해왔으므로 Service 클래스에서 오버라이딩된 메소드인 getProduct() 호출 가능
전체 흐름 (중요)
main() -> Service
- main() -> Service 로 넘어간다는 의미 = main() 에서 Service 클래스의 메소드를 호출한다
- main() (Controller)에서는 Service 클래스 객체를 구해와야만 Service 의 메소드 getProduct()를 호출 가능
+ 이때 main() 에서는 Service 객체를 getbean("ps") 로 객체 ps를 구해온다
- Service 객체를 구해왔으므로 Service 클래스에서 오버라이딩된 메소드인 getProduct() 호출 가능
Service -> DAO
- Service -> DAO로 넘어간다는 의미 = DAO 에서 DAO클래스의 메소드를 호출한다
- Service 클래스의 오버라이딩한 getProduct() 안에서는 DAO 의 getProduct() 를 호출하고 있다, 호출하면서 매개변수 "라면" 전달
- 이때 Service 클래스 객체는 DAO 객체를 구해와야만 DAO 의 메소드 getProduct() 를 호출 가능
- 구해온다 = 프로퍼티 자료형이 DAO
- 이미 main() 에서 beans11.xml 을 읽어올때 Service 객체가 생성되며 프로퍼티인 DAO 객체도 구해졌으므로 바로 DAO의 getProduct() 호출 가능
DAO
- DAO 클래스의 getProduct() 에 매개변수로 "라면" 이 전달되어 왔다
- 현재는 DB연동 안되었으므로 그냥 돌아간다, 돌아갈때 DTO 객체를 생성하고, 매개변수로 받은 "라면" 과 2000 을 저장한 DTO 객체를 Service 로 돌려줌
+ DTO 객체
- 프로퍼티 name , 프로퍼티 price
- 생성자는 public Product(String name, int price)
- 프로퍼티 name, price를 결합해서 리턴해주는 메소드 toString() 이 있다
DAO -> Service (돌아옴)
- DAO 로 부터 돌려받은 것(DTO 객체)을 그대로 main() 으로 리턴함
Service -> main() (돌아옴)
- Service 로 부터 돌려받은 결과(DTO 객체)를 출력함
Spring DI 예제 12 (DAO, DTO, Service 등도 있다, sample 11와 비슷)
- src/main/java/sample12/ 안의 파일들
- DAO, DTO, Service 가 있다, sample11 과 비슷
Spring DI 예제 12 구조 & 흐름
- Web Application 에서는 갈땐 Controller-> Service -> DAO, 올땐 DAO -> Service -> Controller
- 현재는 Web Application 이 아닌 Application 이므로 Controller 클래스는 없다, 그 역할을 메인메소드가 하고 있는거임
- 갈때 : main() -> Service -> dao , 돌아올땐 반대로 dao -> Service -> main() 으로 돌아오자
- service 와 dao 폴더에는 인터페이스 하나, 구현 클래스 하나 씩 있다
- Ex01.java : 메인메소드를 가진 클래스이다, Controller 클래스의 역할을 대신한다
- dao/BookDao.java : 인터페이스, 추상메소드 getBook()
- dao/BookDaoImpl.java : BookDao를 상속하는 구현클래스, 추상메소드 getBook() 를 오버라이딩
- service/BookService.java : 인터페이스, 추상메소드 getBook()
- service/BookServiceImpl.java : BookService를 상속하는 구현클래스, 추상메소드 getBook() 를 오버라이딩
- model/Book.java : DTO 클래스, 책제목 title 필드, 가격 price 필드, setter / getter 메소드가 있다, 값을 저장하고 돌려줄때 사용
main() -> Service -> DAO 로 가는 방법
- 먼저 Spring 환경설정 파일 beans11.xml 에서 구현 클래스 두개 ProductServiceImpl, ProductDaoImpl 로 bean 를 만들 것
- main() 메소드에서 getBean 으로 Service 클래스 객체를 가져오고, 그걸로 Service 객체의 메소드 사용 가능
- Service 클래스에서는 DAO 객체를 가져와서, 그걸로 DAO 객체의 메소드 사용 가능
main() vs Service 에서 객체를 구하는 방법
- main() 에서 Service 객체를 구할때는 beans12.xml 을 읽어서 getbean() 으로 가져옴
- Service 클래스에서 DAO 객체를 구할때는 Service 클래스의 프로퍼티의 자료형을 DAO 로해서 그 프로퍼티로 DAO 객체를 구함
+ main() 에서 beans12.xml 을 읽을때 이미 Service 객체, DAO 객체 다 생성되고 할당되었다 -> 즉 Service 클래스에서 DAO 객체를 프로퍼티로 이미 자동으로 주입받았다
+ toString()
- toString() 은 Object 클래스의 메소드이고, toString() 을 메소드 오버라이딩해서 사용
+ 모든 클래스는 Object 클래스를 상속받는다
- DTO 리턴시 자동으로 toString() 이 실행되어 리턴된 값이 실행됨
- Ex01.java
package sample12;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import sample12.model.Book;
import sample12.service.BookService;
public class Ex01 {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("/sample12/beans12.xml");
BookService bs = (BookService) ac.getBean("bs");
Book book = bs.getBook();
System.out.println(book);
}
}
+ 현재는 Application 이므로 main() 에서 beans12.xml 를 불러와야만 beans12.xml 가 실행된다
+ Web Application 으로 넘어가면 web.xml 이 실행될때 환경설정 파일을 불러오면서 beans12.xml 가 실행된다
- main() -> Service 로 가기 위해 beans12.xml 에서 Service 객체 bs 를 구해와서 Service 의 getBook() 호출
- beans12.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="bd" class="sample12.dao.BookDaoImpl"></bean>
<bean id="bs" class="sample12.service.BookSericeImpl">
<property name="bd" ref="bd"></property>
</bean>
</beans>
<첫번째 객체>
- DAO 클래스로 객체 "bd" 를 생성
<두번째 객체>
- Service 클래스로 객체 "bs" 를 생성
- 객체 bs 를 생성하고, 자료형이 BookDao 인 프로퍼티 bd 에 값을 Setter DI 로 할당하는데, 이때 위에서 생성된 BookDaoImpl 객체 bd 를 할당함
- 프로퍼티 자료형은 인터페이스 BookDao , 주입되는 값은 구현클래스 BookDaoImpl, 즉 업캐스팅으로 주입
- BookServiceImpl.java
package sample12.service;
import sample12.dao.BookDao;
import sample12.model.Book;
public class BookSericeImpl implements BookService {
private BookDao bd;
// BookDao bd = new BookDaoImpl()
public void setBd(BookDao bd) {
this.bd = bd;
}
public Book getBook() {
return bd.getBook("대박인생");
}
}
- main() 에서 beans12.xml 을 불러올때 이미 메모리에 BookServiceImpl 객체 생성되고, 프로퍼티 bd 에도 값이 주입되었음, DAO 의 클래스 사용 가능해졌다
- main() 에서 Service 의 getBook() 을 호출했다, 여기서는 DAO 로 가기위해 DAO 객체 bd 로 bd.getBook("대박인생") 호출
- BookDaoImpl.java
package sample12.dao;
import sample12.model.Book;
public class BookDaoImpl implements BookDao {
public Book getBook(String title) { // title="대박인생"
return new Book(title, 20000);
}
}
- Service 클래스에서 이 DAO 의 getBook() 을 호출했다, "대박인생" 이 매개변수로 넘어옴
- 여기서 DTO 객체를 생성하고, DTO 객체 프로퍼티 title 에 "대박인생" , price에 20000 을 저장해서 돌려줌
Book.java (DTO)
package sample12.model;
public class Book {
private String title;
private int price;
public Book(String title, int price) {
this.title = title; // title="대박인생"
this.price = price; // price=20000
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public String toString() {
return "책[제목:" + title + ", 가격:" + price + "]";
}
}
+ 값을 리턴할때, 전달할때 DTO 객체 생성
- Object toString() 을 오버라이딩해서 저장된 값들을 한꺼번에 돌려주려한다
- toString() 은 DTO 객체가 리턴될때 자동으로 실행됨, 실행되면 값들을 리턴해줌
- 이제 DAO -> Service 로 돌아가야한다
- BookServiceImpl.java (중복)
package sample12.service;
import sample12.dao.BookDao;
import sample12.model.Book;
public class BookSericeImpl implements BookService {
private BookDao bd;
// BookDao bd = new BookDaoImpl()
public void setBd(BookDao bd) {
this.bd = bd;
}
public Book getBook() {
return bd.getBook("대박인생");
}
}
- 돌려받은 값을 그대로 main() 으로 리턴함
- Ex01.java (중복)
package sample12;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import sample12.model.Book;
import sample12.service.BookService;
public class Ex01 {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("/sample12/beans12.xml");
BookService bs = (BookService) ac.getBean("bs");
Book book = bs.getBook();
System.out.println(book);
}
}
- Service 의 getBook() 으로부터 결과를 바당옴
<toString()>
- toString() 은 DTO 객체가 리턴될때 자동으로 실행됨, 그래서 객체 book 을 출력해도 book.toString() 과 같은 결과가 나옴
+ 만약 DTO 객체에 toString() 을 오버라이딩하지 않았으면, System.out.println(book) 할때 주소값이 나옴
- DTO 의 getter 메소드로 개별적인 값을 불러올 수도 있다
어노테이션을 이용한 DI
- 어노테이션 기반으로 처리시 Spring 환경설정 파일에 bean 객체 생성하지 않음
- Sample13 예제를 본격적으로 하기전에 Simple13 을 간략하게 보면서 어노테이션을 이용한 DI 가 뭔지 보자
- beans13.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 http://www.springframework.org/schema/context/spring-context-4.1.xsd">
<!--
1. sample13 패키지 하위 클래스를 스캔한다는 의미를 가진다.
2. @Component, @Controller, @Service, @Repository 어노테이션이
붙어있는 클래스는 @Autowired 어노테이션을 이용해서 필요한 빈 객체를
setter 메소드 없이 주입을 받는다.
-->
<context:component-scan base-package="sample13"/>
</beans>
- bean 을 더이상 만들고 있지 않고 단 한줄만 있다, 이게 어노테이션 기반
- base-package 는 베이스가 되는, 기본이 되는 패키지를 의미함
- 저 한줄은 sample13 패키지 하위의 모든 클래스를 모두 읽어오라는 의미
- 지정된 패키지 sample13 하위의 클래스들에는 클래스명 위에 4가지 어노테이션 중 하나가 붙어있어야함
- @Component, @Controller, @Service, @Repository
어노테이션 4가지
- @Component : 아무 클래스에나 붙일 수 있는 어노테이션
- @Controller : 컨트롤러 클래스 위에 붙이는 어노테이션
- @Service : Service 클래스 위에 붙이는 어노테이션
- @Repository : Repository 클래스 위에 붙이는 어노테이션
- 클래스명 위에 이 4개의 어노테이션 중 한가지만 붙어있어도 된다
- sample13 에서는 모두 @Component 를 붙이지만, 나중엔 클래스에 맞는 어노테이션을 붙인다
ex) Service 클래스 위에 @Service 어노테이션
- 이후 Setter 메소드가 없어도 생성이 필요한 객체 위해 @Autowired 어노테이션을 붙이면 main() 에서 beans13.xml을 읽어올때 필요한 bean 객체가 생성 및 주입됨
필요한 bean 객체를 주입하기 위한 조건
1. base-package 에 적힌 해당 패키지의 모든 클래스를 읽어오는 코드가 있어야함
2. 어노테이션 4개 중 하나 붙어있어야함
3. 만들고자 하는 객체 위에 @Autowired 어노테이션을 붙여야함
ex) Service -> DAO 로 갈때
- Service 에서 DAO 객체를 구해와야하는데, 이때 Setter 메소드 필요없고, bean 객체를 직접 생성하지 않아도 된다
- 자동으로 DAO bean 객체를 생성해줌
- 조건에 맞는지 보자
1) base-package 에 sample13 패키지가 적혀있다
2) Service 클래스 위에 @Component 어노테이션이 붙어있다
3) 필요한 객체인 DAO 객체(Service의 프로퍼티) 위에 @Autowired 어노테이션이 붙어있다
- 조건이 맞으므로 Service 클래스에 Setter 메소드가 없어도 Service 클래스에 DAO 객체가 생성되어서 프로퍼티에 값(객체)이 주입됨, 이때 객체 생성 및 주입 시점은 main() 에서 beans13.xml 을 읽어왔을때이다.
- 즉 아래의 빨간선으로 표시한 내용이 자동으로 실행된다, 실행되는 시점은 main() 에서 beans13.xml 을 읽어왔을때
- sample11.xml 부분
+ 현재는 DAO 에서 DB 연동안하므로 DAO 에서 객체 생성하진 않음, 나중엔 SqlSession 객체를 생성
Spring DI 예제 13 (어노테이션 기반)
- src/main/java/sample13/ 안의 파일들
- 더이상 bean 을 만들지 않고, 어노테이션 기반으로 처리함
Spring DI 예제 13 구조 & 흐름
- Web Application 에서는 갈땐 Controller-> Service -> DAO, 올땐 DAO -> Service -> Controller
- 현재는 Web Application 이 아닌 Application 이므로 Controller 클래스는 없다, 그 역할을 메인메소드가 하고 있는거임
- 갈때 : main() -> Service -> dao , 돌아올땐 반대로 dao -> Service -> main() 으로 돌아오자
- Ex01.java : 메인메소드를 가진 클래스이다, Controller 클래스의 역할을 대신한다
- productDao.java : 인터페이스, 추상메소드 getProduct()
- ProductDaoImpl.java : productDao를 상속하는 구현클래스, 추상메소드 getProduct() 를 오버라이딩
- ProductService.java : 인터페이스, 추상메소드 getProduct()
- ProductServiceImpl.java : ProductService를 상속하는 구현클래스, 추상메소드 getProduct() 를 오버라이딩
- Prodjct.java : DTO 클래스, 값을 저장하고 돌려줄때 사용
어노테이션
- Service 구현 클래스와 DAO 구현 클래스에만 어노테이션이 붙어있다
- Service 구현 클래스, DAO 구현 클래스위에 @Component 어노테이션이 붙어있음
문제
- 흐름은 main() -> Service -> DAO -> Service -> main()
- Service -> DAO 로 갈때는 @Autowired 로 DAO 객체를 구해온다
- 그럼 main() -> Service 로 갈때는 bean 객체가 없는데 어떻게 getbean() 으로 Service 객체를 구해올까?
- Ex01.java
package sample13;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Ex01 {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("/sample13/beans13.xml");
ProductService ps = ac.getBean(ProductService.class);
Product product = ps.getProduct();
System.out.println(product);
}
}
- 먼저 beans13.xml 을 불러온다
- getBean() 안에 bena 이 없고, Service 클래스명을 직접 구해와야한다 (인터페이스명도 되고 구현클래스명도 됨)
- 이러면 Service 객체를 구해와서 ps 로 받고, Service 클래스 안의 메소드 getProduct() 를 호출
- ProductServiceImpl.java
package sample13;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class ProductServiceImpl implements ProductService {
@Autowired
private ProductDao pd;
public Product getProduct() {
return pd.getProduct("짜장면");
}
}
- beans13.xml 을 main() 에서 불러올때 이미 DAO 객체 pd 에 주입이 되었다
- 여기 SetterPd() 메소드가 없어도 주입됨!
- 객체 pd 가 주입되어있으므로 pd.getProduct() 로 DAO 클래스의 getProduct("짜장면") 호출
- ProductDaoImpl.java
package sample13;
import org.springframework.stereotype.Component;
@Component
public class ProductDaoImpl implements ProductDao {
public Product getProduct(String name) {//name="짜장면"
return new Product(name, 2500);
}
}
- DTO 객체를 생성하고 매개변수로 넘어온 "짜장면", 2500 을 객체에 저장해서 그 객체를 리턴
- 다시 Service 로 돌아가야한다
- ProductServiceImpl.java (중복)
package sample13;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class ProductServiceImpl implements ProductService {
@Autowired
private ProductDao pd;
public Product getProduct() {
return pd.getProduct("짜장면");
}
}
- 받은 DTO 객체를 그대로 main() 으로 돌려줌
- Ex01.java (중복)
package sample13;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Ex01 {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("/sample13/beans13.xml");
ProductService ps = ac.getBean(ProductService.class);
Product product = ps.getProduct();
System.out.println(product);
}
}
- DTO 객체를 돌려받음
- toString() 은 써도 안써도 자동 실행됨
나중에 할 예제의 흐름
- Controller 클래스에서 Service 객체를 생성할때 @Autowired 사용해서 생성
- Controller -> Service로 넘어가는 방법 = Service 객체 생성
Web Application 에서의 어노테이션
- 잠시 Web Application 인 springtest 프로젝트를 보자
어노테이션으로 Controller -> Service 로 가는 방법
- 조건에 맞는지 보자
1) servlet-context.xml 에서 base-package 를 설정하고 있다
- com.myhome.springtest 란 패키지 안의 모든 클래스를 읽어옴, 그 안에는 Controller 클래스인 HomeController.java
- Spring Project 생성시 입력헀던 top-level 패키지이다, 이 패키지 com.myhome.springtest 는 src/main/java 폴더 하위에 생성된다
2) HomeController 클래스 위에 @Controller 어노테이션이 있다
3) Controller 클래스 HomeController 클래스 안에서 Service 객체 생성이 필요한 곳에서 @Autowired 를 붙임
- 1,2,3 조건 3개를 모두 만족하므로 Controller 에서 Service 객체를 생성할때 자동으로 생성해서 Controller 클래스의 프로퍼티에 주입해준다
Spring 환경설정 파일과 어노테이션
- Web Application 에서 Spring 의 환경설정 파일 중 root-context.xml 은 어노테이션 뿐 아니라 기존 처럼 bean 태그로 객체 생성하는 방법도 사용한다
- Web Application 에서 Spring 의 환경설정 파일 중 servlet-context.xml 은 어노테이션 기반이다, base-package 가 지정되어있음,
Spring DI 예제 14 (어노테이션 기반, Sample13 과 비슷)
- src/main/java/sample14/ 안의 파일들
- 더이상 bean 을 만들지 않고, 어노테이션 기반으로 처리함
Spring DI 예제 14 구조 & 흐름
- Web Application 에서는 갈땐 Controller-> Service -> DAO, 올땐 DAO -> Service -> Controller
- 현재는 Web Application 이 아닌 Application 이므로 Controller 클래스는 없다, 그 역할을 메인메소드가 하고 있는거임
- 갈때 : main() -> Service -> dao , 돌아올땐 반대로 dao -> Service -> main() 으로 돌아오자
- Ex01.java : 메인메소드를 가진 클래스이다, Controller 클래스의 역할을 대신한다
- BookDao.java : 인터페이스, 추상메소드 getBook()
- BookDaoImpl.java : BookDao를 상속하는 구현클래스, 추상메소드 getBook() 를 오버라이딩
- BookService.java : 인터페이스, 추상메소드 getBook()
- BookServiceImpl.java : BookService를 상속하는 구현클래스, 추상메소드 getBook() 를 오버라이딩
- Book.java : DTO 클래스, 값을 저장하고 돌려줄때 사용
예제 sample14 / sample15 의 흐름
- 현재는 Controller 클래스가 없으므로 main() 에서 그 역할을 한다
- main() 에서 Service 객체를 생성하는 것이 아니라 그냥 불러왔음
어노테이션
- Service 구현 클래스와 DAO 구현 클래스에만 어노테이션이 붙어있다
- Service 구현 클래스, DAO 구현 클래스위에 @Component 어노테이션이 붙어있음
- beans14.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 http://www.springframework.org/schema/context/spring-context-4.1.xsd">
<context:component-scan base-package="sample14"/>
</beans>
- 저 한줄이 첫번째 조건이다
- Ex01.java
package sample14;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
public class Ex01 {
public static void main(String[] args) {
ApplicationContext ac = new GenericXmlApplicationContext("/sample14/beans14.xml");
BookService bs = ac.getBean(BookService.class);
Book book = bs.getBook();
System.out.println(book);
}
}
- 지금은 Controller 클래스가 아니라 main 메소드이므로 어노테이션을 안쓰고 있음
- bean 을 만들지 않았으므로 Service 객체가 없다
- 그래서 getBean(BookService.class) 로 Service 클래스 객체를 구해온다
- BookSeviceImpl.java
package sample14;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class BookServiceImpl implements BookService {
@Autowired
private BookDao bd;
public Book getBook() {
return bd.getBook("바람과 함께 사라지다");
}
}
- Service 클래스 위에 @Component 어노테이션
- DAO 객체를 @Autowired 로 프로퍼티 bd 에 주입
- DAO 객체를 구해왔으므로 DAO 의 getBook() 메소드 호출, 매개변수로 "바람과 함께 사라지다" 를 준다
- BookDaoImpl.java
package sample14;
import org.springframework.stereotype.Component;
@Component
public class BookDaoImpl implements BookDao {
public Book getBook(String title) {
return new Book(title, 25000);
}
}
- 매개변수로 전달된 타이틀값과 가격으로 DTO Book 객체를 생성해서 객체를(주솟값을) 리턴
- DAO 클래스 위에 @Componet 어노테이션
- 다시 돌아가는 과정은 리턴만 하면 되므로 생략
Spring DI 예제 15 (어노테이션 기반, Sample14 과 비슷)
- src/main/java/sample15/ 안의 파일들
- 더이상 bean 을 만들지 않고, 어노테이션 기반으로 처리함
- 이제 어노테이션 @Componet 대신 Service 클래스 위에 @Service, DAO 클래스 위에 @Repository
Spring DI 예제 15 구조 & 흐름
- Web Application 에서는 갈땐 Controller-> Service -> DAO, 올땐 DAO -> Service -> Controller
- 현재는 Web Application 이 아닌 Application 이므로 Controller 클래스는 없다, 그 역할을 메인메소드가 하고 있는거임
- 갈때 : main() -> Service -> dao , 돌아올땐 반대로 dao -> Service -> main() 으로 돌아오자
- Ex01.java : 메인메소드를 가진 클래스이다, Controller 클래스의 역할을 대신한다
- BookDao.java : 인터페이스, 추상메소드 getBook()
- BookDaoImpl.java : BookDao를 상속하는 구현클래스, 추상메소드 getBook() 를 오버라이딩
- BookService.java : 인터페이스, 추상메소드 getBook()
- BookServiceImpl.java : BookService를 상속하는 구현클래스, 추상메소드 getBook() 를 오버라이딩
- Book.java : DTO 클래스, 값을 저장하고 돌려줄때 사용
어노테이션
- 이제 어노테이션 @Componet 대신 Service 클래스 위에 @Service, DAO 클래스 위에 @Repository
- 인터페이스 위에는 어노테이션 쓰지 않음, 구현 클래스 위에만 어노테이션 쓴다
클래스와 해당 어노테이션 관계
- Controller 클래스 위엔 @Controller
- DAO 클래스 위엔 @Repository
- Service 클래스 위엔 @Service
- @Component 로 해도 동작은 한다
예제 sample14 / sample15 의 흐름
- 현재는 Controller 클래스가 없으므로 main() 에서 그 역할을 한다
+ 나중에 Web Application 할때 할 예제의 흐름 (중복, 중요)
Controller -> Service 로 가는 법
- Controller 클래스에서 Service 객체를 생성할때 @Autowired 사용해서 생성
- Controller -> Service로 넘어가는 방법 = Service 객체 생성, 이때 주입할 프로퍼티 위에 @Autowired 를 써준다
1) servlet-context.xml 에서 top-level 패키지안의 모든 클래스 읽기, 즉 Conroller 클래스를 읽어옴
2) Controller 클래스에서 클래스명 위에 @Controller 붙이기
3) Service 객체를 생성해서 주입할 프로퍼티 위에 @Autowired 를 써준다
- 이 조건을 모두 만족시켜서 Controller 클래스에서 Service 클래스의 객체를 만들 수 있다
- Controlelr 클래스에 Setter 메소드가 없어도 만들고 주입 가능함
+ 지금 예제는 일반 Application 이므로 servlet-context.xml 에서 Controller 패키지를 읽어오는 작업을 하지도 않았고 Controller 패키지가 없으므로 main() 에서 Service 객체를 .class 로 불러와서 main() -> Service 로 이동
- Ex01.java
package sample15;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
public class Ex01 {
public static void main(String[] args) {
ApplicationContext ac = new GenericXmlApplicationContext("/sample15/beans15.xml");
BookService bs = ac.getBean(BookService.class);
Book book = bs.getBook();
System.out.println(book);
}
}
- beans15.xml 파일을 읽어온다
- beans15.xml 에 bean 태그로 객체를 생성하지 않았으므로 BookService.class (경로설정) 로 Service 클래스의 객체를 생성함
- 생성한 Service 객체로 Service 의 getBook() 메소드 호출
- beans15.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 http://www.springframework.org/schema/context/spring-context-4.1.xsd">
<!--
1. sample15 패키지 하위 클래스를 스캔한다는 의미를 가진다.
2. @Component, @Controller, @Service, @Repository 어노테이션이
붙어있는 클래스는 @Autowired 어노테이션을 이용해서 필요한 빈 객체를
setter 메소드 없이 주입을 받는다.
-->
<context:component-scan base-package="sample15"/>
</beans>
- BookServiceImpl.java
package sample15;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
/*@Component*/
@Service
public class BookServiceImpl implements BookService {
@Autowired
private BookDao bd;
public Book getBook() {
return bd.getBook("바람과 함께 사라지다");
}
}
- Service 클래스 위에는 @Service 를 주로 붙인다
- @Autowired 를 프로퍼티 bd 위에 쓰면 생성된 DAO 객체를 bd 에 주입해줌
- 이때 이 Service 클래스안에 setBd() 인 Setter 메소드가 없어도 자동으로 주입해준다
- 이때 생성 및 주입은 main() 에서 beans15.xml 을 읽어왔을때 이미 했음
+ 조건 1,2,3 을 만족하므로 자동으로 생성, 주입이 가능한 것임 (base-package="sample15", @Service, @Autowired)
- BookDaoImpl.java
package sample15;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;
/*@Component*/
@Repository
public class BookDaoImpl implements BookDao {
public Book getBook(String title) {//title="바람과 함께 사라지다"
return new Book(title, 25000);
}
}
- DAO 클래스 위에는 @Repository 를 주로 붙인다
- 현재는 DAO 에서는 아무 객체도 생성하고 있지 않지만 나중에 SQL문 실행을 위해서 SqlSession 객체를 @Autowired 로 생성 및 주입해야함
- DTO 인 Book 객체를 만들어서 받은 "바람과 함께 사라지다" 와 25000 을 저장해서 객체를 돌려줌
+ Book.java (DTO) : Object 의 toString() 메소드를 오버라이딩 했다, DTO 리턴시 자동으로 실행됨
- 이제 다시 돌아가야한다
- BookServiceImpl.java : 받은 객체를 그대로 리턴한다
- Ex01.java : Service 클래스로부터 DTO 객체를 받아서 책제목과 가격을 리턴하는 toString() 을 실행
Sample 16 & Sample 17
- 콘솔로 만든 회원관리 프로그램
- 같은 내용이다
- Sample 16 은 bean 객체를 생성해서 만들고
- Sample 17 은 어노테이션 기반으로 만들었다
Spring DI 예제 16 (bean 객체 직접 생성, 회원관리 콘솔 프로그램)
- src/main/java/sample16/ 안의 파일들
- bean 을 직접 생성하여 만든 콘솔 회원관리 프로그램
Spring DI 예제 16 구조 & 흐름
- Web Application 에서는 갈땐 Controller-> Service -> DAO, 올땐 DAO -> Service -> Controller
- 현재는 Web Application 이 아닌 Application 이므로 Controller 클래스는 없다, 그 역할을 메인메소드가 하고 있는거임
- 갈때 : main() -> Service -> dao , 돌아올땐 반대로 dao -> Service -> main() 으로 돌아오자
- Ex01.java : 메인메소드를 가진 클래스이다, Controller 클래스의 역할을 대신한다, 명령어에 따라 회원정보 검색, 등록, 삭제, 수정 가능
- MemberService.java : 인터페이스, 추상메소드들이 많이 있다 ex) delete(), insert(), update(), select(), list()
- MemberServiceImpl.java : MemberService 를 상속, MemberService 의 추상메소드들을 모두 오버라이딩
- MemberDao.java : 인터페이스, 추상메소드들이 많이 있다 ex) delete(), insert(), update(), selectByEmail(), list()
- MemberDaoImpl.java : MemberDao를 상속, MemberDao 의 추상메소드들을 모두 오버라이딩
- Member.java : DTO, 필드 id,pass,email,name,reg_date 와 각각의 getter/setter 메소드, toString() 메소드
- RegisterMember.java : DTO, 필드 pass, confirmpass, name, email 와 각각의 getter/setter 메소드, passCheck() 메소드
- DTO 객체가 2개이다
- RegisterMember.java DTO 클래스는 가입할때만 사용된다
Service 클래스와 DAO 클래스의 메소드 명이 같은 게 많다
- 같게 설정해서 어느 메소드에서 어느 메소드를 호출할지 알기 쉽게 해둔 것이다
ex) Service의 delete() 메소드에서 DAO 의 delete() 메소드를 호출
인터페이스를 사용하는 이유
- 예전에는 통일성있는 클래스를 작성하기 위해 인터페이스를 사용
- 지금은 메소드가 많아서 그걸 관리하기 위해 인터페이스를 사용한다 (어떤 메소드가 있는지 색인처럼 확인 가능)
- beans16.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="md" class="sample16.MemberDaoImpl" />
<bean id="ms" class="sample16.MemberServiceImpl">
<property name="md" ref="md" />
</bean>
</beans>
- bean 을 써서 Setter DI 를 직접 하고 있다
- Sample17에서는 같은 내용을 어노테이션으로 처리
- Service 객체 "ms" 와 DAO 객체 "md"가 생성됨
- 그리고 Service 객체 "ms" 의 프로퍼티 md 에 위에서 만들어진 DAO 객체 "md" 가 주입되었다
- 이때 Service 클래스에는 Setter 메소드가 있어야만 함
- Ex01.java
package sample16;
import java.util.Collection;
import java.util.List;
import java.util.Scanner;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Ex01 {
static MemberService ms = null;
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("/sample16/beans16.xml");
ms = (MemberService) ac.getBean("ms");
Scanner sc = new Scanner(System.in);
while (true) {
System.out.println("명령어를 입력하세요?");
String command = sc.nextLine();
if (command.equals("x")) {
System.out.println("프로그램 종료");
break;
//startsWith() 메소드는 캐릭터 라인이, 지정된 접두사로 시작 여부를 감지하는 데 사용
// public boolean startsWith(String prefix)
} else if (command.startsWith("new")) { // 입력한 문자가 new로 시작하면 true 리턴함
insert(command.split(" "));
continue;
} else if (command.startsWith("select")) {
select(command.split(" "));
continue;
} else if (command.equals("list")) {
list();
continue;
} else if (command.startsWith("delete")) {
delete(command.split(" "));
continue;
} else if (command.startsWith("update")) {
update(command.split(" "));
continue;
}
help();
}
sc.close();
}
public static void insert(String[] str) {
if (str.length != 5) {
help();
return;
}
RegisterMember rm = new RegisterMember();
rm.setEmail(str[1]);
rm.setName(str[2]);
rm.setPass(str[3]);
rm.setConfirmPass(str[4]);
if (!rm.passCheck()) {
System.out.println("똑바로 암호 입력해");
} else {
int result = ms.insert(rm);
if (result > 0)
System.out.println("입력 성공");
}
}
public static void select(String[] str) {
if (str.length != 2) {
help();
return;
}
Member member = ms.select(str[1]);
if (member != null)
System.out.println(member);
else
System.out.println("없는 데이터 입니다");
}
public static void list() {
Collection<Member> list = ms.list();
if (list != null) {
for (Member member : list) {
System.out.println(member);
}
}
}
public static void delete(String[] str) {
if (str.length != 2) {
help();
return;
}
int result = ms.delete(str[1]);
if (result > 0)
System.out.println("삭제 성공");
}
public static void update(String[] str) {
if (str.length != 5) {
help();
return;
}
RegisterMember rm = new RegisterMember();
rm.setPass(str[3]);
rm.setConfirmPass(str[4]);
rm.setEmail(str[1]);
rm.setName(str[2]);
if (!rm.passCheck()) {
System.out.println("똑바로 암호 입력해");
} else {
int result = ms.update(rm);
if (result > 0)
System.out.println("수정 성공");
}
}
public static void help() {
System.out.println("잘못 입력했습니다");
System.out.println("명령어 사용법:");
System.out.println("new 이메일 이름 암호 암호확인");
System.out.println("update 이메일 이름 암호 암호확인");
System.out.println("delete 이메일");
System.out.println("select 이메일");
System.out.println();
}
}
Service 객체 구하기 (Ex01.java 부분)
ms = (MemberService) ac.getBean("ms");
Scanner 객체 구하기 (Ex01.java 부분)
Scanner sc = new Scanner(System.in);
- 키보드로 회원정보를 입력받거나 목록을 수정/삭제 하기 위해 Scanner 객체 생성
- While문 안에서 계속해서 입력을 받을 수 있도록 무한 루프가 돈다, x 입력시 빠져나감
While문 안 명령 한 줄 받기 (Ex01.java 부분)
String command = sc.nextLine();
입력에 따라 분기 (Ex01.java 부분)
if (command.equals("x")) {
System.out.println("프로그램 종료");
break;
//startsWith() 메소드는 캐릭터 라인이, 지정된 접두사로 시작 여부를 감지하는 데 사용
// public boolean startsWith(String prefix)
} else if (command.startsWith("new")) { // 입력한 문자가 new로 시작하면 true 리턴함
insert(command.split(" "));
continue;
} else if (command.startsWith("select")) {
select(command.split(" "));
continue;
} else if (command.equals("list")) {
list();
continue;
} else if (command.startsWith("delete")) {
delete(command.split(" "));
continue;
} else if (command.startsWith("update")) {
update(command.split(" "));
continue;
}
help();
- 이때의 입력은 이렇게 입력해야함 (아래)
public static void help() {
System.out.println("잘못 입력했습니다");
System.out.println("명령어 사용법:");
System.out.println("new 이메일 이름 암호 암호확인");
System.out.println("update 이메일 이름 암호 암호확인");
System.out.println("delete 이메일");
System.out.println("select 이메일");
System.out.println();
}
- 분기 문 중 "new" 로 시작한걸 보자
사용자가 new 를 입력했다면 (Ex01.java 부분)
} else if (command.startsWith("new")) { // 입력한 문자가 new로 시작하면 true 리턴함
insert(command.split(" "));
continue;
- startWith() 는 어떤 특정한 문자로 시작하는지 확인 가능하다
- command 가 "new" 로 시작하면 comand.startWith("new") 는 True 를 반환
- True 면 insert() 메소드가 실행됨, 이 메소드는 Ex01 의 main() 아래에 정의되어있다
- 매개변수로는 command.split(" ") 으로 자른 배열을 주고 있다 (split() 의 리턴값은 String 형 배열이다)
- "new lay99@g.com lay 1999 1999" 를 입력했다면 공백을 기준으로 잘라서 배열에 저장해서 insert() 매개변수 인자로 준다
- 이 배열을 insert() 호출시 전달
- Ex01.java 의 insert() (Ex01.java 부분)
- insert() 는 main() 바깥 아래에 있다
public static void insert(String[] str) {
if (str.length != 5) {
help();
return;
}
RegisterMember rm = new RegisterMember();
rm.setEmail(str[1]);
rm.setName(str[2]);
rm.setPass(str[3]);
rm.setConfirmPass(str[4]);
if (!rm.passCheck()) {
System.out.println("똑바로 암호 입력해");
} else {
int result = ms.insert(rm);
if (result > 0)
System.out.println("입력 성공");
}
}
0. 매개변수 String 배열이 넘어왔다
- str[0] = new, str[1] = lay99@g.com, str[2] = lay, str[3] = 1999, str[4] = 1999 가 넘어왔다
- 명령어new, 이메일, 이름, 암호, 암호확인 이 배열 str에 순서대로 들어가 있다
1. 이때 형식이 맞는지를 먼저 확인한다, str.len 이 5개여야하므로 5개가 아니면 help() 호출 후 insert() 를 나간다
- insert() 를 나가서 main() 으로 돌아가서 continue 문에 의해 다시 while 문에 의해 입력을 받음
2. DTO RegisterMember 객체 rm을 생성해서 그 안에 이메일, 이름, 암호, 암호확인을 객체에 저장
3. RegisterMember 의 passCheck() 메소드로 암호와 암호확인이 일치한지 확인
- 불일치시 "똑바로 암호 입력해", 일치시 main() 에서 받아온 Service 객체 ms (전역변수임) 로 ms.insert() 호출
- ms.insert() 호출시 매개변수로 RegisterMember(DTO) 객체 rm 을 전달
- ms.insert() 에서 리턴된 값이 0 보다 크면 "입력 성공" 출력
- Service 클래스의 메소드를 호출했으므로 Service 로 넘어갔다
- MemberServiceImpl.java
package sample16;
import java.util.Collection;
import java.util.Date;
import java.util.List;
public class MemberServiceImpl implements MemberService {
private MemberDao md;
public void setMd(MemberDao md) {
this.md = md;
}
public int insert(RegisterMember rm) {
int result = 0;
Member member = md.selectByEmail(rm.getEmail()); //email주소로 1명의 정보를 구해옴
if (member == null) { // 동일한 email 주소가 없으면 회원가입함
member = new Member(rm.getPass(), rm.getEmail(), rm.getName(), new Date());
md.insert(member);
result = 1;
} else {
System.out.println("이미 데이터가 있습니다");
}
return result;
}
public Member select(String email) {
return md.selectByEmail(email);
}
public Collection<Member> list() {
return md.list();
}
public int delete(String email) {
int result = 0; // 데이터가 이미 있는지 확인
Member member = md.selectByEmail(email);
if (member != null) {
md.delete(email);
result = 1;
} else {
System.out.println("없는네 우찌 삭제하니");
}
return result;
}
public int update(RegisterMember rm) {
int result = 0; // 데이터가 이미 있는지 확인
Member member = md.selectByEmail(rm.getEmail());
if (member != null) {
member.setPass(rm.getPass()); // 비번과 이름 수정
member.setName(rm.getName());
md.update(member);
result = 1;
} else {
System.out.println("없는네 우찌 고치니 ? 헐");
}
return result;
}
}
- DAO 객체를 주입할 필드 md, Setter 메소드 setMd() 가 있다
- 이 필드 md 에는 이미 DAO 객체가 주입되었다 (main 에서 beans16.xml 을 읽었을때 주입되었음)
insert() 부분만 (MemberServiceImpl.java 부분)
public int insert(RegisterMember rm) {
int result = 0;
Member member = md.selectByEmail(rm.getEmail()); //email주소로 1명의 정보를 구해옴
if (member == null) { // 동일한 email 주소가 없으면 회원가입함
member = new Member(rm.getPass(), rm.getEmail(), rm.getName(), new Date());
md.insert(member);
result = 1;
} else {
System.out.println("이미 데이터가 있습니다");
}
return result;
}
- DAO 객체를 받은 md 로 md.selectByEmail() 로 이메일 주소 중복검사를 한다, 여기서 DAO 로 이동한다
- DAO 의 selectByEmail() 를 호출했으므로 DAO 로 갔다
- MemberDaoImpl.java
package sample16;
import java.util.*;
public class MemberDaoImpl implements MemberDao {
private Map<String, Member> map = new HashMap<String, Member>();
private static int nextId = 0;
public void insert(Member member) {
member.setId(++nextId);
map.put(member.getEmail(), member);
}
public Member selectByEmail(String email) {
return map.get(email);
}
public Collection<Member> list() {
return (Collection<Member>) map.values();
}
public void delete(String email) {
map.remove(email);
}
public void update(Member member) {
map.put(member.getEmail(), member);
}
}
- DB 연동을 안하므로, Map 자료구조에 회원 데이터를 저장할 것임, 여기 DAO 의 Map 객체 map 에 저장할 것
- Map 자료구조에서 키값은 String, 밸류값은 Member(DTO) 가 자료형이다
- 키값이 Email 주소이고, 밸류값은 해당 회원의 정보를 담게 할 것임
- nextId 는 몇명이 등록되어있는지 저장하는 역할, insert() 가 되면 이 값을 증가시켜서 setId() 메소드로 넘어온 DTO 객체 member 에 저장 * 아래까지 모두 보고와야 이해됨
<selectbyemail() 메소드>
- map.get() 으로 넘어온 이메일을 키로, 그 키의 밸류인 DTO 객체를 돌려주고 있다
- 해당 키(매개변수로 넘어온) 에 해당하는 밸류가 있으면 밸류를 리턴, 없으면 null 을 리턴
- Service 로 돌아가자
- Service 의 insert() 부분만
public int insert(RegisterMember rm) {
int result = 0;
Member member = md.selectByEmail(rm.getEmail()); //email주소로 1명의 정보를 구해옴, *
if (member == null) { // 동일한 email 주소가 없으면 회원가입함
member = new Member(rm.getPass(), rm.getEmail(), rm.getName(), new Date());
md.insert(member);
result = 1;
} else {
System.out.println("이미 데이터가 있습니다");
}
return result;
}
<DAO selectByEmail() 에서 돌아온 뒤>
- * 가 돌아온 곳의 줄이다
- selectByEmail() 로 부터 돌아와서 Member 객체 member 로 가져온 객체를 저장한다
- select 문에서 돌려주는 값이 없어야 동일한 Email 이 없는 것임, 그래서 member == null 일때 삽입시켜야함
- 만약 객체를 제대로 가져왔다면 이메일 중복이 아니므로 이 회원을 등록(삽입) 할 것임
- 즉, 중복이 아닐때는 DAO 객체를 받은 md 로 md.insert() 해서 DAO 의 insert() 호출
<회원 등록>
- Member DTO 객체 member 에 매개변수로 넘어온 RegisterMember DTO 객체 rm 에서 암호, 이메일, 이름을 꺼내서 세팅
- 그 후 Service 객체 md 로 insert() 호출, 넘겨주는 값은 객체 member 이다
- 그럼 다시 DAO 로 간다
- DAO의 insert() 부분만
public void insert(Member member) {
member.setId(++nextId);
map.put(member.getEmail(), member);
}
- 매개변수로 넘어온 객체 member 에 setId() 로 id(아이디 비번할떄 id 가 아니라 인식을 위한 id) 를 세팅
- 현재는 DB 가 아닌 메모리에 저장한다, 그래서 map 자료구조에 put() 으로 키를 이메일, 밸류를 상세정보를 저장한 member 로 준다
+ map 은 키값 중복 안됨
- DAO 의 insert() 에서 반환은 하지 않음
- 다시 Service로 돌아옴
- Service 클래스 insert() 부분만
public int insert(RegisterMember rm) {
int result = 0;
Member member = md.selectByEmail(rm.getEmail()); //email주소로 1명의 정보를 구해옴
if (member == null) { // 동일한 email 주소가 없으면 회원가입함
member = new Member(rm.getPass(), rm.getEmail(), rm.getName(), new Date());
md.insert(member); // * 돌아온 곳
result = 1;
} else {
System.out.println("이미 데이터가 있습니다");
}
return result;
}
* 돌아온 곳
- DAO의 insert 에서 반환은 하지 않는다, 그냥 삽입만 한 것
- 여기선 DB연동이 아니므로 DAO까지 갔다 온 후 내가 임의로 result 에 1 을 넣고 그 result 를 반환
- 그러면 다시 Ex01.java 로 간다
- Ex01.java 에서 insert() 부분만
public static void insert(String[] str) {
if (str.length != 5) {
help();
return;
}
RegisterMember rm = new RegisterMember();
rm.setEmail(str[1]);
rm.setName(str[2]);
rm.setPass(str[3]);
rm.setConfirmPass(str[4]);
if (!rm.passCheck()) {
System.out.println("똑바로 암호 입력해");
} else {
int result = ms.insert(rm); // * 돌아온 곳
if (result > 0)
System.out.println("입력 성공");
}
}
* 돌아온 곳
- result 에 1이 반환되었으므로 아래의 "입력 성공" 을 출력
- 실제 입력을 해보자
- 이제 검색을 해보자
- 검색할때는 이메일 주소로만 검색 가능
Ex01.java
- main() 에서의 명령에 따른 분기문은 아까와 같은 원리이므로 설명 생략, select() 로 간다
Ex01.java 의 select()
public static void select(String[] str) {
if (str.length != 2) {
help();
return;
}
Member member = ms.select(str[1]);
if (member != null)
System.out.println(member);
else
System.out.println("없는 데이터 입니다");
}
- 형식이 "select 이메일" 이므로 length 가 2인지 확인
- Service 객체 ms 로 ms.select() 하고, 매개변수로는 이메일을 넘김
- Service 클래스 MemberServiceImpl.java 의 select() 부분만
public Member select(String email) {
return md.selectByEmail(email);
}
- DAO 객체 md 로 DAO 의 selectByEmail() 을 호출, 매개변수로는 넘어온 email 을 그대로 넘겨줌
- DAO 클래스 MemberDaoImpl.java의 selectByEmail() 부분만
public Member selectByEmail(String email) {
return map.get(email);
}
- 회원이 저장되어있는 Map 객체 map 에서 get(키) 로 밸류인 Member 객체를 구해옴
- 이제 돌아간다
- Service 클래스 MemberServiceImpl.java 의 select() 부분만
public Member select(String email) {
return md.selectByEmail(email);
}
- 그대로 리턴함, Ex01.java 로 간다
- Ex01.java 의 select() 부분만
public static void select(String[] str) {
if (str.length != 2) {
help();
return;
}
Member member = ms.select(str[1]);
if (member != null)
System.out.println(member);
else
System.out.println("없는 데이터 입니다");
}
- ms.select() 에서 만약 있는 회원이면 객체를 반환해서 member 에 저장, 없으면 null 값이 member 로 들어감
- 그래서 member != null 일땐 member 출력 (Member DTO 클래스에 toString() 이 있으므로 그걸로 회원 정보 모두 출력)
- 검색을 실제로 해보면
- 검색된다
- 이제 목록 출력을 해보자
- 목록 출력시에는 "list" 라고만 입력하면된다
Ex01.java
- main() 에서의 명령에 따른 분기문은 아까와 같은 원리이므로 설명 생략, list() 로 간다
- Ex01.java 의 list() 부분만
public static void list() {
Collection<Member> list = ms.list();
if (list != null) {
for (Member member : list) {
System.out.println(member);
}
}
}
- Service 객체 ms 로 ms.list() 호출하고 Collection<Member>가 자료형인 list 로 반환
+ Collection : 인터페이스, 객체의 모음을 저장, List, Set, Map 이 이 Collection 을 상속함
- Service 클래스 MemberServiceImpl.java 의 list() 부분만
public Collection<Member> list() {
return md.list();
}
- DAO 객체 md 로 md.list() 를 호출하고 있다
- DAO 클래스 MemberDaoImpl.java 의 list() 부분만
public Collection<Member> list() {
return (Collection<Member>) map.values();
}
- 회원 데이터를 저장하는 Map 객체 map 으로 map.values() 로 map 에 저장된 모든 value 값들을 가져와서 리턴함
- map.values() 는 모든 회원 정보, 즉 DTO 객체들의 모음이다
- map.values() 는 리턴자료형이 Collection 이다!
- 그럼 Service 클래스로 다시 돌아가자
- Service 클래스 MemberServiceImpl.java 의 list() 부분만
public Collection<Member> list() {
return md.list();
}
- 그대로 Ex01 로 리턴함
- Ex01.java 의 list() 부분만
public static void list() {
Collection<Member> list = ms.list();
if (list != null) {
for (Member member : list) {
System.out.println(member);
}
}
}
- 돌아와서 데이터들을 받아서 for문을 통해서 하나씩 출력하고 있다 (toString() 사용됨)
- update / delete 는 간략히만 설명
update
- update는 기존에 있는 이메일을 써야하고 이름과 비번을 수정 가능하다
<Ex01.java>
- update 는 RegisterMember의 passCheck() 를 먼저 호출해서 암호와 암호확인이 일치하는지 확인 후 일치시 Service 의 update() 호출
<Service>
- Service 의 update() 안에서 DAO의 selectByEmail() 를 호출해서 있는 이메일인지 확인
- 중복이 아닌 이메일이면 DAO의 update() 를 호출해서 수정
<DAO의 update()>
- update 는 키값이 중복되면 마지막의 키값만 사용할 수 있다는 점을 이용
- 즉 정보수정을 원하는 회원의 키값과 똑같은 키값으로 새로운 데이터(수정용 데이터)를 저장
delete
- map 에서 remove() 메소드로 키값 email 로 해당 데이터를 삭제
Spring DI 예제 16 (어노테이션 기반, 회원관리 콘솔 프로그램)
- src/main/java/sample17/ 안의 파일들
- 어노테이션 기반으로 만든 콘솔 회원관리 프로그램
Spring DI 예제 17 구조 & 흐름
- Web Application 에서는 갈땐 Controller-> Service -> DAO, 올땐 DAO -> Service -> Controller
- 현재는 Web Application 이 아닌 Application 이므로 Controller 클래스는 없다, 그 역할을 메인메소드가 하고 있는거임
- 갈때 : main() -> Service -> dao , 돌아올땐 반대로 dao -> Service -> main() 으로 돌아오자
- Ex01.java : 메인메소드를 가진 클래스이다, Controller 클래스의 역할을 대신한다, 명령어에 따라 회원정보 검색, 등록, 삭제, 수정 가능
- MemberService.java : 인터페이스, 추상메소드들이 많이 있다 ex) delete(), insert(), update(), select(), list()
- MemberServiceImpl.java : MemberService 를 상속, MemberService 의 추상메소드들을 모두 오버라이딩
- MemberDao.java : 인터페이스, 추상메소드들이 많이 있다 ex) delete(), insert(), update(), selectByEmail(), list()
- MemberDaoImpl.java : MemberDao를 상속, MemberDao 의 추상메소드들을 모두 오버라이딩
- Member.java : DTO, 필드 id,pass,email,name,reg_date 와 각각의 getter/setter 메소드, toString() 메소드
- RegisterMember.java : DTO, 필드 pass, confirmpass, name, email 와 각각의 getter/setter 메소드, passCheck() 메소드
- DTO 객체가 2개이다
- RegisterMember.java DTO 클래스는 가입할때만 사용된다
Service 클래스와 DAO 클래스의 메소드 명이 같은 게 많다
- 같게 설정해서 어느 메소드에서 어느 메소드를 호출할지 알기 쉽게 해둔 것이다
ex) Service의 delete() 메소드에서 DAO 의 delete() 메소드를 호출
인터페이스를 사용하는 이유
- 예전에는 통일성있는 클래스를 작성하기 위해 인터페이스를 사용
- 지금은 메소드가 많아서 그걸 관리하기 위해 인터페이스를 사용한다 (어떤 메소드가 있는지 색인처럼 확인 가능)
- beans17.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 http://www.springframework.org/schema/context/spring-context-4.1.xsd">
<context:component-scan base-package="sample17"/>
</beans>
- 조건 3가지 중 첫번재를 만족한다, sample17 패키지를 읽어온다
- MemberServiceImpl.java
package sample17;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service("ms")
public class MemberServiceImpl implements MemberService {
@Autowired
private MemberDao md;
public int insert(RegisterMember rm) {
int result = 0;
Member member = md.selectByEmail(rm.getEmail());
if (member == null) {
member = new Member(rm.getPass(), rm.getEmail(), rm.getName(), new Date());
md.insert(member);
result = 1;
} else {
System.out.println("이미 데이터가 있습니다");
}
return result;
}
public Member select(String email) {
return md.selectByEmail(email);
}
public Collection<Member> list() {
return md.list();
}
public int delete(String email) {
int result = 0; // 데이터가 이미 있는지 확인
Member member = md.selectByEmail(email);
if (member != null) {
md.delete(email);
result = 1;
} else {
System.out.println("없는네 우찌 삭제하니");
}
return result;
}
public int update(RegisterMember rm) {
int result = 0; // 데이터가 이미 있는지 확인
Member member = md.selectByEmail(rm.getEmail());
if (member != null) {
member.setPass(rm.getPass());
member.setName(rm.getName());
md.update(member);
result = 1;
} else {
System.out.println("없는네 우찌 고치니 ? 헐");
}
return result;
}
}
@Service 어노테이션 (MemberServiceImpl.java 부분)
- 조건 3개 중 두번째 조건이다
- @Service 어노테이션 안에서 Service 객체명의 이름을 지정할 수 있다
- sample16 에서는 getBean() 으로 구해왔다
- 여기선 bean 을 직접 만들지 않았으므로 sample15 처럼 getBean(서비스클래스명.class) 로 구해와야한다
- 여기선 @Service 어노테이션 안에 Service 객체명(객체 id값)으로 "ms" 를 지정했으므로 main() 에서 Service 객체를 구해올때 getBean("ms") 로 구해올 수 있다
+ Ex01.java 의 main() 부분
- 만약 @Service("ms") 가 아닌 @Service 만 했다면 ac.getBean(MemberService.class) 로 Service 객체를 구해야한다
- 하지만 Service 클래스 상단에서 @Service("ms") 로 그 객체에 이름을 줬으므로 이름으로 getBean("ms") 불러옴
@Autowired 어노테이션 (MemberServiceImpl.java 부분)
- 조건 3개 중 세번째 조건이다
- DAO 객체를 주입받아야하는 프로퍼티 md 위에 @Autowired 를 작성
- 그럼 DAO 객체 생성과 md 로 주입까지 완료된다
- MemberDaoImpl.java
package sample17;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import org.springframework.stereotype.Repository;
@Repository
public class MemberDaoImpl implements MemberDao {
private Map<String, Member> map = new HashMap<String, Member>();
private static int nextId = 0;
public void insert(Member member) {
member.setId(++nextId);
map.put(member.getEmail(), member);
}
public Member selectByEmail(String email) {
return map.get(email);
}
public Collection<Member> list() {
return (Collection<Member>) map.values();
}
public void delete(String email) {
map.remove(email);
}
public void update(Member member) {
map.put(member.getEmail(), member);
}
}
@Reopsitory 어노테이션 (MemberDaoImpl.java 부분)
- 지금은 DAO 클래스에서 아무 객체도 주입받고 있지 않으므로 @Repository 가 없어도 된다
- 나중에 DAO 클래스에서 SqlSession 객체를 생성 해서 DAO의 프로퍼티로 주입할때는 @Repository 를 위에 붙여야한다
스스로 질문 & 스스로 답변
Q. Service 클래스 상단에서 @Service 만으로 썻으면 main() 에서 Service 객체 구해올때 getBean(Service클래스명.class) 로 구해왔었다, 근데 나중에 Web Application 를 해서 Controller -> Service 로 갈떄도 getBean(Service클래스명.class) 를 쓰는가?
A. 아니다, 지금 Application 에서는 main() 에서 Service 객체를 구해와야하므로 getBean() 을 쓰지만 나중에 Controller -> Service 로 갈때는 Controller 클래스에서 @Autowired 로 Service 객체를 바로 프로퍼티에 주입시키므로 그냥 바로 쓰면 된다, getBean() 으로 직접 구해올 필요 없이 이미 Spring 환경설정 파일을 읽어올때 다 생성되고 주입되었음, 주입된 Service객체를 바로 쓰면 됨
- 이렇게 @Autowired 로 Controller 의 프로퍼티 service 에 바로 Service 객체가 주입됨