Spring Camp 2013 with Scala

SPRING CAMP 2013: THE SPRING, SCALA EVENT OF THE YEAR!


상세내용:

http://springcamp.ksug.org/#javatuning


참가신청:

http://onoffmix.com/event/19279


강연자:

http://springcamp.ksug.org/#speakers


일정:

http://springcamp.ksug.org/#schedule



이번에 참가하는데요;;;


많은 경험하고 왔으면 좋겠네요^^

출처: 엄마가공부하래

서블릿은 기본적으로 상태를 유지하지 않는다. 상태를 유지해줘야 하는 경우에 사용한다. 

도메인 중심 프로그래밍 모델과 상태 유지를 위한 세션 도입의 필요성
ex) 수정 페이지의 처리 방법
  1. hidden 필드 사용시 단점
    • 데이타 보안의 문제.
    • 사용자 정보에 새로운 필드가 추가되거나 수정되었을 때 실수 우려.
  2. DB 재조회
    • 자원 낭비
    • 복잡한 코드
  3. 계층 사이의 강한 결합
    • 각 계층이 도메인 모델을 따라 만든 도메인 오브젝트에만 의존하도록 만들면 각 계층 사이에 의존성과 결합도를 대폭 줄일 수 있다.
    • 모든 계층의 코드가 서로 영향을 주지 않고 독립적으로 확장하거나 변경할 수 있고, 여러 개의 로직에서 공유할 수 있다.
    • 수정할 필요가 있는 필드가 어떤 것인지 모든 계층의 메소드가 다 알고 있게 하는 방식이다. 하지만 다음과 같은 단점이 존재한다. 
      • 애플리케이션이 복잡해질 경우 코드의 중복이 늘어난다.
      • 한쪽을 수정하면 연관된 코드도 다 함께 수정해줘야 한다.
      • 테스트가 힘들어 지고 테스트 코드에도 중복이 일어난다.

==> 그래서 해결책은 ???

[해결책]

@SessionAttributes

  1. 컨트롤러 메소드가 생성하는 모델 정보 중에서 @SessionAttributes에 지정한 이름과 동일한 것이 있다면 이를 세션에 저장해준다.
    • ==> 뷰가 이 모델을 참조해서 기존 사용자 정보를 폼에 뿌려줄 수 있게 하기 위함.
  2. @ModelAttribute가 지정된 파라미터가 있을 때 이 파라미터에 전달해줄 오브젝트를 세션에서 가져온다.
    • ==> 파라미터에 @ModelAttribute가 있으면 해당 타입의 새 오브젝트를 생성한 후에 요청 파라미터 값을 프로퍼티에 바인딩해준다.
    • ==> 하지만 @SeesionAttributes에 선언된 이름과 @ModelAttribute의 모델 이름이 동일하면,
      1. 세션에 같은 이름의 오브젝트가 존재하는지 확인
      2. 존재한다면 세션에 있는 오브젝트를 가져와 @ModelAttribute파라미터로 전달해줄 오브젝트로 사용한다
      3. 존재하지 않는 다면 오브젝트를 새로 만들어서 오브젝트로 사용한다.

@Controller
@SessionAttributes("user")
public class UserController {
            ...
@RequestMapping(value="/user/edit", method=RequestMethod.GET)
    public String form(@RequestParam int id, Model model) {
        model.addAttribute("user", userService.getUser(id));
        return "user/edit";
    }

    @RequestMapping(value="/user/edit", method=RequestMethod.POST)
    public String submit(@ModelAttribute User user) { ... }
}

a. form() : DB에서 user 오브젝트를 생성해서 폼으로 넘긴다. 
b. 폼에서는 user 오브젝트를 참조해서 정보를 뿌려준다.
c. 폼에서 사용자 입력 값을 submit() 으로 전송한다. 
d. submit() : 폼에서 전송해준 파라미터만 바인딩한 뒤에 컨트롤러의 user 파라미터로 넘겨
e. 만약 검증 오류가 발생하면 다시 사용자 폼을 띄워준다. 
  • 하나 이상의 모델을 세션에 저장하도록 지정할 수 있다.
  • @SessionAttributes의 설정은 클래스의 모든 메소드에 적용된다.
  • 컨트롤러 메소드에 의해 생성되는 모든 종류의 모델 오브젝트는 @SessionAttributes에 저장될 이름을 갖고 있는지 확인된다.
  • 여러대로 구성된 서버의 경우는 어떻게 처리해야 하나?????

SessionStatus

  • @SessionAttributes를 사용할 때는 더 이상 필요 없는 세션 애트리뷰트를 코드로 제거해줘야 한다.
  • SessionStatus는 컨트롤러 메소드의 파라미터로 사용할 수 있는 스프링 내장 타입이다.

@RequestMapping(value="/user/edit", method=RequestMethod.POST)
public String submit(@ModelAttribute User user, SessionStatus sessionStatus) { 
    this.userService.updateUser(user);    
    sessionStatus.setComplet();    // 세센에 저장된 정보를 모두 제거한다. 
    return "user/editsuccess";
}

등록 폼을 위한 @SessionAttributes 사용

  • 등록 폼에서도 @SessionAttributes를 사용하자.
  • 더 강력한 상태유지 방식의 애플리케이션을 만들려면 스프링의 포트폴리오 프로젝트인 SWF(Spring Web Flow)를 검토해 보자.

스프링 목 오부젝트와 AbstractDispacherServletTest를 이용해 세션 테스트 만들기
준비중


Spring message 태그를 이용한 메세지 관리

아래 예와 같이 HTML이 Title에 대한 일관성을 확보하기 위하여 JSP 에서 Title 태그에 문자열을 그대로 입력하지않고 메세지 소스를 이용한다. 

<title><spring:message code="${pageTitle}" text="Logged Out" /></title>
<label for="username"><spring:message code="screen.welcome.label.netid" /></label>

ㅁ ResourceBundleMessageSource 사용하기
    - Spring 프레임워크 웹 어플리케이션에서 MessageSource 소스 관리를 위하여
     ResourceBundleMessageSource을 사용한다.

    - 보통 메세지 .properties 파일들이 WEB-INF/classes 디렉토리에 모여져 있다.
    
     - MessageSource가 변경될 경우 MessageSource 를 반영할려면 애플리케이션 서버를 재시작해야한다.
    

더보기



ㅁ ReloadableResourceBundleMessageSource 사용하기 - 이것이 아주 유용한듯
    - 
애플리케이션 서버는 클래스패스에 있는 모든 리소스르 캐싱한다. 따라서 파일이 변경되더라도 적용되지
        않는다. 그러나  ReloadableResourceBundleMessageSource 를 사용하면 MessageSource가 
        변경되어도 애플리케이션을 재시작할 필요가 없다.

     - ReloadableResourceBundleMessageSource 를 사용하기 위해서는 WEB-INF/classes 디렉토리가 
        아닌 다른 디렉토리에서 MessageSource 파일을 관리해야 한다. (예 :  WEB-INF/message)
 

접기

<bean id="messageSource" class="org.springframework.context.
                                                   support.ReloadableResourceBundleMessageSource"
>
    <property name="basenames">
        <list>
            <value>/WEB-INF/messages/DBQuery</value>
            <value>/WEB-INF/messages/Messages</value>
        </list>
    </property>
    <property name="cacheSeconds" value="5"/>
</bean>

접기


    - 파일 변경 확인을 위한 모니터링 시간 설정 [property name="cacheSeconds" value="5"]

ㅁ 클래스패스가 아닌 filesystem 경로 로딩하기
    - 웹 애플리케이션이 아닌 경우 filesystem 경로 로딩할 경우 클래스 패스를 추가하거나 절대경로를 지정해
       주어도 된다.

    - ResourceBundleMessageSource 에선 사용할 수 없었던 file: 을
      ReloadableResourceBundleMessageSource의 basenames에서는 사용할 수 있다.

접기

<bean id="messageSource" 
          class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basenames">
<list>
<value>file:E:/svn/71common/htdocs/WEB-INF/classes/messages/messages_appr_msg</value>
</list>
</property>
<property name="cacheSeconds" value="0"/>
</bean>

접기









추가내용


html 코드에서 위에 설정한 메시지를 사용.

<tr>
    <td width="150" class="ct_td"><spring:message code="movie.title" />&nbsp;*</td>
    <td bgcolor="D6D6D6" width="1"></td>
    <td class="ct_write01">
        <form:input path="title" cssClass="ct_input_g" cssErrorClass="text medium error" size="40" maxlength="50" />
        <form:errors path="title" cssClass="errors" />
    </td>
</tr>


spring:message에는 다음 7가지가 있습니다.

code:          fmt:message의 key에 해당

arguments:                리소스 번들의 메시지에 {0}, {1} 같은 기호 자리에 들어갈 값을 나열

argumentSeparator:    arguments 속성에 값을 구분하는 기호, 기본은 콤마(',') 

text:                          code에 해당하는 메시지가 리소스 번들에 없을 때 사용될 메시지

message:                  MessageSourceResolvable 인터페이스를 구현한 객체 또는 MessageSourceResolvable를 나타내는 spel 식. 에러 메시지를 표시하려고 한다면 필요하겠죠.

htmlEscape:              true일 때 HTML 엔티티를 인코딩

javaScriptEscape:      true일 때 자바스크립트 문자열로 인코딩 

var:                           fmt:message와 동일

scope:                      fmt:message와 동일


출처: https://groups.google.com/forum/#!topic/ksug/elQOAa2-53Y






context:property-placeholder 와 util:properties 를 비교해서 설명해주셔서 기존 소스들을 고치는데 큰 도움이 되었습니다~


spring 설정 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"

xmlns:mvc="http://www.springframework.org/schema/mvc"

xsi:schemaLocation="http://www.springframework.org/schema/mvc 

      http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd

      http://www.springframework.org/schema/beans 

      http://www.springframework.org/schema/beans/spring-beans-3.2.xsd

      http://www.springframework.org/schema/context 

      http://www.springframework.org/schema/context/spring-context-3.2.xsd">

<context:component-scan base-package="com.tutorialspoint" />


 <!-- context:property-placeholder location="classpath:database.properties"  /!-->

<context:property-placeholder location="/WEB-INF/*.properties" />

굵은 표시를 한 부분을 추가한다.

classpath:database.properties의 의미는 classpath로 지정된 경로들에 있는 database.properties를 읽어오라는 뜻이라한다.

classpath:properties/*.properties의 의미는 classpath로 지정된 경로들에 있는 확장자가 properties인 파일들을 모두 읽어 오라는 뜻이라한다.


난 /WEB-/INF/database.properties,/WEB-/INF/file.properties  2개의 properties를 넣어서

<context:property-placeholder location="/WEB-INF/*.properties" />  이렇게 사용하였다.

주의할 점은 properties들에 같은 key 값이 있다면 원하지 않는 데이터가 읽힐수 있다고 한다. 그럴 경우 다른 방법을 사용해야한다고 한다.


spring 설정 xml에 추가로 다음과 같이 db연결 정보를 변경한다.


(두번째)

 기존코드

     <bean id="dataSource"
        class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="cubrid.jdbc.driver.CUBRIDDriver" />
        <property name="url"
            value="jdbc:cubrid:localhost:30000:springdemo:::?charset=UTF-8" />
        <property name="username" value="dba" />
        <property name="password" value="admin" />
    </bean> <!-- Definition for studentJDBCTemplate bean -->
    

 신규코드

     <bean id="dataSource"
        class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="${jdbc.driver}" />
        <property name="url" value="${jdbc.url}" />
        <property name="username" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />


(세번째)

database.properties

jdbc.driver=cubrid.jdbc.driver.CUBRIDDriver
jdbc.url=jdbc:cubrid:localhost:30000:springdemo:::?charset=UTF-8
jdbc.username=dba
jdbc.password=admin


file.properties

file_dir.url=/static/file_upload_data/
file_dir.thumbnail.url=/static/file_upload_data/thumbnail/
file_dir.english_word_audio_file.url=/static/english_word_audio_file/


(네번째)

자바 코드에서 해당 properties 값을 가져다 쓸때는 아래와 같이 했다.

@Value("${file_dir.url}")
private String fileUploadLoc;

속성으로 지정해야했다. 지역변수로는 annotation을  이용해서는 받아올수 없었다.

properites에 해당 키값이 없다면 컴파일시에 오류를 낸다.


위에 내용은 내가 메인으로 사용할 properties의 내용을 가져올때 사용할 방법이다.

나혼자 만들때는 문제가 안되겠지만 혹시나 나중에 다른사람들과 협업을 할때를 고려하여 다른 방법도 적는다.


SpEL이용

(첫번째)

<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"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:util="http://www.springframework.org/schema/util"
    xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.2.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd">


<util:properties id="db" location="/WEB-INF/database.properties" />

<util:properties id="file" location="/WEB-INF/file.properties" />

위에 굵게 표시한 부분을 넣어줘야한다.

주의 할점은  첫번째와는 다르게 <util:properties id="db" location="/WEB-INF/*.properties" /> 이렇게

사용할수가 없다. 에러가 발생한다.   파일명을 온전히 써주어야한다.


(두번째)


이전코드

   <bean id="dataSource"
        class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="${jdbc.driver}" />
        <property name="url" value="${jdbc.url}" />
        <property name="username" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />

 신규코드

 <bean id="dataSource"
    class="org.springframework.jdbc.datasource.DriverManagerDataSource">
       <property name="driverClassName" value="#{db['jdbc.driver']}" />
       <property name="url" value="#{db['jdbc.url']}" />
       <property name="username" value="#{db['jdbc.username']}"  />
       <property name="password" value="#{db['jdbc.password']}"  />
         </bean> <!-- Definition for studentJDBCTemplate bean -->  


(세번째 properties 설정은 동일하다.)

(네번째) 소스코드에서 사용할때 

    @Value("#{file['file_dir.url']}")
    private String fileUploadLoc;

소스코드에서 사용할때 첫번째 것은 동일한 key가 두개이상 존재할때 내가 원하는 값을 선택할수 없는 문제가 있고.

두번째것은 값이 실제로 존재하지 않아도 오류를 내지않고 null로 들어가게 된다.


두방법을 모두 한꺼번에 사용해도 에러가 발생되지 않는다.

단 두번째 방법을 사용하고 spring 설정 xml에 첫번째 방법의 태그를 지우고

첫번째 방법과 같이 

@Value("${file_dir.url}")
private String fileUploadLoc;

쓰게 되면 fileUploadLoc에는 "${file_dir.url}"  라는 문자열이 들어가게 된다. 조심해야한다.


JSP에서 properties 읽기(펌 : http://nkjava.blogspot.in/2013/07/springmvc-read-property-in-jsp.html)

Spring config
<util:properties id="db" location="/WEB-INF/database.properties" />
jsp
<spring:eval expression="@db.getProperty('jdbc.password')" />

테스트 해보니까 SpEL만 되는 것 같다.  참고 자료에는 context:property-placeholder 태그도 사용했지만 

없어도 정상동작 하는 것을 확인하였다.


저작자 표시


출처: spring 설정 xml과 소스코드에서 properties 사용하기

<util:properties/> 와 Spring EL 로 값 가져오기


예제로 맨든 이클립스 프로젝트(maven project)

 SpringProperty.zip




XML 설정 - xmlns를 설정해주는걸 알게 되었음. - 당연한건데;;;;;;ㅠㅠ

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?xml version=" 1.0"="" encoding="UTF-8" ?=""><beans xmlns="http://www.springframework.org/schema/beans"
    xsi:schemaLocation="
 
    ....
 
    <util:properties id="prop" location="classpath:config/properties/sample.properties"/>
 
    ....
 
</beans>





sample.properties

1
2
3
4
sample.prop1 = test
 
# 우쭈쭈~
sample.prop2 = \uc6b0\ucb48\ucb48~






Spring EL 로 값 가져오기(SampleBean.java) - 기존 블로그들에는 설명만 있어서 알 수 없었던 부분

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package com.tistory.stove99;
 
import java.util.Properties;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
 
@Component
public class SampleBean {
    // Spring EL 로 값 가져오기
    // Spring EL 이기 때문에 자유롭게 메소드 호출도 가능함. String 의 concat 메소드 호출
    @Value("#{prop['sample.prop1'].concat(' abc')}") private String value1;
    @Value("#{prop['sample.prop2']}") private String value2;
     
     
     
    // util:properties 로 생성된 빈은 java.util.Properties 의 인스턴스이기 때문에
    // 요렇게 Autowired 해서 쓸 수 있다.
    @Autowired Properties prop;
     
     
     
     
    public String val(String key){
        return prop.getProperty(key);
    }
     
    public String toString(){
        return String.format("value1 : %s, value2 : %s", value1, value2);
    }
}

Spring EL에 대해서 더 알아보고 싶으면 요 사이트를 참고하면 친절하게 알려줌.

http://static.springsource.org/spring/docs/3.0.x/reference/expressions.html






Test(SampleTest.java)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import org.springframework.context.support.ClassPathXmlApplicationContext;
 
import com.tistory.stove99.SampleBean;
 
 
public class SampleTest {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("config/spring/*-context.xml");
         
        SampleBean sample = context.getBean(SampleBean.class);
         
        // test
        System.out.println(sample.val("sample.prop1"));
         
        // value1 : test abc, value2 : 우쭈쭈~
        System.out.println(sample);
    }
}


저작자 표시 비영리 변경 금지

출처: 스토브 훌로구

전자정부 샘플 소스를 설치하고 보면.


dispatcher-servlet.xml 에는


<context:component-scan base-package="egovframework">

        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>

        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>

        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/>

</context:component-scan>


이렇게 선언되있고


context-common.xml 에는


<context:component-scan base-package="egovframework">

       <context:exclude-filter expression="org.springframework.stereotype.Controller" type="annotation"/>

</context:component-scan>



이렇게 되있는데요.


dispatcher-servlet.xml 에서 include-filter  Controller 를 해주고


context-common.xml 에서 exclude-filter Controller 를 해주는데


이게 맞는건가요?


안녕하세요 프레임워크센터입니다.


해당 Component-scan 부분은 비즈니스레이어와 클라이언트 레이어를 분리하기 위하여 각각servlet과 spring선언 부분에 나뉘어 선언이 되어 있습니다.


한군데로 합치셔도 무방하며 이렇게 2가지 레이어로 분리한 이유는 하나의 클라이언트에서 여러 비즈니스레이어를 공유할 수 있는 장점이 있기 때문입니다.


수고하세요.



출처: http://www.egovframe.go.kr/uss/olh/qna/QnaInqireCoUpdt.do?qaId=QA_00000000000008013&passwordConfirmAt=

+ Recent posts