@Component
기본 스테레오타입
이 애노테이션을 붙인 클래스는 스프링 빈이 된다.
빈의 기본 행위를 변경하지 않지만, 스피링 차기 버전에서 부가적인 의미를 부여할 것임
@Controller
스프링은 이 스테레오타입 붙은 클래스를 스프링 MVC 지원에서 컨트롤러로 사용한다.
@Repository
이 애노테이션을 가진 클래스는 저장소를 나타낸다.
(예를 들어, DAO 등)
자동 예외 변환을 해 줌
@Service
이 애노테이션은 애플리케이션에서 비즈니스 로직을 구현한 클래스에 붙인다.
빈의 기본 행위를 변경하지 않지만, 스피링 차기 버전에서 부가적인 의미를 부여할 것임
출처 - http://kerrigancap.truesolution.co.kr/ezboard.php?BID=study&GID=root&UID=1996&mode=view
applicationContext를 xml이 아닌 애노테이션으로 지정하는 방법이 있다.
스프링은 기본적으로 4가지 타입의 애노테이션을 지원하며
원하는 경우 확장해서 자신의 애노테이션을 지정할 수 있다.
@Component : 가장 기본적인 애노테이션 (아래 해당 사항이 없는 경우)
@Repository : 데이터 액세스 계층의 DAO또는 리포지토리 클래스 (http://smack.kr/300)
@Service : 서비스 계층의 클래스에 사용
@Controller : 프레젠테이션 계층의 MVC 컨트롤러에 사용
출처 - http://bluesky.thoth.kr/?document_srl=7067643
없으면 에러 낼거야
이건 2.5이전부터 있던건데 그냥 한번 짚고 넘어 갑니다.
@Required 가 붙은 setter 메소드는 반드시 XML 설정에 의존성 삽입이 정의 되어 있어야 합니다.
(@Autowired도 안됩니다.)
XML 설정에 명시적인 의존성 삽입이 없다면 런타임 예외가 던져 집니다.
이 애노테이션을 처리하기 위한 후처리기는 다음과 같습니다.
자동으로 묶어 줄게
@Autowried 는 타입에 일치하는 bean을 의존성 삽입 해줍니다.
필드에 사용 할 경우 setter가 존재하지 않아도 됩니다.
private Foo foo;
여러개의 인자를 가진 메소드에 사용 할 수 있습니다.
public void setUp(Foo foo, Bar bar) {
this.foo = foo;
this.bar = bar;
}
@Autowired의 속성인 required의 기본값이 true이기 때문에 발견되는 bean이 없다면 예외가 던져 집니다.
발견 되는 bean이 없더라도 예외가 던져지지 않기를 원하면 required 속성을 fasle로 설정하면 됩니다.
private Foo foo;
일치하는 타입을 찾기 때문에 발견되는 bean이 여러개인 경우 예외가 던져 집니다.
이를 방지 하기 위해 <bean /> 요소의 추가된 속성인 primary를 설정 합니다.
발견되는 bean이 여러개 일지라도 primary가 설정된 bean이 참조 됩니다.
<bean id="foo2" class="example.Foo"/>
이 애노테이션을 처리하기 위한 후처리기는 다음과 같습니다.
정확한 이름을 알려줄게
@Autowired을 사용 할 때 타입으로 bean을 찾기 때문에 여러개의 bean이 발견되면 primary로 해결 했습니다. 하지만 @Qualifier를 이용해 찾으려는 bean의 id를 넘길 수 있습니다.
@Qualifier("foo")
private Foo foo;
필드에만 사용 가능 하기 때문에 메소드에 적용시 다음과 같이 설정 합니다.
public void setUp(@Qualifier("foo") Foo foo, @Qualifier("foo2") Foo foo2) {
this.foo = foo;
this.foo2 = foo2;
}
이 애노테이션을 처리하기 위한 후처리기는 @Autowried의 후처리기와 동일 합니다.
JSR-250 애노테이션 지원
-@Resource
-@PostConstruct
-@PreDestroy
위 세가지 애노테이션을 사용하기 위해서는 후처리기를 등록 해야 합니다.
@Resource
JNDI의 리소스 및 Spring 컨테이너의 bean을 찾아 autowring 합니다.
name 속성을 생략 할 경우 해당 필드명으로 찾고 해당 bean이 발견되지 않는다면 타입으로 찾습니다.
필드에 사용 할 경우 setter가 존재하지 않아도 됩니다.
private DataSource dataSource;
한개의 인자를 가진 setter 메소드만 적용 할 수 있습니다.
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
타입으로 찾지 않길 원한다면 다음과 같은 설정을 합니다.
<property name="fallbackToDefaultTypeMatch" value="false"></property>
</bean>
JNDI 리소스만 참조 하려면 다음과 같은 설정을 합니다.
<property name="alwaysUseJndiLookup" value="true"/>
</bean>
@PostConstruct 와 @PreDestroy
기존에는 생명 주기를 다루는 방법이 2가지가 있었습니다.
1.InitializingBean, DisposableBean을 구현하는 방법
2.XML 설정파일에서 <bean /> 요소의 init-method, destroy-method 속성 설정
2.5에서 추가된 두 애노테이션은 기존의 방식과 동일한 결과를 가져 옵니다.
public void init() {
...
}
@PreDestroy
public void preDestroy() {
...
}
만약 3가지 방식 모두 사용할 경우
우선순위는 애노테이션->XML설정->인터페이스 구현 입니다.
이거 하나면 OK
위에서 나온 애노테이션을 사용하기 위해서는 3가지의 BeanPostProcessor를 등록해야 했습니다.
하지만 이거 하나면 3개가 모두 등록 됩니다.
알아서 찾아줄게
위에서 나온 애노테이션으로 XML설정을 줄이더라도 bean 선언은 반드시 해야 했습니다.
하지만 component-scan 을 이용하면 bean선언 조차 생략 할 수 있습니다.
component-scan의 대상이 되기 위해서는 스테레오타입 애노테이션을 사용해야 합니다.
4개의 스테레오타입 애노테이션
@Component
- 스테레오타입 애노테이션의 조상 입니다.
@Controller
-Spring MVC에서 컨트롤러로 인식 합니다.
@Service
-역할부여 없이 스캔 대상이 되는데 비즈니스 클래스에 사용하면 될 것 같습니다.
@Repository
-DAO에 사용되며 DB Exception을 DataAccessException으로 변환해 줍니다.
간단한 예제 입니다.
테스트에 사용될 2개의 클래스
public class Bar {
}
@Component("foo")
public class Foo {
@Autowired
private Bar bar;
public Bar getBar() {
return bar;
}
}
지정된 패키지의 하위패키지까지 재귀적으로 스테레오타입이 있는 클래스를 찾습니다.
필터를 이용해 제외 검색 대상에서 제외 시킬 수 있으며
<context:annotation-config /> 까지 자동으로 등록됩니다.
테스트는 통과 합니다.
@ContextConfiguration(locations={"Beans.xml"})
public class ScanTest {
@Autowired
private Foo foo;
@Test
public void fooTest() {
assertNotNull(foo.getBar());
}
}
@Scope를 함께 사용 하여 Scope를 설정 할 수 있습니다.
@Component("bar")
public class Bar {
}
역시 scope문제가 발생 합니다.
이 문제를 해결하기 위해 <context:component-scan /> 요소의 scoped-proxy 속성이 존재 합니다.
scoped-proxy 속성은 <aop:scoped-poxy/> 요소처럼 WebApplicationContext 에서만 유효하며
"session", "globalSession", "request" 이외의 scope는 무시 됩니다.
아래 3가지 값을 지정 할 수 있습니다.
no - 디폴트값, proxy를 생성하지 않습니다.
interfaces - JDK Dynamic Proxy를 이용한 Proxy 생성
targetClass - CGLIB를 이용한 Proxy 생성
Bar는 인터페이스 기반이 아니기 때문에 CGLIB를 이용 했습니다.
만약 스캔된 bean들 중 @Scope의 값이 "session", "request" 일 때
scoped-proxy 속성 값이 "targetClass", "interfaces" 가 아닐경우 예외가 던져지고 초기화에 실패 합니다.
component-scan을 이용하면 method-lookup도 불가능 하므로 참조되는 bean이 web-scope가 아니고
prototype일 경우의 문제는 해결 할수 없을것 같습니다.
출처 - http://jjaeko.tistory.com/20
1. 스프링 2.5 부터 어노테이션 기반 설정 가능
@Component 스테레오타입 애노테이션의 조상입니다.
@Controller Spring MVC에서 컨트롤러로 인식합니다.
@Service 역할부여 없이 스캔 대상이 되는데 비즈니스 클래스에 사용하면 될 것 같습니다.
@Repository DAO에 사용되며 DB Exception을 DataAccessException으로 변환해 줍니다.
2. xml 파일에 <context:component-scan base-package="xxx.yyy.zzz" />와
<context:annotation-config /> 태그를 사용하여 자동 빈 스캔 component-scan 태그 base-package 속성 필수 헤더 스키마 선언
- <beans default-autowire="byName"
- 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.xsd">
장점
XML 설정파일에 대한 설정을 최소화 할 수 있고 단순하다.
코드가 간결해진다.
단점
Java1.5 이상의 환경에서 지원된다.
Annotation에 대한 학습이 필요하다.
4. Annotation 종류
@Scope 설정(prototype, singleton. request, session, globalSession, 기본값은 singleton)
@Autowired = byType
@Resource = byName
@PostConstruct = init-method(어노테이션과 XML 동시 설정 가능. 그럴 경우 어노테이션이 먼저 수행) PostConstruct는 bean이 생성된 이후에 수행해야 할 작업(초기화 작업)들을 선언해주는 역할을 합니다.
@PreDestroy = destroy-method 인스턴스가 삭제되기 전에 수행해야 할 작업들을 선언(destruction callback 기능)해주는 역할을 합니다.
추가내용
어노테이션 관련 글 스프링의 구조는 JSP(View) - Controller(URL mapping) - Service(Business Logic) - Dao(Data Access)
어노테이션이 확실히 좋은 기능임은 틀림없으나 과도한 사용은 득보다 실이 많다는 의견이 많다.
어노테이션을 이용하면 메타정보가 소스코드에 들어가므로 수정될 시 재컴파일이 필요
소스코드가 같이 제공되지 않으면 사용에 제약이 따름
SpringMVC 주요 패턴인 템플릿(Template) 패턴이나 전략(Strategy) 패턴으로 기존 기능을 확장시키기 어려운 점
순수 Spring 컨트롤러에선 xxx-servlet.xml 파일을 통해 url 맵핑이 이루어지는데 어노테이션을 이용할 경우 코드의 양이 방대할수록 계층 구조를 파악하기 힘듬
Struts2는 struts.xml 파일을 통해 url mapping이 이루어지므로 관련 없음
BEA의 EJB 팀 리더이자 OpenJPA 프로젝트의 리더인 패트릭 린스키가 한 말 중 일부분
- 대형 시스템에는 어노테이션이 적합하지 않다. 본격적인 시스템에서는 xml이 필수
- 규모가 있는 시스템일 경우 xml를 단계별로 분리하여 설정을 독립하거나 어노테이션 + xml 사용을 권장
Spring의 아버지 로드 존슨의 어노테이션 도입
- 1.x 버전 당시에는 스프링 사용자들의 어노테이션 DI 요청을 묵살함
- EJB3와 Google Guice의 annotation-driven DI의 인기와 영향력에 의해 Spring2.1에 어노테이션 도입 어노테이션 도입
- 배경에 대한 설명은 "고객과 엔드유저가 원하는 것이 옳은 것이다"라고 말함
- 스프링의 설정은 xml로 충분하다고 고집한 로드 존슨이 닷넷의 어트리뷰트 도입 필요성을 인식하고 가장 먼저 Spring에 어노테이션을 이용한 URL mapping인 @RequsetMapping을 적용
JDK 핵심 개발자이면서 Java5+ 개발에 깊이 관여한 조슈아 블록에 대한 입장
- 어노테이션 사용에 긍정적인 입장 CoC(관습에 의한 설정) 보다는 어노테이션이 낫다고 주장
- 어노테이션 정보(타입)를 얻기위해 사용되는 리플렉션 보다는 인터페이스 사용 선호
- 어노테이션은 타입이 없고 인터페이스는 타입이 있음
결론
시스템 복잡성이 아니라면 어노테이션 사용은 적합하게 쓰이면 코드가 간결해지고 유지보수가 용이해짐
대형 시스템엔 계층 구조가 잘 파악되기 위해서는 xml 사용이 필수
어노테이션은 메타정보가 소스코드에 들어가므로 파악하기 어려움
현재 오픈소스에도 많이 사용되고 계속 발전하는 어노테이션에 주목할 필요성이 보임
9.Annotation
Spring XML 만을 독립적으로 사용할 경우 때때로 방대하고 복잡한 속성 파일들로 인해 시스템 개발 및 유지보수의 지연을 초래할 가능성이 높아진다. 이러한 문제점을 해결하기 위해 Spring Framework에서는 별도 XML 정의없이도 사용 가능한 annotation 지원에 주력하고 있는 실정이다. Spring 2.0에서는 @Transactional, @Required, @PersistenceConetxt/@PersistenceUnit과 같은 Transaction 관리 또는 Persistence 관리 영역에 대한 annotation들을 지원했다면 Spring 2.5부터는 Bean 또는 Dependency 정의 등과 같이 Spring 속성 정의 XML과 직접적으로 관련된 annotation들을 선보이고 있다. 본 문서에서는 annotation 사용 용도를 Bean Management, Dependency Injection, Life Cycle로 구분하고 각각의 경우에 따른 사용법에 대해 상세히 살펴보도록 한다.
기본적으로, Annotation은 JDK 1.5 이상에서 활용이 가능하며, Spring Container가 Annotation을 인식할 수 있도록 하기 위해서는 속성 정의 XML 파일 내에 다음과 같은 정의가 추가되어야 함에 유의해야 한다.
<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:schemalLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/contxt/spring-context-2.5.xsd"> <context:annotation-config/> </beans>
XML vs. Annotation
다음은 특정 서비스를 구성하는 구현 클래스, DAO 클래스, 속성 정의 XML에 대해 XML을 이용하는 경우와 Annotation을 이용하는 경우로 나누어 비교해 본 그림이다.
Stereotype Annotation을 사용하면 Spring Framework의 컨테이너에 의해 관리되어야 하는 Bean들을 정의할 수 있다. 일반적으로 Parent Stereotype Annotation인 @Component 를 활용하면 모든 Bean에 대한 정의가 가능하다. 그러나 Spring Framework에서는 레이어별로 구성 요소를 구분하여 다음과 같은 Annotation을 사용할 것을 권장하고 있고, 향후 지속적으로 레이어별 특성을 반영할 수 있는 속성들을 추가해 나아갈 예정이다.
@Service
비즈니스 로직을 처리하는 클래스를 정의하는데 사용한다.
@Controller
프리젠테이션 레이어를 구성하는 Controller 클래스를 정의하는데 사용하며, Spring MVC 기반인 경우에 한해 활용 가능하다.
@Repository
데이터 접근 로직을 처리하는 클래스를 정의하는데 사용하며, 퍼시스턴스 레이어에서 발생한 Exception에 대한 Translation이 지원된다.
본 문서에서는 위에서 나열한 annotation을 사용하는 방법에 대해서 자세히 살펴보도록 한다.
Stereotype Annotation을 사용하여 Bean을 정의하면 XML에 따로 Bean 정의를 명시하지 않아도 Spring Container가 Bean을 인식하고 관리할 수 있다. 단, 자동 인식이 되기 위해서는 서비스 속성 정의 XML 내에<context:component-scan /> 을 정의해 주어야 한다. 이 설정을 추가하면 Spring Container는 클래스패스 상에 존재하는 클래스들을 스캔하여 Stereotype Annotation이 정의된 클래스들 Bean으로 인식하고 자동으로 등록한다.
<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:schemalLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/contxt/spring-context-2.5.xsd"> <context:component-scan base-package="anyframe.example" /> </beans>
<context:component-scan />을 정의한 경우 Annotation 인식을 위한 설정 <context:annotation-config/> 을 별도로 추가하지 않아도 된다.
다음은 서비스 레이어의 구성 요소인 ProductServiceImpl 클래스에 대해 @Service라는 Stereotype Annotation을 사용한 예이다.
@Service
public class ProductServiceImpl extends GenericServiceImpl<Product, String>
implements ProductService {
@Resource
MessageSource messageSource;
@Resource
ProductDao productDao;
}
위 예제에서는 해당 클래스의 클래스명(소문자로 시작)이 Bean name으로 셋팅되어 해당 Bean을 찾을 때 productServiceImpl 이라는 문자열을 사용해야 한다.
ProductService service = (ProductService) context.getBean("productServiceImpl");
해당 Annotation에 속성을 부여하면, 원하는 Bean name을 지정하는 것 또한 가능하다.
@Service("productService")
public class ProductServiceImpl extends GenericServiceImpl<Product, String>
implements ProductService {
@Resource
MessageSource messageSource;
@Resource
ProductDao productDao;
}
이 경우에 해당되는 Bean을 찾기 위해서는 속성으로 정의한 Name을 활용해야 한다.
ProductService service = (ProductService) context.getBean("productService");
<context:component-scan>의 여러 속성들을 이용하면 검색 대상의 범위를 조정하여 자동으로 검색되어 Bean으로 등록되는 클래스들을 filtering 할 수 있다. base-package 속성은 <context:component-scan> 내에 정의 가능한 속성으로 검색 대상 패키지를 정의하는 용도로 사용된다. 이외에도 <context:component-scan>은 하위 element로 <context:include-filter>, <context:exclude-filter>를 가질 수 있는데, 다양한 Filter Type(type)에 해당하는 표현식(expression)을 정의함으로써 이에 해당하는 클래스들을 포함 또는 제외시킬 수가 있다. 다음은 <context:include-filter>, <context:exclude-filter> 사용 예이다.
<context:component-scan base-package="anyframe.example"> <context:include-filter type="regex" expression=".*Stub.*Repository"/> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/> </context:component-scan>
정의 가능한 Filter Type은 4가지이며, 다음과 같다.
Filter Type | Example Expressions |
---|---|
annotation | org.example.SomeAnnotation |
assignable | org.example.SomeClass |
regex | org\.example\.Default.* |
aspectj | org.example..*Service+ |
참고
@Controller, @Service, @Repository가 적용된 클래스를 auto detection하는 디폴트 설정을 사용하지 않고자 하는 경우에는 <context:component-scan />태그에 use-default-filters="false" 속성을 추가하면 된다.Spring Framework에서는 Bean의 인스턴스 생성 메커니즘에 따라 5가지 Scope 을 제공하는데 이러한 Bean Scope을 정의하기 위해서는 다음과 같이 @Scope을 사용하도록 한다.
@Scope("prototype")
@Service("productService")
public class ProductServiceImpl extends GenericServiceImpl<Product, String>
implements ProductService {
@Resource
MessageSource messageSource;
@Resource
ProductDao productDao;
}
특정 Bean의 기능 수행을 위해 다른 Bean을 참조해야 하는 경우 사용하는 Annotation으로는 @Autowired 또는 @Resource가 있다.
@Autowired
Spring Framework에서 지원하는 Dependency 정의 용도의 Annotation으로, Spring Framework에 종속적이긴 하지만 정밀한 Dependency Injection이 필요한 경우에 유용하다.
@Resource
JSR-250 표준 Annotation으로 Spring Framework 2.5.* 부터 지원하는 Annotation이다. 특정 Framework에 종속되지 않은 어플리케이션을 구성하기 위해서는 @Resource를 사용할 것을 권장한다. @Resource를 사용하기 위해서는 클래스패스 내에 jsr250-api.jar 파일이 추가되어야 함에 유의해야 한다.
@Autowired와 @Resource를 사용할 수 있는 위치는 다음과 같이 약간의 차이가 있으므로 필요에 따라 적절히 사용하면 된다.
@Autowired : 멤버변수, setter 메소드, 생성자, 일반 메소드에 적용 가능
@Resource : 멤버변수, setter 메소드에 적용가능
@Autowired나 @Resource를 멤버변수에 직접 정의하는 경우 별도 setter 메소드는 정의하지 않아도 된다.
@Resource annotation은 Bean name을 지정하여 Dependency Injection을 하고자 하는 경우에 사용한다. @Resource는 name이라는 속성을 가지고 있어서, Spring Container가 @Resource로 정의된 요소에 injection하기 위한 Bean을 검색할 때, name 속성에 지정한 이름을 검색할 Bean Name으로 사용한다.
@Service("productService")
public class ProductServiceImpl extends GenericServiceImpl<Product, String>
implements ProductService {
@Resource
MessageSource messageSource;
@Resource (name="productDao")
ProductDao productDao;
명시적으로 name 속성에 이름을 지정하지 않는 경우, 검색할 Bean Name은 다음과 같은 규칙을 따른다.
@Resource가 멤버 변수에 정의되었을 때 : 멤버 변수의 이름
@Resource가 setter 메소드에 정의되었을 때 : 해당 setter 메소드의 이름에서 'set'을 제외한 이름(첫 글자는 소문자)
예) setFoo(...) --> 'foo'
해당하는 Bean Name으로 injection할 Bean을 찾지 못했을 경우에는 @Autowired 처럼 Bean의 type으로 검색한다.
@Resource를 이용하면 BeanFactory, ApplicationContext, ResourceLoader, ApplicationEventPublisher, MessageSource 인터페이스와 하위 인터페이스들을 별도 설정 없이 바로 사용 가능하다.
@Service("productService")
public class ProductServiceImpl extends GenericServiceImpl<Product, String>
implements ProductService {
@Resource
ApplicationContext context;
}
서로 다른 Bean 간의 Dependency 정의를 위한 또 다른 Annotation인 @Autowired는 Spring에 종속적이긴 하지만, 적용할 수 있는 위치가 @Resource보다 다양하고, 정밀한 Dependency Injection이 필요한 경우에 유용하다.
다음은 @Autowired를 사용한 예이다.
@Service("productService")
public class ProductServiceImpl extends GenericServiceImpl<Product, String>
implements ProductService {
@Autowired
ProductDao productDao;
}
@Autowired 적용 위치 별로 사용 예를 들면 다음과 같다.
생성자 및 멤버 변수
@Service("productService") public class ProductServiceImpl extends GenericServiceImpl<Product, String> implements ProductService { @Autowired ProductDao productDao; MessageSource messageSource; @Autowired public ProductServiceImpl(MessageSource messageSource) { this.messageSource = messageSource; } }
위의 예제와 같이 @Autowired를 사용하면 ProductServiceImpl 클래스가 생성될 때 Spring Container에 의해서 MessageSource 타입의 Bean이 생성자의 argument로 자동으로 injection 된다. 또한 productDao 멤버변수에도 @Autowired가 적용되어 있으므로 ProductDao 타입의 Bean이 자동 injection된다.setter 메소드
@Service("productService") public class ProductServiceImpl extends GenericServiceImpl<Product, String> implements ProductService { ProductDao productDao; @Autowired public void setProductDao(ProductDao productDao) { this.productDao = productDao; } }
Spring Container에 의해서 자동으로 setProductDao() 메소드가 호출되어 ProductDao 타입의 Bean이 productDao 멤버변수로 injection된다.일반 메소드
@Service("productService") public class ProductServiceImpl extends GenericServiceImpl<Product, String> implements ProductService { ProductDao productDao; MessageSource messageSource; @Autowired public void prepare(ProductDao productDao, MessageSource messageSource) { this.productDao = productDao; this.messageSource = messageSource; } }
@Resource 와는 달리 위의 prepare()와 같은 일반 메소드에도 @Autowired를 적용함으로써 Spring Container에 의한 Dependency Injection 처리를 할 수 있다. 위의 예제에서는 ProductDao 타입의 Bean이 productDao로, MessageSource 타입의 Bean이 messageSource로 injection된다배열이나 Collection 형태의 멤버변수와 메소드
@Service("productService") public class ProductServiceImpl extends GenericServiceImpl<Product, String> implements ProductService { ProductDao productDao; @Autowired Category[] categories; }
@Service("productService") public class ProductServiceImpl extends GenericServiceImpl<Product, String> implements ProductService { ProductDao productDao; Set<Category> categories; @Autowired public void setCategories(ProductDao productDao, Set<Category> categories) { this.productDao = productDao; this.categories = categories; } }
위 예제 소스의 경우, Spring Container에 등록된 Category 타입의 Bean들이 모두 categories 배열(또는 collection)에 injection된다.Map(key=Bean Name, value=Bean 객체) 형태의 멤버변수와 메소드
@Service("productService") public class ProductServiceImpl extends GenericServiceImpl<Product, String> implements ProductService { ProductDao productDao; Map<String, Category> categories; @Autowired public void setCategories(ProductDao productDao, Map<String, Category> categories) { this.productDao = productDao; this.categories = categories; } }
위 예제 소스의 경우, Spring Container에 등록된 Category 타입의 Bean들이 Bean name이 key로, Bean 객체가 value인 쌍으로 모두 categories Map에 injection된다.
기본적으로 @Autowired가 적용된 참조 관계는 반드시 해당 빈이 존재해야 하지만, required 속성을 false로 설정하는 경우에는 해당되는 Bean을 찾지 못하더라도 에러가 발생하지 않는다.
@Service
public UserService implements UserService {
@Autowired(required=false)
private UserDAO userDAO;
}
또한, @Resource에서 설명했던 바와 같이 @Autowired도 BeanFactory, ApplicationContext, ResourceLoader, ApplicationEventPublisher, MessageSource 인터페이스와 하위 인터페이스들을 별도 설정 없이 바로 사용할 수 있게 해준다.
@Service("productService")
public class ProductServiceImpl extends GenericServiceImpl<Product, String>
implements ProductService {
@Autowired
ApplicationContext context;
}
기본적으로 @Autowired는 type-driven injection 형태로 동작하여, @Autowired가 정의되었을 경우 Spring Container가 해당 Bean을 찾을 때 객체의 type을 기준으로 검색을 한다. 이와 같은 경우 동일한 객체 type의 Bean이 여러 개 검색되었을 때 injection 대상이 되는 Bean을 결정하기 위한 세밀한 제어를 요하는데, 이 때 @Qualifier를 사용할 수 있다.
다음은 @Qualifier를 사용한 예이다.
@Service("productService")
public class ProductServiceImpl extends GenericServiceImpl<Product, String>
implements ProductService {
@Autowired
@Qualifier("sports")
Category sportsCategory;
}
위와 같이 정의하면 "sports"라는 qualifier 속성 값이 정의된 Bean이 sportsCategory 멤버변수로 injection된다.
위의 @Qualifier에 의해 연결될 Bean은 다음과 같이 정의할 수 있다.
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd"> <context:annotation-config/> <bean class="anyframe.example.domain.Category"> <qualifier value="sportsCategory"/> <!-- inject any dependencies required by this bean --> </bean> <bean class="anyframe.example.domain.Category"> <qualifier value="livingCategory"/> <!-- inject any dependencies required by this bean --> </bean> <bean id="productService" class="anyframe.example.annotation.sales.service.impl.ProductServiceImpl"/> </beans>
IoC의 Life Cycle 에서 설명한 바와 같이 Bean의 LifeCycle은 Initializaion ->Activation -> Destruction으로 구성되어 있으며, LifeCycle 메소드를 정의하는 경우 컨테이너 기동시 또는 종료시 필요한 로직을 수행할 수 있게 된다. Bean을 초기화 또는 소멸화 하는 시점에 별도 작업이 필요한 경우 기존에는 InitializingBean과 DesposableBean 인터페이스를 상속하거나, Bean 정의시 명시적으로 초기화 메소드나 소멸화 메소드를 별도로 지정해야 했다. 그러나, 다음과 같은 Annotation을 사용하면 XML 정의 또는 별도 인터페이스 상속없이 Bean의 LifeCycle 관리가 가능해진다.
JSR-250 표준 Annotation으로 Bean 초기화시 필요한 작업을 담은 메소드에 대해 정의한다. @PostConstruct를 사용하기 위해서는 클래스패스 내에 jsr250-api.jar 파일이 추가되어 있어야 한다.
@PostConstruct
// 메소드명은 자유롭게 정의할 수 있다.
public void initialize() {
// ...
}
JSR-250 표준 Annotation으로 Bean 소멸시 필요한 작업을 담은 메소드에 대해 정의한다. @PreDestroy를 사용하기 위해서는 클래스패스 내에 jsr250-api.jar 파일이 추가되어 있어야 한다.
@PreDestroy
// 메소드명은 자유롭게 정의할 수 있다.
public void dispose() {
// ...
}
앞에서 설명한 바와 같이, Spring 2.5에서 bean lifecycle을 관리할 수 있는 방법은 다음과 같이 세가지가 있다.
InitializingBean과 DisposableBean callback 인터페이스 이용
사용자가 작성한 초기화/소멸화 메소드를 XML에서 init-method/destroy-method 속성을 이용하여 정의
@PostConstruct와 @PreDestroy annotation 이용
위의 3가지 방법이 동시에 존재하는 경우(예를 들어, 3가지 방법이 각각 정의된 클래스가 Parent-child 관계를 가지는 경우), 실행되는 순서는 다음과 같다.
Initialization 메소드
@PostConstruct를 이용하여 정의한 메소드
InitializingBean 인터페이스의 afterPropertiesSet() 메소드
XML에서 init-method 속성으로 정의된 초기화 메소드
Destroy 메소드
@PreDestroy를 이용하여 정의한 메소드
DisposableBean 인터페이스의 destroy() 메소드
XML에서 destroy-method 속성으로 정의된 소멸화 메소드
다운로드
다음에서 테스트 DB를 포함하고 있는 hsqldb.zip과 example 코드를 포함하고 있는 anyframe.example.annotation.zip 파일을 다운받은 후, 압축을 해제한다. 그리고 hsqldb 폴더 내의 start.cmd (or start.sh) 파일을 실행시켜 테스트 DB를 시작시켜 놓는다.
Maven 기반 실행
Command 창에서 압축 해제 폴더로 이동한 후 mvn jetty:run이라는 명령어를 실행시킨다. Jetty Server가 정상적으로 시작되었으면 브라우저를 열고 주소창에 http://localhost:8080/anyframe.example.annotation를 입력하여 실행 결과를 확인한다.
Eclipse 기반 실행 - m2eclipse, WTP 활용
Eclipse에서 압축 해제 프로젝트를 import한 후, 해당 프로젝트에 대해 마우스 오른쪽 버튼을 클릭하고 컨텍스트 메뉴에서 Maven > Enable Dependency Management를 선택하여 컴파일 에러를 해결한다. 그리고 해당 프로젝트에 대해 마우스 오른쪽 버튼을 클릭한 후, 컨텍스트 메뉴에서 Run As > Run on Server (Tomcat 기반)를 클릭한다. Tomcat Server가 정상적으로 시작되었으면 브라우저를 열고 주소창에 http://localhost:8080/anyframe.example.annotation를 입력하여 실행 결과를 확인한다.
Eclipse 기반 실행 - WTP 활용
Eclipse에서 압축 해제 프로젝트를 import한 후, build.xml 파일을 실행하여 참조 라이브러리를 src/main/webapp 폴더의 WEB-INF/lib내로 복사시킨다. 해당 프로젝트를 선택하고 마우스 오른쪽 버튼을 클릭한 후, 컨텍스트 메뉴에서 Run As > Run on Server를 클릭한다. Tomcat Server가 정상적으로 시작되었으면 브라우저를 열고 주소창에 http://localhost:8080/anyframe.example.annotation를 입력하여 실행 결과를 확인한다. (* build.xml 파일 실행을 위해서는 ${ANT_HOME}/lib 내에 maven-ant-tasks-2.0.10.jar 파일이 있어야 한다.)
출처 - http://dev.anyframejava.org/docs/anyframe/4.0.0/reference/html/ch09.html
Spring 2.5 가 되면서 dependency injection 관련해서 서비스 구성이 무지 편해졌다.
annotation 을 통해서 빈들을 지정된 클래스패스에서 자동으로 인식하게 할수 있게 되었고, 필요자원을 세팅시킬수 있게 되었다. 넘 편리~~
Stereotype-annotation(@Component, @Service, @Repository, @Controller) 을 통해서 자동으로 클래스패스에서 컴포넌트를 인식(auto-detect) 시킬수 있다. @Component는 자동인식이 되는 일반 컴퍼넌트로 다른 녀석들의 부모역할을 한다. @Service는 비즈니스 서비스를 의미하고, @Repository는 Dao 에 적용하면 좋은 녀석인데, DB Exception Translation을 자동으로 해준다(Hibernate DAO를 POJO로 구성해 본 사람은 이 용도가 무지 좋다는 것을 알거다). 마지막으로 @Controller는 웹용으로 MVC 콤퍼넌트로 사용된다.
요 녀석들을 사용해서 간단해게 테스트 해밨다.
private String destination;
public Delivery(String name, String dest) {
this.productName = name;
this.destination = dest;
}
public String getProductName() {
return productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
public String getDestination() {
return destination;
}
public void setDestination(String destination) {
this.destination = destination;
}
}
}
public class DeliveryDaoImpl implements DeliveryDao {
List results = new ArrayList();
results.add(new Delivery("Apple", "서울"));
results.add(new Delivery("Melon", "부산"));
return results;
}
}
public class CargoTranportServiceImpl implements TransportService {
private DeliveryDao deliveryDao;
public List<Delivery> send(String obj) {
return deliveryDao.getAll();
}
JEE 형식의 common annotation도 spring 에서 지원한다. @Resource( name="deliveryDao" ) 처럼 해주면 된다.
context 구성은 다음과 같다.
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd">
context:component-scan을 해주면 <context:annotation-config /> 설정이 자동으로 된다.
유닛테스트.
AbstractDependencyInjectionSpringContextTests {
protected String[] getConfigLocations() {
return new String[] { "applicationContext.xml" };
}
this.transportService = transportService;
}
List results = transportService.send("");
assertEquals(2, results.size());
}
}
출처 - http://readyset.tistory.com/19
Spring MVC Annotation 기초Program/Java2009/12/27 15:49
출처 - http://mikyungnet.tistory.com/24
'Framework & Platform > Spring' 카테고리의 다른 글
spring mvc - No mapping found for HTTP request with URI (0) | 2012.05.10 |
---|---|
spring - annotation 2 (0) | 2012.05.09 |
Sping MVC - Model (0) | 2012.05.09 |
Spring - @Controller (0) | 2012.05.09 |
Spring - @RequestMapping (0) | 2012.05.09 |