Java Property 는 기본적으로 String 으로 인식 하기 때문에, 다른 타입의 값을 setter 하는 경우 SpringExpressionLanguage (SpEL) 문법을 사용하여 설정 파일에서 타입 캐스팅을 해줘야 사용이 가능 하다.


<beans:property name="port" value="#{T(java.lang.Integer).parseInt(config['key'])}" 


Primitive Type 은 Null 값을 허용할 수 없기 때문에, Wrapper 클래스로 타입 캐스팅을 해서 사용 하자. Web 환경에서는 Null 값을 setter 할 수 없어 예외 상황이 발생 하기도 한다.


Primitive Wrapper Class Constructor Argument
boolean Boolean boolean or String
byte Byte byte or String
char Character char
int Integer int or String
float Float float, double or String
double Double double or String
long Long long or String
short Short short or String


Wrapper 클래스 종류는 위 표를 참고 하자.


참고 사이트


Java Web Application 은 /webapps 하위 에 있는 /WEB-INF 는 직접 적으로 접근이 불가능 하기 때문에 Servlet 을 통과하게 되어 있다. 단순하게 페이지만 보여주는 용도라면 모든 case by case 를 Controller 에 작성 하는 것은 불편 하기 때문에 view-controller 를 사용하면 편리하게 개발이 가능 하다.


dispatcher-servlet 를 설정 하는 xml 에 추가를 하자.


<mvc:view-controller path="/" view-name="/sample"/>


path, view-name 을 인자로 받는다. path 는 @RequestMapping 에서 사용 했던 value 값 이고, view-name 은 ModelAndView 에 setViewName(String viewName) 이라고 보면 된다.


<mvc:view-controller path="/" view-name="redirect:sample/"/>


redirection 하는 경우에는 view-name 의 앞에 redirect: 를 작성 하자.


@Controller, @Service, @Repository, @Component 를 사용하여 Bean 을 생성 하는 도중 중복 되어 충돌이 나는 현상 이다.


Annotation-specified bean name 'class' for bean class [class] conflicts with existing, non-compatible bean definition of same name and class [class]


해당 Annotation 의 경우 value 값을 지정 하지 않으면 Class 의 첫번째 문자열을 소문자로 하여 Bean ID 값을 생성 한다. 같은 Class 를 value 값을 지정하지 않았기 때문 이다.


@Controller("myController") 이와 같이 value 값을 설정 하여 해결 하자.


JSP Form 태그의 enctype="multipart/form-data" 속성을 적용 하지 않은 경우 발생 한다.


해당 속성을 추가 하도록 하자.


AOP pointcut 에 1개 이상의 조건을 사용 하는 경우 AND&&, OR|| 를 사용 한다. Java Annotation 방식으로 사용 하지 않고, XML 에서 사용 하는 경우 예외가 발생 한다.


XML은 기본적으로 <, >, & 등 사용 되고 있으므로, 이스케이프 문자로 변환 해야 한다.


  • &lt; <
  • &gt; >
  • &amp; &


아래와 같이 &amp; 로 수정해서 사용 하자.


<aop:pointcut id="test" expression="execution(public * *..*Sample.*(..)) &amp;&amp; !execution(public * *..*Sample.exclude(..))"/>


참고 사이트


Spring MVC 기능을 기반으로 개발 하면, 핵심 기능인 DI(Dependency Injection) 기능을 많이 사용하게 된다. 의존성 주입 이라고 보면 되겠다. setSample(new Sample()) 강제적으로 setter 할 필요 없이, Spring Container 가 @Inject, @Autowired, @Resource 를 사용하여 대신 setter 를 해준다고 보면 된다.


생각을 반대로 바꿔보자. Spring Container 에게 특정 객체를 달라고 요청 하는 것이다. 반대로 객체를 받아오는 경우를 DL(Dependency Lookup) 이라 한다. Spring ApplicationContext 에게 getBean(String id, class<?> clazz) 함수를 호출하여 반대로 필요한 객체를 받게 된다.


객체는 Bean Scope 의 영향을 받는다.


  • singleton: 그 말대로 하나의 객체만 존재 한다.
  • prototype: 객체를 매번 생성 하기 때문에, 다른 객체가 제공 된다.
  • request: http 요청을 기준으로 객체를 제공 한다. http 요청 생명 주기에 단 하나의 객체만 존재 한다. 다른 http 요청에 영향 받지 않는다.
  • session: session 발생을 기준으로 객체를 제공 한다. session 생명 주기에 단 하나의 객체만 존재 한다.
  • global session: 테스트를 해봐야 할 것 같다. (WAS의 ServletContext 와 같은 개념인듯 싶다.)


DL(Dependency Lookup) 의 사용을 위해서는 ApplicationContextAware 인터페이스 구현을 필요로 한다.


public class Sample implements ApplicationContextAware {
    private ApplicationContext applicationContext;

    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    public void sample() {
        Target target = this.applicationContext.getBean("ID", Target.class); // DL

        // Doing...
    }
}


위 샘플 소스를 참조 하면 ApplicationContext 를 받아 왔다. 이제 반대로 객체를 받아서 사용해 보자. 중앙에서 관리 하는 기능을 구현해야 할 일이 있다면, 편하게 사용되는 것 같다.


JSP 페이지 에서 날짜 값을 파라미터로 전달 할 때, 서버에서 String 으로 오는 날짜 값을 파싱하여, Date 객체로 넣는 작업이 예외 발생 시 Bad Request 가 발생 하였다. 이를 해결 하기 위해서는 직접 파싱 후 바인딩 하는 작업을 해줘야 한다. @initBinder 기능을 사용하면 되겠다.


@InitBinder
public void initBinder(WebDataBinder binder) throws Exception {
    binder.registerCustomEditor(Date.class, new PropertyEditorSupport() {

        public void setAsText(String text) throws IllegalArgumentException {
            try {
                setValue(new SimpleDateFormat("yyyy-MM-dd").parse(text));
            } catch (ParseException e) {
                setValue(null);
            }
        }
    });
}


요청을 받는 Controller 에 해당 코드를 추가 한다. setAsText(String text) 구현은 자유롭게 하면 되겠다. 위 코드는 샘플 이다. String 으로 전달되는 값을 Date 객체로 변경하여 넣어준다. 위 코드를 작성 했다면, Date 로 전달 되는 값을 받아볼 수 있게 된다.


Spring MVC 기반으로 개발 하는 경우, URL 기준이 아닌, Method 기준 으로 AOP 를 사용할 때가 있다. 스타일 차이라고 생각 한다.


보통 Interceptor 를 사용 하면, HttpServletRequest, HttpServletResponse 객체를 기본적으로 사용할 수 있다. Tomcat Filter 의 기능과 비슷하기 때문이다. 처리 방법에 따라 다르겠지만, 시점이 Controller 호출 전 이기 때문에, @ModelAttribute 를 사용하여 개발 한다면, Http 요청 시 사용되는 파라미터가 Model 로 값이 들어가기 전이기 때문에, 원시 적인 방법인 request.getParameter(String key) 함수를 사용해야 한다. 더 나은 방법은 AOP 기법을 활용 하는 것이다.


AOP 는 Method 기준으로 사용되기 때문에 원하는 시점을 선택하여 적용이 가능 하다. Controller 를 호출 하기전에 끼어드는 방식으로 Around 방식을 사용하면 Model 에 값이 들어간 후 이기 때문에, 데이터의 가공을 편하게 할 수 있다. Model은 joinPoint.getArgs() 함수를 사용하여 값 확인이 가능 하다. 아래는 예제 소스 이다.


/**
 * CustomAspect
 *
 * @author whitelife
 * @since 2014.10.05
 * @version 0.1
 */
public class CustomAspect {

    /**
     * Custom Aspect
     *
     * @param joinPoint Proxy Method Info
     * @return joinPoint
     * @throws Throwable
     */
    public Object customAspect(ProceedingJoinPoint joinPoint) throws Throwable {
        Object result = null;

        // (Before) doing...        

        for (Object obj : joinPoint.getArgs()) {
            if (obj instanceof HttpServletRequest || obj instanceof MultipartHttpServletRequest) {
                HttpServletRequest request = (HttpServletRequest) obj;

                // Doing...

            }
        }

        try {
            result = joinPoint.proceed();
        } catch (Throwable e) {
            throw e;
        }

        // (After) doing...     

        return result;
    }
}


ProceedingJoinPoint 클래스는 Target Class, Target Method 정보 를 담고 있다. proceed 메소드 호출 시 Target Method 를 실행 한다. 보통 joinPoint.getArgs() 함수 를 사용하여 Target Method 의 인자 값을 확인하여 사용하지만, 매번 Controller Method 에 인자로 HttpServletRequest 를 넣어줘야 하는 불편함이 있다.


HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();


위 방법을 사용하면, AOP 에서도 HttpServletRequest 객체에 접근이 가능하다. Controller 개발 시 인자로 HttpServletRequest 를 받을 필요가 없다. 편리하게 사용할 수 있겠다.


ServletRequestAttributes 는 HttpRequest 가 오는 경우, RequestContextListener.requestInitialized(ServletRequestEvent requestEvent) 함수에 의해 값이 전달 되기 때문에 값을 받을 수 있는 것이다. RequestContextListener 의 일부 이다. 아래와 같이 구현 되어 있다.


public void requestInitialized(ServletRequestEvent requestEvent) {
    if (!(requestEvent.getServletRequest() instanceof HttpServletRequest)) {
        throw new IllegalArgumentException(
                "Request is not an HttpServletRequest: "
                        + requestEvent.getServletRequest());
    }

    HttpServletRequest request = (HttpServletRequest) requestEvent
            .getServletRequest();
    ServletRequestAttributes attributes = new ServletRequestAttributes(
            request);
    request.setAttribute(REQUEST_ATTRIBUTES_ATTRIBUTE, attributes);
    LocaleContextHolder.setLocale(request.getLocale());
    RequestContextHolder.setRequestAttributes(attributes);
}


ArgumentResolver 를 사용하여 구현도 가능 하지만, 해당 기능은 공통적으로 필요한 값을 추가 하는 용도라고 판단이 된다.


무조건 정답이라고 할 수는 없다. 적합한 방법을 사용하여 작업 하면 된다고 판단 된다.


참고 사이트


+ Recent posts