'분류 전체보기'에 해당되는 글 243건
- 2009.05.10 :: [JSP] WARNING: Parameters: Invalid chunk ignored. remove
- 2009.05.09 :: [이클립스]Eclipse JSP EDITOR(JSP 에디터)
- 2009.05.09 :: [jQuery 관련] Javascript 전용 IDE Aptana
- 2009.05.09 :: Eclipse Memory Analyzer (MAT)힙 덤프를 분석해주는 이클립스 플러그인입니다
- 2009.05.09 :: HTTP header 정보를 보여주는 간단한 플러그인
- 2009.05.08 :: 사람을 위한 자동화: Eclipse 플러그인으로 코드 품질 높이기 (한글)
- 2009.05.08 :: asp에서 md5변환하기
- 2009.05.07 :: 자바 Annotation 사용하기
- 2009.05.07 :: Log4J 레이아웃
- 2009.05.07 :: Log4J 사용하기
- 2009.05.07 :: 이클립스에서 실행 중에 특정 표현식 값 확인하기
- 2009.05.07 :: 이클립스에서 에러 위치로 이동 단축키 1
- 2009.05.07 :: [이클립스 플러그인]LogWatcher
- 2009.05.07 :: Spring XML설정파일을 위한 12가지 가장 좋은 선택
- 2009.05.07 :: JXL로 Excel파일 만들고 PC에 저장하기
- 2009.05.07 :: 자바 JXL API 를 이용한 Excel 파일 만들기
- 2009.05.06 :: JUNIT
- 2009.05.06 :: JSEclipse 설치
- 2009.05.06 :: eclipse.ini 설정
- 2009.05.06 :: Property Editor
- 2009.05.06 :: Singleton 패턴
- 2009.05.06 :: Template Method패턴
- 2009.05.06 :: Adapter 패턴
- 2009.05.06 :: Iterator패턴
- 2009.05.06 :: 스프링 샘플
- 2009.05.06 :: 스프링 > 스트럿츠2
- 2009.05.06 :: [스프링]Spring Mapping Patterns
- 2009.05.06 :: 스프링 handlerMapping
- 2009.05.06 :: 스프링2.5 - 커스텀 태그
- 2009.05.06 :: 스프링 Controller
tomcat작업시 parameter 전송때마다 로그에 기록되는 다음과 같은 메세지 제거 방법
WARNING: Parameters: Invalid chunk ignored. remove
1.parameter로 넘길 폼태그에서 name="xxx"에서 xxx가 정상적이지 않을때.
2. tomcat설치경로/conf/logging.properties 편집
3. org.apache.tomcat.util.http.Parameters.level = SEVERE 추가
tomcat 재시작...
이라고 한다...
2009. 5. 10 오후 3:50:54 org.apache.tomcat.util.http.Parameters processParameters
경고: Parameters: Invalid chunk ignored.
2009. 5. 10 오후 3:50:54 org.apache.tomcat.util.http.Parameters processParameters
경고: Parameters: Invalid chunk ignored.
2009. 5. 10 오후 3:50:54 org.apache.tomcat.util.http.Parameters processParameters
경고: Parameters: Invalid chunk ignored.
http://amateras.sourceforge.jp/cgi-bin/fswiki_en/wiki.cgi?page=EclipseHTMLEditor
Eclipse HTML Editor Plugin
Eclipse HTML Editor is an Eclipse plugin for HTML/JSP/XML Editing. It works on Eclipse 3.0 (or higher), JDT and GEF. It has following features.
- HTML/JSP/XML/CSS/DTD/JavaScript Hilighting
- HTML/JSP Preview
- JSP/XML Validation
- Contents Assist (HTML Tags/Attributes, XML based on DTD and JSP taglib and more)
- Wizards for creating HTML/JSP/XML files
- Outline View
- Editor Preferences
- Editor Folding
- Web Browser (It works as an Eclipse's editor)
- Image Viewer
- Tag Palette
- CSS code completion and outline
- DTD code completion, outline and validation
- JavaScript code completion, outline and validation
Screen Shot
Download & Installation
Download from here. Put the downloaded JAR file into ECLIPSE_HOME/dropins.
EclipseHTMLEditor requires GEF (GEF is optinal. If GEF isn't installed, the Palette view would be unavailable). You can install it using Eclipse Update Manager.
Note: SWT Browser widget (HTMLEditor uses it for preview HTML and JSP) requires Mozilla in the Linux. See details at The SWT FAQ. Also you can disable preview in the preference dialog. Choose [Window]->[Preferences]->[Amateras] and check [Disable Preview]. Then you would be able to use HTMLEditor without Mozilla.
License
![](http://blogfiles5.naver.net/data41/2009/3/7/260/290165_gozjkim.jpg)
Aptana는 Javascript 전용 개발툴(IDE)입니다.
css, html 자바스크립트 코드 어시스트도 제공되고 대표적인 자바스크립트 프레임웍 라이브러리 까지 제공한답니다.
이클립스 기반에 플러그인으로 개발되고 리치클라이언트와 플러그인 방식 모두를 지원한다는군요.
jQuery등의 자바스크립트 라이브러리를 이용해 개발시 유용한 IDE라고 생각되서 jQuery를 본격적으로 공부하기 전에 추천한방 때리고 갑니다.
홈페이지
설치
http://www.aptana.com/studio/download
이클립스에서 software update를 통해 플러그인을 설치하거나 플러그인을 사이트에서 직접 다운받아서 설치해도 되는데
저는 위 화면에서와 같이 Aptana사이트에서 제공하는 별도에 Full 설치본을 다운받아서 쓰고 있습니다.
그 이유는 이클립스 플러그인 update방식으로 설치 하게 되면 제대로 설치가 안되는 경우가 종종 있고 기존에 이클립스에 다른 플러그인들이 덕지덕지 붙어있는 상태에서 Aptana까지 더해지면 이클립스가 너무 무거워져서 사용하기 불편해지기 때문입니다.
뭐...컴퓨터 사양이 좋으신분은 이클립스 플러그인으로 설치하셔도 무방합니다.
설치가 완료되고 실행하면 위에 화면이 나옵니다.
자~ 이제 jQuery 테스트 프로젝트를 함 만들어 볼까요~
일단 프로젝트 생성 전에 저처럼 Aptana studio를 설치하셨다면 초기에 jQuery1.3 플러그인은 설치가 되지 않으셨을 겁니다.
따로 플러그인을 설치해야되는데 지금부터 설명해 드리겠습니다.
아래화면 처럼 버튼을 누르고 Plugins Manager를 선택하게 되면...
아래와 같이 설치되지 않은 플러그인 목록이 나옵니다.
아래 목록에서 원하는 플러그인을 선택(오른쪽 클릭 -> install)해서 설치하시기만 하면 됩니다!
참~ 쉽죠~
jQuery 1.3을 선택해서 플러그인을 설치하신 다음에
Project부분에서 오른쪽 클릭 후 New -> Default Web Project를 클릭합니다.
프로젝트명을 입력하신후에 Next~
다음의 화면에서 jQuery 1.3을 선택합니다~
Next~
Finish~
완료~
여기 까지 기본적인 소개와 설치 그리고 프로젝트 설정까지 설명을 해드렸습니다.
이클립스 기반의 IDE이기 때문에 일단 이정도만 설명을 보시고 나머지는 실제로 사용해보시면 모든 기능을 쉽게 사용하실 수 있으실 겁니다.
다음에는 Aptana IDE를 사용하면서 본격적으로 jQuery API를 예제와 함께 하나씩 파헤쳐보도록 하겠습니다.
언제가 될지는 모르겠군요 ㅎㅎ
http://download.eclipse.org/technology/mat/0.7/update-site/ :이클립스 update URL
힙 덤프를 분석해주는 이클립스 플러그인입니다.
상당히 다양한 뷰로 분석이 가능합니다. OQL(Object Query Language)라는 개념을 제공해서 쿼리를 사용해서 덤프 내의 정보를 조회할 수 있습니다.
select * from java.lang.String이처럼 쿼리를 실행하면 덤프에 남아있는 String 인스턴스 정보 전체를 찾아 줍니다. 이 정보를 가지고 여러 가지 분석을 할 수 있습니다.
TPTP의 힙 덤프 분석 버전이라고 할까요? 너무 많은 정보를 제공하는데, 문제는 여기서 어떤 방법으로 좋은 정보를 추려낼 수 있는지가 관건입니다.
활용법이나 기능들은 너무 많으니...관련 링크를 참조하시면 될 것 같습니다.
다섯 개의 유용한 플러그인으로 Eclipse 내에서 코드 품질 분석 자동화 하기 ![]() |
![]() |
![]() |
난이도 : 초급 Paul Duvall, CTO, Stelligent Incorporated 2007 년 4 월 24 일 코드를 빌드하기 전에 코드에서 중대한 문제를 발견할 수 있다면 어떨까요? 재미있게도, 소프트웨어에 문제가 드러나기 전에 문제를 발견할 수 있도록 해주는 JDepend와 CheckStyle 같은 Eclipse 플러그인이 있습니다. 사람을 위한 자동화 시리즈에서는, 자동화 전문가 Paul Duvall이 Eclipse에서 정적인 분석 플러그인을 설치, 구성, 사용하는 방법을 예제를 통해 설명합니다. 이제 여러분도 개발 사이클에서 문제를 조기에 방지할 수 있습니다.
소프트웨어를 개발할 때, 필자가 기본적으로 강조하는 부분은 결함이 코드 베이스에 들어가지 않도록 하는 것 또는 이것의 수명을 제한하는 것이다. 다시 말해서, 가능하면 빨리 결함을 찾으려고 노력한다. 더 나은 코드를 작성하는 방법을 배우고, 소프트웨어를 효과적으로 테스트 하는 방법을 배워갈수록, 결함을 더욱 잘 찾아내게 된다. 하지만, 중대한 결함을 찾을 수 있는 보안 망(safety net)도 갖고 싶기는 하다. 본 시리즈의 8월 기사에서, 감시 툴을 빌드 프로세스에 통합하면(Ant 또는 Maven을 사용함) 잠재적 결함들을 찾는데 효과적이라는 결론을 내렸다. 비록, 이러한 방식이 일관성을 유지하고 IDE를 능가하는 부분도 있지만, 약간 보수적(reactionary) 이기도 하다. 여러분은 소프트웨어를 로컬에서 구현하거나, Continuous Integration 빌드가 실행되기를 기다려야 한다. 하지만, Eclipse 플러그인을 사용하면, Continuous Integration을 통해 빌드 또는 통합하기 전에 위반 사항들을 발견할 수 있다. 바로 이것을 진보적인 프로그래밍(progressive programming)이라고 할 수 있으며, 코딩 하면서 품질 체크도 실행할 수 있다. 이 글에서는 "5대" 코드 분석 영역에 대해 설명하겠다.
이러한 분석 영역들은 다음과 같은 Eclipse 플러그인을 사용한다.
Eclipse 플러그인 설치는 매우 간단하며, 단지 몇 단계만 거치면 된다. 시작하기 전에, 플러그인의 다운로드 사이트의 URL을 파악하는 것이 좋다. 표 1에서 필자가 이 글에서 사용하는 플러그인 리스트를 정리했다. 표 1. 코드 향상 플러그인과 설치 URL 리스트
이제 여러분은 유용한 플러그인을 어디서 구하는지 알았으니, 설치는 간단하다. Eclipse를 시작하고 다음 단계들을 수행한다.
이 단계를 수행하면 Eclipse 플러그인이 설치된다. 플러그인의 이름과 관련 다운로드 위치를 변경하라.
코드 베이스에 대한 관리능력은 소프트웨어의 총 비용에 직접적인 영향을 미친다. 더욱이, 관리능력은 개발자의 욕구 불만에도 기여한다. 코드 변경이 쉬울수록, 새로운 제품 기능을 추가하기도 쉽다. CheckStyle 같은 툴은 큰 클래스, 긴 메소드, 사용되지 않는 변수 등에 대한 관리능력에 영향을 미치는 코딩 표준의 위반 사항들을 찾는데 도움이 된다.
Eclipse용 CheckStyle 플러그인을 사용하면 코딩 하면서 소스 코드 내에서 다양한 위반 사항에 대해 알 수 있고, 개발자들이 체크인 전에 위반 사항을 고치게 된다. CheckStyle 플러그인을 지속적인 코드 리뷰에 사용하는 것으로 생각하면 된다. CheckStyle 플러그인을 설치하고, 다음과 같이 구성한다. (그림 4)
Eclipse는 워크스페이스를 재구현 하고 Eclipse 콘솔 안에 코딩 위반 사항들을 발견해 낸다. (그림 5) 그림 5. Eclipse내에 나타난 CheckStyle 위반 사항 리스트 ![]() CheckStyle 플러그인을 사용하여 Eclipse 내에 코딩 표준 검사를 포함시키면, 코딩 하는 동안 코드를 향상시킬 수 있고, 개발 사이클에서 조기에 소스 코드의 잠재적 결함을 발견할 수 있다. 여러분의 시간, 좌절감, 나아가서 프로젝트 비용까지 줄일 수 있다는 이점도 있다.
Coverlipse는 상응하는 테스트를 갖고 있는 소스 코드의 비율을 평가하는데 사용할 수 있는 Eclipse 플러그인이다. Coverlipse를 사용하여 코드를 작성하는 동안 코드 커버리지를 평가할 수 있다. 벌써 패턴을 인식하고 있나? Coverlipse 플러그인을 설치하고 Run Eclipse 메뉴 아이템을 선택하여 JUnit과 연결시킨다. 이렇게 하면 JUnit, SWT 애플리케이션, 자바™ 애플리케이션 같은 실행 구성들이 나타난다. 오른쪽 클릭하여 JUnit w/Coverlipse 노드에서 New를 선택한다. 여기서부터, JUnit 테스트의 위치를 규명해야 한다. (그림 6) 그림 6. Coverlipse를 구성하여 코드 커버리지 확보하기 ![]() Run을 클릭하면, Eclipse는 Coverlipse를 실행하고 소스 코드에 마커를 삽입한다. (그림 7) 이것은 JUnit 테스트로 연결되었던 코드의 부분들을 나타낸다. 그림 7. Coverlipse에 의해서 생성된 리포트와 삽입된 클래스 마커 ![]() 보다시피, Coverlipse Eclipse 플러그인을 사용하여 훨씬 더 빠르게 코드 커버리지를 결정할 수 있다. 이 기능은 코드를 CM 시스템에 체크인 하기 전에 테스팅 하는데 도움이 된다. 이 얼마나 진보적인 프로그래밍인가?
PMD 플러그인은 CPD(Copy Paste Detector)라고 하는 기능을 제공하는데, 이것은 중복된 코드를 찾아낸다. Eclipse에서 이 툴을 사용하려면, CPD 유틸리티가 포함된 PMD용 Eclipse 플러그인을 설치해야 한다. 중복 코드를 찾아내려면, Eclipse 프로젝트를 오른쪽 클릭하고, PMD | Find Suspect Cut and Paste를 선택한다. (그림 8) 그림 8. CPD 플러그인을 사용하여 Copy and Paste 체크 실행하기 ![]() CPD를 실행했다면, 그림 9. Eclipse 플러그인에서 생성된 CPD 텍스트 파일 ![]() 중복 코드를 직접 찾기란 어려운 일이지만, CPD 같은 툴을 사용하면 코딩 하는 동안 중복 코드를 빠르게 찾아낼 수 있다.
JDepend는 무료로 사용할 수 있는 오픈 소스 툴로서, 패키지 의존성에 대한 객체 지향식의 측정법을 제공한다. 코드 베이스의 복원력에 대한 지표라고 할 수 있다. 다시 말해서, JDepend는 아키텍처의 정합성을 측정하는데 사용된다. Eclipse 플러그인 외에도, JDepend는 Ant 태스크, Maven 플러그인, 자바 애플리케이션을 사용하여 측정 결과를 얻는다. 각각은 같은 정보를 다르게 전달한다. 하지만, Eclipse 플러그인이 가진 중요한 차이점 및 효과라고 한다면 정보를 (여러분이 코딩한 대로) 소스 코드에 매우 근접하게 제공한다는 점이다. 그림 10은 소스 폴더를 오른쪽 클릭하고, Run JDepend Analysis를 선택하여 JDepend용 Eclipse 플러그인을 사용하는 방법을 보여준다. 소스 코드를 갖고 있는 폴더를 선택하라. 그렇게 하지 않으면, 메뉴 옵션을 볼 수 없다. 그림 10. JDepend Analysis를 사용하여 코드 분석하기 ![]() JDepend 분석을 실행할 때 생성되는 리포트는 그림 11에 나와있다. 왼쪽 패인은 패키지를 나타내고, 오른쪽 패인은 각 패키지의 의존성을 나타낸다. 그림 11. Eclipse 내 프로젝트의 패키지 의존성 ![]() JDepend 플러그인은 이 아키텍처가 시간이 흘러도 유지될 수 있는지의 여부를 볼 수 있는 많은 정보를 제공하고 있다. 가장 중요한 것은 코딩 하는 동안 데이터를 볼 수 있다는 점이다.
"5대" 코드 분석 영역의 마지막 영역은 복잡성이다. Eclipse는 Metrics라고 하는 플러그인을 제공한다. 이것은 복잡성을 포함하여 유용한 코드 메트릭스를 제공하는데, 이것은 메소드의 고유 경로의 수를 측정한 것이다. Metrics 플러그인을 설치하고 Eclipse를 재시작 한다. 다음 단계들을 수행한다.
앞서 언급했지만, Eclipse Metrics는 소프트웨어를 개발하는 동안 코드를 향상시킬 수 있는 강력한 메트릭스를 제공한다.
이 글에서 언급한, 코딩 표준, 코드 중복, 코드 커버리지, 의존성 분석, 복잡성 모니터링 등 "5대" 코드 품질 평가를 하는 것이 중요하다고 생각한다. 개발 사이클에서 조기에 코드의 품질을 향상시킬 수 있는 PMD와 FindBugs 같은 기타 Eclipse 플러그인들도 많이 있다는 것도 알아두기 바란다. 원하는 툴이나 선호하는 평가 방식과 별도로, 코드를 적극적으로 향상시키려는 노력을 기울여야 하며, 수동의 코드 리뷰 프로세스도 더욱 효율성 있게 해야 한다. 이 툴을 사용해 보면, 그 동안 이 툴 없이 어떻게 살았는지에 대한 의문이 일 것이다. 교육
제품 및 기술 얻기
토론
|
오늘 MD5변환 함수가 필요해서 찾아 보던 중 잘 정리된 함수가 있어서 올려 봅니다. MD5알고리즘이 공개인데 라이센스가 조금 애매 하네요 ^^
사용법은 response.Write md5("루틴") 결과 205289a1c64632f4f6c9fd556f4a6f42
알고리즘에 대해서 궁금하신 분은 http://mytears.org/resources/doc/Encode/md5.html 가시면 잘 정리되어 있습니다.
다음은 md5.asp 입니다.
0: <% 1: ' Derived from the RSA Data Security, Inc. MD5 Message-Digest Algorithm, [출처] asp에서 md5변환하기|작성자 루틴 |
Annotation 용도
컴파일러를 위한 정보
컴파일러가 에러를 탐지하거나 경고를 경감시키는 용도
컴파일 시와 설치 시 처리
소프트웨어 도구가 코드, XML 파일 등을 생성하기 위한 용도
실행 시 처리
일부는 실행 시에 검사 용도
Annotation 위치
클래스, 필드, 메소드, 다른 프로그램 요소의 선언부
문서화 Annotation
문서화 목적으로 클래스마다 다음과 같은 주석을 작성한다고 가정하자.
public class Generation3List extends Generation2List {
// Author: John Doe
// Date: 3/17/2002
// Current revision: 6
// Last modified: 4/12/2004
// By: Jane Doe
// Reviewers: Alice, Bill, Cindy
// class code goes here
}
이를 Annotation을 사용하여 다음과 같이 변경할 수 있다.
우선 Annotation Type을 정의한다.
@interface ClassPreamble {
String author();
String date();
int currentRevision() default 1;
String lastModified() default "N/A";
String lastModifiedBy() default "N/A";
String[] reviewers();
}
다음과 같이 Annotation Type을 사용할 수 있다.
@ClassPreamble (
author = "John Doe",
date = "3/17/2002",
currentRevision = 6,
lastModified = "4/12/2004",
lastModifiedBy = "Jane Doe",
reviewers = { "Alice", "Bob", "Cindy" }
)
public class Generation3List extends Generation2List {
}
Javadoc에 포함시키고 싶다면,
다음과 같이 Annotation Type의 정의 앞에 @Documented라는 Annotation을 붙인다.
@Documented
@interface ClassPreamble {
String author();
String date();
int currentRevision() default 1;
String lastModified() default "N/A";
String lastModifiedBy() default "N/A";
String[] reviewers();
}
컴파일러 용 Annotation
@Deprecated
메소드, 클래스, 필드가 더 이상 사용하지 않을 것을 권장한다.
현재는 backward compatiblity 차원에서 지원되나, 차후 버전에는 지원되지 않을 수 있다.
컴파일러는 이 Annotation이 붙은 요소를 사용하면 컴파일 시 경고 메시지를 보여준다.
@Override
메소드를 명시적으로 오버라이드한다.
오버라이드할 때 이 Annotation이 필요하지는 않다.
다만 상위 메소드를 하위 메소드에서 명시적으로 오버라이드함을 표기함으로써
컴파일러가 컴파일 시 오버라이드가 발생하는지를 검사한다.
상위 메소드명과 다르게 작성함으로써
오버라이딩이 아닌 오버로딩의 발생을 조기에 차단할 수 있다.
@SuppressWarnings
컴파일러가 컴파일 시에 경고 메시지를 보여주지 않도록 한다.
현재 사용가능한 경고는 "deprecation"과 "unchecked"가 있다.
"unchecked"는 Generics를 사용하지 않을 경우 발생하는 경고이다.
처리 용 Annotation
JDK 5부터 Annotation Processing Tool인 apt를 제공하고,
JDK 6부터는 자바 컴파일러의 일부가 되었다.
실행 시에 Annotation 정보를 사용하기 위해서 다음과 같이 Annotation Type 정의 앞에
@Retention(RetentionPolicy.RUNTIME)라는 Annotation을 붙인다.
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
@interface AnnotationForRuntime {
// Elements that give information
// for runtime processing
}
Reference:
http://java.sun.com/docs/books/tutorial/java/javaOO/annotations.html
Log4J는 다음과 같은 레이아웃을 제공한다.
SimpleLayout
PatternLayout
HTMLLayout
XMLLayout
SimpleLayout은 다음과 같은 포맷으로 고정되어 있다.
레벨 - 메시지
PatternLayout은 C의 printf()처럼 포맷 설정에 대한 자유도가 크다.
HTMLLayout은 HTML 테이블을 포맷으로 한다.
XMLLayout은 XML 문서를 포맷으로 한다.
Reference:
http://www.allapplabs.com/log4j/log4j_layouts.htm
System.out.println()을 사용하면 되는데 굳이 logger를 사용할 필요가 있을까?
logger를 사용하면, 다음과 같은 장점들이 있다.
- 로그 레벨별로 logging 여부를 결정할 수 있다.
- 로그를 콘솔뿐만 아니라 파일 등에 쓸 수 있다.
- 레벨이나 appender 설정을 파일에서 할 수 있다.
가장 보편적으로 사용되는 Apache Log4J를 사용해보자.
우선 log4j.properties 파일을 다음과 같이 작성한다.
log4j.rootLogger=ALL, console, another_appender
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%d [%c:%p] - %m%n
log4j.appender.another_appender=org.apache.log4j.RollingFileAppender
log4j.appender.another_appender.File=log/example.log
log4j.appender.another_appender.MaxFileSize=100KB
# Keep one backup file
log4j.appender.another_appender.MaxBackupIndex=1
log4j.appender.another_appender.layout=org.apache.log4j.PatternLayout
log4j.appender.another_appender.layout.ConversionPattern=%d [%c:%p] - %m%n
log4j.properties 파일을 클래스 패스에 포함시켜야만 Log4J가 찾을 수 있다.
이클립스를 사용한다면, properties 폴더에 log4j.properties 파일을 넣고,
Build Path -> Use as Source Folder를 선택한다.
다음으로 Log4J를 사용하는 클래스를 작성한다.
package com.izeye.log4jexample;
import org.apache.log4j.Logger;
public class Log4JExample {
private static final Logger logger = Logger.getLogger(Log4JExample.class);
private static final Logger anotherLogger = Logger.getLogger("another");
public static void main(String[] args) {
String message = "TEST MESSAGE";
while (true) {
logger.debug(message);
logger.info(message);
logger.warn(message);
logger.error(message);
logger.fatal(message);
anotherLogger.debug(message);
anotherLogger.info(message);
anotherLogger.warn(message);
anotherLogger.error(message);
anotherLogger.fatal(message);
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
위 클래스를 실행하면, 콘솔의 결과는 다음과 같다.
2009-02-04 19:26:58,375 [com.izeye.log4jexample.Log4JExample:DEBUG] - TEST MESSAGE
2009-02-04 19:26:58,390 [com.izeye.log4jexample.Log4JExample:INFO] - TEST MESSAGE
2009-02-04 19:26:58,390 [com.izeye.log4jexample.Log4JExample:WARN] - TEST MESSAGE
2009-02-04 19:26:58,390 [com.izeye.log4jexample.Log4JExample:ERROR] - TEST MESSAGE
2009-02-04 19:26:58,390 [com.izeye.log4jexample.Log4JExample:FATAL] - TEST MESSAGE
2009-02-04 19:26:58,390 [another:DEBUG] - TEST MESSAGE
2009-02-04 19:26:58,390 [another:INFO] - TEST MESSAGE
2009-02-04 19:26:58,390 [another:WARN] - TEST MESSAGE
2009-02-04 19:26:58,390 [another:ERROR] - TEST MESSAGE
2009-02-04 19:26:58,390 [another:FATAL] - TEST MESSAGE
2009-02-04 19:26:58,375 [com.izeye.log4jexample.Log4JExample:DEBUG] - TEST MESSAGE
2009-02-04 19:26:58,390 [com.izeye.log4jexample.Log4JExample:INFO] - TEST MESSAGE
2009-02-04 19:26:58,390 [com.izeye.log4jexample.Log4JExample:WARN] - TEST MESSAGE
2009-02-04 19:26:58,390 [com.izeye.log4jexample.Log4JExample:ERROR] - TEST MESSAGE
2009-02-04 19:26:58,390 [com.izeye.log4jexample.Log4JExample:FATAL] - TEST MESSAGE
2009-02-04 19:26:58,390 [another:DEBUG] - TEST MESSAGE
2009-02-04 19:26:58,390 [another:INFO] - TEST MESSAGE
2009-02-04 19:26:58,390 [another:WARN] - TEST MESSAGE
2009-02-04 19:26:58,390 [another:ERROR] - TEST MESSAGE
2009-02-04 19:26:58,390 [another:FATAL] - TEST MESSAGE
......
log/example.log의 내용도 위와 동일하다.
log4j.rootLogger는 모든 logger의 부모 logger이다.
따라서 등록된 모든 logger가 이 logger의 설정에 영향을 받는다.
com.izeye.log4jexample.Log4JExample 클래스 logger는
WARN 이상의 메시지만 콘솔로 출력하고,
'another' logger는 ERROR 이상의 메시지만
log/example.log 파일로 출력하도록 하기 위해서 다음과 같이 수정한다.
#log4j.rootLogger=ALL, console, another_appender
log4j.logger.com.izeye.log4jexample.Log4JExample=WARN, console
log4j.logger.another=ERROR, another_appender
우선 프로그램을 디버그 모드로 실행시킨다.
다음과 같이 Expressions 뷰를 연다.
Window -> Show View -> Expressions
Expressions 뷰에서 마우스 오른쪽 버튼을 누르고 'Add Watch Expression'을 선택한다.
원하는 표현식을 작성한다.
작성한 표현식의 값을 확인할 수 있다.
이클립스에서 소스 내에 에러가 있으면
에러가 있는 소스 라인의 앞에는 빨간 x 표시가 생기고,
소스 라인 내 에러의 위치에 빨간 밑줄이 생긴다.
이러한 빨간 밑줄을 순차적으로 찾을 수 있는 단축키를 제공한다.
단축키는 다음과 같다.
Ctrl + '.'
* LogWatcher
Log실시간으로 로그를 불러와 확인할 수 있습니당..
다운로드 : http://graysky.sourceforge.net/
설치는 간단해여..
그냥 받아서 압축푸시고 D:\eclipse\plugins 폴더안에 넣어주시면 됩니다.
D:\eclipse는 자신이 이클립스를 설치한 위치입니다.
![](http://blogfiles7.naver.net/data19/2007/2/15/294/log01-rlaaudtnr8.jpg)
[출처] [이클립스 플러그인]LogWatcher |작성자 신녀
[퍼옴 : http://openframework.or.kr/blog/?p=40 ]
Spring XML설정파일을 위한 12가지 가장 좋은 선택
원제는 Twelve Best Practices for Spring XML Configuration Files 이다.
원문의 위치는 http://lizjason.com/blog/?p=12
ONJava.com의 Twelve Best Practices For Spring XML Configurations 글을 기반으로 예제및 설명에 대한 일부 수정
Spring은 강력한 자바 애플리케이션 프레임워크이고 자바 애플리케이션의 넓은 범위에서 사용된다. 이것은 단순함과 테스트의용이성을 달성하기 위해 의존성삽입(Dependency Injection)을 사용한다. 의존성과 bean생성은 XML설정파일에대개 명시된다. XML설정은 장황하고 큰 프로젝트에서는 관리하기가 어려울수도 있다. 설정파일의 가독성과 관리의 용이성이 고려되는만큼, 나는 다음의 사항이 매우 유용하리라고 생각한다.
1. autowire를 사용하지 말라.
내 의견에서, autowire는 시장광고용(marketing) 기능이다. 이것은 실제 프로젝트에서 결코 사용되지 말아야 한다. 이것은 몇몇 타이핑의 수고와 설정조각을 줄이지만, 명백함과 설정의 유지보수성을 희생한다.
trollswagen, naimdjon, Johannes Brodwall, 토지님께서 autowire는 오히려 xml이커지면 커질수록 xml의 구조를 쉽게 파악하게 해주는 정말 좋은 기능중에 하나라는 의견을 주셨습니다. autowire에 관련된사항은 프로젝트 도입시 장,단점을 다시 살펴 사용하길 권합니다.
2. 명명 규칙을 사용하라.
이것은 자바코드와 같은 의도이다. 예를 들면 bean id를 위해, 당신은 자바 클래스 필드 명명규칙을 따를수 있다. OrderServiceDAO의 인스턴스를 위한 bean id는 orderServiceDAO가 될것이다.
3. 단축형태(shortcut forms)를 사용하라.
단축형태는 자식요소에서 속성으로 프라퍼티값과 참조를 이동시켜 다소 덜 장황하게 만든다. 이 단축형태는 1.2버전 이후 지원된다.
단축형태는 다음과 같은 기능이다.
1.2이전버전에서는 다음과 같이 셋팅한다.
<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName"><value>com.mysql.jdbc.Driver</value></property>
<property name="url"><value>jdbc:mysql://localhost:3306/mydb</value></property>
<property name="username"><value>root</value></property>
</bean>
1.2이후 단축형태를 사용하면 다음과 같이 셋팅이 가능하다.
<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/mydb" />
<property name="username" value="someone" />
</bean>
4. 인자를 맞추기 위한 인덱스보다 타입을 선호하라.
인덱스를 사용하는 것은 때때로 다소 덜 장황하게 만든다. 하지만 이것은 에러를 좀더 생성하고 읽기 어렵다.
다음의 소스는 인덱스를 사용하는 예제이다.
<bean id="billingService" class="com.lizjason.spring.BillingService">
<constructor-arg index="0" value="lizjason"/>
<constructor-arg index="1" value="100"/>
</bean>
하지만 다음처럼 타입을 사용하는 것이 추천한다.
<bean id="billingService" class="com.lizjason.spring.BillingService">
<constructor-arg type="java.lang.String" value="lizjason"/>
<constructor-arg type="int" value="100"/>
</bean>
5. 가능하다면 bean정의를 재사용하라.
당신은 중복을 제거하기 위한 기법처럼 상속을 사용할수 있다. 당신이 할필요가 있는 모든것은 상위 bean에 abstract=true를 명시하고 자식 bean에 parent참조를 두는 것이다. 당신이클래스나 factory메소드를 명시하지 않는다면, bean은 함축적으로 abstract상태가 된다.
예를 들면 다음과 같이 셋팅한다.
<bean id="txProxyTemplate" abstract="true" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager">
<ref bean="transactionManager" />
</property>
<property name="transactionAttributes">
<props>
<prop key="save*">PROPAGATION_REQUIRED</prop>
<prop key="remove*">PROPAGATION_REQUIRED</prop>
<prop key="*">PROPAGATION_REQUIRED,readOnly</prop>
</props>
</property>
</bean>
<bean id="userManager" parent="txProxyTemplate">
<property name="target">
<bean class="org.appfuse.service.impl.UserManagerImpl">
<property name="userDAO" ref="userDAO" />
</bean>
</property>
</bean>
6. import 보다는 ApplicationContext를 통해 bean정의를 조립(assembling)하는 것을 선호하라.
Ant스크립트내 import처럼, 그것들은 모듈화된 bean정의를 조립하는데 유용하다. 어쨌든, 이것은ApplicationContext를 통해 그것들을 조립하기 위해 좀더 유연하다. 당신은 ApplicationContext의생성자를 위해 bean정의의 배열을 전달할수 있다.
다음은 import를 조합하여 xml을 설정하는 소스이다.
<beans>
<import resource="billingServices.xml"/>
<import resource="shippingServices.xml"/>
<bean id="orderService" class="com.lizjason.spring.OrderService"/>
<beans>
import를 사용하여 xml을 조합하는 방식보다는 아래와 같이 ApplicationContext를 사용하는 것이 좀더 유연한 개발을 도와준다.
String[] serviceResources = {"orderServices.xml", "billingServices.xml", "shippingServices.xml"};
ApplicationContext orderServiceContext = new ClassPathXmlApplicationContext(serviceResources);
7. 가능하다면, bean 확인자로 id를 사용하라.
id를 사용하는 것은 가독성을 증가시키지 않는다. 하지만 이것은 bean참조를 확인하기 위해 XML파서에 영향을 끼칠수 있다. 만약 id가 XML IDREF제한을 위해 사용될수 없다면, 당신은 이름(name)을 사용할수 있다.
8. 개발시에는 의존성체크(dependency-check)를 사용하라.
당신은 bean정의의 dependency-check속성을 디폴트인 none이 아닌 다른값으로 셋팅할수 있다. 그래서 컨테이너는 당신을 위해 의존성체크를 할수 있다.
<bean id="orderService" class="com.lizjason.spring.OrderService" dependency-check="objects">
<property name="companyName" value="lizjason"/>
<constructor-arg ref="orderDAO"/>
</bean>
9. 각각의 XML파일을 위해 헤더(header) 주석을 추가하라.
XML파일내 내부 주석대신에 서술적인 id와 name을 사용하는것이 선호된다. 어쨌든, 각각의 파일이 정의된 bena을 요약하는 헤더를 가진다면 이해하기가 쉽다.
<beans>
<description>
이 파일은 거래(billing) 서비스 관련 bean을 정의하고
서비스 bean템플릿을 제공하는 baseService.xml파일을 의존한다.
</description>
...
</beans>
10. 변경을 위해 팀멤버간 의사소통을 하라.
당신이 자바소스코드를 리팩토리할때, 당신은 설정파일을 그 상황에 따라 변경하고 팀멤버에게 알릴 필요가 있다.
11. 생성자 삽입(constructor injection)보다 setter 삽입(setter injection)을 선호하라.
생성자 삽입은 bean들이 비정상적인 상태에서 생성될수 없다는 것을 확인할수 없다. 하지만 setter 삽입은 좀더 유연하고 관리가 가능하다. 특히 클래스가 다중 프라퍼티를 가진다면 더욱 그러하다.
다음은 생성자 삽입을 사용하는 예이다.
<bean id="orderService" class="com.lizjason.spring.OrderService">
<constructor-arg ref="orderDAO"/>
</bean>
다음은 setter삽입을 사용하는 예이다.
<bean id="billingService" class="com.lizjason.spring.BillingService">
<property name="billingDAO" ref="billingDAO">
</bean>
12. 의존성 삽입을 남용하지 말라.
마지막에, Spring ApplicationContext는 당신을 위해자바객체를 생성할수 있다. 하지만 모든 자바객체가 의존성삽입을 통해서 생성될수는 없다. 기억하라. 강력한 IDE인Eclipse와 IntelliJ를 사용하여, 자바코드는 좀더 읽고, XML파일보다 유지및 관리가 쉽다. 즉 의존성삽입을 설정하는XML파일보다는 자바코드가 개발자의 입장에서는 가독성이 좋다.
[출처] Spring XML설정파일을 위한 12가지 가장 좋은 선택|작성자 쭌
<%@ page language="java" contentType="text/html;charset=EUC-KR" %>
<%@ page import="java.util.*, java.io.*" %>
<%@ page import="jxl.*, jxl.write.*, jxl.format.*" %>
<%@ page import="kr.co.kt.adims.form.Top10Form" %>
<%
String filename = "excelConvert.xls";
WritableWorkbook workbook = Workbook.createWorkbook(new File("c:/"+filename));
WritableSheet sheet = workbook.createSheet("Sheet1", 0);
jxl.write.WritableCellFormat format= new WritableCellFormat();
jxl.write.WritableCellFormat format0= new WritableCellFormat();
format.setBackground(jxl.format.Colour.GRAY_25 );
format.setBorder(jxl.format.Border.ALL,jxl.format.BorderLineStyle.THIN );
format.setAlignment(jxl.format.Alignment.CENTRE);
format0.setBackground(jxl.format.Colour.WHITE );
format0.setBorder(jxl.format.Border.ALL,jxl.format.BorderLineStyle.THIN );
format0.setAlignment(jxl.format.Alignment.CENTRE);
sheet.setColumnView(0,8);
jxl.write.Label label = null;
jxl.write.Blank blank = null;
label = new jxl.write.Label(0,0,"순위",format);
sheet.addCell(label);
label = new jxl.write.Label(1,0,"본부",format);
sheet.addCell(label);
label = new jxl.write.Label(2,0,"서버팜",format);
sheet.addCell(label);
label = new jxl.write.Label(3,0,"DHCP서버",format);
sheet.addCell(label);
label = new jxl.write.Label(4,0,"지사",format);
sheet.addCell(label);
label = new jxl.write.Label(5,0,"지점",format);
sheet.addCell(label);
label = new jxl.write.Label(6,0,"L3명(DHCP)",format);
sheet.addCell(label);
label = new jxl.write.Label(7,0,"EquipAlias",format);
sheet.addCell(label);
label = new jxl.write.Label(8,0,"LocationCode",format);
sheet.addCell(label);
label = new jxl.write.Label(9,0,"Mac Address",format);
sheet.addCell(label);
label = new jxl.write.Label(10,0,"수집날짜",format);
sheet.addCell(label);
label = new jxl.write.Label(11,0,"패킷수",format);
sheet.addCell(label);
int cnt = 0;
ArrayList staList = null;
Top10Form staForm = new Top10Form();
staList = (ArrayList)request.getAttribute("topList");
cnt = staList.size();
for(int i=0; i<cnt; i++)
{
staForm = (Top10Form)staList.get(i);
label = new jxl.write.Label(0, i+1, staForm.getNo(), format0);
sheet.addCell(label);
label = new jxl.write.Label(1, i+1, staForm.getBonbu(), format0);
sheet.addCell(label);
label = new jxl.write.Label(2, i+1, staForm.getServerParm(), format0);
sheet.addCell(label);
label = new jxl.write.Label(3, i+1, staForm.getEquipAlias(), format0);
sheet.addCell(label);
label = new jxl.write.Label(4, i+1, staForm.getJisa(), format0);
sheet.addCell(label);
label = new jxl.write.Label(5, i+1, staForm.getJijum(), format0);
sheet.addCell(label);
label = new jxl.write.Label(6, i+1, staForm.getL3Name1(), format0);
sheet.addCell(label);
label = new jxl.write.Label(7, i+1, staForm.getL3Name2(), format0);
sheet.addCell(label);
label = new jxl.write.Label(8, i+1, staForm.getL3LocationCode(), format0);
sheet.addCell(label);
label = new jxl.write.Label(9, i+1, staForm.getName(), format0);
sheet.addCell(label);
label = new jxl.write.Label(10, i+1, staForm.getRegDate(), format0);
sheet.addCell(label);
label = new jxl.write.Label(11, i+1, String.valueOf(staForm.getCnt()), format0);
sheet.addCell(label);
}
workbook.write();
workbook.close();
%>
<%
//여기부터 화일 다운로드 창이 자동으로 뜨게 하기 위한 코딩(임시화일을 스트림으로 저장)
File file = new File ("c:/"+filename); //해당 경로의 파일 객체를 만든다.
byte[] bytestream = new byte[(int)file.length()]; //파일 스트림을 저장하기 위한 바이트 배열 생성.
FileInputStream filestream = new FileInputStream(file); //파일 객체를 스트림으로 불러온다.
int i = 0, j = 0; //파일 스트림을 바이트 배열에 넣는다.
while((i = filestream.read()) != -1) {
bytestream[j] = (byte)i;
j++;
}
filestream.close(); //FileInputStream을 닫아줘야 file이 삭제된다.
try{
boolean success = file.delete(); //화일을 생성과 동시에 byte[]배열에 입력후 화일은 삭제
if(!success) System.out.println("<script>alert('not success')</script>");
} catch(IllegalArgumentException e){
System.err.println(e.getMessage());
}
// response.setContentType("application/x-msdownload;charset=EUC-KR"); //응답 헤더의 Content-Type을 세팅한다.
response.setHeader("Content-Disposition","attachment; filename="+filename); //Content-Disposition 헤더에 파일 이름 세팅.
OutputStream outStream = response.getOutputStream(); // 응답 스트림 객체를 생성한다.
outStream.write(bytestream); // 응답 스트림에 파일 바이트 배열을 쓴다.
outStream.close();
%>
[출처] JXL로 Excel파일 만들고 PC에 저장하기|작성자 쭌
#. 자바 JXL API 를 이용한 Excel 파일 만들기
1. JXL API 다운 받기
2. JSP에서 사용할 class 만들기
3. JSP에서 적용하기
4. 요구 사항에 맞게 확장하기
1. 환경 설정
JAVA에서 Excel을 만들기 위해서는 JXL API 나 POI를 이용해야 한다.
우선 이번장에서는 JXL을 이용하여 만드는 방법에 대해서 소개하기로 한다.
Java Excel API - A Java API to read, write and modify Excel spreadsheets ( 이하 JXL )은
다음과 같은 홈페이지에서 다운 받을 수 있다
파일 다운 받기 :
http://www.andykhan.com/jexcelapi/download.html
API 정보 :
http://www.andykhan.com/jexcelapi/index.html
우선 사이트에서 가장 최신 버젼의 JExcelApi를 다운 받는다.
압축을 해제한 후에 각 사용자 환경에 맡게 jar 파일을 옮긴다.
( 일반 JAVA User - j2sdk 폴더 밑에 jrelibext 파일로 옮긴다. )
2. JSP에서 사용할 class 만들기
- 파일 다운로드 -
1. Parameter 정보
JSP에서 class로 넘겨주는 정보는 다음과 같다.
Vector data :: Query를 통해 나온 Recordset 집합
String[] column :: column 이름 <- Excel 에서 Head 역할
int[] columntype :: column 타입 <- 문자형, 정수형, 실수형 으로 구분하였다.
String FilePath :: Excel 파일을 저장하기위한 절대 경로
String SheetName :: Sheet 이름
2. Excel File 생성하기
Excel 파일음 다음과 같이 생성을 한다
WritableWorkbook workbook = Workbook.createWorkbook(new File(FilePath));
WritableSheet sheet = workbook.createSheet(SheetName, 0);
- FilePath :: Excel 파일을 저장하기위한 절대 경로
- SheetName :: 시트 이름
- 0 :: 시트 인덱스 번호
3. Cell Type 지정하기
a. Text 형
jxl.write.WritableCellFormat format_column = new WritableCellFormat();
jxl.write.WritableCellFormat format_data = new WritableCellFormat();
b. 정수형 ( 1000단위 마다 , 찍기 X )
jxl.write.WritableCellFormat format_integer1 = new WritableCellFormat(NumberFormats.INTEGER);
c. 정수형 ( 1000단위 마다 , 찍기 O )
jxl.write.NumberFormat moneytype1 = new NumberFormat("###,##0");
jxl.write.WritableCellFormat format_integer2 = new WritableCellFormat(moneytype1);
d. 실수형 ( 1000단위 마다 , 찍기 X )
jxl.write.WritableCellFormat format_float1 = new WritableCellFormat(NumberFormats.FLOAT);
e. 실수형 ( 1000단위 마다 , 찍기 0 , 소수점 2자리 허용 )
jxl.write.NumberFormat moneytype2 = new NumberFormat("###,##0.00");
jxl.write.WritableCellFormat format_float2 = new WritableCellFormat(moneytype2);
4. CELL Color & BackGround 지정하기
format_column.setBackground(jxl.format.Colour.GRAY_25 );
format_column.setBorder(jxl.format.Border.ALL,jxl.format.BorderLineStyle.THIN );
.
.
5. DB To Excel
a. Head 부분 생성하기
for ( cellnum = 0; cellnum < column.length; cellnum++ ) {
label = new jxl.write.Label(cellnum,0,column[cellnum],format_column);
sheet.addCell(label);
}
b. Data 집어 넣기
for ( rownum = 1; rownum <= data.size() ; rownum++) {
dataResult = (String[])data.get(rownum-1);
for (cellnum = 0; cellnum < dataResult.length - 1; cellnum++) {
num = new jxl.write.Number(cellnum, rownum, Long.parseLong(dataResult[cellnum+1]),format_integer2);
sheet.addCell(num);
}
}
3. JSP에서 사용하기
<%@ page import="java.util.Vector, crm.util.Util, java.sql.*" contentType="text/html; charset=euc-kr" %>
<jsp:useBean id="cExcel" class="crm.util.CreateExcel" scope="page"/>
cExcel.CreateExcelFile(queryResult,column,columntype,file_path+file_name,sheet_name,str_search);
4. 요구 사항에 맞게 확장하기
JXL API를 제공하는 홈페이지에 가면 JXL API SDK를 다운 받을 수 있다.
JXL API SDK를 다운 받은 후 사용자 요구 사항에 맞게 수정을 하면 될 것이다.
[출처] [본문스크랩] 자바 JXL API 를 이용한 Excel 파일 만들기|작성자 쭌
update url :
http://download.macromedia.com/pub/labs/jseclipse/autoinstall
JSEclipse 설치법(다른 이클립스 플러그인 설치방법이 이와 같습니다.)
JSEclipse를 다운받고 압축을 풀어두세요.
Eclipse에서 Help-> Software Updates -> Find and Install
메뉴가 뜨면
Search for new features to install 을 선택하고 Next 버튼을 클릭합니다.
오른쪽위에 보이는 New Local Site버튼을 클릭하세요.
JSEclipse 압축을 풀어둔 폴더를 선택하고 확인버튼을 클릭합니다.
그 다음 부터는 보이는 거 다 체크하시고 긍정적인 대답만 하시면 설치OK
(이클립스가 다시 켜집니다.)
Html파일을 만드시고 그곳에서 오른쪽 버튼을 클릭했을 때 Edit in JSEclipse 팝업메뉴가 있으면 설치 성공입니다!
-showsplash
org.eclipse.platform
--launcher.XXMaxPermSize=256M
-framework
plugins\org.eclipse.osgi_3.4.0.v20080605-1900.jar //이부분은 기존것 사용
-vmargs
-Dosgi.requiredJavaVersion=1.5
-Xverify:none
-XX:+UseParallelGC
-XX:PermSize=64M
-XX:MaxPermSize=128M
-XX:MaxNewSize=64M
-XX:NewSize=64M
-Xms128m
-Xmx512M
http://propedit.sourceforge.jp/eclipse/updates/
자바프로그램에서 propeties 라는 파일이 있습니다. classpath에 위치하면서 설정이나 다국어 처리를 위한 파일로 많이 씁니다.
주의할 점은 한글을 써 놓으면 그대로 쓰지 못합니다. 유니코드로 바꿔줘야하는데, %JAVA_HOME%\bin\native2ascii.exe 프로그램을 쓰고는 합니다.
이렇게 파일의 한글을 알아볼 수 없게 되어버립니다. 이쯤되면 한 마디 나오죠. "어쩌라고"
이클립스에서 프로퍼티 편집을 쉽게 하도록 플러그인이 일본에서 만들어졌습니다.
구글에서 propedit 하면 처음에 나오는 사이트입니다.
http://propedit.sourceforge.jp/index_en.html
이클립스에서 바로 플러그인을 설치할 수 있습니다. 업데이트 주소가 필요한데, 사이트에서 다음과 같은 주소를 찾을 수 있습니다.
http://propedit.sourceforge.jp/eclipse/updates/
이 주소를 복사하고 다음과 같이 이클립스 메뉴에서 Help > Software Updates > Find and Install... 을 선택합니다.
두 번째 메뉴을 선택합니다. Search for new features to install
참고로 첫 번째 메뉴 Search for updates of the currently installed features 는 현재 이클립스에 있는 컴포넌트들의 업데이트들을 찾아서 설치하는 메뉴입니다.
우측의 New Remote Site... 버튼을 클릭하고 Name에는 propedit , 아래 URL에는 아까 복사한 업데이트 URL을 붙여넣기 합니다. 그리고 아래쪽의 Finish 버튼을 누릅니다.
이후 Updaes 타이틀이 붙어있는 창이 뜨고 Search Results가 나옵니다. propedit 를 펼쳐보면 항목이 3개 있는데, 가운데 플러그인은 3.3에서는 동작하지 않아서 체크하지 않았습니다. 세 번째 플러그인은 vi 키보드 편집처럼 에디터를 쓸 수 있도록 해주는 것입니다. 같이 설치하도록 하겠습니다. 잘도 만드네요.
설치를 마치면 이클립스를 재시작합니다.
프로퍼티 파일의 아이콘이 바뀝니다. 오른 버튼으로 Open With라는 메뉴를 보면 가능한 편집기 리스트가 나옵니다. 첫 번째 Properties File Editor는 이클립스 내장 에디터입니다. 맨 처음 보았던 것이죠. 두 번째 있는 PropertiesEditor가 새로 설치한 플러그인에서 지원하는 에디터입니다. 아이콘이 다름을 알 수 있습니다. 이것으로 열어보겠습니다.
에디터 탭을 옮겨서 두 에디터를 비교하면 다음과 같습니다. 에디터 탭의 아이콘으로 구분이 가능합니다.
같은 파일이 편집기에 따라서 다르게 보임을 알 수 있습니다. 편집은 가독성있는 문자코드로 하고 저장만 유니코드로 자동변환되어 저장됩니다.
잘 쓰시기 바랍니다.
오늘은 요기까지입니다.
-지정한 클래?스의 인스턴스가 '절대로' 1개밖에 존재하지 않는 것을 '보증'하고 싶을 때
-인스턴스가 1개밖에 존재하지 않는 것을 프로그램 상에서 표현하고 싶을 때
?
밑줄을 친 것은 static이다.
싱글톤클래스에서는 인스턴스를 1개밖에 만들 수 없으며, singleton은 static필드로서 Singleton클래스의 인스턴스에서 초기화된다. 이 초기화는 Singleton클래스를 로드할 때 1회만 실행된다.
생성자는 private. 외부에서 생성자의 호출을 금지하기 위해서이다!!
인스턴스를 얻는 메소드로 getInstance가 준비되어 있다.
public class Singleton {
private static Singleton singleton = new Singleton();
private Singleton() {
System.out.println("인스턴스를 생성했습니다.");
}
public static Singleton getInstance() {
return singleton;
}
}
//테스트를 위한 메인클래스
public class Main {
public static void main(String[] args) {
System.out.println("Start.");
Singleton obj1 = Singleton.getInstance();
Singleton obj2 = Singleton.getInstance();
if (obj1 == obj2) {
System.out.println("obj1과 obj2는 같은 인스턴스입니다.");
} else {
System.out.println("obj1과 obj2는 같은 인스턴스가 아닙니다.");
}
System.out.println("End.");
}
}
다른 예제
public class TicketMaker {
private int ticket = 1000;
private static TicketMaker singleton = new TicketMaker(); //싱글톤객체생성
private TicketMaker() { //생성자
}
public static TicketMaker getInstance() { //인스턴스를 얻기 위한 메소드
return singleton;
}
public synchronized int getNextTicketNumber() { //synchronized선언함으로 복수의 스레드에서호출되어도 올바로 작동시키기 위한 조치.사용하지않으면 복수의 스레드에 같은 값 반환 위험.
return ticket++;
}
}
public class Main {
public static void main(String[] args) {
System.out.println("Start.");
for (int i = 0; i < 10; i++) {
System.out.println(i + ":" + TicketMaker.getInstance().getNextTicketNumber());
}
System.out.println("End.");
}
}
템플릿의 기능을 가진 패턴.
상위클래스에 템플릿에 해당하는 메소드가 정의, 추상메소드가 사용.
상위 클래스의 프로그램만 보면 추상 메소드를 어떻게 호출하고 있는 알 수 있지만, 최종적으로 어떤 처리가 수행되는지는 알 수 없다.
추상메소드를 실제로 구현하는 것은 하위 클래스다. 서로 다른 하위 클래스가 서로 다른 구현을 하더라도 처리의 큰 흐름은 상위 클래스에서 결정한대로 이루어진다.
(파워포인트2007로 작성...연결선이 맘에들지 않는다.;)
클래스 일람
AbstractDisplay : 추상클래스로서 메소드 display만 구현되고 있다.
CharDisplay : 메소드 open,print,close를 구현
StringDisplay : 위와 같지만 printLine도 추가
Main : 동작 테스트용
public abstract class AbstractDisplay { // 추상 클래스 AbstractDisplay
public abstract void open(); // 하위 클래스에 구현을 맡기는 추상 메소드 (1) open
public abstract void print(); // 하위 클래스에 구현을 맡기는 추상 메소드 (2) print
public abstract void close(); // 하위 클래스에 구현을 맡기는 추상 메소드 (3) close
public final void display() { // 추상 클래스에서 구현되고 있는 메소드 display
open(); // 우선 open하고…
for (int i = 0; i < 5; i++) { // 5번 print을 반복하고…
print();
}
close(); // … 마지막으로 close한다. 이것이 display 메소드에서 구현되고 있는 내용.
}
}
//CharDisplay
public class CharDisplay extends AbstractDisplay { // CharDisplay는 AbstractDisplay의
// 하위 클래스.
private char ch; // 표시해야 할 문자
public CharDisplay(char ch) { // 생성자에서 전달된 문자 ch을
this.ch = ch; // 필드에 기억해 둔다.
}
public void open() { // 상위 클래스에서는 추상 메소드였다.
// 여기에서 오버라이드해서 구현.
System.out.print(“<<”); // 개시 문자열“<<”을 표시한다.
}
public void print() { // print 메소드도 여기에서 구현한다.
// 이것이 display에서 반복해서 호출된다.
System.out.print(ch); // 필드에 기억해 둔 문자를 1개 표시한다.
}
public void close() { // close 메소드도 여기에서 구현.
System.out.println(“>>”); // 종료 문자열 “>>”을 표시.
}
}
//StringDisplay
public class StringDisplay extends AbstractDisplay { // StringDisplay도
// AbstrctDisplay의 하위 클래스.
private String string; // 표시해야 할 문자열.
private int width; // 바이트 단위로 계산한 문자열의 「폭」.
public StringDisplay(String string) { // 생성자에서 전달된 문자열 string을
this.string = string; // 필드에 기억.
this.width = string.getBytes().length; // 그리고 바이트 단위의 폭도 필드에
// 기억해 두고 나중에 사용한다.
}
public void open() { // 오버라이드해서 정의한 open 메소드.
printLine(); // 이 클래스의 메소드 printLine에서
// 선을 그리고 있다.
}
public void print() { // print 메소드는
System.out.println(“|” + string + “|”); // 필드에 기억해 둔 문자열의
// 전후에 “|”을 붙여서 표시.
}
public void close() { // close 메소드는
printLine(); // open 처럼 printLine 메소드에서
// 선을 그리고 있다.
}
private void printLine() { // open과 close에서 호출된 printLine 메소드이다.
// private이기 때문에 이 클래스 안에서만 사용된다.
System.out.print(「+「); // 테두리의 모서리를 표현하는”+” 마크를 표시.
for (int i = 0; i < width; i++) { // width개의 “-“을 표시하고
System.out.print(「-「); // 테두리 선으로 이용한다.
}
System.out.println(「+「); // 테두리의 모서리를 표현하는 “+” 마크를 표시.
}
}
//Main
public class Main {
public static void main(String[] args) {
// 'H'을 가진 CharDisplay 인스턴스를 1개 만든다>
AbstractDisplay d1 = new CharDisplay('H');
// “Hello, world.”을 가진 StringDisplay의 인스턴스를 1개 만든다.
AbstractDisplay d2 = new StringDisplay(“Hello, world.”);
// “안녕하세요.”를 가진 StringDisplay의 인스턴스를 1개 만든다.
AbstractDisplay d3 = new StringDisplay(“안녕하세요.”);
d1.display(); // d1, d2, d3 모두 AbstractDisplay의 하위클래스의 인스턴스이기 때문에
d2.display(); // 상속한 display메소드를 호출할 수 있다.
d3.display(); // 실제 동작은 CharDisplay나 StringDisplay에서 결정한다.
}
}
이 패턴을 이용하면 상위클래스의 템플릿메소드에서 알고리즘이 기술되어 있으므로, 하위 클래스측에서는 알고리즘을 일일이 기술할 필요가 없다.
상위클래스에서 하위 클래스에게 요청
-상위클래스에서 정의되어 있는 메소드를 하위클래스에서 이용할 수 있다
-하위클래스에 약간의 메소드를 기술해서 새로운 기능을 추가할 수 있다
-하위클래스에서 메소드를 오버라이드하면 동작을 변경할 수 있다.
추상클래스의 의의
추상메소드는 본체가 기술되어 있지 않아서 구체적인 처리내용을 알 수 없으나, 메소드의 이름을 결정하고 메소드를 사용한 템플릿메소드에 의해 처리를 기술하는 것은 가능하다.
실제의 처리 내용은 하위 클래스에서 결정하지만, 추상클래스의 단계에서 처리의 흐름을 형성하는 것은 중요하다.
예를 들어 직류 12볼트로 작동하는 노트북을 교류100볼트의 AC전원에 연결한다고 가정하자.
이때 우리는 AC어댑터라는 장치를 사용한다. AC어댑터는 교류100볼트를 직류12볼트로 교환해 준다.
교류100볼트------AC어댑터--------직류12볼트
상속을 사용한 Adapter패턴
제공되고 있는 것 / 교류 100볼트 / Banner클래스
교환장치 / 어댑터 / PrintBanner 클래스
필요한 것 / 직류 12볼트 / Print인터페이스
public interface Print {
public abstract void printWeak();
public abstract void printStrong();
}
//PrintBanner클래스
public class PrintBanner extends Banner implements Print {
public PrintBanner(String string) {
super(string); //super는 Banner클래스의 생성자를 가리킴
}
public void printWeak() {
showWithParen();
}
public void printStrong() {
showWithAster();
}
}
//Banner클래스
public class Banner {
private String string;
public Banner(String string) {
this.string = string;
}
public void showWithParen() {
System.out.println("(" + string + ")");
}
public void showWithAster() {
System.out.println("*" + string + "*");
}
}
//Main클래스
public class Main {
public static void main(String[] args) {
Print p = new PrintBanner("Hello");
p.printWeak();
p.printStrong();
}
}
PrintBanner클래스가 어댑터의 역할을 완수한다. 준비된 Banner클래스를 확장해서 showWithParen메소드와 showWithAster메소드를 상속한다. 또한, 필요한 Print인터페이스를 구현해서 printWeak메소드와 printStrong 메소드를 구현한다.
메인클래스는 PrintBanner클래스가 어떻게 실현되고 있는지 모른다. 실행되는지 모른다는 것은 메인 클래스를 전혀 변경하지 않고 PrintBanner클래스의 구현을 바꿀 수 있다는 것..
Iterator패턴이란, 무엇인가 많이 모여있는 것들을 순서대로 지정하면서 전체를 검색하는 처리를 실행하기 위한 것이다.
1.Aggregate인터페이스
package Iterator.Sample; public interface Aggregate { public abstract Iterator iterator(); }인터페이스에 선언되어 있는 메소드 iterator 메소드 하나뿐이다. 이 메소드는 집합체에 대응하는 Iterator를 1개 작성하기 위한 것이다
iterator메소드를 사용해서 Iterator인터페이스를 구현한 클래스의 인스턴스를 1개 만든다.
2.Iterator인터페이스
package Iterator.Sample; public interface Iterator { public abstract boolean hasNext(); public abstract Object next(); }
다음요소가 존재하는지 여부를 위한 hasNext, 존재하면 true반환
다음요소를 얻기위한 next 메소드.. Object타입으로 알 수 있듯 집합체의 요소 1개를 반환.. 또한, 다음 요소를 반환하도록 내부 상태를 다음으로 진행시켜두는 역할을 한다. 여기서는 메소드 이름만.. 구현은 BookShelfIterator
3.Book클래스
package Iterator.Sample; public class Book { private String name; public Book(String name) { this.name = name; } public String getName() { return name; } }
책이름 얻기 getName
책이름은 생성자에서 객체를 초기화할 때 인수로 지정한다.
4.BookShelf클래스
package Iterator.Sample; public class BookShelf implements Aggregate { private Book[] books; private int last = 0; public BookShelf(int maxsize) { this.books = new Book[maxsize]; } public Book getBookAt(int index) { return books[index]; } public void appendBook(Book book) { this.books[last] = book; last++; } public int getLength() { return last; } public Iterator iterator() { return new BookShelfIterator(this); } }
서가를 나타내는 클래스.. 이 클래스를 집합체로 다루기 이해 Aggregate를 구현하고 있다.
iterator메소드는 BookShelf클래스에 대응하는 이터레이터로서, BookShelfIterator라는 클래스의 인스턴스를 생성해서 그것을 반환한다. 이 서가의 책을 나열하고 싶을때 iterator메소드를 호출한다.
5.BookShelf클래스
package Iterator.Sample; public class BookShelfIterator implements Iterator { private BookShelf bookShelf; private int index; public BookShelfIterator(BookShelf bookShelf) { this.bookShelf = bookShelf; this.index = 0; } public boolean hasNext() { if (index < bookShelf.getLength()) { return true; } else { return false; } } public Object next() { Book book = bookShelf.getBookAt(index); index++; return book; } }
Iterator인터페이스를 구현하고있다.
index는 현재 주목하고 있는 책을 가리킨다.
생성자에서 전달된 BookShelf의 인스턴스를 bookshelf에 저장하고 index를 0으로 한다.
hasNext는 index가 책의 권수 bookShelf.length()값보다 작은지 ,큰지로 판정
next메소드는 현재 처리하고 있는 책을Book의 객체 반환하고, 다시 다음으로 진행시키기 위한 메소드.
우선 반환값으로 반환해야 할 책을 book라는 변수로 저장하고 index를 다음으로 진행시킨다.
6.Main클래스
package Iterator.Sample; import java.util.*; public class Main { public static void main(String[] args) { BookShelf bookShelf = new BookShelf(4); bookShelf.appendBook(new Book("Zround the World in 80 Days")); bookShelf.appendBook(new Book("Bible")); bookShelf.appendBook(new Book("Cinderella")); bookShelf.appendBook(new Book("Daddy-Long-Legs")); Iterator it = bookShelf.iterator(); while (it.hasNext()) { Book book = (Book)it.next(); System.out.println(book.getName()); } } }
bookShelf.iterator()에 의해 얻어지는 it가 서가를 검색하기 위한 이터레이터의 인스턴스이다.
스프링 프레임워크의 프로세스는 대충 이해 했습니다.
스프링MVC에서 삽질 3일++ , 스프링JDBC에 도전해서 이틀동안 삽질한 결과 데이터베이스 연동을 할 수 있었네요..ㅋ
스프링.. 좋거나 혹은 나쁘거나 입니다. 제가 본 것은 빙산의 일각에 불과하지만 처음부터 너무 진을 빼놓네요.. 스트럿츠2가 그립습니다.
클래스를 많이 제공하는 것은 기호에 맞게 선택할 수 있는 폭이 넓어 좋지만, 반면 어떤 것을 적용해야 할지 어떤 것이 최선인가 하는데에 시간 투자를 많이 해야 합니다.
서블릿 설정파일에서도 시간을 많이 소비하였고 아직도 헷갈립니다. 톰캣과 궁합이 안좋은가...
JDBC연동 부분은 확실히 편하긴 하지만 커맨드,서비스,컨트롤러,DAO + servlet.xml 설정파일과 어떻게 연동이 되는가하는 부분에서도 하루를 날렸네요.
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/dataSource.xml</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>
org.springframework.web.filter.CharacterEncodingFilter
</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>EUC-KR</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>dispatcherJdbc</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/boardService-servlet.xml
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<servlet-name>dispatcherBank</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/sampleBankingServlet-servlet.xml , /WEB-INF/sampleBanking-services.xml
</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherJdbc</servlet-name>
<url-pattern>/test/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>dispatcherBank</servlet-name>
<url-pattern>/bank/*</url-pattern>
</servlet-mapping>
최범균님 책에 나와있는데로 다시 web.xml을 설정을 해보았다.. 잘 될 줄 알았는데... 역시나 에러... 어플 하나는 꼭 안되더군..
public class TestDAO {
private DataSource dataSource;
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
public Object insert(TestCommand test) throws Exception{
Connection conn = null;
PreparedStatement pstmt = null;
try{
conn = DataSourceUtils.getConnection(dataSource);
pstmt = conn.prepareStatement("insert into member(id,name,password) values(?,?,?)");
pstmt.setString(1, test.getId());
pstmt.setString(2, test.getName());
pstmt.setString(3, test.getPassword());
pstmt.executeUpdate();
}catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}finally{
DataSourceUtils.releaseConnection(conn, dataSource);
}
return dataSource.getConnection();
}
}
DAO파일은 dataSource객체만 선언해 준 것 빼고 거의 비슷하게 했다. 스프링의 jdbc템플릿을 쓰지 않았다.
public class JoinController extends SimpleFormController{
public JoinController(){}
//서비스 객체 생성
private TestService service;
public TestService getService() {
return service;
}
public void setService(TestService service) {
this.service = service;
}
public ModelAndView onSubmit(Object command) throws Exception{
TestCommand test = (TestCommand) command;
//서비스를 호출
service.insertMemeber(test);
//직접 DAO접근
//to.insert(test);
return new ModelAndView(getSuccessView(), "success" , service.insertMemeber(test));
}
}
컨트롤러는 간단하다. 서비스는 DAO와 연결해주는 빈이고.. 이것을 컨트롤러에서 실행한다.. 이상하게도 DAO에 직접 접근하면 에러가 나더라..
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<bean id = "testDao"
class = "sboard.DAO.TestDAO">
<property name="dataSource">
<ref bean="dataSource" />
</property>
</bean>
<bean id="simpleUrlMapping"
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/testjoin.htm">joinController</prop>
</props>
</property>
</bean>
<bean id="testService"
class="sboard.services.TestService"
p:tdao-ref="testDao" />
<bean id="joinController"
class="sboard.controller.JoinController">
<property name="sessionForm">
<value>true</value>
</property>
<property name="commandName">
<value>joinCommand</value>
</property>
<property name="commandClass">
<value>sboard.commands.TestCommand</value>
</property>
<property name="formView">
<value>join</value>
</property>
<property name="successView">
<value>memberlist</value>
</property>
<property name="service">
<ref bean="testService" />
</property>
</bean>
<!--뷰리졸버 세팅 -->
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass">
<value>org.springframework.web.servlet.view.JstlView</value>
</property>
<property name="prefix">
<value>/WEB-INF/test/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>
</beans>
dataSource는 dataSource.xml에 정의 되어 있다. 그것을 가져와서 testDao에 담고,,, 서비스 클래스에 선언된 tdao객체를 초기화하기 위해 testService선언.. 파라미터로 testDao객체를 넘겨준다. 컨트롤러에서 사용할 서비스객체를 초기화하기 위해서 컨트롤러 빈 내에
service프로퍼티에 testService빈을 파라미터로 넘겨준다.(복잡하다...)
삽질한 만큼 끝내 결과를 보게 되서 다행입니다.;;
오늘도 또 봤지요... 스프링 인 액션2 책을 구해서 봤는데 약간 이해가 되다가도 예제가 그지같군요..(예제가 단적이지 않고 통합되어 있어서 소스를 분석해야함)
그래도 오기로.. 신상철박사님의 사이트에서 예제를 다운받아 실행이라도 되게끔 세팅해 보았습니다. 중간에 오류도 많았구여.
SimpleFormController로 컨트롤러를 구현한 예제였습니다.
차분히 컨트롤러 파일부터 한줄한줄 분석해 보았습니다.
아주 조금씩 이해가 되갈라 카네여..
작년에 스트럿츠2로 만들던 웹게임소스가 있어서 봤는데... 헐.. 로직이 쉽고 설정파일의 가독성이 이렇게나 아름다울수가!!!
스트럿츠2에 새삼 감사했지만, 강력함은 스프링에 못 미칩니다. 스트럿츠2는 단조롭죠.. 물론 더 알아봐야 겠지만요./
package springexample.controller;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.SimpleFormController;
import springexample.commands.AccountDetail;
import springexample.commands.LoginCommand;
import springexample.services.AccountServices;
import springexample.services.AuthenticationService;
/**
* SimpleFormController is a concrete FormController implementation that
* provides configurable form and success views, and an onSubmit chain for
* convenient overriding. Automatically resubmits to the form view in case
* of validation errors, and renders the success view in case of a valid
* submission.
*/
public class LoginBankController extends SimpleFormController {
public LoginBankController() {
}
// The submit behavior can be customized by overriding one of the onSubmit methods. 오버라이딩 필수
protected ModelAndView onSubmit(Object command) throws Exception {
LoginCommand loginCommand = (LoginCommand) command; //오브젝트의 커맨드객체를 형변환시킨다.커맨드에는 폼에서 전달한 아뒤와 패스워드 정보가 담겨있다.
authenticationService.authenticate(loginCommand); //로그인인증.. 아작스처리가 낫지 않을까?
AccountDetail accountdetail = accountServices.getAccountSummary(loginCommand.getUserId());//커맨드를통해정보를담자..머이리 복잡한것인가!!
return new ModelAndView(getSuccessView(), "accountdetail", accountdetail);
}
//어디서 많이 본듯한 장면아닌가..스트럿츠2의 액션인가?? 겟셋..
private AuthenticationService authenticationService;
private AccountServices accountServices;
public AccountServices getAccountServices() {
return accountServices;
}
//sampleBankingServlet-servlet.xml에서 <property name="accountServices">로 설정해 객체를 넘겼다.
//해당객체를 주입하여 인자로 넘겨줌으로써 위에서 선언한 객체변수들을 쓸 수 있는 것이다.
public void setAccountServices(AccountServices accountServices) {
this.accountServices = accountServices;
}
public AuthenticationService getAuthenticationService() {
return authenticationService;
}
public void setAuthenticationService(
AuthenticationService authenticationService) {
this.authenticationService = authenticationService;
}
}
패키지는 controller , services , commands로 나뉜다..
controller : 말그대로 컨트롤러 클래스 위치
commands : User , Account , Login 등 빈즈 위치
services : 비즈니스 로직이 구현된 클래스 위치.
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="simpleUrlMapping"
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/login.html">loginBankController</prop>
</props>
</property>
</bean>
<bean id="logonValidator" class="springexample.commands.LogonValidator"/>
<!-- LoginBankController is a SimpleFormController -->
<bean id="loginBankController"
class="springexample.controller.LoginBankController">
<!--AbstractFormController에 sessionForm()가 정의되어있어 사용가능하다 -->
<property name="sessionForm">
<value>true</value>
</property>
<!-- This is Command object. The command object can be
accessed in a view through <spring:bind path="loginCommand"> -->
<!--커맨드명설정 BaseCommandController클래스에 commandName()메소드가 정의되어있다 -->
<property name="commandName">
<value>loginCommand</value>
</property>
<!--커맨드클래스로 LoginCommand클래스 사용. 파라미터타입은 class타입 -->
<property name="commandClass">
<value>springexample.commands.LoginCommand</value>
</property>
<!-- BaseCommandController에 setValidator()메소드가 정의되어있다. -->
<property name="validator">
<ref bean="logonValidator"/>
</property>
<!-- Indicates what view to use when the user asks for a new form
or when validation errors have occurred on form submission. -->
<!--SimpleFormController클래스에 setFormView()메소드 정의되어있다. 인자로넘긴 login은 login.jsp로 연결
결국 onSubmit()에서 에러가 나면 폼입력화면..또는 처음요청은 GET방식이니까 폼입력으로.. -->
<property name="formView">
<value>login</value>
</property>
<!-- Indicates what view to use when successful form submissions
have occurred. Such a success view could e.g. display a submission
summary. More sophisticated actions can be implemented by
overriding one of the onSubmit() methods.-->
<!-- onSubmit()이 성공하면 getSuccessView()를 리턴하는데 여기서 setSuccessView를 세팅해준다.
accountdetail.jsp로 가는거다. -->
<property name="successView">
<value>accountdetail</value>
</property>
<property name="authenticationService">
<ref bean="authenticationService" />
</property>
<property name="accountServices">
<ref bean="accountServices" />
</property>
</bean>
<!--뷰리졸버 세팅 -->
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass">
<value>org.springframework.web.servlet.view.JstlView</value>
</property>
<property name="prefix">
<value>/WEB-INF/jsp/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>
</beans>
오늘도 또 봤지요... 스프링 인 액션2 책을 구해서 봤는데 약간 이해가 되다가도 예제가 그지같군요..(예제가 단적이지 않고 통합되어 있어서 소스를 분석해야함)
그래도 오기로.. 신상철박사님의 사이트에서 예제를 다운받아 실행이라도 되게끔 세팅해 보았습니다. 중간에 오류도 많았구여.
SimpleFormController로 컨트롤러를 구현한 예제였습니다.
차분히 컨트롤러 파일부터 한줄한줄 분석해 보았습니다.
아주 조금씩 이해가 되갈라 카네여..
작년에 스트럿츠2로 만들던 웹게임소스가 있어서 봤는데... 헐.. 로직이 쉽고 설정파일의 가독성이 이렇게나 아름다울수가!!!
스트럿츠2에 새삼 감사했지만, 강력함은 스프링에 못 미칩니다. 스트럿츠2는 단조롭죠.. 물론 더 알아봐야 겠지만요./
package springexample.controller;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.SimpleFormController;
import springexample.commands.AccountDetail;
import springexample.commands.LoginCommand;
import springexample.services.AccountServices;
import springexample.services.AuthenticationService;
/**
* SimpleFormController is a concrete FormController implementation that
* provides configurable form and success views, and an onSubmit chain for
* convenient overriding. Automatically resubmits to the form view in case
* of validation errors, and renders the success view in case of a valid
* submission.
*/
public class LoginBankController extends SimpleFormController {
public LoginBankController() {
}
// The submit behavior can be customized by overriding one of the onSubmit methods. 오버라이딩 필수
protected ModelAndView onSubmit(Object command) throws Exception {
LoginCommand loginCommand = (LoginCommand) command; //오브젝트의 커맨드객체를 형변환시킨다.커맨드에는 폼에서 전달한 아뒤와 패스워드 정보가 담겨있다.
authenticationService.authenticate(loginCommand); //로그인인증.. 아작스처리가 낫지 않을까?
AccountDetail accountdetail = accountServices.getAccountSummary(loginCommand.getUserId());//커맨드를통해정보를담자..머이리 복잡한것인가!!
return new ModelAndView(getSuccessView(), "accountdetail", accountdetail);
}
//어디서 많이 본듯한 장면아닌가..스트럿츠2의 액션인가?? 겟셋..
private AuthenticationService authenticationService;
private AccountServices accountServices;
public AccountServices getAccountServices() {
return accountServices;
}
//sampleBankingServlet-servlet.xml에서 <property name="accountServices">로 설정해 객체를 넘겼다.
//해당객체를 주입하여 인자로 넘겨줌으로써 위에서 선언한 객체변수들을 쓸 수 있는 것이다.
public void setAccountServices(AccountServices accountServices) {
this.accountServices = accountServices;
}
public AuthenticationService getAuthenticationService() {
return authenticationService;
}
public void setAuthenticationService(
AuthenticationService authenticationService) {
this.authenticationService = authenticationService;
}
}
패키지는 controller , services , commands로 나뉜다..
controller : 말그대로 컨트롤러 클래스 위치
commands : User , Account , Login 등 빈즈 위치
services : 비즈니스 로직이 구현된 클래스 위치.
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="simpleUrlMapping"
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/login.html">loginBankController</prop>
</props>
</property>
</bean>
<bean id="logonValidator" class="springexample.commands.LogonValidator"/>
<!-- LoginBankController is a SimpleFormController -->
<bean id="loginBankController"
class="springexample.controller.LoginBankController">
<!--AbstractFormController에 sessionForm()가 정의되어있어 사용가능하다 -->
<property name="sessionForm">
<value>true</value>
</property>
<!-- This is Command object. The command object can be
accessed in a view through <spring:bind path="loginCommand"> -->
<!--커맨드명설정 BaseCommandController클래스에 commandName()메소드가 정의되어있다 -->
<property name="commandName">
<value>loginCommand</value>
</property>
<!--커맨드클래스로 LoginCommand클래스 사용. 파라미터타입은 class타입 -->
<property name="commandClass">
<value>springexample.commands.LoginCommand</value>
</property>
<!-- BaseCommandController에 setValidator()메소드가 정의되어있다. -->
<property name="validator">
<ref bean="logonValidator"/>
</property>
<!-- Indicates what view to use when the user asks for a new form
or when validation errors have occurred on form submission. -->
<!--SimpleFormController클래스에 setFormView()메소드 정의되어있다. 인자로넘긴 login은 login.jsp로 연결
결국 onSubmit()에서 에러가 나면 폼입력화면..또는 처음요청은 GET방식이니까 폼입력으로.. -->
<property name="formView">
<value>login</value>
</property>
<!-- Indicates what view to use when successful form submissions
have occurred. Such a success view could e.g. display a submission
summary. More sophisticated actions can be implemented by
overriding one of the onSubmit() methods.-->
<!-- onSubmit()이 성공하면 getSuccessView()를 리턴하는데 여기서 setSuccessView를 세팅해준다.
accountdetail.jsp로 가는거다. -->
<property name="successView">
<value>accountdetail</value>
</property>
<property name="authenticationService">
<ref bean="authenticationService" />
</property>
<property name="accountServices">
<ref bean="accountServices" />
</property>
</bean>
<!--뷰리졸버 세팅 -->
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass">
<value>org.springframework.web.servlet.view.JstlView</value>
</property>
<property name="prefix">
<value>/WEB-INF/jsp/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>
</beans>
(key=”<mapping-pattern>”)
● /myapp/*.foo (pattern)
– /myapp/x.foo, /myapp/yz.foo (examples that match the pattern)
● /myapp/p*ttern
– /myapp/p1ttern, /yapp/pxttern
● /**/example
– /myapp/example, youapp/yourdir/example
● /myapp/**/mydir/foo.*
– /myapp/yourapp/yourdir/mydir/foo.x
● /**/*.jsp
– /yourapp/yourdir/x.jsp, /myapp/mydir/yz.jsp
두개의 servlet.xml 파일을 만들어서 각각 요청에 맞게 사용하고 싶은데.. 어떻게 해야할까...
책에 ApplicationContext설정을 보니까
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/front.xml</param-value>
</init-param>
이런식으로 쓰더라구..
처음 만들었던 dispatcher-servlet.xml과 새로 만든 hello-servlet.xml 을 web.xml에 위에 맞게 각각 설정해줬다..
hello.htm , hello2.htm 요청을 하니 hello2.htm만 정상적으로 뜬다,.
어찌된 걸까... 하나를 주석처리하고 실행해보면 각각 잘 된다.
그렇다면 설정에 문제가 있다고 생각했다...
삽질에 삽질...
책을 다시 봤다..
웹요청과 컨트롤러 매핑 : HandlerMapping 챕터에서 설명이 나와있었다..
문제는 <servlet-mapping> 두 서블릿 모두 .htm 으로 url-pattern을 설정한 것...
<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<display-name>
SpringTest</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
<!--한글인코딩 -->
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>
org.springframework.web.filter.CharacterEncodingFilter
</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>EUC-KR</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>
dispatcher
</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
</servlet>
<servlet>
<servlet-name>
hello
</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
</servlet>
<!--http://localhost/spring/say/hello.htm으로 호출 -->
<servlet-mapping>
<servlet-name>
dispatcher
</servlet-name>
<url-pattern>/say/*</url-pattern>
</servlet-mapping>
<!--http://localhost/spring/hello2.htm으로 호출 -->
<servlet-mapping>
<servlet-name>
hello
</servlet-name>
<url-pattern>*.htm</url-pattern>
</servlet-mapping>
</web-app>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
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 base-package="kame.spring" />
<bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/index.htm">indexController</prop>
<prop key="/hello2.htm">helloController</prop>
</props>
</property>
</bean>
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"
p:prefix="/WEB-INF/test/"
p:suffix=".jsp" />
<bean name="indexController" class="org.springframework.web.servlet.mvc.ParameterizableViewController"
p:viewName="index" />
</beans>
============================================================================
dispatcher-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<!--핸들러-->
<bean id="handlerMapping"
class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" />
<bean name="/hello.htm" class="kame.spring.chap04.HelloController" />
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/view/" />
<property name="suffix" value=".jsp" />
</bean>
</beans>
스프링은 설정파일 부분이 너무 복잡하다. 방식도 다양하고 상속관계도 알아둘 필요가 있다.
아무튼 성공!!
<form:form>
<form:input path = "userId" />
...
<input type="submit" value="회원가입" />
</form:form>
<form:form commandName = "memberInfo">
....
</form:form>
커맨드객체의 이름이 기본 값인 command가 아니라면 위와 같이 commandName속성에 커맨드 객체의 이름을 명시해 주어야 한다.
<input>을 위한 커스텀태그 <form:input>,<form:password>,<form:hidden>
<form:form commandName = "memberInfo">
<p>
<form:label path="userId">회원아뒤</form:label>
<form:input path="userId"/>
<form:errors path="userId"/>
</p>
path속성에서 지정한 커맨드 객체의 프로퍼티 값이 출력된다. 그러니까 memberInfo.userId 가 되겠지...(안해봤지만ㅋ)
이외에도 <form:select>, <form:options>, <form:option> referenceData()메소드 이용...
체크박스, CSS및 HTML공통속성
에러관련 커스텀 태그 등이 있다.,.. 책을 참조하자..
컨트롤러 부분은 깊게 공부하지 않았다.
이거 일일이 진지하게 파헤쳐보려다간 시간을 너무 많이 날릴 수 있으므로... 충분히 실제 코딩하면서 레퍼런스할 정도로 공부해 두면 되겠다.
컨트롤러 클래스 분류
컨트롤러 부분은 어렵다.(무엇인들 어렵지 않을까..)
목적에 맞게 컨트롤러르 구현하자. 물론 책을 잘 참조해서,.