본문 바로가기

스프링

IOC_DI를위한_빈의존관계설정방법_XML,애노테이션,java_5

* 빈 의존관계 설정 방법

1) 명시적으로 구체적인 빈을 지정하는 방법(ID 직접 지정)

2) 일정한 규칙에 따라 자동으로 선정하는 방법(자동와이어링 aurowiring)

 

* 메타정보 작성 방법으로 빈 의존관계 설정방법 구분

- XML <bean> 태그

- 스키마를 가진 전용태그

- 애노테이션 빈스캐너

- 자바코드에 의한 직접적인 DI

 

XML <bean>을 이용해 주입방법

 

<property>

빈을 주입시 ref 태그 사용 일반 값은 value(정수,실수,스트링 등 기본값 및 다양한 클래스 등록 가능) 사용. 수정자의 타입에 맞게 적절하게 셋팅.

 

프로퍼티 타입이 String이라면 간단하다. XMLvalue 애트리뷰트 값도 스트링이기 때문이다.

Aop pointcut 애트리뷰트 또한 스트링 타입의 값이다.

Int, float, double, Boolean 이나 ClassResource 같은 타입은 value 애트리뷰트 값을 바로 넣어줄 수 없으니 적절한 변환이 필요하다.

 

* 빈이 사용해야 할 단순한 값이나 오브젝트를 코드에 담지 않고 설정을 통해 런타임시 주입해 주는 이유는 무엇일까??

> 환경에 따라 매번 달라질 수 있는 값. 대표적으로 DataSourceDriverClass, URL, Name, Password를 들 수 있다. 환경에 의존적인 정보를 소스코드의 수정 없이 지정해 주기 위함.

> 설정이 바뀌더라도 소스코드를 다시 컴파일 하지 않아도 된다.

> 값의 경우 빈의 의존관계와 달리 자동와이어링 같은 방법이 없기 때문에 항상 XML같은 외부 설정 리소스를 사용하는 것이 마땅하다.

 

<constructor-arg> : 생성자 주입

10-40. 중복되는 타입이 없을 시 사용 가능

* 생성자 주입시에는 실수로 파라미터 순서가 뒤바뀌지 않도록 주의해야함.

 

XML : 자동와이어링

> XML 문서의 양을 대폭 줄여줄 수 있는 획기적인 방법이다. 하지만 그만큼 위험부담이 따르므로 주의하자.

> 프로퍼티나 생성자 파라미터를 지정하지 않고, 미리 정해진 규칙을 이용해 자동으로 DI 설정을 컨테이너에 추가하는 방법.

10-43. 보통 빈의 이름(id)는 클래스 이름이나 빈이 구현한 대표적인 인터페이스 이름을 따른다. StringPrinter 클래스는 Printer 인터페이스를 구현한 것이다. 이럴 때 빈의 이름은 printer를 이용한다. Hello 빈의 printer 프로퍼티도 같은 인터페이스 타입으로 선언되어 있다. 타입이 같은 빈이 여러 개 존재해서 각각을 구분해야 하는 드문 경우를 제외하면 대부분 이런 단순한 명명규칙을 따른다.

10-44. byname: 이름에 의한 자동 와이이링. ref 로 셋팅해주는 부분이 생략됐다. 10-43과 비교했을 때 printer 프로퍼티 선언은 생략되었지만 <bean> 옵션으로 준 autowire=”byName”에 의해 스프링은 Hello 클래스에 setPrinter() 메소드가 있고 이름이 같은 printer 빈이 있으니 printer 빈을 DI 한다고 판단한다.

10-45. byType: 타입에 의한 자동 와이어링. 이름을 이용한 자동와이어링은 명명 규칙을 엄격하게 지켜야한다는 부담이 있다. 규모가 큰 프로젝트에서 정확하게 이름을 부여하기 어렵다면 타입에 의한 자동와이어링을 사용한다. Autowire=”byType” 이나 default-autowire=”byType”을 넣어주면 된다.

 Hello 클래스에는 printer라는 이름의 프로퍼티가 있는데, 사용하고 싶은 빈의 이름은 mainPrinter. 이런 경우 타입에 의한 자동와이어링을 사용한다. 단 타입(interface)이 같아야 한다.

* 단점

> 타입이 같은 빈이 두 개 이상 존재하는 경우에는 적용되지 못한다.(스프링이 못찾음)

> 이름에 의한 자동와이어링은 프로퍼티 이름과 빈의 아이디만 비교하면 되기 때문에 상대적으로 빠르고, 타입은 느리다.

> 빈이 많아지고 프로퍼티가 늘어나면 비교작업에 적지 않은 시간이 소요된다.

> 하나의 빈에 이름/타입에 의한 자동와이어링 방식 2가지를 동시에 적용할 수 없다.

* 생성자에 자동와이어링을 적용하려면 autowire=”construtor” 애트리뷰트를 이용하면 된다.

* 보통 클래스당 하나의 빈이 등록되는 DAO나 서비스 계층 빈은 자동와이어링이 편하다.

 

주입되는 정보를 구체적으로 몰라도 되고, 장점이 많지만 XML 안에서 잘 파악되지 않는 방식은 적용하지 않는게 좋다. 가능한 기술 서비스나 기반 서비스의 경우 id를 이용해 명시적으로 선언하는 것이 바람직하다.

29. <aop:config> : AOP 설정을 담는 부모태그. 필요에 따라 AspectJAdvisorAutoProxyCreator를 빈으로 등록해 사용함.

39. <aop:pointcut> : expression의 표현식을 프로퍼티로 가진 AspectJExpressionPointcut을 빈으로 등록함.

40. <aop:advisor>: advicepointcutref를 프로퍼티로 갖는 DefaultBeanFactoryPointAdvisor를 등록.

 

@Resource

> XML 설정시 <property>를 사용하여 수정자 또는 생성자와 같은 특정 메소드를 이용해 DI 하였지만, @Reource 애노테이션을 사용하면 수정자 메소드 없어도 직접 내부 필트에 DI 할 수 있다.

> 위 스샷은 XML<property> 설정 없이도 수정자 메소드에 DI를 하는 것과 같다.

 

* 필드에 붙어 있을 때는 수정자가 없어도 DI

> 수정자가 없으므로 테스트처럼 컨테이너 밖에서 수동DI가 불편하다. 단위 테스트가 필요한 환경에서는 필드 주입은 바람직하지 못한 방법이다.

> 컨테이너를 활용한 컨테이너 통합테스트를 주로 하는 DAO에서는 수정자 없이 필드 주입만을 사용해도 코드가 간결해져서 바람직한 방법이라고 볼 수 있다.

> 함께 모여있는 필드에 @Resource를 붙이는 것이 상대적으로 멀리떨어져 있는 수정자 주입보다는 DI 구조를 파악하는 것이 좋다.

* name을 생략하면 DI할 빈의 이름이 프로퍼티나 필드 이름과 같다고 가정한다.

> name을 지정하지 않으면 이름으로 찾고 없는 경우 타입으로 찾는다. 권장하지 않는 방법.

> 생략을 하는 경우는 맨아래처럼 빈을 찾는 것이 아닌 현재 컨텍스트에 대한 레퍼런스 정보를 제공해주는 ApplicationContext에 붙여주는 것은 적합하다.

* XML, Resource 이름 없는 자동와이어링 차이점

> XML 자동와이어링은 각 프로퍼티에 주입할 후보 빈이 없을 경우 무시하고 지나간다.

> 애노테이션은 DI 할 빈이 없는 경우 예외가 발생한다.

 

@Resource 애노테이션으로 DI가 이뤄지려면 위와 같은 세가지 방법 중 하나를 선택해야함.

Context:annotation-config, context:component-scan, AnnotaionApplicationContext

* AnnotaionApplicationContext

- 빈 스캐너와 애노테이션 의존관계 정보를 읽는 후처리기를 내장한 애플리케이션 컨텍스트 사용.

 

* Context:annotation-config

- @Resource와 같은 애노테이션 의존 관계 정보를 읽어서 메타정보를 추가해주는 기능을 가진 빈 후처리기를 등록하는 전용태그

- , XML에 등록되어 있는 빈 hello, printer 의 애노테이션 활성화를 위해 사용됨.

 

* context:component-scan

- 빈 스캐닝을 통해 빈을 등록 후(애노테이션 포함) 빈 후처리기로 애노테이션에 해당하는 빈 주입 및 생성

 

@Autowired

> 빈 오브젝트를 모두 생성한 후 후처리기에 의해 의존관계가 설정됨.

> 필드와 수정자 메소드는 @Resource와 사용방법이 비슷하다.

> @Resource와 다른 점은 이름 대신 필드나 프로퍼티 타입을 이용해 후보 빈을 찾는다, 또한 생성자에도 부여할 수 있다(하나의 생성자에만 가능).

> 생성자 주입을 선호한다면 사용하지만 단점 존재

- 수정자 메소드를 만들어야 하며, DI 항목을 잊을 수 있기 때문에 생성자를 추구하는 개발자들이 있다.

- 하지만 생성자 방식을 사용해서 모든 프로퍼티를 다 주입하지 않고 일부만 DI할 수도 있고, 오브젝트 생성 이후에 설정 값을 넣어야 하는 경우도 있으므로 수정자를 통한 DI가 적절하다.

> 스프링에서 가장 유연하면서 가장 강력한 기능을 가진 의존관계 설정 방법. 때로는 같은 타입이 여러 개 존재해서 @Qualifier@Resource를 사용해야 하지만 대개는 클래스나 인터페이스당 하나의 빈이 등록되므로 @Autowired 만으로 충분하다.

 

* 생성자, 수정자 메소드 주입은 각기 장단점이 존재하므로 등장한 메소드 주입방식

> 위처럼 인자값이 여러 개여도 타입에 맞게 주입되므로 코드가 깔끔해진다.

> , 이렇게 만들어진 클래스는 XML을 통해서는 의존관계를 설정할 방법이 없다는 점을 주의해야 한다.

> DI 설정용 메소드는 필드의 경우와 마찬가지로 public일 필요는 없다. 하지만 컨테이너 밖에서 테스트 등을 목적으로 직접 DI 해야하는 경우를 고려한다면 public으로 만들어두는게 좋다.

> 타입에 의한 자동와이어링은 타입이 동일한 빈이 여러 개일 가능성이 있기 때문에 주의해야한다.

 

Printer 인터페이스를 구현한 빈이 두 개 이상 존재한다고 했을 때, 위처럼 사용한다.

> 같은 타입의 빈이 여러 개 등록되는 경우 충돌을 피하려는 목적 보다는 의도적으로 타입이 같은 여러 개의 빈을 등록하고 이를 모두 참조하거나 그중에 선별적으로 빈을 찾을 때 사용하는 것이 좋다.

> 빈 자체가 컬렉션인 경우 @Resource를 이용해야 한다.

> 컬렉션인 경우에도 최소한 한 개의 타입이 일치하는 빈이 있어야함. 없을 시 에러발생

 

@Qualifier

> 위와 같은 상황에서는 Autowired 대신 Resource를 사용하는 것도 해결책이지만

> 보통 빈의 이름은 환경이나 특정 기술을 따라가는 경우가 많다. 위의 <bean> 선언처럼 DB종류에 따라 이름을 결정하는 경우가 많고, 빈 이름은 변경되기 쉽고, 이름 자체로 어떤 의미를 부여하기가 쉽지 않으므로 빈 이름과는 별도로 추가적인 메타정보를 지정해서 의미를 부여해놓고 이를 @Qualifier를 사용하는게 직관적이고 깔끔하다.

> qualifier를 가진 빈이 없는 경우 mainDB라는 이름의 빈이 있는지 한번 더 확인하여 있으면 DI 대상으로 선택한다. , 이름에 의한 빈 선택이 가능하지만 권장되지 않는다. 코드의 혼란을 줄 수 있기 때문에

> DI할 후보 빈이 존재하지 않으면 @Resource와 마찬가지로 빈을 찾을 수 없다는 에러가 발생한다.

 

XML이 아닌 @Componet를 이용해 Java코드 이용시에는 위와 같이 부여해주면 된다.

 

커스텀 한정자 애노테이션을 통해 사용해 좀 더 의미있는 DI 대상을 명시할 수 있다.

 

@Qualifier는 부여 대상이 필드와 수정자, 파라미터 뿐이다. 생성자와 일반 메소드의 경우 각 파라미터마다 하나의 빈이 매핑되기 때문에 이때는 생성자 파라미터가 여러 개일 경우 위와 같이 애노테이션을 지정한다.

 

@Autowired(required=false) : 타입에 의한 빈이 없더라도 상관없다면 위와 같이 선언한다.

 

학습테스트 생성시 getBean()을 사용하면 기존적으로 Object 타입을 리턴한다. 때문에 getBean(“빈 이름”, class)를 사용했다. 위와 같이 타입이 한 개라면 class 만으로 빈을 불러올 수 있지만 @Autowired처럼 @Qualifier를 사용할 수 없으므로 빈 이름을 적어줘야 했다.

 

@Autowired 등의 애노테이션으로 원하는 의존관계 메타정보가 생성되고 DI 될 것인지 확인해보는 습관이 중요하다.

> XML<bean>을 등록하거나 빈 스캐너를 이용해서 @Component가 붙은 클래스를 자동 등록하는 것도 방법이지만, 테스트를 위해 매번 빈 클래스나 XML을 생성하기 보다는 테스트 클래스 안에 스태틱 멤버 클래스로 애노테이션이 달린 클래스를 등록해놓고 이름 XML 없이 직접 컨텍스트에 빈으로 등록해서 사용하는 방법이 유용하다.

 

* 자바 코드에 의한 의존관계 설정

- Java Config에 의해 빈이 생성되고 @Autowired에 의해 후처리기로 의존관계가 설정됨. Java코드로도 애노테이션을 통해서도 주입을 통해 의존관계를 지정할 수 있다.

10-57. Java코드로 DI하는 가장 직관적인방법(066.IOC_DI를위한_빈설정메타정보작성_Java, Configuration,Bean 편 참조)

 

10-57에서의 메소드로 정의된 다른 빈을 가져와 의존정보를 생성시의 단점을 극복하기 위해 사용.

> 메소드의 호출이 자바코드에서 예상되는 것과 다르게(싱글톤 방식의 빈생성) 동작하는 부자연스러움이 불편할 때 사용하는 방식

> @Qualifier를 파라미터에 추가해도 되며, @Configuration이 없는 클래스안에 정의되어도 상관없다.

 

 

빈 의존관계 설정 전략

 

XML 단독 설정

 

 

@Value

1) @Value(“Everyone”) <property .. value=”Everyone”>과 동일하다.

 - 소스코드에 값을 직접 넣어서 변경될 때 다시 컴파일을 해야하는데 이걸 써야할까???

> @Value 애노테이션은 값을 넣어주는 기능이라고 오해하면 안된다. 컨테이너가 런타임시

  주입하는 정보로 봐야한다.

> 위처럼 직접 값을 지정하는 방법은 잘 사용되지 않는다.

 

 

위처럼 리소스나 환경정보에 담긴 값을 사용하도록 지정해주는데 있다. 때문에 설정과 소스를 분리시켜 XML에 설정을 두는 것과 다를 바 없이 편리하게 사용할 수 있다.

@Value는 필드, 수정자, 메소드 파라미터에 사용할 수 있다.

 

자바 코드에서도 @Value 애노테이션 설정이 가능하다. 이것 역시 설정을 변경한다해도 코드를 수정하고 재컴파일하는게 문제가 되지는 않는다.

 

PropertyEditor (내장되어 있어 그냥 사용하면 됨)

> XMLvalue 애트리뷰트나 @Value의 엘리먼트는 모두 텍스트 문자로 작성된다.

> String외의 타입인 경우 타입을 변환할 때 사용된다.

 

Java에서 만든 PropertyEditorSpring에서 구현한 구현체

기본타입외에도 스프링은 문자열을 변환해주는 Editor를 지원한다. (PropertyEditor)

> 멀티스레드 환경에서는 사용못함. 도메인 객체 등에서나 사용 가능.

 

 

나머지 getter,setter

 

PropertyEditor 각 기능을 등록

 

String 값이지만

 

프로퍼티 정보에 맞게 값이 셋팅됨

 

* ConversionServiceFactoryBean

> PropertyEditor와는 달리 작성이 간편하고, 멀티스레드 환경에서 공유해서 사용가능.

> 컨테이너가 스프링 빈의 값을 주입하는 작업에는 기본 변환기인 PropertyEditor로 충분하다.

Collection (컬렉션) 주입

> 스프링은 List, Set, Map, Properties와 같은 컬렉션 타입을 XML로 작성해서 프로퍼티에 주입하는 방법을 제공한다.

> <property>value 애트리뷰트가 생략된다.

 

List, set

 

map

 

Properties props

 

컬렉션에는 value 뿐만 아닌 빈의 레퍼런스에 대한 정의가 가능하다.

 

List는 독립적인 빈으로 생성해 줄 수 있다. 이때 XML에서는 <util:list>, <util:set> 을 사용한다. 또한 리스트에 대한 구현 클래스를 직접 지정할 수도 있다.

 

Util:map, util:properties

Map의 구현클래스 및 독립적인 빈생성 가능.

프로퍼티의 경우 외부 파일을 지정해서 사용할 수 있음.

 

빈 문자열은 “” 로 사용한다. Null을 넣을 때는 <null /> 을 넣어준다.

 

* 프로퍼티 properties 파일을 이용한 값 설정

> Spring XML설정은 애플리케이션 구조가 바뀌지 않는 이상 자주 변경되지 않는다. 반면에 설정정보는 환경에 따라서 자주 바뀔 수 있다. 변경되는 이유와 시점이 다르다면 분리하는 것이 객체지향 설계의 기본 원칙이다.

 

18. Context:property-placeholder 를 사용하여 프로퍼티파일 위치지정. 이제 스프링 컨테이너는 초기화 작업 중에 database.properties 파일을 읽고, 각 키에 ${}을 붙인 값과 동일한 value 선언을 찾는다. 그리고 발견된 value의 값을 프로퍼티 파일에 정의해둔 값으로 바꿔치기함.

> PropertyPlaceHolderConfigurer 바꿔치기

- context:property-placeholder 태그에 의해 자동으로 등록되는 빈. 위 빈에 의해서 바꿔치기됨.

- 빈 팩토리 후처리기.

- 빈 후처리기는 매 빈 오브젝트가 만들어진 직후에 오브젝트의 내용이나 오브젝트 자체를 변경할 때 사용된다. 반면에 빈 팩토리 후처리기는 빈 설정 메타정보가 모두 준비됐을 때 메타정보 자체를 조작하기 위해 사용된다.

> @Value를 통해서도 변경 가능.

 

* 능동 변환: SpEL(Spring Expression Language)

> 다른 빈 오브젝트나 프로퍼티에 쉽게 접근하여 값을 대체시킨다. (메소드, 생성자 호출가능)

 

Util:properties > Properties 타입 빈을 생성사 SpEL을 이용해 사용 가능(xml, @Value)