구조

환경 설정


Chap01-bean-factory : section01
Application
package com.greedy.section01.xmlconfig;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
public class Application {
public static void main(String[] args) {
/* BeanFactory란?
* 스프링 컨테이너의 최상위 컨테이너이며, ApplicationContext와 함께
스프링 컨테이너라고 한다.
* Bean의 생성과 설정, 관리 등의 역할을 맡고 있다.
* */
/* MemberDTO(POJO) 클래스와 spring-context(Configuration) XML 설정 정보를 이용해서
* bean 등록, 생성 예제 확인
* */
/* ApplicationContext의 하위 구현체인 GenericXmlApplicationContext는
XML 설정 메타정보를 읽어와서
* BeanFactory를 동작 시킨다.
* GenericXmlApplicationContext는 설정 메타 정보가 담긴 XML 파일의 경로를
클래스패스 하위 경로 전부 기술해야 설정 파일을 읽어온다.
* */
ApplicationContext context
= new GenericXmlApplicationContext("com/greedy/section01/xmlconfig/spring-context.xml");
/* 1. bean의 id를 이용해 bean을 가져오는 방법
* id를 이용하는 경우 bean의 정확한 타입을 유추할 수 없기 때문에 Object 타입으로
반환하므로 다운캐스팅 필요
* */
//MemberDTO member = (MemberDTO)context.getBean("member");
/* 2. bean의 클래스 메타 정보를 전달하여 가져오는 방법
* 가져오려는 bean의 타입이 명확하기 때문에 형변환이 필요 없다.
* */
//MemberDTO member = context.getBean(MemberDTO.class);
/* 3. bean의 id와 클래스 메타 정보를 전달하여 가져오는 방법
* */
MemberDTO member = context.getBean("member", MemberDTO.class);
System.out.println(member);
}
}
- ApplicationContext은 BeanFactory 를 확장한 여러 인터페이스를 통합해서 상속받은 인터페이스
- 인터페이스이다보니 그 자체로 실질적인 어떤 구현체를 생성할 수 없음
- 해서 ApplicationContext 안에있는 강제화된 기능들을 구현해놓은 구현체인 GenericXmlApplicationContext
- GenericXmlApplicationContext는 XML 설정파일을 읽어서 구동이 되는 클래스

- 우선 Spring Context를 추가한다. 그 안에 ApplicationContext 과 GenericXmlApplicationContext 등이 정의되어있음
- mvn 레파지토리에서 라이브러리를 다운받아옴으로서 추가 해 볼 것이다.

- release 된 버전 중 가장 사용 빈도가 높은 5.2.9 버전을 클릭한 후 .jar 파일을 다운 받는다.
- * 유의할 점, Spring 모듈 끼리는 버전을 맞춰주어야 한다. 현재 5.2.9 버전을 사용한다면 앞으로 추가하는 모듈들 끼리의 버전은 5.2.9버전으로 맞춰 주어야 한다는 뜻이다. 그래야 충돌을 미연에 방지할 수 있다.


- 이후 프로젝트 내 lib라는 폴더를 만들어 다운 받은 파일을 옮겨 붙여 준다.


- 이렇게 해줄 시 물리적으론 jar 파일이 프로젝트 내 존재하는 것은 맞지만, 실질적인 이 프로젝트의 사용 라이브러리로 등록되어있는 것은 아니다.
- 프로젝트 우클릭 > Properties > Java Build Path 카테고리 > Library 탭 > Classpath 선택 > Add JARs... 선택


- 물리적으로 존재하는 폴더 내 .jar 파일을 선택 후 확인을 눌러준다.


- 라이브러리 빌드 등록 후 두가지 기능이 정상적으로 import 가 가능해지는 것을 확인할 수 있다.



- 생성을 할 인스턴트 쪽에 설정 메타정보를 기재해 준다.
- 여기에 기재된 정보는 spring-context.xml 로 만든 bean configuration 파일이다.
- IoC 컨테이너를 만들때 이러한 설정정보를 쓰라고 전달하는 의미이다.


- 그런데, 작성한 설정 정보에 에러줄이 발생한다. 왜일까? 이는 스프링의 구성모듈과 관련있다.

- 방금 Context를 추가를 했는데 다음으로 Context 내에서 Core 안에있는 어떤 기능을 쓰려고 한다.
- xml 파일이 읽어 올때, 리소스를 처리할 수 있는 기능이 이 Core 안에 기재되어있기 때문에 사실상 Context 만으로는 동작하기 어렵다.
- 즉, 좀 더 추가해야하는 기능들이 더 있다는 의미이다.

Spring의 구성모듈

Spring Core 추가
- 마찬가지로 mvn 레파지토리를 이용한다.
- 5.2.9 버전을 jar 파일을 동일하게 다운 받은 후 lib 물리 구조에 붙여넣기 한다.


- 추가이후 build path를 통해 라이브러리 등록을 해준다.


- 이후 사라진 에러를 확인할 수 있다.
- 이 설정정보를 전달받은 스프링 컨테이너가 생성될 수 있는지를 테스트 해보자.

콘솔창의 에러를 확인한다. 현재 springframework 아래 beans 라는 경로를 찾지못했음을 알 수 있다.


- Beans 가 없다는 의미이다.
- 이런식으로 콘솔창을 통해 어떤 기능이 포함되지못했는지 하나씩 유추해가는 방식이 필요하다.

- MVN 레파지 토리에서 Spring Beans를 가져온다.
- 버전을 맞추어 다운 받는 것에 유의한다.
- 앞서 했던 것과 마찬가지로 lib 에 다운받은 파일을 추가후 build path 에서 추가해준다.




- 추가 이후 다시 실행했더니 다음과같은 에러가 콘솔창에 떴다.

- 마찬가지로 MVN에서 commons logging을 가져오도록 한다.

- 1.2 버전을 다운받는다. 마찬가지로 빌드 패스까지 마무리 지어준다.


- 파싱처리에 필요한 Spring expression 이 없음을 확인할 수 있다.
- 마찬가지로 다운받은 후 빌드패스까지 해준다.




- 이와같이 라이브러리 추가를 완료한 후 실행해 주었을때 (이전 에러 출력값이 사라지고) 콘솔창에 아무것도 뜨지않음으로서 GenericXmlApplicationContext 의 객체가 성공적으로 생성되었음을 알 수 있다.

정리
- ApplicationContext의 하위 구현체인 GenericXmlApplicationContext는 XML 설정 메타정보를 읽어와서 BeanFactory를 동작 시킨다.
- GenericXmlApplicationContext는 설정 메타 정보가 담긴 XML 파일의 경로를 클래스패스 하위 경로 전부 기술해야 설정 파일을 읽어온다.
- 여기까지 왔을 때, 이 설정정보가 잘 만들어졌음을 어떻게 알 수 있을까?
- 현재 spring-context.xml 안에는 bean이 있는데, 이 bean 이 잘 등록되어, 생성되었는지를 확인 해본다.
- bean을 꺼내오는 첫번째) getBean 메소드
- bean에는 고유 아이디가 있는데, 이때 "member"라는 아이디를 가진 bean을 리턴하도록 한다.



- 타입 미스매치 에러가 뜬다. 이유는 고유 id만으로 타입을 유추할 수 없기 때문이다. (타입=/=아이디)
- 유추할 수 없는 타입은 Object로 리턴이 되기 때문에, 원하는 타입으로 리턴값을 담기위해서는 다운캐스팅이 필요하다.


- 객체가 잘 등록되어서 생성되었다는 것을 콘솔창에서 확인 해 볼 수 있다.

- 우리는 다음의 과정을 거치면서 MemberDTO member = new MemberDTO(1, "user01"...) 과 같은 객체를 생성한 적이 없다.
- 그러나 콘솔창을 보면 생성되었음을 알게되는데, 이건 누가 해준걸까? 바로 ApplicationContext 이다.
- spring-context 안에 MemberDTO를 다음과같이 등록한다는 정보를 작성해주었기 때문이다.
- 이로서 빈을 생성했고, 잘 읽어 올 수 있었음을 확인하였다.


Bean 가져오는 방법 세가지
1. bean의 id를 이용해 bean을 가져오는 방법
- id를 이용하는 경우 bean의 정확한 타입을 유추할 수 없기 때문에 Object 타입으로 반환하므로 다운캐스팅 필요
MemberDTO member = (MemberDTO)context.getBean("member");
2. bean의 클래스 메타 정보를 전달하여 가져오는 방법
- 가져오려는 bean의 타입이 명확하기 때문에 형변환이 필요 없다.
- 애초에 MemberDTO 타입의 bean을 달라는 의미이다.
- 즉, 반환타입이 Object 가 아니므로 다운캐스팅이 필요없다.

3. bean의 id와 클래스 메타 정보를 전달하여 가져오는 방법
MemberDTO member = context.getBean("member", MemberDTO.class);
System.out.println(member);
- 만약, MemberDTO 타입의 bean이 여러개라면, 2번의 방법이 무의미하다.
- 이럴때 빈의 아이디와 클래스 정보를 둘 다 기입한다. 역시 클래스를 기입해주었으므로 다운캐스팅은 필요없다.

정리
1. 제어의 역전을 구현하기 위해 Bean Factory에 빈을 등록하여 사용한다.
2. GenericXmlApplicationContext는 설정 메타 정보가 XML 에 만들어져있는 환경이다.
- XML에 설정 메타정보가 기입되었던것과 마찬가지로, java-config 파일에서도 똑같이 동작하는지 그 방법과 결과를 살펴보도록 한다.
- 테스트할 클래스를 만들어준다.

- 동일한 테스트를 위해 MemberDTO 파일을 복사해와 경로만 수정해준다.
- 이 DTO 파일이라는 오브젝트를 사용하기위해 설정 메타 정보라는 것이 필요하다.
- 아까는 그 정보를 XML에 파일에 작성했다면 이번엔 자바 클래스에 만들어 보도록 한다.

- 컨테이너를 만들때 전달할 설정정보를 정의할 클래스 정의

- 다음과같이 생성된 ContextConfiguration 파일에 @Configuration 이라는 어노테이션을 붙인다.
- 즉, 이 클래스가 설정 메타 정보를 가지고 있다는 의미를 가진 어노테이션을 추가한다.
- 컨테이너를 생성할 시 해당 어노테이션이 달린 클래스를 먼저 인식하여 컨테이너를 설정한다.

- xml 파일에서 빈을 등록했듯이, 자바 클래스 파일에서도 빈을 등록할 수 있어야 한다.
- 여기서는 '메소드' 형식을 사용한다.
- 리턴타입이 MemberDTO, 리턴타입의 객체에 원하는 정보를 new 생성자를 통해 설정한다.

- bean을 등록하기 위해서는 @Bean 어노테이션을 이용한다.
- 즉, 메소드 위에 'Bean'임을 알 수 있도록 @Bean 어노테이션을 붙여준다.

- 앞서 xml에서 빈의 정보를 id로 알았다. 여기서는 어떻게 해야 id의 역할을 할 수 있을까?
- 이름을 등록하기 위해 @Bean(name="member") 와 같이 사용한다.
- 만일 bean에대한 아이디를 지정하지 않으면 [메소드명]을 사용하게되니 유의한다.

- 다음과같이 작성이 되어야한다.
@Configuration
public class ContextConfiguration {
@Bean(name="member")
public MemberDTO getMember() {
return new MemberDTO(1, "user01", "pass01", "배산임수");
}
}
정리
- @Bean(name="myName") 혹은 @Bean("myName")을 이용하여 bean의 id를 설정할 수 있다.
- 이 때 bean의 이름을 지정하지 않으면 메소드의 이름을 bean의 id로 자동 인식한다.
- Application 에서실행하여 마찬가지로 빈이 잘 생성되고 등록되었는지 확인한다.
- 똑같이 스프링 컨테이너를 만들기 위해 ApplicationContext context 을 사용하는 것은 동일하다.
- 그러나 앞서 xml 파일로 테스트 했다면 이번엔 자바 클래스 파일을 통해서 테스트 하는 것이기 때문에 메타 정보를 연결하는 객체가 달라야 한다. 그러한 설정정보를 읽어왔을때 사용하는 구현체도 달라진다.
- 즉, GenericXmlApplicationContext 구현체가 아닌 AnnotationConfigApplicationContext 을 사용하게 된다.
- 여기에 방금작성한 설정정보가 담긴 ContextConfiguration.class 라는 클래스(타입)을 전달한다.

- AnnotationConfigApplicationContext라는 어노테이션 설정 정보를 읽어서 컨테이너 설정을 하는 구현체 이용한다.
- 인자로 @Configuration 어노테이션이 달린 설정 클래스의 메타 정보를 전달하여 인스턴스를 생성한다.
- Bean이 잘 등록됐는지 확인하기 위해 context를 통해 빈을 꺼내온다.
- 앞서 했던 빈을 꺼내오는 세가지 방법중 이름과 타입을 정확히 기입해서 꺼내오는 방법을 사용

- 위를 실행할시 콘솔창에 다음과같이 aop가 없음을 확인할 수 있다.
- AnnotationConfigApplicationContext 의 기능을 이용하기 위해서 스프링의 aop라는 기능이 필요함을 알 수 있다.

- 앞서 해왔던 대로 MVN 레파지 토리 -> Spring AOP 검색 -> 5.2.9 버전 다운 로드 -> Build Path 등록



- 라이브러리가 잘 build path 등록되었다면 콘솔창에서 다음과같은 결과를 확인할 수 있다.
- xml-config 에서 확인했던것과 마찬가지로 컨테이너에서 빈이 잘 생성되고 등록된것을 알 수 있다.

정리
- 제어의 역전, 빈 팩토리를 통해서 빈을 등록할 수 있다.
- 빈 등록은 xml 을 통한 방식도 있고 자바 클래스를 통한 방식도 있다.
- 각 방식에 따라서 실제 내부적인 구현체 역시 달라진다.
- 그렇지만 구현체가 달라져도 다음 사실은 동일하다 : 설정메타정보를 읽어와서 생성이 된 컨테이너 안에는 -개발자가 직접적으로 객체를 생성하는 것이 아니라- 컨테이너 안에서 관리되는 Bean 객체로서 존재를 하게 된다.

Configuration Metadata를 작성하는 방법에 대해 서술된 부분을 보자

- 다음으로 Component Scan을 통해 Bean이 등록되는 모습을 살펴보자.
- 특정 어노테이션을 이용해 Bean으로 등록해줄 것을 표시할 수 있는데 바로 @Component 이다.
MemberDTO
public class MemberDTO {
private int sequence;
private String id;
private String pwd;
private String name;
public MemberDTO() {}
public MemberDTO(int sequence, String id, String pwd, String name) {
super();
this.sequence = sequence;
this.id = id;
this.pwd = pwd;
this.name = name;
}
public int getSequence() {
return sequence;
}
public void setSequence(int sequence) {
this.sequence = sequence;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "MemberDTO [sequence="
+ sequence + ", id=" + id + ", pwd=" + pwd + ", name=" + name + "]";
}
}
spring-context.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">
<!-- MemberDTO bean 등록 -->
<bean id="member" class="com.greedy.section01.xmlconfig.MemberDTO">
<constructor-arg index="0" value="1"/>
<constructor-arg name="id" value="user01"/>
<constructor-arg index="2"><value>pass01</value></constructor-arg>
<constructor-arg name="name"><value>홍길동</value></constructor-arg>
</bean>
</beans>
Chap01-bean-factory : section02
Application
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Application {
public static void main(String[] args) {
/* AnnotationConfigApplicationContext라는 어노테이션 설정 정보를 읽어서
컨테이너 설정을 하는 구현체 이용한다.
* 인자로 @Configuration 어노테이션이 달린 설정 클래스의 메타 정보를
전달하여 인스턴스를 생성한다.
* */
ApplicationContext context
= new AnnotationConfigApplicationContext(ContextConfiguration.class);
MemberDTO member = context.getBean("member", MemberDTO.class);
System.out.println(member);
}
}
ContextConfiguration
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/* 이 클래스가 설정 메타 정보를 가지고 있다는 의미를 가진 어노테이션을 추가한다.
* 컨테이너를 생성할 시 해당 어노테이션이 달린 클래스를 먼저 인식하여 컨테이너를 설정한다.
* */
@Configuration
public class ContextConfiguration {
/* bean을 등록하기 위해서는 @Bean 어노테이션을 이용한다.
* @Bean(name="myName") 혹은 @Bean("myName")을 이용하여 bean의 id를 설정할 수 있다.
* 이 때 bean의 이름을 지정하지 않으면 메소드의 이름을 bean의 id로 자동 인식한다.
* */
@Bean(name="member")
public MemberDTO getMember() {
return new MemberDTO(1, "user01", "pass01", "배산임수");
}
}
MemberDTO
public class MemberDTO {
private int sequence;
private String id;
private String pwd;
private String name;
public MemberDTO() {}
public MemberDTO(int sequence, String id, String pwd, String name) {
super();
this.sequence = sequence;
this.id = id;
this.pwd = pwd;
this.name = name;
}
public int getSequence() {
return sequence;
}
public void setSequence(int sequence) {
this.sequence = sequence;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "MemberDTO [sequence=" + sequence + ",
id=" + id + ", pwd=" + pwd + ", name=" + name + "]";
}
}
파일구조

chap02-component-scan : section01
- 상단 테스트에서 진행했던 라이브러리를 그대로 가져와 Build Path 지정해주는 작업까지 완료한다.
- 코드 작성을 위해 다음과같은 javaconfig 클래스를 작성한다.

ComponentScan 기능을 이용한 bean 등록 설정해보기
- ComponentScan이란? base-package로 설정 된 하위 경로에 특정 어노테이션을 가지고 있는 클래스를 이용하여 bean으로 등록한다.
- @Component 어노테이션이 작성 된 클래스를 인식하여 bean으로 만들게된다.
- @Component는 좀 더 상위의 개념이며, 특수 목적에 따라 세부 기능을 제공하는 @Controller, @Service, @Repository, @Configuration 등을 인식한다.
- 즉, @Component 어노테이션이 작성된 클래스의 빈을 스캔하여 그것을 빈으로서 등록해주는 것이다.
- 테스트를 위한 설정 : MemberDTO 를 그대로 가져와 패키지만 변경해준다.

- 다음으로 MemberDAO라는 이름의 인터페이스를 작성한다.

- 인터페이스는 추상메소드를 작성하는 공간이며, 구조적인 부분을 강제화 할 수 있는 기능이다.
- 여기서는 필요로하는 두가지 기능을 작성해본다. 1) 회원정보 조회 메소드 2) 회원정보 저장 메소드

- 인터페이스에서 작성한 내용을 강제화하기 위한 기능인 MemberDAOImpl 을 만들어준다.
- 인터페이스의 장점 1) 인터페이스를 이용하면 기능을 구현할 것을 강제화할 수 있다는 점과, 2) 기능에 변화가 있을때마다 해당 인터페이스를 사용하는 클래스와의 결합도를 느슨하게 해준다. (유지보수 용이)

- 인터페이스로 메소드를 강제화 한 후 구현하여 사용하면 강제성이 부여 된다.
- 또한 결합 관계를 느슨하게 만들 수 있다.
- db 연동을 하는 것이 아니므로 데이터를 다음과같이 적어준다.

- 매개변수로 전달받은 회원 번호를 map에서 조회 후 회원 정보를 리턴해주는 용도의 메소드

- 매개변수로 전달받은 회원 정보를 map에 추가하고 성공 실패 여부를 boolean으로 리턴하는 메소드
- 전달받은 멤버의 정보중에서 시퀀스에 해당하는 값을 키값으로, 나머지 객체값(newMember)은 VALUE 값으로 해서 MAP 에 넣겠다는 것.
- MAP은 동일한 값은 중복저장하지 않으므로, 잘 저장되었는지는 MAP의 사이즈 (.size) 를통해서 알아보도록 한다.
- 사이즈가 늘어났다면 true 를, 그렇지않다면 false 를 반환할 것이다.

- 스프링 컨테이너가 스캐닝 기능을 이용하여 해당 클래스를 bean으로 등록할 수 있는 어노테이션을 설정한다.
- value 속성을 이용하면 bean의 id를 설정할 수 있으며, value는 생략 가능하다.
- 이름(id)를 설정하지 않으면 Class의 앞 글자를 소문자로 하여 bean을 생성한다.
- @Controller, @Service, @Repository와 동일한 기능을 가지지만 각 계층을 표현하는 어노테이션은 특정 용도에 맞는 부가적인 혜택이 있으니 게층별로 구분하여 사용하는 것이 좋다. (계층이 명확하지않다면 @Component를 사용해도 좋다.)

- 이때, VALUE 속성을 통해 해당 컴포넌트의 이름을 지어줄 수 있다.
- 만약, VALUE 속성을 지정해 주지않았다면? 마찬가지로 메소드 명을 빈의 이름으로 자동 지정될 것이다.
- memberDAOImpl > 앞글자를 소문자로 만들어 빈의 이름으로 등록했을 것이다.

- 다음으로 필요한 것은 설정파일이다.

- 설정파일임을 스프링 컨테이너에서 알 수 있도록 @Configuration 어노테이션을 붙여준다.
- 단, MemberDAOImpl 에 작성한 @Component 를 잘 스캔 해 빈으로 등록해주는지 확인하는 것이 이번 테스트의 목적이므로, 해당 설정파일에는 아무것도 적지않는다.

- Application1 으로 돌아온다. 만들어야 하는 것은 <스프링 컨테이너>에 해당하는 것이다.
- 어노테이션 기반으로 스프링 파일을 읽어와서 스프링 컨테이너를 만들겠다는 의미이다.
- 전달한 값은 따로 빈 등록을 하지않은 빈 껍데기 설정파일이다.
- 여기서 기대하는 것: @Component 어노테이션이 스캔시 '나를 빈으로 등록해라' 라고 하는 어노테이션이기 때문에 빈으로 잘 등록이 되는지 확인

- context 안에 담겨있는, 정의된 빈의 이름들을 배열로 가져온다.
- 해당 배열안에 어떤 빈들이 등록되어있는지는 반복문을 통해서 출력 해본다.

- 실행시 다음과같은 결과를 콘솔창에서 확인할 수 있다.
- 앞글자가 소문자로 바뀐 설정 파일 클래스는 확인할 수 있지만, 기대했던 클래스는 빈 등록이 되지않은 것 같다.
- 그렇다면 왜 등록이 되지않은 것일까?

- Context Configuration 은 어떤 기준으로 스캔을 하고 있는걸까?
- 본인이 현재 중재하는 경로 기준, 경로가 지엽적이라면 (base package를 설정하지않았다면) 본인의 패키지를 기준으로 스캔을 한다. 즉, 스캔이 안된것이 문제이다.
- 그렇다면 base-package에 대한 설정을 추가를 해주도록 한다.

- 다시 설정파일로 돌아와 다음과같은 어노테이션을 추가로 붙여준다. -> @ComponentScan
- basePackages에 등록되지 않은 패키지는 스캔에서 제외하고, 등록된 패키지 내의 @Component 어노테이션을 탐색한다.
- 이 때 basePackage를 등록하지 않으면 현 설정 클래스가 존재하는 패키지를 자동 basePackage로 설정한다.

- 위와같은 어노테이션을 붙여줌으로서, 명시적으로 탐색할 기준이 되는 베이스 패키지를 설정해 주게 되었다.
- 이제 config 폴더내 설정파일만 탐색하는 것이 아니라, javaconfig 하위의 모든 어노테이션을 탐색할 수 있게 되었다.

- 베이스 패키지 설정 후 다음과 같이 MemberDAO 가 빈으로 등록되었음을 확인할 수 있다.

- 빈등록이 잘 되었다면 좀 전에 작성한 메소드를 이용해보는 것도 가능하다.
- 1번 멤버 조회와 3번 멤버 인서트, 3번 멤버 조회를 차례로 작성한다.

- 콘솔창에 출력된 모습

ComponentScan
스프링 컨테이너는 빈들의 생명주기를 관리하며, 그러한 설정 정보를 설정하는 방식을 지금까지 살펴보았다.
ComponentScan 어노테이션에 basePackages 외에 더 추가할 수 있는 방법들을 알아보고자 한다.
테스트를 위해 별도의 경로를 작성해준다. Application2 라는 이름의 javaconfig 작성

- 테스트를 위한 ApplicationContext 를 생성한다.
- 앞선 테스트와같이 설정 정보를 저장하기 위한 Context Configuration를 전달한 뒤 에러밑줄을 선택해 만들어 준다.


- 만들어진 클래스에는 이것이 [설정정보]임을 나타내는 어노테이션 @Configuration 을 달아준다.
- 또한 basePackages는 다음과같이 작성한다.

- 추가할 수 있는 기능은 다음과같다. 베이스 패키지를 하나이상 설정할 수 있다는 것
- 어노테이션에 추가하는 것은 문법적으로 [ 콤마, ] 로 나타낸다.
- 만약 단일 값인 경우엔 패키지명을 포함한 클래스이름을 나열하면 되지만, 하나 이상의 베이스 패키지인 경우 {} 중괄호를 이용하여 나열한다. (중괄호=나열)

- 베이스 패키지를 설정하면 그 하위의 파일들은 전부다 스캔이 된다. 그러나 관련없는 클래스까지 스캔하는 것은 어떤 관점에서는 낭비이므로, 임의의 타입은 스캐닝에서 제외시킬 수 있다.
- excludeFilter로 스캐닝에서 제외할 타입을 기술하면 해당 타입은 스캐닝에서 제외한다.
1. 타입으로 설정
- ASSIGNABLE_TYPE이란 것은 타입을 지정한다는 것이다.
- 스캐닝에서 제외할 클래스를 다음과같이 작성해주면 된다.
- 단, 같은 패키지 하위에 같은 베이스 패키지를 기술하면 오류의 원인이 될 수 있으므로 사전에 중복된 베이스 패키지는 주석 처리한다.
@Configuration
@ComponentScan(basePackages="com.greedy.section01.javaconfig",
excludeFilters= {
@ComponentScan.Filter(
type=FilterType.ASSIGNABLE_TYPE,
classes= {MemberDAO.class}
})
public class ContextConfiguration2 {
}
- 앞선 설정파일의 빈 등록 코드를 그대로 가져와 실행해 주면 콘솔창에 다음과같이 뜬다.
- MemberDAO.class를 찾을 수 없다는 의미이므로, 컴포넌트 스캔에 잘 제외되었음을 알 수 있다.


2. 어노테이션 종류로 설정
- Component 어노테이션이 붙어있는 클래스를 스캔하지않겠다는 의미
@Configuration
@ComponentScan(basePackages="com.greedy.section01.javaconfig",
excludeFilters= {
@ComponentScan.Filter(
type=FilterType.ANNOTATION,
classes= {org.springframework.stereotype.Component.class}
})
public class ContextConfiguration2 {
}

3. 표현식으로 설정
REgular Expression 표현식으로 타입을 설정한 후, section01 하위의 모든 클래스를 제외하겠다는 의미
@Configuration
@ComponentScan(basePackages="com.greedy.section01.javaconfig",
excludeFilters= {
@ComponentScan.Filter(
type=FilterType.REGEX,
pattern= {"com.greedy.section01.*"}
public class ContextConfiguration2 {
}

정리
- 컴포넌트 스캔이라는 기술을 사용할 때, base package 하위의 모든 클래스를 스캔하는 것이 시간이 소요될 수 있으니 이 스캐닝과 관련이 없는 대상은 제외해주는 옵션을 사용할 수 있다. : [ , excludeFilters = { } ]
- 그 옵션을 사용하는 방법은 다양한데 세가지만 살펴보았다 : 1. 타입 2. 어노테이션 3. 표현식으로 설정
또 다른 테스트를 위해 같은 경로 내에 ContextConfiguration3 을 만들어준다.

- 설정 메타 정보 클래스를 만드는 것이 용도이다.
- 1) basePackages의 기본 설정 경로를 지정하고 2) useDefaultFilters를 false로 하면 모든 어노테이션을 스캔하지 않는다.
- 이 때 스캔할 대상 클래스만 따로 지정할 수 도 있다.
- excludeFilters 가 제외하는 방식이라면, includeFilters 는 포함하는 방식이다.
- exclude 필터 설정하는 방식과 동일하다
- useDefaultFilters=false : 기본적으로 필터링 하지 않음을 의미

- 확인 테스트를 위한 Application3을 만들고 코드는 (1,2) 그대로 유지한다.
- MemberDAO 만 빈으로 생성되고 등록되었음을 확인할 수 있다.



ContextConfiguration 을 XML 형태로 테스트
javaconfig / xmlconfig 호환해서 테스트 해보기 위함
다음 클래스를 작성한다.

- 앞서 작성한 기능을 그대로 사용하여 테스트 해보기 위해 section01 에서 위 코드르를 그대로 가져온다.
- 복사 붙여넣기 후 패키지명 변경

- xml 설정 정보를 만들기위해 Spring 컨테이너와 그 xml 을 읽어올 수 있는 GenericXmlApplicationContext를 생성한다.

- 설정 메타정보 경로 작성

- 경로를 작성한 메타 정보 파일은 실제 존재하지 않으므로 다음과같은 단계를 통해 만들어주도록 한다.



- 해당 xml 파일에 설정하고 싶은 정보는 다음과같다.
- @Component 혹은 @Repository 등이 기재된 클래스의 [빈스캐닝]을 해올 것
- 자바 config 에서 어노테이션을 붙여 작성했다면, xml에서는 태그를 이용해 다음과같이 작성한다.
- 컴파일 에러 줄이 뜨는 이유 : Context 의 sckima 스키마가 정의되어있지 않기 때문

- 에러 없애는 법은 다음과 같다.
- 하단의 Namespace 클릭 > beans 와 context 체크 선택 > 다시 Source 로 돌아와 에러가 사라진 것을 확인



- 스키마 정의 후 이전엔 존재하지 않았던 스키마정의들을 확인할 수 있다.
- context 스키마가 있어야 정의할 수 잇으므로 하단의 namespaces 탭에서 context 스키마를 추가하고 작성할 것

- 이러한 xml 에서의 설정은 하단의 자바 클래스에서 작성한 어노테이션과 의미적으로 다르지않다.

- 실행하면 다음과같은 콘솔창을 확인할 수 있다.
- xml 설정파일의 빈 스캐닝 하기위한 설정에 따라서 xmlconfig 하위의 클래스들을 스캐닝한 후,
- MemberDAO 에 작성된 어노테이션을 읽어 들여서 ApplicationContext context = ... 가 빈 등록후 생성을 한다.

- 앞서 했던 것과 마찬가지로, 자바 클래스가 아닌 xml config 파일에서 includeFilters 와 excludeFilters 를 어떻게 사용하는지 살펴본다.
- 1번 파일을 그대로 붙여넣기하여 2번 파일을 생성한다.

- context: 뒤에 올 알맞은 엘리먼트로 다음과같은 추천이 뜬다.

- exclude-filter 를 정의하기위해 다음과같이 작성한다.
- type = 타입이 무엇인지 / expression = "빈 스캐팅에서 제외할 경로"

- 위 xml 설정과 자바 config 에서 코드로 작성했던 내용을 비교대조 해볼 수 있다. 의미적으론 일치한다.

- 필터링에서 빠지는지 확인할 어플리 케이션 클래스 작성

- exclude 필터를 넣어 빈 스캐닝에서 제외된 것을 확인할 수 있다.


ContextConfiguration1
package com.greedy.section01.javaconfig.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
/* basePackages에 등록되지 않은 패키지는 스캔에서 제외하고, 등록된 패키지 내의
@Component 어노테이션을 탐색한다.
* 이 때 basePackage를 등록하지 않으면 현 설정 클래스가 존재하는 패키지를 자동
basePackage로 설정한다. */
//@Configuration
//@ComponentScan(basePackages="com.greedy.section01.javaconfig")
public class ContextConfiguration1 {
}
ContextConfiguration2
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import com.greedy.section01.javaconfig.MemberDAO;
/* excludeFilter로 스캐닝에서 제외할 타입을 기술하면 해당 타입은 스캐닝에서 제외한다. */
//@Configuration
//@ComponentScan(basePackages="com.greedy.section01.javaconfig",
// excludeFilters= {
// @ComponentScan.Filter(
// /* 1. 타입으로 설정 */
// //type=FilterType.ASSIGNABLE_TYPE,
// //classes= {MemberDAO.class}
// /* 2. 어노테이션 종류로 설정 */
// //type=FilterType.ANNOTATION,
// //classes= {org.springframework.stereotype.Component.class}
// /* 3. 표현식으로 설정 */
// //type=FilterType.REGEX,
// //pattern= {"com.greedy.section01.*"}
// )
// })
public class ContextConfiguration2 {
}
ContextConfiguration3
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import com.greedy.section01.javaconfig.MemberDAO;
/* basePackages의 기본 설정 경로를 지정하고 useDefaultFilters를 false로
하면 모든 어노테이션을 스캔하지 않는다.
* 이 때 스캔할 대상 클래스만 따로 지정할 수 도 있다.
* */
@Configuration
@ComponentScan(basePackages="com.greedy.section01.javaconfig",
useDefaultFilters=false,
includeFilters= { @ComponentScan.Filter(
/* exclude 필터 설정하는 방식과 동일하다 */
type=FilterType.ASSIGNABLE_TYPE,
classes= {MemberDAO.class}
)})
public class ContextConfiguration3 {
}
Application1
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.greedy.section01.javaconfig.config.ContextConfiguration1;
public class Application1 {
public static void main(String[] args) {
/* ComponentScan 기능을 이용한 bean 등록 설정
* ComponentScan이란?
* base-package로 설정 된 하위 경로에 특정 어노테이션을 가지고 있는
클래스를 이용하여 bean으로 등록한다.
* @Component 어노테이션이 작성 된 클래스를 인식하여 bean으로 만들게 되며
* 특수 목적에 따라 세부 기능을 제공하는
@Controller, @Service, @Repository, @Configuration 등을 인식한다.
* */
ApplicationContext context
= new AnnotationConfigApplicationContext(ContextConfiguration1.class);
String[] beanNames = context.getBeanDefinitionNames();
for(String beanName : beanNames) {
System.out.println("beanName : " + beanName);
}
MemberDAO memberDAO = context.getBean(MemberDAO.class);
System.out.println(memberDAO.selectMember(1));
System.out.println(memberDAO.insertMember(new MemberDTO(
3, "user03", "pass03", "새로운멤버" )));
System.out.println(memberDAO.selectMember(3));
}
}
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.greedy.section01.javaconfig.config.ContextConfiguration2;
public class Application2 {
public static void main(String[] args) {
ApplicationContext context
= new AnnotationConfigApplicationContext(ContextConfiguration2.class);
String[] beanNames = context.getBeanDefinitionNames();
for(String beanName : beanNames) {
System.out.println("beanName : " + beanName);
}
MemberDAO memberDAO = context.getBean(MemberDAO.class);
System.out.println(memberDAO.selectMember(1));
System.out.println(memberDAO.insertMember(new MemberDTO(
3, "user03", "pass03", "새로운멤버")));
System.out.println(memberDAO.selectMember(3));
}
}
Application3
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.greedy.section01.javaconfig.config.ContextConfiguration3;
public class Application3 {
public static void main(String[] args) {
ApplicationContext context
= new AnnotationConfigApplicationContext(ContextConfiguration3.class);
String[] beanNames = context.getBeanDefinitionNames();
for(String beanName : beanNames) {
System.out.println("beanName : " + beanName);
}
MemberDAO memberDAO = context.getBean(MemberDAO.class);
System.out.println(memberDAO.selectMember(1));
System.out.println(memberDAO.insertMember(new MemberDTO(
3, "user03", "pass03", "새로운멤버")));
System.out.println(memberDAO.selectMember(3));
}
}
interface MemberDAO
public interface MemberDAO {
/* 회원 번호로 회원 정보를 조회하는 메소드 */
MemberDTO selectMember(int sequence);
/* 회원 정보를 저장하고 결과를 리턴하는 메소드 */
boolean insertMember(MemberDTO newMember);
}
MemberDAOImpl implements MemberDAO
import java.util.HashMap;
import java.util.Map;
import org.springframework.stereotype.Repository;
/* 인터페이스로 메소드를 강제화 한 후 구현하여 사용하면 강제성이 부여 된다.
* 또한 결합 관계를 느슨하게 만들 수 있다.
* */
/* 스프링 컨테이너가 스캐닝 기능을 이용하여 해당 클래스를 bean으로 등록할
수 있는 어노테이션을 설정한다.
* value 속성을 이용하면 bean의 id를 설정할 수 있으며, value는 생략 가능하다.
* 이름(id)를 설정하지 않으면 Class의 앞 글자를 소문자로 하여 bean을 생성한다.
* @Controller, @Service, @Repository와 동일한 기능을 가지지만 각 계층을
표현하는 어노테이션은
* 특정 용도에 맞는 부가적인 혜택이 있으니 게층별로 구분하여 사용하는 것이 좋다.
* */
//@Component(value="memberDAO")
@Repository(value="memberDAO")
public class MemberDAOImpl implements MemberDAO{
private final Map<Integer, MemberDTO> memberMap;
public MemberDAOImpl() {
memberMap = new HashMap<>();
memberMap.put(1, new MemberDTO(1, "user01", "pass01", "홍길동"));
memberMap.put(2, new MemberDTO(2, "user02", "pass02", "유관순"));
}
/* 매개변수로 전달받은 회원 번호를 map에서 조회 후 회원 정보를
리턴해주는 용도의 메소드 */
@Override
public MemberDTO selectMember(int sequence) {
return memberMap.get(sequence);
}
/* 매개변수로 전달받은 회원 정보를 map에 추가하고 성공 실패 여부를
boolean으로 리턴하는 메소드 */
@Override
public boolean insertMember(MemberDTO newMember) {
int before = memberMap.size();
memberMap.put(newMember.getSequence(), newMember);
int after = memberMap.size();
return (after > before) ? true : false;
}
}
MemberDTO
public class MemberDTO {
private int sequence;
private String id;
private String pwd;
private String name;
public MemberDTO() {}
public MemberDTO(int sequence, String id, String pwd, String name) {
super();
this.sequence = sequence;
this.id = id;
this.pwd = pwd;
this.name = name;
}
public int getSequence() {
return sequence;
}
public void setSequence(int sequence) {
this.sequence = sequence;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "MemberDTO [sequence="
+ sequence + ", id=" + id + ", pwd=" + pwd + ", name=" + name + "]";
}
}
chap02-component-scan : section02
spring-context1.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.3.xsd">
<!-- context 스키마가 있어야 정의할 수 잇으므로 하단의 namespaces 탭에서
context 스키마를 추가하고 작성할 것 -->
<context:component-scan base-package="com.greedy.section02.xmlconfig"/>
</beans>
spring-context2.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.3.xsd">
<!-- context 스키마가 있어야 정의할 수 잇으므로 하단의 namespaces 탭에서
context 스키마를 추가하고 작성할 것 -->
<context:component-scan base-package="com.greedy.section02.xmlconfig">
<context:exclude-filter type="assignable"
expression="com.greedy.section02.xmlconfig.MemberDAO"/>
</context:component-scan>
</beans>
Application1
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
public class Application1 {
public static void main(String[] args) {
ApplicationContext context
= new GenericXmlApplicationContext("com/greedy/section02/xmlconfig/config/spring-context1.xml");
String[] beanNames = context.getBeanDefinitionNames();
for(String beanName : beanNames) {
System.out.println("beanName : " + beanName);
}
MemberDAO memberDAO = context.getBean(MemberDAO.class);
System.out.println(memberDAO.selectMember(1));
System.out.println(memberDAO.insertMember(new MemberDTO(
3, "user03", "pass03", "새로운멤버")));
System.out.println(memberDAO.selectMember(3));
}
}
Application2
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
public class Application2 {
public static void main(String[] args) {
ApplicationContext context
= new GenericXmlApplicationContext("com/greedy/section02/xmlconfig/config/spring-context2.xml");
String[] beanNames = context.getBeanDefinitionNames();
for(String beanName : beanNames) {
System.out.println("beanName : " + beanName);
}
MemberDAO memberDAO = context.getBean(MemberDAO.class);
System.out.println(memberDAO.selectMember(1));
System.out.println(memberDAO.insertMember(new MemberDTO(
3, "user03", "pass03", "새로운멤버")));
System.out.println(memberDAO.selectMember(3));
}
}
MemberDAO
public interface MemberDAO {
/* 회원 번호로 회원 정보를 조회하는 메소드 */
MemberDTO selectMember(int sequence);
/* 회원 정보를 저장하고 결과를 리턴하는 메소드 */
boolean insertMember(MemberDTO newMember);
}
MemberDAOImpl implements MemberDAO
import java.util.HashMap;
import java.util.Map;
import org.springframework.stereotype.Repository;
/* 인터페이스로 메소드를 강제화 한 후 구현하여 사용하면 강제성이 부여 된다.
* 또한 결합 관계를 느슨하게 만들 수 있다.
* */
/* 스프링 컨테이너가 스캐닝 기능을 이용하여 해당 클래스를 bean으로 등록할 수 있는
어노테이션을 설정한다.
* value 속성을 이용하면 bean의 id를 설정할 수 있으며, value는 생략 가능하다.
* 이름(id)를 설정하지 않으면 Class의 앞 글자를 소문자로 하여 bean을 생성한다.
* @Controller, @Service, @Repository와 동일한 기능을 가지지만 각 계층을
표현하는 어노테이션은
* 특정 용도에 맞는 부가적인 혜택이 있으니 게층별로 구분하여 사용하는 것이 좋다.
* */
//@Component(value="memberDAO")
@Repository(value="memberDAO")
public class MemberDAOImpl implements MemberDAO{
private final Map<Integer, MemberDTO> memberMap;
public MemberDAOImpl() {
memberMap = new HashMap<>();
memberMap.put(1, new MemberDTO(1, "user01", "pass01", "홍길동"));
memberMap.put(2, new MemberDTO(2, "user02", "pass02", "유관순"));
}
/* 매개변수로 전달받은 회원 번호를 map에서 조회 후 회원 정보를
리턴해주는 용도의 메소드 */
@Override
public MemberDTO selectMember(int sequence) {
return memberMap.get(sequence);
}
/* 매개변수로 전달받은 회원 정보를 map에 추가하고 성공 실패 여부를
boolean으로 리턴하는 메소드 */
@Override
public boolean insertMember(MemberDTO newMember) {
int before = memberMap.size();
memberMap.put(newMember.getSequence(), newMember);
int after = memberMap.size();
return (after > before) ? true : false;
}
}
MemberDTO
public class MemberDTO {
private int sequence;
private String id;
private String pwd;
private String name;
public MemberDTO() {}
public MemberDTO(int sequence, String id, String pwd, String name) {
super();
this.sequence = sequence;
this.id = id;
this.pwd = pwd;
this.name = name;
}
public int getSequence() {
return sequence;
}
public void setSequence(int sequence) {
this.sequence = sequence;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "MemberDTO [sequence="
+ sequence + ", id=" + id + ", pwd=" + pwd + ", name=" + name + "]";
}
}
'Programming > Spring Framework' 카테고리의 다른 글
| 5. Spring AOP (1) (0) | 2022.04.11 |
|---|---|
| 4. Spring DI 관리 (0) | 2022.04.07 |
| 3. Spring IOC (1) (0) | 2022.04.06 |
| 2. Spring Framework (0) | 2022.04.06 |
| 1. Apache Maven (0) | 2022.04.06 |




































