* 필요한 환경 : jdk5.0 이상

@Required
목적 : 필수 프로퍼티를 지정
설정 위치 : setter메소드
추가설정 : RequiredAnnotationBeanPostProcessor 클래스를 빈으로 등록시켜줘야 한다. 해당 설정 대신에<context:annotation-config> 태그를 사용해도 된다.

@Autowired
목적 : 의존관계를 자동설정할 때 사용하며 타입을 이용하여 의존하는 객체를 삽입해 준다. 그러므로 해당 타입의 빈객체가 존재하지 않거나 또는 2개 이상 존재할 경우 스프링은 예외를 발생시키게 된다.
설정 위치 : 생성자, 필드, 메소드(굳이 setter메소드가 아니여도 된다)
추가설정 : AutowiredAnnotationBeanPostProcessor 클래스를 빈으로 등록시켜줘야 한다. 해당 설정 대신에 <context:annotation-config> 태그를 사용해도 된다.
옵션 : required - @Autowired어노테이션을 적용한 프로퍼티에 대해 굳이 설정할 필요가 없는 경우에 false값을 주며 이때 해당 프로퍼티가 존재하지 않더라도 스프링은 예외를 발생시키지 않는다. 디폴트값은 true

@Qualifier
목적 : @Autowired의 목적에서 동일 타입의 빈객체가 존재시 특정빈을 삽입할 수 있게 설정한다.
설정위치 : @Autowired 어노테이션과 함께 사용된다.
추가설정 : 동일타입의 빈객체 설정에서 <qualifier value="[alias명]" />를 추가하여 준다.
옵션 : name - alias명

@Resource
목적 : 어플리케이션에서 필요로 하는 자원을 자동 연결(의존하는 빈 객체 전달)할 때 사용
         @Autowired 와 같은 기능을 하며 @Autowired와 차이점은 @Autowired는 타입으로(by type),  @Resource는 이름으로(by name)으로 연결시켜준다는 것이다.
설정위치 : 프로퍼티, setter메소드
추가설정 : CommonAnnotationBeanPostProcessor 클래스를 빈으로 등록시켜줘야 한다. 해당 설정 대신에 <context:annotation-config> 태그를 사용해도 된다.
옵션 : name

@PostConstruct
목적 : 의존하는 객체를 설정한 이후에 초기화 작업을 수행하기 위해 사용
설정위치 : 초기화 작업 수행 메소드
추가설정 : CommonAnnotationBeanPostProcessor 클래스를 빈으로 등록시켜줘야 한다. 해당 설정 대신에 <context:annotation-config> 태그를 사용해도 된다.

@PreConstruct
목적 : 컨테이너에서 객체를 제거하기 전에 해야할 작업을 수행하기 위해 사용
설정위치 : 해당 작업 메소드
추가설정 : CommonAnnotationBeanPostProcessor 클래스를 빈으로 등록시켜줘야 한다. 해당 설정 대신에 <context:annotation-config> 태그를 사용해도 된다.

'Program > Java' 카테고리의 다른 글

애노테이션 기반 스케줄링  (0) 2009.12.27
스프링 3.0의 MVC 간편화  (0) 2009.12.27
Spring3.0 @MVC REST  (0) 2009.12.27
Spring MVC - Annotation Base HandlerInterceptor  (0) 2009.12.27
Spring MVC Annotation 기초  (2) 2009.12.27

스프링 3.0의 RESTful 기능

- URI 템플릿
@PathVariable 애노테이션으로 URI의 특정 위치의 값을 맵핑해올 수 있음.
@RequestMapping("/hotels/{hotelId}")
public String getHotel(@PathVariable hotelId, ModelMap model) {
  List<Hotel> hotels = hotelService.getHotels();
  model.addAttribute("hotels", hotels);
  return "hotels";
}
이때 /hotels/1 이런 요청이 들어오면 hotelId 매개변수로 1이라는 값을 받아올 수 있음.

두 개 이상 맵핑 할 수도 있고 변수명을 다르게 줄 수도 있음.
@RequestMapping(value="/hotels/{hotel}/bookings/{booking}", method=RequestMethod.GET)
public String getBooking(@PathVariable("hotel") long hotelId, @PathVariable("booking") long bookingId, ModelMap model) {
  Hotel hotel = hotelService.getHotel(hotelId);
  Booking booking = hotel.getBooking(bookingId);
  model.addAttribute("booking", booking);
  return "booking";
}

Ant 스타일 경로를 지정할 수도 있음.
@RequestMapping(value="/hotels/*/bookings/{booking}", method=RequestMethod.GET)
public String getBooking(@PathVariable("booking") long bookingId, ModelMap model) {

}

데이터 바인딩도 사용할 수 있음.
@InitBinder
public void initBinder(WebDataBinder binder) {
  binder.initBeanPropertyAccess();
  SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
  dateFormat.setLenient(false);
  binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false));
}

@RequestMapping("/hotels/{hotel}/dates/{date}")
public void date(@PathVariable("hotel") String hotel, @PathVariable Date date)
                throws IOException {

}
/hotels/1/dates/2008-12-18 이런 요청을 받으면 2008-12-18 이 부분 문자열을 Date 타입으로 바인딩 해 줌.

- Content Negotiation
Accept HTTP 헤더로 뷰를 판단함. 서버 응답은 Content-Type를 통해 뷰를 전달한다. 이런 과정을 content negotiation이라고 한다.

주요 클래스: ContentNegotiatingViewResolver

- Views
RSS 피드를 만들 때 사용할 수 있는 뷰: AbstractAtomFeedView, AbstractRssFeedView
XML 뷰: MarshallingView
JSON 뷰: JacksonJsonView

- HTTP Method Conversion
HTTP에서는 GET, POST, DELETE, PUT을 제공하지만 HTML은 GET, POST만 지원한다. 따라서 POST를 사용하면서 hidden parameter로 Method를 명시하면 해당 Method로 변환해주는 HiddenHttpMethodFilter 제공.
스프링 form 태그에서 이 기능을 지원하기 때문에 form태그의 method에 delete, put도 사용 가능.
<form:form method="delete">
    <p class="submit"><input type="submit" value="Delete Pet"/></p>
</form:form>

@RequestMapping(method = RequestMethod.DELETE)
public String deletePet(@PathVariable int ownerId, @PathVariable int petId) {
    this.clinic.deletePet(petId);
    return "redirect:/owners/" + ownerId;
}

- ETag support
ETga는 HTTP 1.1 호환 응답 헤더로 해당 URL에 있는 컨텐츠가 변경 됐는지 확인할 때 사용.
스프링은 ShallowEtagHeaderFilter를 제공하여 JSP로 랜더링한 결과를 캐싱하고 MD5 해시를 만들고 응답에 ETag를 반환해준다. 다음에도 사용자가 같은 자원을 요청하면 이전에 만든 해시를 If-None-Match 값으로 사용한다. 두 개 해시값을 비교해서 같으면 304(Not Modified)를 반환한다.

차후에 제공할 deep ETag는 모데인 객체나 RDBMS 테이블과 관련되어 있어 좀 더 복잡한데 JPA의 @Version(낙천적-롹킹할 때 사용하는 애노테이션) 기반 또는 ApsectJ의 애스팩트로 지원할 예정~ 이라고 합니다.

출처 : http://whiteship.me/2163


Spring core에서는 AOP를 이용하여 처리내용의 전, 후에 일괄적으로 적용하고 싶은 처리를 추가할수가 있다.
하지만 AOP는 interface를 구현한 곳에만 사용할 수가 있습니다.
그래서 Spring MVC의 Controller에는 상용할수 없죠
자 그럼 만약 로그인 체크를 해야한다면 모든 Controller에 로그인 체크 로직을 넣어줘야할까?. 아니다!
그럼 상위 Controller를 만들고 여기에 로그인 체크로직을 넣고 모든 Controller가 이를 상속 받아 쓰면 어떨까. 그남아 났군..

이런 것을 위해 Spring MVC 에서는 HandlerMapping이 HandlerInterceptor를 이용하해서 Controller가 요청하는 처리의 전과, 후후에 원하는 기능을 수행할수 있도록 해준다고한다.
( 뭐 깊숙한 내용까지 알려면 더 공부를하고, 아니면 그냥 Controller에서도 AOP와 같이 쓸수 있도록 제공해준다고 알자 )

HandlerInterceptor는 세가지 메소드를 정의한다

더보기

- preHandle : 클라이언트의 요청을 컨트롤러에 전달하기 전에 호출됨. false를 리턴하면 다음 내용은 실행하지 않는다.
- postHandle : 클라이언트의 요청을 처리한 뒤에 호출됨. 컨트롤러에서 예외가 발생되면 실행하지 않는다.
- afterCompletion : 클라이언트 요청 처리뒤 클리이언트에 뷰를 통해 응답을 전송한뒤 실행 됨. 뷰를 생설항때 예외가 발생해도 실행된다.

Interceptor의 구현
org.springframework.web.servlet.HandlerInterceptor 를 직접 구현해서 클래스를 작성해도 되지만 이러면 세개의 메소드를 모두 구현해야한다. 즉 preHandle 만 필요해도 postHandle과 afterCompletion 두 메소드를 모두 구현해야한다.
이를위해 Spring에는 HandlerInterceptorAdaptor 클래스를 제공한다.
이는 이미 HandlerInterceptor interface를 구현한 클래스로 위의 세개의 메소드를 이미 구현하고 있어 필요한것만 구현해서 사용하면 된다.

- Interceptor 구현


public class LoginCheckInterceptor extends HandlerInterceptorAdaptor {
    @Override
    public boolean preHandle(HttpServletRequest request,
                                         HttpServletResponse response, 
                                         Object handler) throws Exception{ 
        if ( !isLogin() ) { 
            return false;
        }
        return true;
    }
}


- dispatcher-servlet.xml의Interceptor 설정

<bean id="annotationMapper"
            class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
    <property name="interceptors">
        <list>
            <ref bean="userLoginInterceptor"/>
        </list>
    </property>
    <property name="alwaysUseFullPath" value="true"/>
</bean>

<bean id="userLoginInterceptor" class="com.questionbox.interceptor.UserLoginInterceptor" />

위와 같이 할때는 모든 페이지 URL에서 userLoginInterceptor를 실행하게 된다.

그리고
<list>
   
<ref bean="userLoginInterceptor"/>
    <ref bean="userLoginInterceptor2"/>
    <ref bean="userLoginInterceptor3"/>
</list>

과 같이 여러 인터페이스를 줄수도 있다.

참고 사이트 : http://www.scottmurphy.info/spring_framework_annotation_based_controller_interceptors

'Program > Java' 카테고리의 다른 글

어노테이션을 이용한 설정1 - context:annotation-config  (0) 2009.12.27
Spring3.0 @MVC REST  (0) 2009.12.27
Spring MVC Annotation 기초  (2) 2009.12.27
Annotation (since tiger / 1.5)  (0) 2009.12.27
About JAXB  (0) 2009.12.27
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의 어노테이션만 로딩을 하고
<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>
</context:component-scan>
applicationContext.xml에는 아래와 같이해서 Controller를 제외한 어노테이션만 로딩을 해야한다.
<context:component-scan base-package="com.enz.adnad">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"></context:exclude-filter>
</context:component-scan>


아래는 dispactcher-servlet.xml 에 설정하는 ViewResolver 로써 /WEB-INF/spring/ 로 시작하고 .jsp 로 끝나는 파일을 찾아 옵니다.
controller에서 return "hello/hello_jabsiri"; 와 같이 하면 /WEB-INF/spring/hello/hello_jabsiri.jsp 파일을 찾습니다.
<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에 대한 공부는 각자의 몫이고 필자 또한 나중에 정리를 할 것이다. )

다음은 사용의 예이다.
@Controller
public class HelloJabsiri {
@RequestMapping(value="/hello/hello_jabsiri.jab")
public String helloJabsiri(){
return "hello/hello_jabsiri"; //리턴값은 .jsp 확장자를 뺀 경로
}
}
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 객체들의 맴버변수가 같은것을 알아서 매핑시켜 값을 넘겨주기도 한다.

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


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

@Controller
@RequestMapping("/hello/*")
public class HelloJabsiri {
@RequestMapping(value="hello_jabsiri.jab")
public String helloJabsiri(){
return "hello/hello_jabsiri";
}
}

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

아래와 같이 설정해주면 사용할수 있지만 위에서처럼 scan으로 등록했으므로 안해도 된다.
<bean class=org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor />

(필자는 그냥  <context:component-scan base-package="kr.pe.jabsiri" /> 설정후 아무것도 하지 않는다. 뭐 몰라서 그런것도 있다. )

사용 코드는는 다음과 같다.
@Controller
public class HelloJabsiriController {
@Autowired
private HelloJabsiriService helloJabsiriService;
...........
}

@Service("helloJabsiriService")
public class HelloJabsiriServiceImpl implements HelloJabsiriService {
@Autowired
private HelloJabsiriDao helloJabsiriDao;
...........
}
위의 코드는 Controller에서 HelloJabsiriServiceImpl 의 interface인 HelloJabsiriService를 의존관계로 등록 한것이다.
자세히 보면 @Autowired를 적용한 변수명과 @Service 안에있는 String 값이 같다는걸 눈치챌수 있다.

4. @Service("xxxService")
@Service를 적용한 Class는 비지니스 로직이 들어가는 Service로 등록이 된다.
@Service("helloJabsiriService")
public class HelloJabsiriServiceImpl implements HelloJabsiriService {
@Autowired
private HelloJabsiriDao helloJabsiriDao;
public void helloJabsiri() {
System.out.println( "HelloJabsiriServiceImpl :: helloJabsiri()");
helloJabsiriDao.selectHelloJabsiri();
}
}
helloJabsiriDao.selectHelloJabsiri(); 와 같이 @Autowired를 이용한 객체를 이용하여 Dao 객체를 호출한다.

 
5. @Repository("xxxDao")
@Repository를 적용한 Class는 DataBaseAccess를 할수 있는 Dao로 등록된다.
@Repository("helloJabsiriDao")
public class HelloJabsiriDaoImpl implements HelloJabsiriDao {
public void selectHelloJabsiri() {
System.out.println("HelloJabsiriDaoImpl :: selectHelloJabsiri()");
}
}


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



6. @Transactional (이건 추후 작성 하겠음 )

'Program > Java' 카테고리의 다른 글

Spring3.0 @MVC REST  (0) 2009.12.27
Spring MVC - Annotation Base HandlerInterceptor  (0) 2009.12.27
Annotation (since tiger / 1.5)  (0) 2009.12.27
About JAXB  (0) 2009.12.27
StringUtils  (0) 2009.12.22

+ Recent posts