@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 설정에 명시적인 의존성 삽입이 없다면 런타임 예외가 던져 집니다.
이 애노테이션을 처리하기 위한 후처리기는 다음과 같습니다.

<bean class="org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor"/>


자동으로 묶어 줄게 
@Autowried 는 타입에 일치하는 bean을 의존성 삽입 해줍니다.
필드에 사용 할 경우 setter가 존재하지 않아도 됩니다.
@Autowried
private Foo foo;

여러개의 인자를 가진 메소드에 사용 할 수 있습니다.
@Autowried
public void setUp(Foo foo, Bar bar) {
    this.foo = foo;
    this.bar = bar;
}

@Autowired의 속성인 required의 기본값이 true이기 때문에 발견되는 bean이 없다면 예외가 던져 집니다.
발견 되는 bean이 없더라도 예외가 던져지지 않기를 원하면 required 속성을 fasle로 설정하면 됩니다.
@Autowried(required=false)
private Foo foo;

일치하는 타입을 찾기 때문에 발견되는 bean이 여러개인 경우 예외가 던져 집니다.
이를 방지 하기 위해 <bean /> 요소의 추가된 속성인 primary를 설정 합니다.
발견되는 bean이 여러개 일지라도 primary가 설정된 bean이 참조 됩니다.
<bean id="foo" class="example.Foo" primary="true" />
<bean id="foo2" class="example.Foo"/>

이 애노테이션을 처리하기 위한 후처리기는 다음과 같습니다.
<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>


정확한 이름을 알려줄게 
@Autowired을 사용 할 때 타입으로 bean을 찾기 때문에 여러개의 bean이 발견되면 primary로 해결 했습니다.  하지만 @Qualifier를 이용해 찾으려는 bean의 id를 넘길 수 있습니다.
@Autowried
@Qualifier("foo")
private Foo foo;

필드에만 사용 가능 하기 때문에 메소드에 적용시 다음과 같이 설정 합니다.
@Autowried
public void setUp(@Qualifier("foo") Foo foo, @Qualifier("foo2") Foo foo2) {
    this.foo = foo;
    this.foo2 = foo2;
}

이 애노테이션을 처리하기 위한 후처리기는 @Autowried의 후처리기와 동일 합니다.


JSR-250 애노테이션 지원
-@Resource
-@PostConstruct
-@PreDestroy
위 세가지 애노테이션을 사용하기 위해서는 후처리기를 등록 해야 합니다.
<bean class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor"/> 

@Resource
JNDI의 리소스 및 Spring 컨테이너의 bean을 찾아 autowring 합니다.
name 속성을 생략 할 경우 해당 필드명으로 찾고 해당 bean이 발견되지 않는다면 타입으로 찾습니다.

필드에 사용 할 경우 setter가 존재하지 않아도 됩니다.
@Resource(name="dataSource")
private DataSource dataSource;

한개의 인자를 가진 setter 메소드만 적용 할 수 있습니다.
@Resource(name="dataSource")
public void setDataSource(DataSource dataSource) {
    this.dataSource = dataSource;
}

타입으로 찾지 않길 원한다면 다음과 같은 설정을 합니다.
<bean class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor">
    <property name="fallbackToDefaultTypeMatch" value="false"></property>
</bean>

JNDI 리소스만 참조 하려면 다음과 같은 설정을 합니다.
<bean class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor">
    <property name="alwaysUseJndiLookup" value="true"/>
</bean>

@PostConstruct 와 @PreDestroy
기존에는 생명 주기를 다루는 방법이 2가지가 있었습니다.
1.InitializingBean, DisposableBean을 구현하는 방법
2.XML 설정파일에서 <bean /> 요소의 init-method, destroy-method 속성 설정

2.5에서 추가된 두 애노테이션은 기존의 방식과 동일한 결과를 가져 옵니다.
@PostConstruct
public void init() {
    ...
}
    
@PreDestroy
public void preDestroy() {
    ...
}

만약 3가지 방식 모두 사용할 경우 
우선순위는 애노테이션->XML설정->인터페이스 구현 입니다.


이거 하나면 OK
위에서 나온 애노테이션을 사용하기 위해서는 3가지의 BeanPostProcessor를 등록해야 했습니다.
하지만 이거  하나면 3개가 모두 등록 됩니다.
<context:annotation-config />


알아서 찾아줄게
위에서 나온 애노테이션으로 XML설정을 줄이더라도 bean 선언은 반드시 해야 했습니다.
하지만 component-scan 을 이용하면 bean선언 조차 생략 할 수 있습니다.
component-scan의 대상이 되기 위해서는 스테레오타입 애노테이션을 사용해야 합니다.

4개의 스테레오타입 애노테이션
@Component
- 스테레오타입 애노테이션의 조상 입니다.
@Controller
-Spring MVC에서 컨트롤러로 인식 합니다.
@Service
-역할부여 없이 스캔 대상이 되는데 비즈니스 클래스에 사용하면 될 것 같습니다.
@Repository
-DAO에 사용되며 DB Exception을 DataAccessException으로 변환해 줍니다.

간단한 예제 입니다. 
테스트에 사용될 2개의 클래스
@Component("bar")
public class Bar {
}

@Component("foo")
public class Foo  {
    @Autowired
    private Bar bar;
    
    public Bar getBar() {
        return bar;
    }
}

지정된 패키지의 하위패키지까지 재귀적으로 스테레오타입이 있는 클래스를 찾습니다.
필터를 이용해 제외 검색 대상에서 제외 시킬 수 있으며
<context:annotation-config /> 까지 자동으로 등록됩니다.
<context:component-scan base-package="example" />

테스트는 통과 합니다.
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"Beans.xml"})
public class ScanTest {
    @Autowired
    private Foo foo;
    
    @Test
    public void fooTest() {
        assertNotNull(foo.getBar());
    }
}

@Scope를 함께 사용 하여 Scope를 설정 할 수 있습니다.
@Scope("request")
@Component("bar")
public class Bar {
}
singleton인 foo에서 request scope인 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를 이용 했습니다.
<context:component-scan base-package="example" scoped-proxy="targetClass"/>

만약 스캔된 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 속성 필수 헤더 스키마 선언

  1. <beans default-autowire="byName"  
  2. xmlns="http://www.springframework.org/schema/beans"  
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  4. xmlns:context="http://www.springframework.org/schema/context"  
  5. xsi:schemaLocation="  
  6. http://www.springframework.org/schema/beans  
  7. http://www.springframework.org/schema/beans/spring-beans.xsd  
  8. http://www.springframework.org/schema/context  
  9. 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 사용이 필수
어노테이션은 메타정보가 소스코드에 들어가므로 파악하기 어려움
현재 오픈소스에도 많이 사용되고 계속 발전하는 어노테이션에 주목할 필요성이 보임

출처 - http://sooin01.tistory.com/entry/%EC%8A%A4%ED%94%84%EB%A7%81-%EC%96%B4%EB%85%B8%ED%85%8C%EC%9D%B4%EC%85%98Spring-Annotation






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을 이용하는 경우로 나누어 비교해 본 그림이다.

9.1.Bean Management

Stereotype Annotation을 사용하면 Spring Framework의 컨테이너에 의해 관리되어야 하는 Bean들을 정의할 수 있다. 일반적으로 Parent Stereotype Annotation인 @Component 를 활용하면 모든 Bean에 대한 정의가 가능하다. 그러나 Spring Framework에서는 레이어별로 구성 요소를 구분하여 다음과 같은 Annotation을 사용할 것을 권장하고 있고, 향후 지속적으로 레이어별 특성을 반영할 수 있는 속성들을 추가해 나아갈 예정이다.

  • @Service

    비즈니스 로직을 처리하는 클래스를 정의하는데 사용한다.

  • @Controller

    프리젠테이션 레이어를 구성하는 Controller 클래스를 정의하는데 사용하며, Spring MVC 기반인 경우에 한해 활용 가능하다.

  • @Repository

    데이터 접근 로직을 처리하는 클래스를 정의하는데 사용하며, 퍼시스턴스 레이어에서 발생한 Exception에 대한 Translation이 지원된다.

본 문서에서는 위에서 나열한 annotation을 사용하는 방법에 대해서 자세히 살펴보도록 한다.

9.1.1.Auto Detecting

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");

9.1.2.Using Filters to customize scanning

<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 TypeExample Expressions
annotationorg.example.SomeAnnotation
assignableorg.example.SomeClass
regexorg\.example\.Default.*
aspectjorg.example..*Service+

참고

@Controller, @Service, @Repository가 적용된 클래스를 auto detection하는 디폴트 설정을 사용하지 않고자 하는 경우에는 <context:component-scan />태그에 use-default-filters="false" 속성을 추가하면 된다.

9.1.3.Scope Definition

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;
}       

9.2.Dependency Injection

특정 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 메소드는 정의하지 않아도 된다.

9.2.1.@Resource

@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;
}

9.2.2.@Autowired

서로 다른 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;
}

9.2.3.@Qualifier

기본적으로 @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>
    	

9.2.4.@Resource vs. @Autowired

@Resource와 @Autowired을 비교하면 다음과 같다.

Annotation@Resource@Autowired
Injection 방식name-matching injectiontype-driven injection
사용가능한 위치멤버변수, setter 메소드멤버변수, setter 메소드, 생성자, 일반 메소드

9.3.LifeCycle Annotation

IoC의 Life Cycle 에서 설명한 바와 같이 Bean의 LifeCycle은 Initializaion ->Activation -> Destruction으로 구성되어 있으며, LifeCycle 메소드를 정의하는 경우 컨테이너 기동시 또는 종료시 필요한 로직을 수행할 수 있게 된다. Bean을 초기화 또는 소멸화 하는 시점에 별도 작업이 필요한 경우 기존에는 InitializingBean과 DesposableBean 인터페이스를 상속하거나, Bean 정의시 명시적으로 초기화 메소드나 소멸화 메소드를 별도로 지정해야 했다. 그러나, 다음과 같은 Annotation을 사용하면 XML 정의 또는 별도 인터페이스 상속없이 Bean의 LifeCycle 관리가 가능해진다.

9.3.1.@PostConstruct

JSR-250 표준 Annotation으로 Bean 초기화시 필요한 작업을 담은 메소드에 대해 정의한다. @PostConstruct를 사용하기 위해서는 클래스패스 내에 jsr250-api.jar 파일이 추가되어 있어야 한다.

@PostConstruct
// 메소드명은 자유롭게 정의할 수 있다.
public void initialize() {
        // ...
}      

9.3.2.@PreDestroy

JSR-250 표준 Annotation으로 Bean 소멸시 필요한 작업을 담은 메소드에 대해 정의한다. @PreDestroy를 사용하기 위해서는 클래스패스 내에 jsr250-api.jar 파일이 추가되어 있어야 한다.

@PreDestroy
// 메소드명은 자유롭게 정의할 수 있다.
public void dispose() {
        // ...
}       

9.3.3.Combining lifecycle mechanisms

앞에서 설명한 바와 같이, Spring 2.5에서 bean lifecycle을 관리할 수 있는 방법은 다음과 같이 세가지가 있다.

  • InitializingBean과 DisposableBean callback 인터페이스 이용

  • 사용자가 작성한 초기화/소멸화 메소드를 XML에서 init-method/destroy-method 속성을 이용하여 정의

  • @PostConstruct와 @PreDestroy annotation 이용

위의 3가지 방법이 동시에 존재하는 경우(예를 들어, 3가지 방법이 각각 정의된 클래스가 Parent-child 관계를 가지는 경우), 실행되는 순서는 다음과 같다.

Initialization 메소드

  1. @PostConstruct를 이용하여 정의한 메소드

  2. InitializingBean 인터페이스의 afterPropertiesSet() 메소드

  3. XML에서 init-method 속성으로 정의된 초기화 메소드

Destroy 메소드

  1. @PreDestroy를 이용하여 정의한 메소드

  2. DisposableBean 인터페이스의 destroy() 메소드

  3. XML에서 destroy-method 속성으로 정의된 소멸화 메소드

9.4.Resources

  • 다운로드

    다음에서 테스트 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 파일이 있어야 한다.)

    표 9.1. Download List

    NameDownload
    hsqldb.zipDownload
    anyframe.example.annotation.zipDownload
    maven-ant-tasks-2.0.10.jarDownload

출처 - 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 콤퍼넌트로 사용된다.

요 녀석들을 사용해서 간단해게 테스트 해밨다.

public class Delivery {
 private String productName;
 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 interface DeliveryDao {
 public List<Delivery> getAll();
}

@Repository
public class DeliveryDaoImpl implements DeliveryDao {
 public List<Delivery> getAll() {
  
  List results = new ArrayList();
  results.add(new Delivery("Apple", "서울"));
  results.add(new Delivery("Melon", "부산"));
  
  return results;
 }
}
제대로 하면 DB 관련된 Dao 여야 하지만 수도 코드로 두개 배송목록을 리턴하도록 했다.

public interface TransportService {
 public List<Delivery> send(String obj);
}

@Service
public class CargoTranportServiceImpl implements TransportService {
 @Autowired
 private DeliveryDao deliveryDao;
 
 public List<Delivery> send(String obj) {
  
  return deliveryDao.getAll();
 }
}
@Autowired 를 사용해서 annotation으로 자원 세팅을 할 수 있다.
JEE 형식의 common annotation도 spring 에서 지원한다. @Resource( name="deliveryDao" ) 처럼 해주면 된다.

context 구성은 다음과 같다.
 <context:component-scan base-package="com.mple.service" />
</beans>

context:component-scan을 해주면 <context:annotation-config /> 설정이 자동으로 된다.

유닛테스트.
public class TransportServiceTest extends
  AbstractDependencyInjectionSpringContextTests {
 @Override
 protected String[] getConfigLocations() {
  return new String[] { "applicationContext.xml" };
 }
 private TransportService transportService;
 public void setTransportService(TransportService transportService) {
  this.transportService = transportService;
 }
 public void testSend() {
  List results = transportService.send("");
  assertEquals(2, results.size());
 }
}


출처 - http://readyset.tistory.com/19






Spring 2.5 버전서부터 추간된 기능중에 Annotation(어노테이션)의 기능이 있다.
실제로 사용해본결과 이전의 xml에서 설정하여 사용하는것보단 훨씬 개발하기 편해진것은 확실하다.
( 솔찍히 Spring은 이번에 처음 써보는것나 마찮가지다. )
아직 많은 사용법을 모르지만  간단하게 정리를 해보고자 한다.

* 참고로 이문서는 어디까지나 어노테이션의 사용에 대한 것이지 Spring에 기본적인 내용은 필자도 잘아지 못한다.
( 필자도 앞으로 공부해나가야할 부분이다. )
즉, 간단하게 이미 spring 2.5 이전 버전을 쓰시는 분들은 그냥 "어노테이션을 이렇게 쓰는구나" 정도로 이해해 주시고
Spring을 안쓰시다 2.5를 처음 쓰시는 분들은 Spring의 이전 버전도 어느정도 지식을 쌓으시는 것이 좋을것으로 판단 된다.

Spring 2.5( 중에서도 Spring MVC) 에서 annotation을 사용하기 위해서는 dispatcher-servlet.xml 에 다음의 설정을 추가해야한다.
그래야만 어노테이션이 적용된 class( @Controller, @Service, @Repository 를 포함한 class )를 로딩할수가 있다.

- dispatcher-servlet.xml -
<context:component-scan base-package="kr.pe.jabsiri" /> 가존 작성중 틀린것


2009-06-26 수정내용
dispactcher-servlet.xml에는 아래와 같이해서 Controller의 어노테이션만 로딩을 하고
1
2
3
<context:component-scan base-package="com.enz.adnad" use-default-filters="false"> <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"></context:include-filter><br>
 
</context:component-scan>
applicationContext.xml에는 아래와 같이해서 Controller를 제외한 어노테이션만 로딩을 해야한다.
1
2
3
4
5
6
7
8
<context:component-scan base-package="com.enz.adnad"><br><span id="callbacknestmikyungnettistorycom242716" style="width:1px; height:1px; float:right"><embed allowscriptaccess="always" id="bootstrappermikyungnettistorycom242716" src="http://mikyungnet.tistory.com/plugin/CallBack_bootstrapperSrc?nil_profile=tistory&nil_type=copied_post" width="1" height="1" wmode="transparent" type="application/x-shockwave-flash" enablecontextmenu="false" flashvars="&callbackId=mikyungnettistorycom242716&host=http://mikyungnet.tistory.com&embedCodeSrc=http%3A%2F%2Fmikyungnet.tistory.com%2Fplugin%2FCallBack_bootstrapper%3F%26src%3Dhttp%3A%2F%2Fs1.daumcdn.net%2Fcfs.tistory%2Fv%2F0%2Fblog%2Fplugins%2FCallBack%2Fcallback%26id%3D24%26callbackId%3Dmikyungnettistorycom242716%26destDocId%3Dcallbacknestmikyungnettistorycom242716%26host%3Dhttp%3A%2F%2Fmikyungnet.tistory.com%26float%3Dleft" swliveconnect="true"></span>
 
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"></context:exclude-filter><br>
 
</context:component-scan><br>
 
<br>
<br>
아래는 dispactcher-servlet.xml 에 설정하는 ViewResolver 로써 /WEB-INF/spring/ 로 시작하고 .jsp 로 끝나는 파일을 찾아 옵니다.
controller에서 return "hello/hello_jabsiri"; 와 같이 하면 /WEB-INF/spring/hello/hello_jabsiri.jsp 파일을 찾습니다.
1
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" p:prefix="/WEB-INF/spring/" p:suffix=".jsp">

1. @Controller
위에서 설정한 컴포넌트 스캔을 통하여 로딩된( <bean> tag를 써서 일일이 등록된 class도 )@Controller를 적용한 Class는 DefaultAnnotationHandlerMapping을 통해 컨트롤로 사용된다.
( DefaultAnnotationHandlerMapping을 통해 컨트롤러로 등록이 되는지 어쩐지는 나도 잘 모른다 그냥그렇다고 한다. 즉, 기본적인 Spring에 대한 공부는 각자의 몫이고 필자 또한 나중에 정리를 할 것이다. )

다음은 사용의 예이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
@Controller<br>
 
public class HelloJabsiri {<br>
 
    @RequestMapping(value="/hello/hello_jabsiri.jab")<br>
 
    public String helloJabsiri(){<br>
 
        return "hello/hello_jabsiri"//리턴값은 .jsp 확장자를 뺀 경로<br>
 
    }<br>
 
}
return 값의 "hello/hello_jabsiri" 는 dispatcher-servlet.xml 에서 정의한viewResolver 의  p:suffix=".jsp" p:prefix="/WEB-INF/spring/" 내용에 해당하는곳에서 jsp를 찾는다.
즉 /WEB-INF/spring/[ return 값].jsp 의 패턴에 해당하는 jsp를 찾는다.

여기서 잠시 주목할 사항은 helloJabsiri() 메소드는 아무런 파라미터도 받지 않는다는 것이다.
맞다 아무것도 안받아도된다.!!
Spring MVC에서 MultiController ( 명칭이 맞나;? )는 파라미터를 받아도되고 않받아도 되고, 받되 순서가 뒤죽 박죽이어도 된다.

다음과 같이 Member bean을 받을수 있는데 이는 html상의 form input type의 id와 Member 객체들의 맴버변수가 같은것을 알아서 매핑시켜 값을 넘겨주기도 한다.

1
2
3
4
5
6
7
8
9
@RequestMapping("member.do")<br>
 
public String member(Member member, ModelMap modelMap){ <br>
 
    modelMap.put("member", member);<br>
 
    return "member/member";<br>
 
}
이것외에 @RequestParam과 메소드의 파라미터를 받는 방법등은 첨부파일을 받아서 꼭!!! 보길 바란다.


2. @RequestMapping
위의 HelloJabsiri의 Controller에 보면 @RequestMapping(value="/hello/hello_jabsiri.jab") 이 보일 것이다.
이는 @RequestMapping의 value 형태의 url이 들어오면 helloJabsiri method를 호출 하겠다는 것이다.
위의 코드는 다음과 같이 바뀔수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Controller<br>
 
@RequestMapping("/hello/*")<br>
 
public class HelloJabsiri {<br>
 
    @RequestMapping(value="hello_jabsiri.jab")<br>
 
    public String helloJabsiri(){<br>
 
        return "hello/hello_jabsiri";<br>
 
    }<br>
 
}

3. @Autowired
@Autowired 어노테이션은 Spring에서 의존관계를 자동으로 설정할때 사용된다.
이 어노테이션은 생성자, 필드, 메서드 세곳에 적용이 가능하다.

아래와 같이 설정해주면 사용할수 있지만 위에서처럼 scan으로 등록했으므로 안해도 된다.
1
2
3
4
<bean class=org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor /><br>
 
<br>
(필자는 그냥  <context:component-scan base-package="kr.pe.jabsiri" /> 설정후 아무것도 하지 않는다. 뭐 몰라서 그런것도 있다. )<br>

사용 코드는는 다음과 같다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Controller<br>
 
public class HelloJabsiriController {<br>
 
    @Autowired<br>
 
    private HelloJabsiriService helloJabsiriService;<br>
 
    ...........<br>
 
}<br>
 
<br>
@Service("helloJabsiriService")<br>
 
public class HelloJabsiriServiceImpl implements HelloJabsiriService {<br>
 
    @Autowired<br>
 
    private HelloJabsiriDao helloJabsiriDao;<br>
 
                ...........<br>
 
}
위의 코드는 Controller에서 HelloJabsiriServiceImpl 의 interface인 HelloJabsiriService를 의존관계로 등록 한것이다.
자세히 보면 @Autowired를 적용한 변수명과 @Service 안에있는 String 값이 같다는걸 눈치챌수 있다.

4. @Service("xxxService")
@Service를 적용한 Class는 비지니스 로직이 들어가는 Service로 등록이 된다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Service("helloJabsiriService")<br>
 
public class HelloJabsiriServiceImpl implements HelloJabsiriService {<br>
 
    @Autowired<br>
 
    private HelloJabsiriDao helloJabsiriDao;<br>
 
    public void helloJabsiri() {<br>
 
        System.out.println( "HelloJabsiriServiceImpl :: helloJabsiri()");<br>
 
        helloJabsiriDao.selectHelloJabsiri();<br>
 
    }<br>
 
}
helloJabsiriDao.selectHelloJabsiri(); 와 같이 @Autowired를 이용한 객체를 이용하여 Dao 객체를 호출한다.

 
5. @Repository("xxxDao")
@Repository를 적용한 Class는 DataBaseAccess를 할수 있는 Dao로 등록된다.
1
2
3
4
5
6
7
8
9
10
11
@Repository("helloJabsiriDao")<br>
 
public class HelloJabsiriDaoImpl implements HelloJabsiriDao {<br>
 
    public void selectHelloJabsiri() {<br>
 
        System.out.println("HelloJabsiriDaoImpl :: selectHelloJabsiri()");<br>
 
    }<br>
 
}


Controller에 있는 @Autowired는 @Service("xxxService")에 등록된 xxxService와 변수명이 같아야 하며
Service에 있는 @Autowired는 @Repository("xxxDao")에 등로된 xxDao와 변수명이 같아야 한다.


출처 - 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
Posted by linuxism
,