├── .gitignore ├── 0318 └── 0318.md ├── 0325 ├── 1.SpringMessageSource.md ├── 10.AOPetc.md ├── 11.SpringAspectJ.md ├── 2.SpringContextEvent.md ├── 3.SpringAnnotation.md ├── 4.ObjectScan.md ├── 5.javaCodeConfig.md ├── 6.AOP.md ├── 7.SpringAOP.md ├── 8.xmlAOP.md └── 9.AnnotationAOP.md ├── 0401 ├── 01.스프링MVC의 주요 구성 요소 및 처리 흐름.md ├── 01.스프링MVC의 주요 구성 요소 및 처리 흐름.md ├── 02.DispatcherServlet 과 ApplicationContext.md ├── 02.DispatcherServlet 과 ApplicationContext.md ├── 03.컨트롤러 구현.md ├── 03.컨트롤러 구현.md ├── 04.파일업로드 처리.md ├── 04.폼값 검증 및 에러메세지-MarkdownPadPreview.html ├── 04.파일업로드 처리.md ├── 04.폼값 검증 및 에러메세지-MarkdownPadPreview.html ├── 0401.md ├── 05.폼값 검증 및 에러메세지.md ├── 05.폼값 검증 및 에러메세지.md ├── 06.HandlerInterceptor.md ├── 07.예외처리.md └── 07.예외처리.md ├── 0408 ├── 0408.md └── chap08.md ├── 0408_jdbc ├── 0415.md ├── 0422 ├── 0422.md ├── 0422_chap12.md ├── 0422_chap12_2.md ├── 11장 OXM.md └── chap13 springmail.md ├── 0429 └── SpringJms.md ├── 0506 ├── 0506.md ├── SpringJms.md └── imgs │ └── jmx-example.png └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /0318/0318.md: -------------------------------------------------------------------------------- 1 | Spring 3.0 프로그래밍 1, 2장 2 | =========================== 3 | 4 | ## 1. 스프링 프레임워크 소개 5 | 6 | ### 01. 스프링 프레임워크란 7 | 8 | 스프링이란? 엔터프라이즈 애플리케이션에서 필요로 하는 기능을 제공하는 프레임워크다. 9 | 10 | * 스프링은 경량 컨테이너다. 11 | * DI (Dependency Injection)을 지원한다. 12 | * AOP (Aspect Oriented Programming)을 지원한다. 13 | * POJO (Plain Old Java Object)를 지원한다. 14 | * 트랜잭션 처리를 위한 일관된 방법을 제공한다. 15 | * 영속성과 관련된 API를 지원한다. 16 | * 다양한 API에 대한 연동을 지원한다. 17 | * MVC 프레임워크를 제공하고 있다. 18 | 19 | ### 02. 스프링 프레임워크 설치와 모듈 구성 20 | 21 | 다운로드 [http://www.springsource.org/download](http://www.springsource.org/download) 22 | 23 | #### 모듈 구성 24 | 25 | * core : DI 기능을 비록한 프레임워크의 기반을 제공한다. 26 | * beans : BeanFactory 인터페이스를 통해 구현된다. 27 | * context : 국제화, 이벤트 처리, 리소스 로딩, 서블릿 컨테이너를 위한 컨텍스트 생성 등의 기능을 추가로 제공한다. 28 | 29 | #### 외부 의존 모듈 30 | 31 | 다양한 외부 라이브러리를 spring-framework-3.0.1.RELEASE-dependencies.zip 파일로 제공한다. 32 | 33 | 스프링 프레임워크를 사용하기 위해서 모든 외부 의존 모듈이 필요한 것은 아니다. 34 | 35 | ### 03. Dependency Injection과 스프링 프레임워크 36 | 37 | #### Dependency Injection 38 | 39 | * 코드에 직접 의존 객체 명시 40 | 41 | 코드에 직접 의존 클래스를 명시하는 것이 쉽지만 단위 테스트를 어렵게 만드는 단점이 있다. 42 | 43 | ```java 44 | public class WriteArticleServiceImpl { 45 | private ArticleDao articleDao = new MySQLArticleDao(); 46 | ... 47 | } 48 | ``` 49 | 50 | * Factory 패턴이나 JNDI 등으로 의존 클래스를 검색하는 방법 51 | 52 | 올바르게 동작하는 Factory 또는 JNDI 객체가 필요하다. 53 | 54 | ```java 55 | public class WriteArticleServiceImpl { 56 | private ArticleDao articleDao = ArticleDaoFactory.create(); 57 | ... 58 | } 59 | ``` 60 | 61 | * 외부 조립기를 사용하는 방법 62 | 63 | 조립기의 역할을 하는 Assembler 가 객체를 생성한 후 전달해준다. 64 | 65 | ```java 66 | public class WriteArticleServiceImpl { 67 | private ArticleDao articleDao; 68 | 69 | public WriteArticleServiceImpl(ArticleDao articleDao) { 70 | this.articleDao = articleDao; 71 | } 72 | ... 73 | } 74 | ``` 75 | 76 | 77 | ```java 78 | public class Assembler { 79 | public WriteArticleService getWriteArticleService() { 80 | ArticleDao articleDao = new MySQLArticleDao(); 81 | 82 | // 의존 객체 전달 83 | WriteArticleService service = WriteArticleServiceImpl(articleDao); 84 | return service; 85 | } 86 | ... 87 | } 88 | 89 | public class UsingService { 90 | public void useService() { 91 | ... 92 | // 조립기로부터 사용할 객체를 구함 93 | WriteArticleService service = assembler.getWriteArticleService(); 94 | service.write(); 95 | ... 96 | } 97 | } 98 | ``` 99 | 100 | ##### 장점 101 | 102 | * Factory 패턴을 사용할 경우 구현 클래스에 대한 의존 관계는 줄일 수 있지만 Factory 클래스에 대한 의존 관계가 추가된다. 하지만 DI 패턴을 사용하면 WriteArticleServiceImpl 클래스는 오직 ArticleDao 인터페이스에만 의존하게 되며 그 외 다른 클래스에는 의존하지 않게 된다. 103 | * DI 패턴은 단위 테스트를 수행하는 게 수월해진다. Mock 객체를 사용하여 MySQLArticleDao 클래스를 사용하지 않고도 WriteArticleServiceImple 클래스를 테스트 할 수 있게 된다. 104 | 105 | #### 스프링에서의 DI 106 | 107 | ##### (1) DI 패턴을 적용한 자바코드 108 | 109 | ```java 110 | package madvirus.spring.chap01; 111 | 112 | public class WriteArticleServiceImpl implements WriteArticleService { 113 | 114 | private ArticleDao articleDao; 115 | 116 | public WriteArticleServiceImpl(ArticleDao articleDao) { 117 | this.articleDao = articleDao; 118 | } 119 | 120 | @Override 121 | public void write(Article article) { 122 | System.out.println("WriteArticleServiceImpl.write() 메서드 실행"); 123 | articleDao.insert(article); 124 | } 125 | 126 | } 127 | ``` 128 | 129 | ##### (2) 스프링 설정 파일을 이용한 의존 관계 설정 130 | 131 | ```xml 132 | 133 | 134 | 138 | 139 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | ``` 151 | 152 | 스프링은 객체를 빈(bean)으로 관리한다. 153 | 태그 : 생성자에 전달할 파라미터 154 | 155 | ##### (3) 클래스패스 설정 및 ApplecationContext를 이용한 빈 객체 사용 156 | 157 | ```java 158 | package madvirus.spring.chap01; 159 | 160 | import org.springframework.beans.factory.BeanFactory; 161 | import org.springframework.beans.factory.xml.XmlBeanFactory; 162 | import org.springframework.core.io.ClassPathResource; 163 | import org.springframework.core.io.Resource; 164 | 165 | public class Main { 166 | 167 | public static void main(String[] args) { 168 | Resource resource = new ClassPathResource("applicationContext.xml"); 169 | BeanFactory beanFactory = new XmlBeanFactory(resource); 170 | WriteArticleService articleService = (WriteArticleService) beanFactory 171 | .getBean("writeArticleService"); 172 | articleService.write(new Article()); 173 | } 174 | } 175 | ``` 176 | 177 | XmlBeanFactory 클래스는 resource가 나타내는 XML 파일로부터 스프링 설정 내용을 로딩하여 빈 객체를 생성하는 BeanFactory 구현 클래스 178 | 179 | ### 04. AOP와 스프링 180 | 181 | 로깅이나 트랜잭션 처리, 보안과 같은 기능은 대부분 필요로 한다. 공통 관신 사항들을 객체 지향 기법을 사용해서 여러 모듈에 적용하는데 한계가 있다. 한계를 극복하기 위해 AOP 기법을 사용한다. 182 | 183 | #### AOP 소개 184 | 185 | AOP(Aspect Oriented Programming)는 공통의 관심 사항을 적용해서 발생하는 의존 관계의 복잡성과 코드 중복을 해소해 주는 프로그래밍 기법이다. 186 | 187 | 핵심 로직 코드의 수정없이 웹 어플리케이션에 보안, 로깅, 트랜젝션과 같은 공통 관심 사항을 AOP를 이용하여 간단하게 적용할 수 있다. 188 | 189 | 메서드 실행시간 구하는 LoggingAspect.java 190 | 191 | ```java 192 | package madvirus.spring.chap01; 193 | 194 | import org.apache.commons.logging.Log; 195 | import org.apache.commons.logging.LogFactory; 196 | import org.aspectj.lang.ProceedingJoinPoint; 197 | import org.springframework.util.StopWatch; 198 | 199 | public class LoggingAspect { 200 | private Log log = LogFactory.getLog(getClass()); 201 | 202 | public Object logging(ProceedingJoinPoint joinPoint) throws Throwable { 203 | log.info("기록 시작"); 204 | StopWatch stopWatch = new StopWatch(); 205 | try { 206 | stopWatch.start(); 207 | Object retValue = joinPoint.proceed(); 208 | return retValue; 209 | } catch (Throwable e) { 210 | throw e; 211 | } finally { 212 | stopWatch.stop(); 213 | log.info("기록 종료"); 214 | log.info(joinPoint.getSignature().getName() + "메서드 실행 시간 : " 215 | + stopWatch.getTotalTimeMillis()); 216 | } 217 | } 218 | } 219 | ``` 220 | 221 | Aspect를 어떤 클래스의 어떤 메서드에 적용할지 설정 222 | 223 | ```xml 224 | 225 | 226 | 233 | 234 | 235 | 236 | 237 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | ``` 247 | 248 | 테스트 249 | 250 | ```java 251 | package madvirus.spring.chap01; 252 | 253 | import org.springframework.context.ApplicationContext; 254 | import org.springframework.context.support.ClassPathXmlApplicationContext; 255 | 256 | public class MainForAop { 257 | 258 | public static void main(String[] args) { 259 | String[] configLocations = new String[] { "applicationContext.xml", "commonConcern.xml" }; 260 | ApplicationContext context = new ClassPathXmlApplicationContext( 261 | configLocations); 262 | WriteArticleService articleService = (WriteArticleService) context.getBean("writeArticleService"); 263 | articleService.write(new Article()); 264 | } 265 | } 266 | ``` 267 | 268 | 269 | 270 | ## 2. 스프링 DI와 객체 관리 271 | 272 | ### 01. 스프링 컨테이너 273 | 274 | ### 02. 빈(Bean) 생성과 의존 관계 설정 275 | 276 | ### 03. 빈 객체 범위 277 | 278 | ### 04. 라이프 사이클 279 | 280 | ### 05. 외부 설정 프로퍼티 281 | 282 | ### 06. 컨테이너 간 계층 283 | 284 | -------------------------------------------------------------------------------- /0325/1.SpringMessageSource.md: -------------------------------------------------------------------------------- 1 | # Spring MessageSource 를 이용한 메세지 다국어 처리 # 2 | ------------------------------- 3 | 4 | Spring 은 다국어 처리를 위한 방법으로 org.springframework.context.MessageSource 인터페이스를 제공한다. 5 | 이 인터페이스는 아래와 같은 source 로 이루어 져 있다. 6 | ```java 7 | public interface MessageSource{ 8 | String getMessage(String code, Object[] args, String defaultMessage, Locale locale); 9 | String getMessage(String code, Object[] args, Locale locale) throws NoSushMessageException; 10 | String getMessage(MessageSourceResolvable resolvable, Locale locale) thorws NoSushMessageException; 11 | } 12 | ``` 13 | MessageSource 에는 getMessage 라는 method 로 3개의 입력 파라미터가 다른 method가 오버로딩 되어 있다. 14 | 15 | ApplicationContext 를 통하여 메시지를 가져 오기 위해서 Spring 에서는 "messageSource" 인 빈 객체를 정의 하여야 한다. 16 | ```xml 17 | 19 | 20 | com.interwater.message.greeting 21 | 22 | utf-8 23 | 24 | ```` 25 | ReourceBundelMeaasgeSource Class 는 java.util.ResourceBundle를 통하여 메시지를 읽어 오는 MessageSource 구현 Class 이다. (properties 파일에서 메시지를 가져 오는) 26 | 따라서 message properties 파일을 통해서 실제 사용할 언어 message를 가져 오게 된다. 27 | 28 | 29 | - message.properties - 기본 메시지, 시스템의 언어 및 지역에 맞는 프로퍼티 파일이 존재 하지 않을 경우에 사용된다. 30 | 31 | - message_en.properties - 영어 메시지. 32 | 33 | - message_ko.properties - 한글 메시지. 34 | 35 | - message_en_UK.properties - 영국을 위한 영어 메시지 36 | 37 | - message_zh.properties - 중국을 위한 중국어 메시지 38 | 39 | properties 파일 명명은 basname_언어코드_국가코드.properties 형식을 가지게 되며 상기 bean 파일에서는 basename 이 com.interwater.message.greeting 이 base name 이므로 실제 사용하려면, greeting_ko.properties 와 같이 file 명을 만들어서 사용하여야 한다. 40 | 41 | 그리고 프로퍼티 바일의 경우 유니코드를 이용하여 값을 표시 하여 주어야 하는데, 이를 위해서는 보통 %JAVA_HOME%/bin/native2ascii.exe 명령어를 이용한다. 42 | 43 | 하지만 properties 파일을 생성해서 native2ascii 를 이용하여 변경하는것은 꽤나 불편한 작업니다. 44 | 따라서 간단한 eclipse 플러그인을 소개 한다. 45 | 46 | http://propedit.sourceforge.jp/eclipse/updates/ 에서 Properties Editor를 install 한다. 47 | ![](http://cfile8.uf.tistory.com/image/113C5C4C514C10490372DD) 48 | 49 | 설치가 완료 되면 아래 스크린샷 과 같이 properties editor 메뉴가 생기며, 이를 통해 쉽게 unicode 로 프로퍼티 파일을 작성할수 있다. 50 | 51 | ![](http://cfile2.uf.tistory.com/image/19024550514C10983A56C5) 52 | 53 | 54 | 자... 그럼 빈 객체에서 이 메세지를 활용하기 위한 방법에 대하여 알아 보겠다. 55 | 보통 Spring 에서 제공하는 MessageSource 를 사용하려면 interface 로 ApplicationContextAware 또는 MessageSourceAware 를 이용하여 getMessage메서드를 이용하여 메시지를 이용하면 된다. 56 | 57 | ex) 58 | ```java 59 | public class Sample implements ApplicationContextAware { 60 | 61 | private ApplicationContext applicatonContext; 62 | 63 | @Override 64 | public void setApplicationContext(ApplicationContext applicatonContext) 65 | throws BeansException { 66 | 67 | this.applicatonContext = applicatonContext; 68 | } 69 | 70 | public String login(String username, String password) { 71 | Object[] args = new String[] { username }; 72 | String message = applicatonContext.getMessage("login.hi", args, Locale.getDefault()); 73 | return message; 74 | } 75 | } 76 | ``` 77 | ```java 78 | login.hi=Member ID {0} is log in.!!! 79 | ``` 80 | MessageSourceAware 는 위와 동일하나 단지 implement (override) 가 바뀌고 ApplicationContext 를 MessageSource 로 바꾸면 된다. 81 | -------------------------------------------------------------------------------- /0325/10.AOPetc.md: -------------------------------------------------------------------------------- 1 | # Spring AOP 기타 활용 # 2 | ------------------------ 3 | 4 | ## 1. JointPoint ## 5 | Around Advice(ProceedingJoinPoint) 를 제외한 나머지 advice type은 JointPoint 를 첫번째 입력 파라미터로 받음으로 해서 JoinPoint를 전달 받을수 있다. 6 | ex) public void before(JoinPoint joinPoint){...} 7 | 8 | 그럼 이 joinPoint 의 메소드에 대하여 살펴 보자. 9 | - Signature getSignature() : 호출되는 method의 정보를 구한다. 10 | - Object getTarget() : 대상 객체를 구한다. 11 | - Object[] getArgs() : 파라미터 목록을 구한다. 12 | 13 | 요기서 Signature 의 메서드는 다음과 같다. 14 | - String getName() : 메서드의 이름을 구한다. 15 | - String toLongString() : 메서드의 완전한 문장을 구한다.(리턴타입, 파라미터 포함) 16 | - String toShortString() : 메서드를 축약하여 표현한 문장을 구한다. 17 | 18 | ## 2. 파라미터 접근 ## 19 | 20 | 요기서 파라미터라 함은 target method에 입력되는 파라미터이다. 21 | 입력 파라미터에 접근 하는 방법은 jointPoint의 getArgs method를 이용하여 처리 하는 방법이 있으며, 부가적으로 args 인자값을 이용하는 방법이 있다. 22 | 23 | xml 방식으로는 아래와 같이 접근 한다. 24 | ```xml 25 | 26 | ``` 27 | 28 | 어노테이션 방식은 아래와 같다. 29 | 30 | ```java 31 | @AfterReturning(pointcut="args(eye, info)", argName="jointPoint, eye, info") 32 | ``` 33 | 34 | 위와 같이 설정되었을 경우 advice method는 아래과 같이 구현될 수 있다. 35 | 36 | ```java 37 | public void afterReturning(Joint point, EyeClolor eye, Info info){.....} 38 | ``` 39 | 40 | 위에서 xml의 arg-name 이나 어노테이션의 argName의 경우 생략 가능하고 생략한 경우 pointcut에서 설정된 파라미터 갯수를 파악후 정의 순서에 따라서 파라미터를 처리 하게 된다. 만약 인자값의 갯수가 틀리면 IllegalArgumentException 이 발생한다.(debug 모드에서는 파라미터 이름 까지검증) 41 | 42 | ## 3. AspectJ의 Pointcut 표현식 ## 43 | 앞선 포스팅에서 pointcut excution 에서의 표현식은 AspectJ 의 표현식을 따르며 기본 형식은 다음과 같다. 44 | 45 | - execution(수식어패턴 리턴타입패턴 글래스이름패턴 이름패턴(파라미터패턴)) 46 | 수식어 패턴은 public, protected 등의 값을 가지며 생략 가능하다. 리턴타입 패턴은 리턴 type를 명시한다. 각 패턴은 *을 사용하여 모든 값을 표현 할수 있으며 .. 을 통해서 0 개 이상이라는 의미를 표현할수 있다. 47 | 48 | 앞서 예시에서 적용한 execution(public * com.interwater.service..*(..)) 는 public 수식어를 가지며 모든 리턴 타입에 com.interwater.service 패키지 내의 하위 패키지 포함 모든 클래스를 의미 함을 알수 있으며 입력 파라미터는 0개 이상이다. 49 | 50 | excution 이외에 within 명시자는 특정타입에 속하는 method 를 정의 할수 있다. 51 | 52 | ex) ``` within(com.interwater.service.*) , within(com.interwater.service.greet.HelloService) ``` 53 | 54 | 또한 bean 명시자는 bean name 을 통해서 Pointcut을 정의 할수 있다. 55 | 56 | ex) ``` bean(helloService) ``` 57 | 58 | 위 3가지 명시자를 활용하여 && 또는 || 을 통해서 연산자를 적용 할수도 있다.xml의 경우 (and, or 또는 &&) 59 | 60 | ex) 61 | 62 | ```java 63 | @AfterThrowing(pointcut ="execution(public * get*()) || excution(public * set*())" ) 64 | ``` 65 | ```xml 66 | 67 | ``` 68 | 69 | 70 | 4. CGLIB 이용 71 | 앞선 포스팅에서 Spring AOP 는 대상 객체가 한개 이상의 인터페이스를 구현하고 있는 경우 자바의 다이나믹 프록시 기능을 이용하여 프록시 객체를 생성하는 방법이었다. 만약 인터페이스를 구현하지 않은 경우에는 CGLIB 를 이용하여 처리 가능하다. 72 | 73 | ex) ``` 48 | 49 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | ``` 64 | 65 | - 스프링 설정파일에 를 추가한다. 66 | 67 | ##2. AspectJ를 이용한 객체의 DI 처리 ## 68 | 69 | Spring은 Spring 컨테이너에 의해 생성되는 Object 에 대한 의존 관계를 처리 해준다. 하지만 Spring Context가 생성하지 않은 객체(bean)에 대해서는 DI기능을 사용할 수 없다. 70 | 이러한 경우 AspectJ를 활용하여 DI 기능을 처리 할수 있다. 71 | 72 | 우선 org.springframework.aspects-3.0.1.RELEASE-A.jar 를 classpath 에 추가하여 @Configurable 어노테이션을 사용할수 있도록 한다. 73 | 74 | ```java 75 | @Configurable 76 | public class Greet { 77 | private hiDao; 78 | public void setHello(HiDao hiDao) { 79 | this.hiDao = hiDao; 80 | } 81 | } 82 | ``` 83 | 84 | 위 코드는 아래의 xml 설정과 동일하다. 85 | 86 | ```xml 87 | 88 | 89 | 90 | ``` 91 | 92 | 만약 빈 객체의 이름을 정의하고자 한다면 @configurable("greet1") 과 같이 이름을 지정할수 있다. 93 | 94 | 위와 같은 방법은 Spring 에서 bean 을 정의한 방법이 아닌 AspectJ에서 정의한 방법이므로 META-INF/aop.xml의 weaver 태그 하위에 아래 코드가 추가되어야 한다. 95 | 96 | ```xml 97 | 98 | 99 | 100 | ``` 101 | 102 | 또한 이 bean 을 spring과 연동하기 위해서는 spring 설정 파일에 ``` ``` 라는 태그가 추가 되어야 한다. 103 | 104 | Configurable 옵션 105 | 106 | - preConstruction : DI를 통해서 전달받은 객체는 기본적으로 생성자 호출후에 적용된다 하지만 preConstruction=true 속성을 지정하면 생성자 호출전 적용된다. 107 | - autowire : 자동 DI 설정으로 autowire=Autowire.BY_TYPE, autowire=Autowire.BY_NAME 두가지가 있다. 108 | - dependencyCheck : autowire 설정을 이용하였을때에 dependencyCheck=true 로 지정하면 java 기본타입을 제외한 나머지 propery가 정상적으로 생성되었는지 체크 하게 되며, DI 가 정상적으로 생성되지 않았다면 예외를 발생시킨다. 109 | 110 | AspectJ의 경우 사실 프로젝트시에 Spring내의 기능만으로도 충분이 거의 모든 기능을 커버 할수 있으므로 사실 거의 사용할 일은 없을듯 하다. 111 | 112 | 113 | *유의 사항* : Aspect가 추가된 프로그램이 was 내에서 돌아 간다면 부가적인 설정이 필요 없으나 일반적인 java application 에서 돌아 간다면 java -javaagent:경로/org.springframework.instrument-3.0.1.RELEASE-A.jar 와 같이 agent 옵션을 통해서 실행하여야 한다. -------------------------------------------------------------------------------- /0325/2.SpringContextEvent.md: -------------------------------------------------------------------------------- 1 | # Spring Context Event # 2 | ------------------------------- 3 | 4 | 서비스를 런칭하여 서비스를 운영하다 보면 로그인 이벤트, 회원 가입 이벤트, 구매 이벤트 등등 다양한 이벤트를 진행하게 된다. 5 | 하지만 각 이벤트를 로직에 넣어서 진행하다 보면 다양한 이벤트의 관리가 어려워 진다. 6 | 7 | 이를 위하여 Spring의 ApplicationContext 에서는 이벤트를 발생시킬수 있는 publishEvent 메서드를 제공한다. 8 | 9 | 이를 사용하기 위해서는 ApplicationContextAware를 implements 하여 publishEvent 메서드를 구연함으로써 처리 할수 있다. 10 | 11 | 아래 코드는 ApplicatonContextAware 를 implements 하여 구현한 source 예제 이다. 12 | 13 | ```java 14 | public class MemberService implements ApplicationContextAware { 15 | 16 | private ApplicationContext applicationContext; 17 | 18 | @Override 19 | public void setApplicationContext(ApplicationContext applicationContext) 20 | throws BeansException { 21 | this.applicationContext = applicationContext; 22 | } 23 | 24 | public void login(Member member) { 25 | System.out.println("login 시작"); 26 | applicationContext.publishEvent(new MemberLoginEvent(this, 27 | member)); 28 | System.out.println("login 종료"); 29 | } 30 | } 31 | ``` 32 | 33 | 위 source 와 같이 이벤트 발생시 publishEvent 메소드를 실행함으로서 이벤트를 발생시킬수 있다. 34 | 35 | 이때 MemberLoginEvent Class 는 ApplicationEvent 를 상속 받아 구현한다. 36 | 하지만 실제 event 로직은 MemberLoginEvent내에 존재 하는 것이 아닌 event 실행에 따른 파라미터 정도만 클래스내에 변수로 처리 하고 실제 이벤트 처리 로직은 ApplicationListener를 통해서 구현되어야 한다. 37 | 즉, MemberLoginEvent는 이벤트를 등록하는 과정이며, 실제 실해을 spring 에서 이벤트 발생을 listening 하고 있는 class 에서 실행하게 된다. 38 | 39 | 아래는 sample source를 참고 하자. 40 | 41 | ```java 42 | public class MemberLoginEvent extends ApplicationEvent { 43 | 44 | private Member member; 45 | 46 | public MemberLoginEvent(Object source, Member member) { 47 | super(source); 48 | try { 49 | Thread.sleep(3000); 50 | } catch (InterruptedException e) { 51 | // TODO Auto-generated catch block 52 | e.printStackTrace(); 53 | } 54 | this.member = member; 55 | } 56 | 57 | public Member getMember() { 58 | return member; 59 | } 60 | 61 | } 62 | ``` 63 | --> event listener 64 | ```java 65 | public class MemberEventListener implements 66 | ApplicationListener { 67 | 68 | @Override 69 | public void onApplicationEvent(MemberLoginEvent event) { 70 | try { 71 | Thread.sleep(10000); 72 | } catch (InterruptedException e) { 73 | // TODO Auto-generated catch block 74 | e.printStackTrace(); 75 | } 76 | System.out.println("회원 로그인 이벤트 발생: " + event.getMember()); 77 | } 78 | 79 | } 80 | ``` 81 | --> xml 82 | ``` xml 83 | 84 | ``` 85 | 86 | 위 code와 같이 MemberLoginEvent 의 생성자가 실행이 완료된 직후 eventlistener 가 이벤트 process 를 진행하게 된다. 87 | 88 | source를 실행해 보면 사실 이 기능은 event 를 async 방식으로 실행하지 않고 sync 방식으로 실행한다. 즉, event 처리를 service 단에서 처리 하여도 동일한 결과가 된다. 하지만 앞서 언급했듯이 서비스에서 다양한 event 가 진행되고 있다면 관리/운영의 효율성을 위해서 이 기능을 사용하는 것이 좋다. 89 | 90 | 아니면 그냥 공통 event를 위한 공통 class 를 만들어서 사용하여도 무방할듯 ...-_-;;;;?? 스프링 이벤트 기능의 사용에 대하여서는 서비스의 종류와 규모에 따라서 고민해 보아야 할듯하다. 91 | -------------------------------------------------------------------------------- /0325/3.SpringAnnotation.md: -------------------------------------------------------------------------------- 1 | # Spring 어노테이션 # 2 | ------------------------------- 3 | 4 | 스프링은 어노테이션을 사용함으로서 설정 파일의 크기를 줄이고, 간단히 java code상에서 설정을 활용함으로서 좀더 간편히 개발할수 있다. 5 | 물론 개발자의 성향에 따라서 어노테이션 기능을 지양하는 개발자도 다수 있다. 6 | 7 | ## 1. @Required 어노테이션 ## 8 | 9 | Required 어노테이션은 필수 프로퍼티 검사를 위한 용도로 활용된다. 10 | 11 | 12 | ```java 13 | public class Phone { 14 | 15 | private int number; 16 | 17 | public Phone() { 18 | } 19 | 20 | @Required 21 | public void setNumber(int number) { 22 | this.number = number; 23 | } 24 | 25 | @Override 26 | public String toString() { 27 | return "Phone [number=" + number + "]"; 28 | } 29 | 30 | } 31 | ``` 32 | 33 | 위 source 에서 보면 setNumber 위에 required 라는 어노테이션이 달려 있다. 즉 필수 값 설정인 것이다. 하지만 이와 같은 어노테이션 만으로는 정상적으로 작동되지 않는다. 34 | 이 어노테이션 기능을 사용하기 위해서는 2가지 방법이 있다. 35 | 36 | 첫째로, bean 객체 등록이 있다. 37 | ```xml 38 | 39 | ``` 40 | 두번째로 context:annotation-config 를 사용하는 방법이 있다. 41 | ```xml 42 | 49 | 50 | 51 | 52 | ``` 53 | 54 | 일반적으로는 두번째 방법을 많이 활용한다. 55 | ※ annotation-config 는 어노테이션과 관련하여 아래 BeanPostProcessor를 등록하는 기능을 담당한다. 56 | ```xml 57 | 58 | 59 | 60 | 61 | ``` 62 | ## 2. @AutoWired 어노테이션 ## 63 | AutoWired 의 기능은 의존 관계를 자동으로 설정할때에 사용되는 기능이며, 생성자, 필드, 메서드의 세곳에 적용가능하다. 64 | 이때 의존 관계의 의미는 각 Bean간의 의존 관계로 생각하면 된다 65 | 아래 source를 보자 66 | 67 | 68 | --- service ----- 69 | ```java 70 | public class WriteArticleServiceImpl implements WriteArticleService { 71 | 72 | private ArticleDao articleDao; 73 | 74 | public void setArticleDao(ArticleDao articleDao) { 75 | this.articleDao = articleDao; 76 | } 77 | 78 | @Override 79 | public void write(Article article) { 80 | System.out.println("WriteArticleServiceImpl.write() 메서드 실행"); 81 | articleDao.insert(article); 82 | } 83 | 84 | } 85 | ``` 86 | --- dao ----- 87 | ```java 88 | public interface ArticleDao { 89 | 90 | void insert(Article article); 91 | } 92 | ``` 93 | --- dao imple ---- 94 | ```java 95 | public class MysqlArticleDao implements ArticleDao { 96 | 97 | @Override 98 | public void insert(Article article) { 99 | System.out.println("MysqlArticleDao.insert() 실행"); 100 | } 101 | 102 | } 103 | ``` 104 | 위 코드를 실행하기 위해서는 아래 설정 파일이 명시 적으로 선언 되어 있어야 한다. 105 | 106 | ```xml 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | ``` 115 | 116 | 즉, WriteArticleServiceImpl 서비스의 articleDao라는 field(property)는 mysqlArticleDao 와 의존 관계이다. 라는 의미이다. 117 | 위 코드를 간단히 하기위해서 autowired 를 사용할수 있다. 118 | 상기 붉은색 블럭을 제거 하고 service source의 private ArticleDao articleDao; 또는 public void setArticleDao(ArticleDao articleDao) { 상단에 autowired 어노테이션 설정을 사용하면 된다. 119 | 120 | autowired 사용시 만약 bean 객체가 존재 하지 않거나 두개 이상 존재 할때에는 exception을 발생시킨다. 121 | 122 | 만약 빈객체가 존재하지 않아도 상관없을경우(꼭, 필요한 빈이 아닌경우) @Autowired(required=false) 를 어노테이션으로 활용할 수도 있다. 그리고 만약 빈 객체가 두개 이상 존재 할때에는 @qualifier 어노테이션을 통해 지정된 수식어를 가진 빈 객체를 연결할수 있다. 123 | 124 | ex) 125 | ```java 126 | @Autowired 127 | @Qualifier("first") 128 | private Hello hello; 129 | ``` 130 | ```xml 131 | 132 | 133 | 134 | 135 | 136 | 137 | ``` 138 | ## 3. @Resource 어노테이션 ## 139 | Resource 어노테이션은 java 6 및 JEE5 버전에 추가된 어노테이션 기능으로 어플리케이션에서 필요로 하는 자원을 자동으로 연결할때 사용된다. 140 | 사용위치는 autowired와 유사하지만 어노테이션 뒤에 name 속성이 들어 가게 된다. 141 | ex) @Resource(name="hello") 142 | 143 | 이 어노테이션을 적용하기 위해서는 CommonAnnotationBeanPostProcesser를 등록하거나 를 사용하면 된다. 144 | 145 | 아마 Autowired와 Resource 어노테이션의 기능을 숙지 하였다면 한가지 의문점이 생길것이다. 146 | 두 기능이 유사하기 때문이다. 147 | 두기능의 차이점은 아래와 같다. 148 | 149 | - @Autowired 150 | Spring Framework에서 지원하는 Dependency 정의 용도의 Annotation으로, Spring Framework에 종속적이긴 하지만 정밀한 Dependency Injection이 필요한 경우에 유용하다. 151 | - @Resource 152 | JSR-250 표준 Annotation으로 Spring Framework 2.5.* 부터 지원 가능한 Annotation이다. Annotation 사용으로 인해 특정 Framework에 종속적인 어플리케이션을 구성하지 않기 위해서 @Resource를 사용한다. @Resource를 사용하기 위해서는 클래스패스 내에 jsr250-api.jar 파일이 추가되어야 한다. 153 | 154 | ## 4. @PostConstruct 및 @PreDestroy 어노테이션 ## 155 | 이 두가지 어노테이션은 javqx.annotation 패키지에 존재하며 Resource 어노테이션과 동일하게 Java6 및 JEE5 에 추가된 기능으로 라이프 사이클 초기화 및 제거 과정을 제공한다. 156 | 157 | @PostConstruct 어노테이션은 의존 객체 설정후 초기화 작업을 수행할 때 사용되며 반대로 @PreDestroy은 컨테이너에서 객체를 제거하기 전에 호출되는 메소드를 설정한다. 158 | 159 | 아래 source를 보자 160 | ```java 161 | public class GreetController { 162 | 163 | @Autowired 164 | private Hello hello; 165 | 166 | @PostConstruct 167 | public void init() { 168 | hello.set("hi Spring"); 169 | } 170 | 171 | @PreDestroy 172 | public void close() { 173 | system.out.println("bye"); 174 | } 175 | } 176 | ``` 177 | 위 코드에서 hello 빈이 setting 된 이후에 init 메소드가 실행되며, close 는 컨테이너에서 객체를 제거하기 전에 호출되어 진다. 178 | 179 | 이는 스프링 설정파일에서 180 | ```xml 181 | 182 | ``` 183 | 와 동일한 기능을 처리 한다. 184 | -------------------------------------------------------------------------------- /0325/4.ObjectScan.md: -------------------------------------------------------------------------------- 1 | # Spring Bean 객체 스캔 # 2 | ------------------------------- 3 | 4 | 5 | 스프링 2.5 버전 부터는 빈을 어노테이션 기반을 통해서 자동으로 등록 하는 기능을 제공한다. 6 | 7 | ## 1. 어노테이션을 이용한 자동 스캔 8 | 9 | @Component 어노테이션이 적용되면 적용된 클래스(또는 하위 어노테이션이 적용된 클래스) 를 자동으로 bean 으로 등록하는 기능을 제공한다. 10 | 11 | ```java 12 | @Component 13 | public class GreetController { 14 | 15 | @Autowired 16 | private Hello hello; 17 | 18 | @PostConstruct 19 | public void init() { 20 | hello.set("hi Spring"); 21 | } 22 | 23 | 24 | @PreDestroy 25 | public void close() { 26 | system.out.println("bye"); 27 | } 28 | } 29 | 30 | ``` 31 | ```xml 32 | 33 | ``` 34 | 35 | 위 코드에서 GreetController 는 아래 bean이 생성된다. 36 | 37 | ```xml 38 | 39 | ``` 40 | 41 | 만약 id 를 변경 하고 싶다면 Component("greetCtl") 과 같이 어노테이션을 주면 된다. 42 | 43 | 이 외에 @Repository(dao), @Service(service), @Controller(controller) 의 어노테이션이 있으며 이는 Component 어노테이션의 특수한 형태 이다. 즉, 위 3개의 어노테이션은 Component 의 특정한 형태로 볼수 있다. 44 | 45 | ## 2. 자동 검색된 빈의 이름과 범위 46 | 47 | 일반적으로 위와 같이 생성된 빈은 singleton 형태를 가진다. 만약 다른 범위의 bean 을 생성하고자 한다면 @Scope 어노테이션을 사용하면된다. 48 | 49 | ex) @Scope("prototype") 50 | 51 | Scope value의 종류는 아래와 같다 . 52 | Spring framework에서 지정하는 scope의 종류는 5가지가 있다.(http://www.egovframe.org/wiki/doku.php?id=egovframework:rte:fdl:ioc_container:bean_scope) 53 | 54 | - singleton : 하나의 Bean 정의에 대해서 Spring IoC Container 내에 단 하나의 객체만 존재한다. 55 | 56 | - prototype : 하나의 Bean 정의에 대해서 다수의 객체가 존재할 수 있다. 57 | 58 | - request : 하나의 Bean 정의에 대해서 하나의 HTTP request의 생명주기 안에 단 하나의 객체만 존재한다; 즉, 각각의 HTTP request는 자신만의 객체를 가진다. Web-aware Spring ApplicationContext 안에서만 유효하다. 59 | 60 | - session : 하나의 Bean 정의에 대해서 하나의 HTTP Session의 생명주기 안에 단 하나의 객체만 존재한다. Web-aware Spring ApplicationContext 안에서만 유효하다. 61 | 62 | - global session : 하나의 Bean 정의에 대해서 하나의 global HTTP Session의 생명주기 안에 단 하나의 객체만 존재한다. 일반적으로 portlet context 안에서 유효하다. Web-aware Spring ApplicationContext 안에서만 유효하다. 63 | 64 | 또한 태그와 동일한 프록시 객체를 생성하고 싶다면 proxyMode=ScopedProxyMode.TARGET_CLASS를 설정할수 있다. 65 | ex) @Scope("prototype", proxyMode=ScopedProxyMode.NO) 66 | ScopedProxyMode 에 정의된 값은 아래와 같다. 67 | 68 | - NO : 프록시를 생성하지 않음 69 | - INTERFACES : 인터페이스에 대하여 프록시를 생성(JDK 다이나믹 프록시를 이용) 70 | - TARGET_CLASS : 클래스에 대한 프록시 생성(CGLIB 이용) 71 | - DEFAULT - 기본값. NO와 동일 하나 태그에서 설정 변경 가능하다. 72 | 73 | 74 | ## 3. 스캔 대상 클래스 범위 지정하기 75 | 76 | ``` 그리고 태그를 이용하면 포함시킬 클래스와 포함 시키지 않을 클래스를 구체적으로 명시 할수 있다. 77 | 78 | ```xml 79 | 80 | 81 | 82 | 83 | ``` 84 | 85 | 위와 같이 설정할수 있으며 type 속성에 따라서 expression에 올수 있는 값은 다양하며, type 속성에 올수 있는 값은 아래와 같다. 86 | 87 | - annotation : org.example.SomeAnnotation 88 | 89 | - assignable : org.example.SomeClass 90 | 91 | - regex : org\.example\.Default.* 92 | 93 | - aspectj : org.example..*Service+ -------------------------------------------------------------------------------- /0325/5.javaCodeConfig.md: -------------------------------------------------------------------------------- 1 | # Spring java Code 기반 설정 # 2 | ------------------------------- 3 | 4 | Spring JavaConfig 프로젝트라는 것이 있다. 5 | 이는 프로젝트 XML 이 아닌 java 코드를 이용하여 컨테이너를 설정할 수 있는 기능을 제공하는 프로젝트로. 이를 이용하면 XML 이 아닌 자바코드를 이용하여 생성할 빈 객체 그리고 각 빈과의 연관등을 처리 할수 있는 방법이다. 6 | 7 | ## 1. Configuration 어노테이션과 Bean 어노테이션 ## 8 | 9 | org.springframework.context.annotation 의 Configuration 어노테이션과 Bean 어노테이션 코드를 이용하여 스프링 컨테이너에 새 로운 빈 객체를 제공할 수 있다. 10 | 11 | ```java 12 | @Configuration 13 | public class SpringConfig { 14 | 15 | @Bean 16 | public Greet greet(){ 17 | return new Hello(); 18 | } 19 | } 20 | ``` 21 | 22 | 위 source에서 @Bean 어노테이션은 새로운 빈 객체를 제공할때 사용되며 아래 XML 과 동일한 설정이다. 23 | 24 | ```xml 25 | 26 | ``` 27 | 28 | 만약 bean id를 greet 가 아닌 다른 것으로 변경하고 싶다면 @Bean(name="hello") 와 같이 설정하면 된다. 29 | 30 | 이러한 방식으로 생성된 각 빈간의 의존 관계는 메서드를 호출 함으로써 의존 관계를 설정할수 있다. 31 | 32 | ```java 33 | @Configuration 34 | public class SpringConfig { 35 | 36 | @Bean 37 | public Greet greet(){ 38 | Hello hello= new Hello(); 39 | hello.setGreet(defaultGreet()); 40 | return hello 41 | } 42 | 43 | @Bean 44 | public HiGreet defaultGreet(){ 45 | return new DefaultHiGreet(); 46 | } 47 | } 48 | ``` 49 | 50 | 또한 @Bean 어노테이션의 autowire 속성을 이용하여 연관 자동 설정을 할수 있다. 51 | 52 | ```xml 53 | 54 | ``` 55 | 56 | 위 코드는 아래 코드로 설정할수 있다. 57 | ```java 58 | @Configuration 59 | public class SpringConfig { 60 | 61 | @Bean(autowire = Autowire.BY_NAME) 62 | public GreetController greetController(){ 63 | GreetController greetController = new GreetController(); 64 | ... 65 | return new Hello(); 66 | } 67 | } 68 | ``` 69 | 70 | Autowire 방식은 BY_NAME(이름을 통한 자동 연관), BY_TYPE(type을 통한 자동 연관) 이렇게 2가지가 있으며 NO로 설정할 경우 자동 연관 처리를 하지 않는다. 71 | 72 | ## 2. @Configuration 어노테이션 설정정보 사용 ## 73 | 위에서 적용된 어노테이션을 프로그램에 활용하기 위해서는 2가지 방법이 있다. 74 | 첫째로는 AnnotationConfigApplicationContext를 이용하는 방법이 있다. 75 | 76 | ```java 77 | public static void main(String[] args) { 78 | ApplicationContext context = new AnnotationConfigApplicationContext(GreetConfig.class); 79 | GreetController greetController = context.getBean("homeController", HomeContriller.class); 80 | .... 81 | } 82 | ``` 83 | 84 | 두번째 방법으로 xml 설정을 활용하는 방법이 있다. 85 | ```xml 86 | 87 | 88 | ``` 89 | 또는 90 | 91 | ```xml 92 | 93 | 94 | ``` 95 | 96 | 그리고 ImportResource를 통해서 Configuration 설정 클래스에서 xml 를 활용할수도 있다. 97 | @configuration 어노테이션 바로 하단에 아래 코드를 넣어 주면 된다. 98 | 99 | ```java 100 | @importResource({"classpath://greet-repository.xml", "classpath://greet-datasource.xml"}) 101 | ``` 102 | 103 | ## 3. Configuration 어노테이션 클래스 간의 의존 ## 104 | 프로그램 규모 또는 개발 상황에 따라서 2가지 이상의 Configuration를 사용 하는 경우도 발생할수 있다. 105 | 106 | 예를 들면 아래와 같다. 107 | 108 | ```java 109 | @Configuration 110 | public class GreetConfig { 111 | 112 | @Bean 113 | public Greet greet(){ 114 | Hello hello= new Hello(defaultGreet()); 115 | return hello 116 | } 117 | } 118 | ``` 119 | ```java 120 | @Configuration 121 | public class DefaultGreetConfig { 122 | 123 | @Bean 124 | public HiGreet defaultGreet(){ 125 | return new DefaultHiGreet(); 126 | } 127 | } 128 | ``` 129 | 130 | 위 source는 GreetConfig 의 greet 에서 defaultGreet 라는 bean을 사용 하는 경우이다. 131 | 이러한 경우 아래와 같이 Autowired 방법을 사용하거나 Import 어노테이션을 통해서 처리 될수 있다. 132 | 133 | --- Autowired --- 134 | ```java 135 | @Configuration 136 | public class GreetConfig { 137 | @Autowired 138 | private DefaultGreetConfig defaultGreetConfig; 139 | 140 | @Bean 141 | public Greet greet(){ 142 | Hello hello= new Hello(defaultGreetConfig.defaultGreet()); 143 | return hello 144 | } 145 | } 146 | ``` 147 | 148 | --- import --- 149 | ```java 150 | @Configuration 151 | @Import({DefaultGreetConfig .class}) 152 | public class GreetConfig { 153 | @Bean 154 | public Greet greet(){ 155 | Hello hello= new Hello(defaultGreet()); 156 | return hello; 157 | } 158 | } 159 | ``` 160 | 161 | ## 4. initMethod , destoryMethod ## 162 | 앞서 Spring xml 설정에서 init 과 destory 설정을 통해서 초기화/종료 메서드를 지정할수 있음을 알게 되었다. 163 | java Code기반 설정에서는 InitMethod , destoryMethod를 통해서 지정 / 처리 가능하다. 164 | ```java 165 | @Bean(initMethod="init") 166 | ``` 167 | 위 구분을 넣으며 init method르 초기화시 활용하게 된다. 168 | 169 | ## 5. scope, Qualifier 어노테이션 ## 170 | Bean 어노테이션 하단에 Scpoe 어노테이션을 넣음으로 해서 scpoe 처리 및 proxyMode 처리가 가능하다. 171 | 또한 Qualifier 어노테이션을 통해서 두개이상의 빈객체에서 한정자를 활용하여 어느 특정빈을 선택하는 방법도 가능하다. 172 | 173 | 이는 앞선 포스트에 설명하였으므로 생략 하도록 한다. -------------------------------------------------------------------------------- /0325/6.AOP.md: -------------------------------------------------------------------------------- 1 | # AOP 란 # 2 | ------------------------------ 3 | 4 | 이번 포스팅에서는 AOP 에 대하여 간단히 소개 하며 차후 Spring 에서 사용 하는 AOP 에 대하여 포스트 하도록 하겠다. 5 | 6 | AOP 란, Aspect Oriented Programming 즉, 관점 지향 프로그래밍 기법이다. 7 | 하지만 관점지향 프로그래밍이라고만 하면 다소 난해하거나 이해하기 어려울 수 있다. 쉽게 풀어서 쓰자면 여러 Process 를 진행함에 있어서 공통적으로 처리하여야 하는 로직이 존재 하는데(업무 선처리로직 또는 업무 후처리로직) 이를 각각의 Process 로직내에 주입하는 방식의 프로그래밍 기법이다. 8 | 9 | ![](http://cfile3.uf.tistory.com/image/2424F044514EBECF014A02) 10 | 11 | 위와 같은 프로그램에서 보면 process 내에서 로직이 실행되기전에 logging 하고 로직 처리 이후 logging 처리된다. 이러한 로그 처리는 프로그램 전체에서 보면 공통으로 처리되어야 하는 부분으로 볼수 있다. 12 | 따라서 AOP 방식으로 code 가 이루어 지면 아래와 같이 변경될수 있다. 13 | 14 | ![](http://cfile23.uf.tistory.com/image/1252DD46514EBF362AA730) 15 | 16 | 이러한 방식의 프로그래밍 기법을 AOP 라고 한다. 17 | 18 | ## AOP 용어 ## 19 | AOP 와 관련하여 다양한 용어가 있는데, AOP 프로그래밍을 하면서 기본적으로 알고 있어야할 용어 들이다. 20 | 21 | - Advice : 언제 공통 관심 기능을 핵심 로직에 적용할 지를 정의 하고 있다. 22 | - JoinPoit : Advice를 적용 가능한 지점을 의미 한다. 23 | - PointCut : Joinpoint의 부분집합으로 실제 Advice가 적용되는 joointpoint 를 나타낸다. 24 | - Weaving : Advice를 핵심 로직 코드에 적용하는 것을 말한다. 25 | - Ascpet : 여러 객체에 공통적으로 적용되는 부분을 Aspect라고 한다. 26 | 27 | Weaving의 방식에는 크게 세가지 방식이 있다. 28 | 29 | 1 Compile 시에 Weaving 하기(AspectJ) 30 | 31 | 2 Class Loading 시에 Weaving 하기(AOP Libaray) 32 | 33 | 3 Runtime 시에 Weaving 하기 (Proxy) 34 | 35 | 36 | 37 | http://www.theregister.co.uk/2007/10/31/aspect_oriented_programming/ 참고 38 | -------------------------------------------------------------------------------- /0325/7.SpringAOP.md: -------------------------------------------------------------------------------- 1 | # Spring AOP # 2 | ------------------------ 3 | 4 | 이번 포스팅에서는 Spring 에서 지원 하는 AOP 에 대해서 정리 하고자 한다. 5 | 6 | Spring 자체적으로는 프록시 기반의 AOP 를 지원한다. 이는 직접적으로 Class 에 접근 하는 것이 아니므로 필드값 변경과 같은 Joinpoint 는 제공 하지 않으며 메서드 호출을 위한 JoinPoint만 제공 된다. 7 | 좀더 다양한 Joinpoint 를 활용하고자 한다면 AspectJ와 같은 AOP 를 활용 하여야 한다. 8 | 9 | 스프링에서 AOP 구현 방법은 3가지가 있다. 10 | - XML 기반의 POJO Class 구현 11 | - Aspect 5/6 에서 정의한 @Aspect 어노테이션 기반의 AOP 구현 12 | - 스프링 API를 이용한 AOP 구현 13 | 14 | 위 3가지 방법다 내부적으로는 프록시를 활용하여 구현되므로 method 호출에 대해서만 AOP가 적용된다. 15 | 아래는 Proxy의 구성도 이다. 16 | 17 | ![](http://cfile25.uf.tistory.com/image/015EAF39514F0FB22CE65D) 18 | 19 | (http://thecafetechno.com/tutorials/spring/spring-proxying-mechanisms/) 20 | 21 | 위 그림과 같이 Interface 또는 Abstract Class 를 통하여 Proxy class 를 구현 할수 있다. 22 | 만약 대상객체(업무 Process)가 interface 를 구현하고 있지 않다면 Spring 은 CGLIB를 이용하여 클래스에 대한 프록시 객체를 생성하게 되며 CGLIB 는 대상 class 를 상속받아서 프록시를 구현하도록 되어 있다. 23 | 24 | ![](http://cfile25.uf.tistory.com/image/01340A3B514F108A18AD04) 25 | 26 | Sqeuenct diagram 으로 보면 아래와 같다. 27 | 28 | ![](http://cfile26.uf.tistory.com/image/24270337514F0FF514E3C6) 29 | 30 | 스프링은 Proxy 를 이용하여 메서드를 호출할때에 Aspect 를 적용하기 때문에 구현 가능한 Advice 의 종류는 아래와 같다. 31 | 32 | - Before Advice : method 호출전 33 | - After Returning Advice : method 호출후 예외가 없을 경우 34 | - After Throwing Advice : method 호출후 예외 발생시 35 | - After Advice : 예외와 무관하게 method 호출후 36 | - Around Advice : method 호출 전후 또는 예외 발생 시점에 공통으로 실행 37 | 38 | 범용적 사용을 위해서 일반적으로 Around Advice 가 많이 이용되고 있다. 39 | 40 | 다름 포스팅에서는 실제 AOP 구현 방법에 대하여 살펴 보도록 하겠다. 41 | 42 | 다만 Spring API를 통한 AOP 구현 방법은 특정한 경우가 아니면 사용할 경우가 거의 없으므로 XML 기반의 POJO Class 구현 @Aspect 어노테이션을 이용한 구현 두가지를 살펴 보겠다. -------------------------------------------------------------------------------- /0325/8.xmlAOP.md: -------------------------------------------------------------------------------- 1 | # Spring XML 기반 POJO 클래스를 이용한 AOP 구현 # 2 | ------------------------ 3 | 4 | XML 를 이용하여 AOP 를 구현하는 과정은 다음과 같다. 5 | 6 | 1. 공통 기능을 처리하는 Advice Class 생성 7 | 2. xml 에 Aspect 설정 8 | 9 | 우선 advice Class sample 을 생성하도록 하겠다. 10 | 11 | ```java 12 | public class ProfilingAdvice { 13 | 14 | public Object trace(ProceedingJoinPoint joinPoint) throws Throwable { 15 | String signatureString = joinPoint.getSignature().toShortString(); 16 | System.out.println(signatureString + " 시작"); 17 | long start = System.currentTimeMillis(); 18 | try { 19 | Object result = joinPoint.proceed(); 20 | return result; 21 | } finally { 22 | long finish = System.currentTimeMillis(); 23 | System.out.println(signatureString + " 종료"); 24 | System.out.println(signatureString + " 실행 시간 : " + (finish - start) 25 | + "ms"); 26 | } 27 | } 28 | } 29 | ``` 30 | 31 | 위 source에서 보면 advice class 가 정확히 언제 실행된는지 명확하지 않다. 단지 Joinpoint에서 실행될 trace() method 만 구현되어 있는데 입력 파라미터인 joinPoint 를 이용하여 Around Advice 를 구현할수 있다. 32 | 33 | 34 | 이 advice 를 활용하기 위해서는 xml 설정이 필요 하며 그 설정은 아래와 같다. 35 | ```xml 36 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 52 | 53 | 54 | 55 | 56 | ```xml 57 | 58 | 59 | ```xml ``` AOP 설정 정보임을 나타낸다. 60 | ```xml ``` Aspect 를 설정한다. 61 | ```xml ``` PointCut을 설정한다. 62 | ```xml ``` Around Advice를 설정한다. 이밖에 aop:after, aop:after-returning, aop:after-throwing, aop:before 등의 설정이 가능하다. 63 | 64 | 위 xml 을 간략히 설명하면 com.interwater.service 내의 모든 public method를 Pointcut 으로 설정(aop:pointcut)하며 Around Advice로 ProfilingAdvice Class 의 trace 라는 method에 적용된다.(aop:around) 65 | 66 | 위 설명 파일에서 excution 은 advice 를 적용할 패키지, 클래스 그리고 메서드 등을 표현할때에 사용되며, 차후 따로 포스팅을 하도록 하겠다. 67 | 68 | 실제 위 코드를 통하여 com.interwater.service 내의 public method를 실행하게 되면 pointcut으로 설정된 PofilingAdvice 의 trace가 Object result = joinPoint.proceed(); 이전 source들이 먼저 실행되며, public method가 종료된 이후 finally 이후 source가 실행된다. 69 | 70 | 위에서는 Around advice 설정에 대한 sample code 이다. 71 | 그럼 각각의 advice 에 대한 method 구현 방식을 알아 보자. 72 | 하기 설명에서 method 명은 xml advice 설정에서 정의된 method 명이다.(즉 method 명은 가변적이다.) 73 | 74 | * Before : 75 | 76 | ```java 77 | public void before(){} 또는 public void before(JoinPoint joinPoint){} 78 | ``` 79 | 입력 파라미터를 받는 경우는 joinPoint 의 getArgs() method를 통해서 활용 할수 있다. 80 | 다만 유의 사항으로 만약 pointcut에서 예외가 발생할 경우 그 이후 메인 업무 로직을 실행하지 않는다. 81 | 82 | * After Returning 83 | ```java 84 | public void afterReturning(){}, public void afterReturning(Object ret){} 85 | public void afterReturning(JoinPoint joinPoint, Object ret){} 86 | ``` 87 | 88 | 입력 파라미터를 받고자 하는 경우(target logic 의 return 값) xml 설정에서 returning="파라미터명" 을 넣어 주면 된다. 위 method 에서는 ret를 받으므로 returning="ret" 라고 추가 하여 주면 된다. JoinPoint 값은 target process method가 호출 될때 입력된 parameter를 받기 위한 값이다. 89 | 90 | * After Throwing 91 | ```java 92 | public void afterThrowing(){}, public void afterThrowing(Throwable ex){} 93 | ``` 94 | 95 | AfterReturning 방식과 동일하다 다만 예외 발생기 return 값이 없으므로 이 경우 throwing="exception 파라미터" 를 통해서 exception class 를 파라미터로 받을수 있다. 위 method에서는 throwing="ex" 라는 태크를 추가 하면 된다. 만약 특정 예외가 발생한 경우에 한해서 실행되게 하고 싶다면 96 | public void afterThrowing(FileNotFoundException ex){} 와 같은 형식으로 method를 작성 하면 된다. 97 | 98 | * After 99 | ```java 100 | public void before(){} 또는 public void before(JoinPoint joinPoint){} 101 | ``` 102 | 103 | after advice 의 경우 target process method 에서 전달 되는 parameter를 갖지 않으며 다만 taget 메서드호출에 대한 정보 및 전달 파라미터를 사용하고자 하는 경우 joinPoint 파라미터를 활용할수 있다. 104 | 105 | * around 106 | 위 sample code에서 있으므로 생략 하겠다. 다만 around method의 경우 입력파라미터로 ProceedingJoinPoint를 반드시 파라미터로 받도록 하여야 한다. -------------------------------------------------------------------------------- /0325/9.AnnotationAOP.md: -------------------------------------------------------------------------------- 1 | # Spring @Aspect 어노테이션을 활용한 AOP 구현 # 2 | ------------------------ 3 | 4 | @Aspect 어노테이션을 활용할 AOP 는 앞서 설명한 AOP 와 거의 유사하다. 5 | 다만 어노테이션이 들어간다는 점과 xml 설정에서 ``` ``` 만 들어 간다는 점이 다르다. 6 | 7 | 우선 xml 파일에 ``` ``` 그리고 advice 클래스를 bean 으로 등록 하자. 8 | 9 | 그럼, Spring XML 기반 POJO 클래스를 이요한 AOP 구현 에서 작성했던 ProfilingAdvice 는 아래와 같이 바뀐다. 10 | 11 | ```java 12 | @Aspect 13 | public class ProfilingAdvice { 14 | 15 | @PointCut("execution(public * com.interwater.service..*(..))" ) 16 | private void profileTarget(){} 17 | 18 | @Around("profileTarget()") 19 | public Object trace(ProceedingJoinPoint joinPoint) throws Throwable { 20 | String signatureString = joinPoint.getSignature().toShortString(); 21 | System.out.println(signatureString + " 시작"); 22 | long start = System.currentTimeMillis(); 23 | try { 24 | Object result = joinPoint.proceed(); 25 | return result; 26 | } finally { 27 | long finish = System.currentTimeMillis(); 28 | System.out.println(signatureString + " 종료"); 29 | System.out.println(signatureString + " 실행 시간 : " + (finish - start) 30 | + "ms"); 31 | } 32 | } 33 | } 34 | ``` 35 | 36 | 37 | 위 코드에서 PointCut 어노테이션과 profileTarget 대신에 Around 어노테이션을 ``` @Around("execution(public *.com.interwater.service..*(..))") ```로 변경해도 동일한 결과를 가진다. 38 | 39 | ```java 40 | @Aspect 41 | public class ProfilingAdvice { 42 | @Around("execution(public * com.interwater.service..*(..))") 43 | public Object trace(ProceedingJoinPoint joinPoint) throws Throwable { 44 | ................... 45 | } 46 | } 47 | ``` 48 | 49 | 그 이외에 xml 기반에서 언급 되었던 returning 이나 throwing 은 advice 종류 뒤에 추가로 넣어 주면 된다. 50 | 51 | ex) ``` @AfterThrowing("execution(public * com.interwater.service..*(..))", throwing="ex") ``` 52 | 53 | 첫번째 예시에서 사용한 @PointCut 어노테이션은 다수의 advice 를 설정할때에 주로 사용 된다. 54 | 만약 다수의 advice 가 존재 하여 실행순서를 정의 하고자 할때에는 xml 방식에서는 order="2" 와 같이 어노테이션 방식으로는 @Aspect 하단에 @Order(2)와 같이 추가 하여 Aop 실행 순서를 정의 할수 있다. -------------------------------------------------------------------------------- /0401/01.스프링MVC의 주요 구성 요소 및 처리 흐름.md: -------------------------------------------------------------------------------- 1 | # 01. 스프링 MVC의 주요 구성 요소 및 처리 흐름 # 2 | 3 | 4 | ![](http://cfile10.uf.tistory.com/image/18768635509CA9292C9D98) 5 | 6 | 1. 클라이언트의 요청이 DispatcherServlet에 전달 7 | 2. DispatcherServlet은 HandlerMapping을 사용하여 클라이언트의 요청을 처리할 컨트롤러 객체를 구함 8 | 3. DispatcherServlet은 컨트롤러 객체의 handleRequest() 메서드를 호출하여 클라이언트의 요청을 처리 9 | 4. 컨트롤러의 handlerRequest() 메서드는 처리 결과 정보를 담은 ModelAndView 객체를 리턴 10 | 5. DispatcherServlet은 ViewResolver로부터 응답 결과를 생성할 뷰 객체를 구함 11 | 6. 뷰는 클라이언트에 전송할 응답을 생성 12 | 13 | ---------- 14 | 15 | **1. 단계 : DispatcherServlet 설정 및 스프링 컨텍스트 설정**
16 | - 자바 웹 어플리케이션의 설정 파일인 web.xml에 다음의 정보를 추가
17 | - a : 클라이언트의 요청을 전달받을 DispatcherServlet 설정
18 | - b : 공통으로 사용할 어플리케이션 컨텍스트 설정
19 | 20 | DispatcherServlet의 설정은 웹 어플리케이션의 /WEB-INF/web.xml파일에 추가하면 됨. 21 | 22 | 23 | 24 | dispatcher 25 | org.springframework.web.servlet.DispatcherServlet 26 | 27 | 28 | 29 | dispatcher 30 | *.do 31 | 32 | 33 | 34 | 35 | 36 | DispatcherServlet은 WEB-INF/ 디렉토리에 위치한 [서블릿명]-servlet.xml 파일을 스프링 설정 파일로 사용한다. 37 | 38 | **2 단계 : Controller 구현 및 설정 추가** 39 | 40 | HandlerMapping은 클라이언트의 요청을 어떤 Controller가 처리할지에 대한 정보를 제공
41 | http://host:port[/컨텍스트경로]/hello.do 42 | 43 | 44 | @Controller 45 | public class HelloController{ 46 | 47 | @RequestMapping("/hello.do") 48 | public ModelAndView hello(){ 49 | return ...; 50 | } 51 | 52 | } 53 | 54 | [서블릿명]-servlet.xml 설정
55 | dispatcher-servlet.xml 56 | 57 | 58 | 59 | **3단계 : 설정 파일에 ViewResolver 설정 추가** 60 | 61 | @Controller 62 | public class HelloController{ 63 | 64 | @RequestMapping("/hello") 65 | > public ModelAndView hello(){ 66 | > ModelAndView mav = new ModelAndView(); 67 | > mav.setViewName("hello"); 68 | > mav.addObject("greeting",getGreeting()); 69 | > return mav; 70 | > } 71 | } 72 | 73 | private String getGreeting(){ 74 | ... 75 | return "안녕하세요"; 76 | } 77 | 78 | **4단계 : 뷰 코드 구현**
79 | hello.jsp 80 | 81 | 82 | 83 | 인사말 : ${greeting} 84 | 85 | 86 | 87 | **5단계 실행**
88 | [http://localhost:8080/chap06/hello.do](http://localhost:8080/chap06/hello.do) 89 | -------------------------------------------------------------------------------- /0401/01.스프링MVC의 주요 구성 요소 및 처리 흐름.md: -------------------------------------------------------------------------------- 1 | # 01. 스프링 MVC의 주요 구성 요소 및 처리 흐름 # 2 | 3 | 4 | ![](http://cfile10.uf.tistory.com/image/18768635509CA9292C9D98) 5 | 6 | 1. 클라이언트의 요청이 DispatcherServlet에 전달 7 | 2. DispatcherServlet은 HandlerMapping을 사용하여 클라이언트의 요청을 처리할 컨트롤러 객체를 구함 8 | 3. DispatcherServlet은 컨트롤러 객체의 handleRequest() 메서드를 호출하여 클라이언트의 요청을 처리 9 | 4. 컨트롤러의 handlerRequest() 메서드는 처리 결과 정보를 담은 ModelAndView 객체를 리턴 10 | 5. DispatcherServlet은 ViewResolver로부터 응답 결과를 생성할 뷰 객체를 구함 11 | 6. 뷰는 클라이언트에 전송할 응답을 생성 12 | 13 | ---------- 14 | 15 | **1. 단계 : DispatcherServlet 설정 및 스프링 컨텍스트 설정**
16 | - 자바 웹 어플리케이션의 설정 파일인 web.xml에 다음의 정보를 추가
17 | - a : 클라이언트의 요청을 전달받을 DispatcherServlet 설정
18 | - b : 공통으로 사용할 어플리케이션 컨텍스트 설정
19 | 20 | DispatcherServlet의 설정은 웹 어플리케이션의 /WEB-INF/web.xml파일에 추가하면 됨. 21 | 22 | 23 | 24 | dispatcher 25 | org.springframework.web.servlet.DispatcherServlet 26 | 27 | 28 | 29 | dispatcher 30 | *.do 31 | 32 | 33 | 34 | 35 | 36 | DispatcherServlet은 WEB-INF/ 디렉토리에 위치한 [서블릿명]-servlet.xml 파일을 스프링 설정 파일로 사용한다. 37 | 38 | **2 단계 : Controller 구현 및 설정 추가** 39 | 40 | HandlerMapping은 클라이언트의 요청을 어떤 Controller가 처리할지에 대한 정보를 제공
41 | http://host:port[/컨텍스트경로]/hello.do 42 | 43 | 44 | @Controller 45 | public class HelloController{ 46 | 47 | @RequestMapping("/hello.do") 48 | public ModelAndView hello(){ 49 | return ...; 50 | } 51 | 52 | } 53 | 54 | [서블릿명]-servlet.xml 설정
55 | dispatcher-servlet.xml 56 | 57 | 58 | 59 | **3단계 : 설정 파일에 ViewResolver 설정 추가** 60 | 61 | @Controller 62 | public class HelloController{ 63 | 64 | @RequestMapping("/hello") 65 | > public ModelAndView hello(){ 66 | > ModelAndView mav = new ModelAndView(); 67 | > mav.setViewName("hello"); 68 | > mav.addObject("greeting",getGreeting()); 69 | > return mav; 70 | > } 71 | } 72 | 73 | private String getGreeting(){ 74 | ... 75 | return "안녕하세요"; 76 | } 77 | 78 | **4단계 : 뷰 코드 구현**
79 | hello.jsp 80 | 81 | 82 | 83 | 인사말 : ${greeting} 84 | 85 | 86 | 87 | **5단계 실행**
88 | [http://localhost:8080/chap06/hello.do](http://localhost:8080/chap06/hello.do) 89 | -------------------------------------------------------------------------------- /0401/02.DispatcherServlet 과 ApplicationContext.md: -------------------------------------------------------------------------------- 1 | # 03. DispatcherServlet 설정 과 ApplicationContext의 관계 # 2 | DispatcherServlet은 기본적으로 웹 어플리케이션의 /WEB-INF/ 디렉토리에 위치한 [서블릿이름]-servlet.xml 파일로 부터 스프링 설정 정보를 읽어옴 3 | 4 | 5 | dispatcher 6 | org.springframework.web.servlet.DispatcherServlet 7 | 8 | 9 | 위와 같이 web.xml을 설정했다면, dispatcher-servlet.xml 파일로부터 설정 정보를 읽어옴. 10 | 11 | 하지만, 한 개 이상의 설정 파일을 사용해야 할 때나, 기본 설정 파일 이름이 아닌 사용자 설정 이름을 사용하고 싶을 때는 다음과 같이 설정한다. 12 | 13 | 14 | dispatcher 15 | org.springframework.web.servlet.DispatcherServlet 16 | 17 | contextConfigLocation 18 | 19 | /WEB-INF/main.xml 20 | /WEB-INF/bbs.xml 21 | 22 | 23 | 24 | 25 | DispatcherServlet을 설정할 때 contextConfigLocation 초기화 파라미터에 설정 파일을 목록을 지정하면 됨. 26 | 27 | DispatcherServlet은 그 자체가 서블릿이기 때문에 1개 이상의 DispatcherServlet을 설정하는 것이 가능 28 | 29 | 30 | front 31 | 32 | org.springframework.web.servlet.DispatcherServlet 33 | 34 | 35 | contextConfigLocation 36 | /WEB-INF/front.xml 37 | 38 | 39 | 40 | rest 41 | 42 | org.springframework.web.servlet.DispatcherServlet 43 | 44 | 45 | contextConfigLocation 46 | /WEB-INF/rest.xml 47 | 48 | 49 | 50 | 위의 경우 DispatcherServlet은 각각 별도의 WebApplicationContext를 생성하게 된다. 51 | front DispatcherServlet은 front.xml 설정 파일을 사용하고 rest DispatcherServlet은 rest.xml 설정 파일을 사용하도록 설정하고 있는데 이때 주의 할 점은 front.xml에서는 rest.xml에 설정한 빈 객체를 사용할 수 없다.(그 반대의 경우도 마찬가지임) 52 | 53 | 만약 위의 같은 경우에 서로 다른 DispatcherSevlet이 공통 빈을 필요로 하는 경우 ContextLoaderListener를 사용하여 공통으로 사용될 빈을 설정할 수 있음. 54 | 55 | 56 | 57 | contextConfigLocation 58 | /WEB-INF/service.xml,/WEB-INF/persistence.xml 59 | 60 | 61 | 62 | 63 | org.springframework.web.context.ContextLoaderListener 64 | 65 | 66 | 67 | 68 | front 69 | 70 | org.springframework.web.servlet.DispatcherServlet 71 | 72 | 73 | 74 | rest 75 | 76 | org.springframework.web.servlet.DispatcherServlet 77 | 78 | 79 | 80 | ContextLoaderListener를 ServletListener로 등록하고 contextConfigLocation 컨텍스트 파라미터를 이용하여 공통으로 사용될 빈 정보를 담고 있는 설정 파일 목록을 지정하면 됨. 81 | 위의 경우라면... service.xml 과 persistence.xml 에 설정한 빈을 그 자식인 front, rest 모두에서 사용할 수 있다. 82 | (ContextLoaderListener는 contextConfigLocation 컨텍스트 파라미터를 명시하지 않으면 /WEB-INF/applicationContext.xml을 설정 파일로 사용함) 83 | 84 | 85 | 설정파일을 class path에 위치한 파일로부터 설정 정보를 읽고 싶다면 다음과 같이 작성한다. 86 | 87 | 88 | contextConfigLocation 89 | 90 | classpath:config/service.xml 91 | classpath:common.xml 92 | /WEB-INF/config/message_conf.xml 93 | 94 | 95 | 96 | 97 | org.springframework.web.context.ContextLoaderListener 98 | 99 | 100 | 101 | *캐릭터 인코딩은 page 216 page 참조* 102 | -------------------------------------------------------------------------------- /0401/02.DispatcherServlet 과 ApplicationContext.md: -------------------------------------------------------------------------------- 1 | # 03. DispatcherServlet 설정 과 ApplicationContext의 관계 # 2 | DispatcherServlet은 기본적으로 웹 어플리케이션의 /WEB-INF/ 디렉토리에 위치한 [서블릿이름]-servlet.xml 파일로 부터 스프링 설정 정보를 읽어옴 3 | 4 | 5 | dispatcher 6 | org.springframework.web.servlet.DispatcherServlet 7 | 8 | 9 | 위와 같이 web.xml을 설정했다면, dispatcher-servlet.xml 파일로부터 설정 정보를 읽어옴. 10 | 11 | 하지만, 한 개 이상의 설정 파일을 사용해야 할 때나, 기본 설정 파일 이름이 아닌 사용자 설정 이름을 사용하고 싶을 때는 다음과 같이 설정한다. 12 | 13 | 14 | dispatcher 15 | org.springframework.web.servlet.DispatcherServlet 16 | 17 | contextConfigLocation 18 | 19 | /WEB-INF/main.xml 20 | /WEB-INF/bbs.xml 21 | 22 | 23 | 24 | 25 | DispatcherServlet을 설정할 때 contextConfigLocation 초기화 파라미터에 설정 파일을 목록을 지정하면 됨. 26 | 27 | DispatcherServlet은 그 자체가 서블릿이기 때문에 1개 이상의 DispatcherServlet을 설정하는 것이 가능 28 | 29 | 30 | front 31 | 32 | org.springframework.web.servlet.DispatcherServlet 33 | 34 | 35 | contextConfigLocation 36 | /WEB-INF/front.xml 37 | 38 | 39 | 40 | rest 41 | 42 | org.springframework.web.servlet.DispatcherServlet 43 | 44 | 45 | contextConfigLocation 46 | /WEB-INF/rest.xml 47 | 48 | 49 | 50 | 위의 경우 DispatcherServlet은 각각 별도의 WebApplicationContext를 생성하게 된다. 51 | front DispatcherServlet은 front.xml 설정 파일을 사용하고 rest DispatcherServlet은 rest.xml 설정 파일을 사용하도록 설정하고 있는데 이때 주의 할 점은 front.xml에서는 rest.xml에 설정한 빈 객체를 사용할 수 없다.(그 반대의 경우도 마찬가지임) 52 | 53 | 만약 위의 같은 경우에 서로 다른 DispatcherSevlet이 공통 빈을 필요로 하는 경우 ContextLoaderListener를 사용하여 공통으로 사용될 빈을 설정할 수 있음. 54 | 55 | 56 | 57 | contextConfigLocation 58 | /WEB-INF/service.xml,/WEB-INF/persistence.xml 59 | 60 | 61 | 62 | 63 | org.springframework.web.context.ContextLoaderListener 64 | 65 | 66 | 67 | 68 | front 69 | 70 | org.springframework.web.servlet.DispatcherServlet 71 | 72 | 73 | 74 | rest 75 | 76 | org.springframework.web.servlet.DispatcherServlet 77 | 78 | 79 | 80 | ContextLoaderListener를 ServletListener로 등록하고 contextConfigLocation 컨텍스트 파라미터를 이용하여 공통으로 사용될 빈 정보를 담고 있는 설정 파일 목록을 지정하면 됨. 81 | 위의 경우라면... service.xml 과 persistence.xml 에 설정한 빈을 그 자식인 front, rest 모두에서 사용할 수 있다. 82 | (ContextLoaderListener는 contextConfigLocation 컨텍스트 파라미터를 명시하지 않으면 /WEB-INF/applicationContext.xml을 설정 파일로 사용함) 83 | 84 | 85 | 설정파일을 class path에 위치한 파일로부터 설정 정보를 읽고 싶다면 다음과 같이 작성한다. 86 | 87 | 88 | contextConfigLocation 89 | 90 | classpath:config/service.xml 91 | classpath:common.xml 92 | /WEB-INF/config/message_conf.xml 93 | 94 | 95 | 96 | 97 | org.springframework.web.context.ContextLoaderListener 98 | 99 | 100 | 101 | *캐릭터 인코딩은 page 216 page 참조* 102 | -------------------------------------------------------------------------------- /0401/03.컨트롤러 구현.md: -------------------------------------------------------------------------------- 1 | # 05. 컨트롤러 구현 # 2 | 3 | 컨트롤러 클래스를 구현하려면 @Controller 어노테이션과 @RequestMapping 어노테이션을 이용한다. 4 | 5 | 6 | @Controller 7 | public class HelloController{ 8 | @RequestMapping("/hello.do") 9 | public String hello(){ 10 | return "hello"; 11 | } 12 | } 13 | 14 | 컨트롤러 클래스를 구현하면 DispatcherServlet이 사용하는 스프링 설정 파일에 해당 컨트롤러 클래스를 등록해 준다. 15 | 16 | 17 | 스프링 설정 파일에 등록이 완료되면 @RequestMapping 어노테이션의 값으로 적절한 URL과 매칭되는 클라이언트 요청을 해당 매서드에서 처리하게 된다. 18 | 19 | **02. 컨트롤러 메서드의 http 전송방식(method) 한정** 20 | 21 | @Controller 22 | public class NewArticleController{ 23 | @RequestMapping(value="/article/newArticle.do", method=RequestMethod.GET) 24 | public String form(){} 25 | 26 | @RequestMapping(value="/article/newArticle.do", method=RequestMethod.POST){ 27 | public String submit(){} 28 | } 29 | 30 | **03. HTML 폼과 커맨드 객체** 31 | 32 | 33 |
34 | 35 | 제목 : 36 | 내용 : 37 | 38 |
39 | 40 | 41 | public class NewArticleCommand { 42 | private String title; 43 | private String content; 44 | private String parentId; 45 | 46 | public void setTitle(String title){ 47 | this.title = title; 48 | } 49 | ..... 50 | } 51 | 52 | 53 | @Controller 54 | @RequestMapping("/article/newArticle.do") 55 | public class newArticleController{ 56 | @RequestMapping(method = RequestMethod.POST) 57 | public String submit(NewArticleCommand command){ 58 | //command.getTitle() : title 파라미터의 값 저장 59 | //command.getContent() : content 파라미터의 값 저장 60 | //command.getParentId() : parentId 파라미터의 값 저장 61 | 62 | return "article/newArticleSubmitted" 63 | } 64 | /* 65 | public Sting submit(@ModelAttribute("command") NewArticleCommand command){ 66 | .... 67 | } 68 | */ 69 | 70 | } 71 | 72 | 73 | 74 | 제목 : ${NewArticleCommand.title} 75 | ... 76 | 79 | 80 | 81 | *커맨드 객체로 List 받기 page 223 참조* 82 | 83 | **04. 컨트롤러 메서드의 파라미터 타입** 84 | 85 | 86 | -.@RequestParam 87 | http://localhost:8080/chap06/search/internal.do?query=q&p=2 88 | 89 | @Controller 90 | public class SearchController { 91 | 92 | @RequestMapping("/search/internal.do") 93 | public ModelAndView searchInternal(@RequestParam("query") String query, 94 | @RequestParam(value = "p", defaultValue = "1") int pageNumber) { 95 | System.out.println("query=" + query + ",pageNumber=" + pageNumber); 96 | return new ModelAndView("search/internal"); 97 | } 98 | } 99 | -.@CookieValue 100 | 101 | @RequestMapping("/cookie/view.do") 102 | public String view( 103 | @CookieValue(value = "auth", defaultValue = "0") String auth) { 104 | System.out.println("auth 쿠키: " + auth); 105 | return "cookie/view"; 106 | } 107 | -.@Requestheader 108 | 109 | -.서블릿API 직접사용 110 | 111 | **05. 컨트롤러 메서드의 리턴타입** 112 | 113 | **06. 뷰지정** 114 | 115 | - 명시적 지정 ModelAndView,String 116 | - 자동 지정 Modle,Map =@RequestMapping 으로 뷰 결정 117 | - 리다이렉트 뷰 rediret 118 | 119 | **07. 모델 생성하기** 120 | 121 | - @RequestMapping 어노테이션이 적용된 메서드의 파마미터나 리턴타입으로 ModelAndView,Model,ModelMap,Map,커맨드 객체 122 | 등을 이용해서 모델을 뷰에 전달하는 방법 123 | 124 | 125 | **08. 요청URL 매칭** 126 | 127 | 128 | 129 | -------------------------------------------------------------------------------- /0401/03.컨트롤러 구현.md: -------------------------------------------------------------------------------- 1 | # 05. 컨트롤러 구현 # 2 | 3 | 컨트롤러 클래스를 구현하려면 @Controller 어노테이션과 @RequestMapping 어노테이션을 이용한다. 4 | 5 | 6 | @Controller 7 | public class HelloController{ 8 | @RequestMapping("/hello.do") 9 | public String hello(){ 10 | return "hello"; 11 | } 12 | } 13 | 14 | 컨트롤러 클래스를 구현하면 DispatcherServlet이 사용하는 스프링 설정 파일에 해당 컨트롤러 클래스를 등록해 준다. 15 | 16 | 17 | 스프링 설정 파일에 등록이 완료되면 @RequestMapping 어노테이션의 값으로 적절한 URL과 매칭되는 클라이언트 요청을 해당 매서드에서 처리하게 된다. 18 | 19 | **02. 컨트롤러 메서드의 http 전송방식(method) 한정** 20 | 21 | @Controller 22 | public class NewArticleController{ 23 | @RequestMapping(value="/article/newArticle.do", method=RequestMethod.GET) 24 | public String form(){} 25 | 26 | @RequestMapping(value="/article/newArticle.do", method=RequestMethod.POST){ 27 | public String submit(){} 28 | } 29 | 30 | **03. HTML 폼과 커맨드 객체** 31 | 32 | 33 |
34 | 35 | 제목 : 36 | 내용 : 37 | 38 |
39 | 40 | 41 | public class NewArticleCommand { 42 | private String title; 43 | private String content; 44 | private String parentId; 45 | 46 | public void setTitle(String title){ 47 | this.title = title; 48 | } 49 | ..... 50 | } 51 | 52 | 53 | @Controller 54 | @RequestMapping("/article/newArticle.do") 55 | public class newArticleController{ 56 | @RequestMapping(method = RequestMethod.POST) 57 | public String submit(NewArticleCommand command){ 58 | //command.getTitle() : title 파라미터의 값 저장 59 | //command.getContent() : content 파라미터의 값 저장 60 | //command.getParentId() : parentId 파라미터의 값 저장 61 | 62 | return "article/newArticleSubmitted" 63 | } 64 | /* 65 | public Sting submit(@ModelAttribute("command") NewArticleCommand command){ 66 | .... 67 | } 68 | */ 69 | 70 | } 71 | 72 | 73 | 74 | 제목 : ${NewArticleCommand.title} 75 | ... 76 | 79 | 80 | 81 | *커맨드 객체로 List 받기 page 223 참조* 82 | 83 | **04. 컨트롤러 메서드의 파라미터 타입** 84 | 85 | 86 | -.@RequestParam 87 | http://localhost:8080/chap06/search/internal.do?query=q&p=2 88 | 89 | @Controller 90 | public class SearchController { 91 | 92 | @RequestMapping("/search/internal.do") 93 | public ModelAndView searchInternal(@RequestParam("query") String query, 94 | @RequestParam(value = "p", defaultValue = "1") int pageNumber) { 95 | System.out.println("query=" + query + ",pageNumber=" + pageNumber); 96 | return new ModelAndView("search/internal"); 97 | } 98 | } 99 | -.@CookieValue 100 | 101 | @RequestMapping("/cookie/view.do") 102 | public String view( 103 | @CookieValue(value = "auth", defaultValue = "0") String auth) { 104 | System.out.println("auth 쿠키: " + auth); 105 | return "cookie/view"; 106 | } 107 | -.@Requestheader 108 | 109 | -.서블릿API 직접사용 110 | 111 | **05. 컨트롤러 메서드의 리턴타입** 112 | 113 | **06. 뷰지정** 114 | 115 | - 명시적 지정 ModelAndView,String 116 | - 자동 지정 Modle,Map =@RequestMapping 으로 뷰 결정 117 | - 리다이렉트 뷰 rediret 118 | 119 | **07. 모델 생성하기** 120 | 121 | - @RequestMapping 어노테이션이 적용된 메서드의 파마미터나 리턴타입으로 ModelAndView,Model,ModelMap,Map,커맨드 객체 122 | 등을 이용해서 모델을 뷰에 전달하는 방법 123 | 124 | 125 | **08. 요청URL 매칭** 126 | 127 | 128 | 129 | -------------------------------------------------------------------------------- /0401/04.파일업로드 처리.md: -------------------------------------------------------------------------------- 1 | # 10. 파일 업로드 처리 # 2 | **10.1 MultipartResolver 설정** 3 | 4 | 파일 업로드가 필용한 경우 html 폼의 entype 속성을 아래와 같이 설정해야 한다. 5 | 6 |
7 | ... 8 |
9 | 10 | Multipart 지원 기능을 사용하려면 먼저 MultipartResolver를 스프링 설정파일에 등록해 주어야 한다. 11 | 12 | 14 | 15 | 16 | **10.2 @RequestParma 어노테이션을 이용한 업로드 파일 접근** 17 | 18 | 19 |
20 | 학번: 21 |
22 | 리포트파일: 23 |
24 | 25 |
26 | 27 | 28 | 29 | @Controller 30 | public class ReportSubmissionController { 31 | 32 | @RequestMapping(value = "/report/submission.do", method = RequestMethod.GET) 33 | public String form() { 34 | return "report/submissionForm"; 35 | } 36 | 37 | @RequestMapping(value = "/report/submitReport1.do", method = RequestMethod.POST) 38 | public String submitReport1( 39 | @RequestParam("studentNumber") String studentNumber, 40 | @RequestParam("report") MultipartFile report) { 41 | printInfo(studentNumber, report); 42 | return "report/submissionComplete"; 43 | } 44 | 45 | **10.3 MultipartHttpServletRequest를 이용한 업로드 파일 접근** 46 | 47 | @RequestMapping(value = "/report/submitReport2.do", method = RequestMethod.POST) 48 | public String submitReport2(MultipartHttpServletRequest request) { 49 | String studentNumber = request.getParameter("studentNumber"); 50 | MultipartFile report = request.getFile("report"); 51 | printInfo(studentNumber, report); 52 | return "report/submissionComplete"; 53 | } 54 | 55 | **10.4 커맨드 객체를 통한 업로드 파일 접근** 56 | 57 | 58 | public class ReportCommand { 59 | 60 | private String studentNumber; 61 | private MultipartFile report; 62 | 63 | public String getStudentNumber() { 64 | return studentNumber; 65 | } 66 | 67 | public void setStudentNumber(String studentNumber) { 68 | this.studentNumber = studentNumber; 69 | } 70 | 71 | public MultipartFile getReport() { 72 | return report; 73 | } 74 | 75 | public void setReport(MultipartFile report) { 76 | this.report = report; 77 | } 78 | 79 | } 80 | 81 | 위 코드와 같이 multipartFile 타입의 프로퍼티 커맨드 클래스를 추가해 주었다면 아래와 같이 @RequestMapping 메서드의 커맨드 객체를 사용함으로서 업로드 파일 정보를 커맨드 객체를 통해서 전달 받을 수 있다. 82 | 83 | @RequestMapping(value = "/report/submitReport3.do", method = RequestMethod.POST) 84 | public String submitReport3(ReportCommand reportCommand) { 85 | printInfo(reportCommand.getStudentNumber(), reportCommand.getReport()); 86 | return "report/submissionComplete"; 87 | } 88 | 89 | 90 | **10.5 @InitBinder 어노테이션과 커스텀 데이터 타입 변환 처리** 91 | 92 | @InitBinder 93 | protected void initBinder(WebDataBinder binder) { 94 | DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); 95 | binder.registerCustomEditor(Date.class, new CustomDateEditor( 96 | dateFormat, true)); 97 | } 98 | 99 | **10.6 WebBindingInitializer 를 이용한 propertyEditor 등록** 100 | @InitBinder 어노테이션이 적용된 메서드가 컨트롤러 단위로 PropertyEditor를 등록하는데 사용한다면 101 | WebBindingInitializer는 전체 컨트롤러에 공통으로 적용되는 PropertyEditor를 등록하는ㄴ데 사용된다. 102 | 103 | public class CustomWebBindingInitializer implements WebBindingInitializer { 104 | 105 | @Override 106 | public void initBinder(WebDataBinder binder, WebRequest request) { 107 | DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); 108 | binder.registerCustomEditor(Date.class, new CustomDateEditor( 109 | dateFormat, true)); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /0401/04.폼값 검증 및 에러메세지-MarkdownPadPreview.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 04.폼값 검증 및 에러메세지 5 | 6 | 274 | 275 | 276 | 277 |

09. 폼 입력값 검증

278 | 279 | 280 | 281 | 282 | -------------------------------------------------------------------------------- /0401/04.파일업로드 처리.md: -------------------------------------------------------------------------------- 1 | # 10. 파일 업로드 처리 # 2 | **10.1 MultipartResolver 설정** 3 | 4 | 파일 업로드가 필용한 경우 html 폼의 entype 속성을 아래와 같이 설정해야 한다. 5 | 6 |
7 | ... 8 |
9 | 10 | Multipart 지원 기능을 사용하려면 먼저 MultipartResolver를 스프링 설정파일에 등록해 주어야 한다. 11 | 12 | 14 | 15 | 16 | **10.2 @RequestParma 어노테이션을 이용한 업로드 파일 접근** 17 | 18 | 19 |
20 | 학번: 21 |
22 | 리포트파일: 23 |
24 | 25 |
26 | 27 | 28 | 29 | @Controller 30 | public class ReportSubmissionController { 31 | 32 | @RequestMapping(value = "/report/submission.do", method = RequestMethod.GET) 33 | public String form() { 34 | return "report/submissionForm"; 35 | } 36 | 37 | @RequestMapping(value = "/report/submitReport1.do", method = RequestMethod.POST) 38 | public String submitReport1( 39 | @RequestParam("studentNumber") String studentNumber, 40 | @RequestParam("report") MultipartFile report) { 41 | printInfo(studentNumber, report); 42 | return "report/submissionComplete"; 43 | } 44 | 45 | **10.3 MultipartHttpServletRequest를 이용한 업로드 파일 접근** 46 | 47 | @RequestMapping(value = "/report/submitReport2.do", method = RequestMethod.POST) 48 | public String submitReport2(MultipartHttpServletRequest request) { 49 | String studentNumber = request.getParameter("studentNumber"); 50 | MultipartFile report = request.getFile("report"); 51 | printInfo(studentNumber, report); 52 | return "report/submissionComplete"; 53 | } 54 | 55 | **10.4 커맨드 객체를 통한 업로드 파일 접근** 56 | 57 | 58 | public class ReportCommand { 59 | 60 | private String studentNumber; 61 | private MultipartFile report; 62 | 63 | public String getStudentNumber() { 64 | return studentNumber; 65 | } 66 | 67 | public void setStudentNumber(String studentNumber) { 68 | this.studentNumber = studentNumber; 69 | } 70 | 71 | public MultipartFile getReport() { 72 | return report; 73 | } 74 | 75 | public void setReport(MultipartFile report) { 76 | this.report = report; 77 | } 78 | 79 | } 80 | 81 | 위 코드와 같이 multipartFile 타입의 프로퍼티 커맨드 클래스를 추가해 주었다면 아래와 같이 @RequestMapping 메서드의 커맨드 객체를 사용함으로서 업로드 파일 정보를 커맨드 객체를 통해서 전달 받을 수 있다. 82 | 83 | @RequestMapping(value = "/report/submitReport3.do", method = RequestMethod.POST) 84 | public String submitReport3(ReportCommand reportCommand) { 85 | printInfo(reportCommand.getStudentNumber(), reportCommand.getReport()); 86 | return "report/submissionComplete"; 87 | } 88 | 89 | 90 | **10.5 @InitBinder 어노테이션과 커스텀 데이터 타입 변환 처리** 91 | 92 | @InitBinder 93 | protected void initBinder(WebDataBinder binder) { 94 | DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); 95 | binder.registerCustomEditor(Date.class, new CustomDateEditor( 96 | dateFormat, true)); 97 | } 98 | 99 | **10.6 WebBindingInitializer 를 이용한 propertyEditor 등록** 100 | @InitBinder 어노테이션이 적용된 메서드가 컨트롤러 단위로 PropertyEditor를 등록하는데 사용한다면 101 | WebBindingInitializer는 전체 컨트롤러에 공통으로 적용되는 PropertyEditor를 등록하는ㄴ데 사용된다. 102 | 103 | public class CustomWebBindingInitializer implements WebBindingInitializer { 104 | 105 | @Override 106 | public void initBinder(WebDataBinder binder, WebRequest request) { 107 | DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); 108 | binder.registerCustomEditor(Date.class, new CustomDateEditor( 109 | dateFormat, true)); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /0401/04.폼값 검증 및 에러메세지-MarkdownPadPreview.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 04.폼값 검증 및 에러메세지 5 | 6 | 274 | 275 | 276 | 277 |

09. 폼 입력값 검증

278 | 279 | 280 | 281 | 282 | -------------------------------------------------------------------------------- /0401/0401.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kenu/springstudy2013/42b4c5a0b3afae99a52d80b907f372a98a057c4e/0401/0401.md -------------------------------------------------------------------------------- /0401/05.폼값 검증 및 에러메세지.md: -------------------------------------------------------------------------------- 1 | # 09. 폼 입력값 검증 # 2 | 3 | **1. Validator 인터페이스를 이용한 폼 값 검증** 4 | 5 | - boolean supports(Class clazz) 6 | - void validate(Object target,Error errors) 7 | 8 | 9 | # public class MemberInfoValidator implements Validator { 10 | 11 | @Override 12 | public boolean supports(Class clazz) { 13 | return MemberInfo.class.isAssignableFrom(clazz); 14 | } 15 | 16 | @Override 17 | public void validate(Object target, Errors errors) { 18 | MemberInfo memberInfo = (MemberInfo) target; 19 | if (memberInfo.getId() == null || memberInfo.getId().trim().isEmpty()) { 20 | errors.rejectValue("id", "required"); 21 | } 22 | .... 23 | } 24 | 25 | @Controller 26 | @RequestMapping("/account/create.do") 27 | public class CreateAccountController { 28 | 29 | @RequestMapping(method = RequestMethod.POST) 30 | public String submit(@ModelAttribute("command") MemberInfo memberInfo, 31 | BindingResult result) { 32 | new MemberInfoValidator().validate(memberInfo, result); 33 | if (result.hasErrors()) { 34 | return "account/creationForm"; 35 | } 36 | return "account/created"; 37 | } 38 | } 39 | 40 | **02. Errors 인테페이스와 BindingResult 인터페이스** 41 | 42 | errors.rejectValue("id","id.invalidLength"); 43 | errors.rejectValue("id","id.invalidCharater"); 44 | 45 | 46 | **03. DefaultMessageCodesResolver** 47 | 48 | - 커스텀 태그를 이용하여 에러 정보를 설정 49 | - 태그를 이용하여 에러 정보 설정 50 | 51 | 52 | 53 | 54 |
55 | 아이디: 56 | 57 | 58 | **04. ValidationUtils 클래스를 이용한 값 검증** 59 | 60 | if (memberInfo.getId() == null || memberInfo.getId().trim().isEmpty()) {} 61 | ==> ValidationUtils.rejectIfEmptyOrWhitespace(errors, "id", "required"); 62 | 63 | ValidationUtils.rejectIfEmpty() 64 | ==> 값이 null 이거나 길이가 0인 경우 65 | ValidationUtils.rejectIfEmptyOrWhitespace() 66 | ==> 값이 null 이거나 길이가 0 이거나 공백인 경우 67 | 68 | **05. @Valid 어노테이션과 @InitBiner 어노테이션을 이용한 검증 실행** 69 | 70 | 앞서 살펴봤던 Validator 이용 코드를 보면 다음과 같이 Validator 객체를 생성한 뒤 직접 Validate() 메서드를 호출 했었다. 71 | @Valid 어노테이션을 이용하면 커맨드 객체를 검사하는 코드를 직접적으로 호출하지 않고 스프링 프레임워크가 호출하도록 설정 할 수 있다. 72 | 73 | @RequestMapping(method = RequestMethod.POST) 74 | public String submit(@Valid LoginCommand loginCommand, 75 | BindingResult result) { 76 | if (result.hasErrors()) { 77 | return formViewName; 78 | } 79 | .. 80 | } 81 | 82 | @InitBinder 83 | protected void initBinder(WebDataBinder binder) { 84 | binder.setValidator(new LoginCommandValidator()); 85 | } 86 | 87 | 커맨드 객체 파라미터에 @valid 어노테이션을 적용하고 있고 submit() 메서드 내부에서 Validator.validate() 메서드를 명시적으로 호출하고 있지 않다. submit() 메서드는 단지 두번째 파라미터로 전달받은 Bindding Result 를 이용해 에러가 발생할 경우 다시 폼을 보여주도록 하고 있다. 88 | 89 | 스프링은 @InitBinder 어노테이션이 적용된 메서드를 이용해서 어떤 Validator를 사용할지 결정한다. 90 | 91 | @valid 어노테이션 사용이 BindingResult 나 Errors 타입의 파라미터을 전달받지 않으면 500 Error 가 발생시킨다. -------------------------------------------------------------------------------- /0401/05.폼값 검증 및 에러메세지.md: -------------------------------------------------------------------------------- 1 | # 09. 폼 입력값 검증 # 2 | 3 | **1. Validator 인터페이스를 이용한 폼 값 검증** 4 | 5 | - boolean supports(Class clazz) 6 | - void validate(Object target,Error errors) 7 | 8 | 9 | # public class MemberInfoValidator implements Validator { 10 | 11 | @Override 12 | public boolean supports(Class clazz) { 13 | return MemberInfo.class.isAssignableFrom(clazz); 14 | } 15 | 16 | @Override 17 | public void validate(Object target, Errors errors) { 18 | MemberInfo memberInfo = (MemberInfo) target; 19 | if (memberInfo.getId() == null || memberInfo.getId().trim().isEmpty()) { 20 | errors.rejectValue("id", "required"); 21 | } 22 | .... 23 | } 24 | 25 | @Controller 26 | @RequestMapping("/account/create.do") 27 | public class CreateAccountController { 28 | 29 | @RequestMapping(method = RequestMethod.POST) 30 | public String submit(@ModelAttribute("command") MemberInfo memberInfo, 31 | BindingResult result) { 32 | new MemberInfoValidator().validate(memberInfo, result); 33 | if (result.hasErrors()) { 34 | return "account/creationForm"; 35 | } 36 | return "account/created"; 37 | } 38 | } 39 | 40 | **02. Errors 인테페이스와 BindingResult 인터페이스** 41 | 42 | errors.rejectValue("id","id.invalidLength"); 43 | errors.rejectValue("id","id.invalidCharater"); 44 | 45 | 46 | **03. DefaultMessageCodesResolver** 47 | 48 | - 커스텀 태그를 이용하여 에러 정보를 설정 49 | - 태그를 이용하여 에러 정보 설정 50 | 51 | 52 | 53 | 54 | 55 | 아이디: 56 | 57 | 58 | **04. ValidationUtils 클래스를 이용한 값 검증** 59 | 60 | if (memberInfo.getId() == null || memberInfo.getId().trim().isEmpty()) {} 61 | ==> ValidationUtils.rejectIfEmptyOrWhitespace(errors, "id", "required"); 62 | 63 | ValidationUtils.rejectIfEmpty() 64 | ==> 값이 null 이거나 길이가 0인 경우 65 | ValidationUtils.rejectIfEmptyOrWhitespace() 66 | ==> 값이 null 이거나 길이가 0 이거나 공백인 경우 67 | 68 | **05. @Valid 어노테이션과 @InitBiner 어노테이션을 이용한 검증 실행** 69 | 70 | 앞서 살펴봤던 Validator 이용 코드를 보면 다음과 같이 Validator 객체를 생성한 뒤 직접 Validate() 메서드를 호출 했었다. 71 | @Valid 어노테이션을 이용하면 커맨드 객체를 검사하는 코드를 직접적으로 호출하지 않고 스프링 프레임워크가 호출하도록 설정 할 수 있다. 72 | 73 | @RequestMapping(method = RequestMethod.POST) 74 | public String submit(@Valid LoginCommand loginCommand, 75 | BindingResult result) { 76 | if (result.hasErrors()) { 77 | return formViewName; 78 | } 79 | .. 80 | } 81 | 82 | @InitBinder 83 | protected void initBinder(WebDataBinder binder) { 84 | binder.setValidator(new LoginCommandValidator()); 85 | } 86 | 87 | 커맨드 객체 파라미터에 @valid 어노테이션을 적용하고 있고 submit() 메서드 내부에서 Validator.validate() 메서드를 명시적으로 호출하고 있지 않다. submit() 메서드는 단지 두번째 파라미터로 전달받은 Bindding Result 를 이용해 에러가 발생할 경우 다시 폼을 보여주도록 하고 있다. 88 | 89 | 스프링은 @InitBinder 어노테이션이 적용된 메서드를 이용해서 어떤 Validator를 사용할지 결정한다. 90 | 91 | @valid 어노테이션 사용이 BindingResult 나 Errors 타입의 파라미터을 전달받지 않으면 500 Error 가 발생시킨다. -------------------------------------------------------------------------------- /0401/06.HandlerInterceptor.md: -------------------------------------------------------------------------------- 1 | # 12. HandlerInterceptor를 통한 요청 가로채기 # 2 | 스프링이 제공하는 HandlerMapping 은 HandlerInterceptor 를 이용하여 컨트롤러가 요청을 처리하기 전과 후에 알맞은 기능을 수행할 수 있도록 하고 있다. 3 | 4 | HandlerInterceptor 인터페이스는 다음과 같은 3개의 메서드를 정의하고 있다. 5 | 6 | boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) 7 | 8 | boolean postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) 9 | 10 | boolean afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception exception) 11 | 12 | preHandle() 메서드는 클라이언트의 요청을 컨트롤러에 전달하기 전에 호출된다. 3번째 파라미터 handler 객체에는 컨트롤러 객체가 전달된다. 1개 이상의 HandlerInterceptor 체인을 형성할 수 있으며, preHandle() 메서드가 false 를 리턴하게 되면 다음 체인의 HandlerInterceptor or Controller 를 실행하지 않고 요청 처리를 종료한다. 13 | 14 | postHandle() 메서드는 컨트롤러가 요청을 처리한 뒤에 호출된다. postHandle() 메서드는 preHandle() 메서드의 실행 순서와 반대로 수행된다. (예를 들어 체인에 1, 2, 3 순서가 명시되어 있을 경우 postHandle() 메서드는 3, 2, 1 순서로 실행된다.) 15 | 컨트롤러 실행 도중 예외가 발생하면 postHandle() 메서드는 실행되지 않는다. 16 | 17 | afterCompletion() 메서드는 클라이언트의 요청을 처리한 뒤, 즉 뷰를 통해서 클라이언트에 응답을 전송한 뒤에 실행된다. 18 | 컨트롤러가 처리하는 도중이나 뷰를 생성하는 과정에 예외가 발생해도 afterCompletion() 메서드는 실행된다. 19 | afterCompletion() 메서드 역시 preHandle() 메서드의 실행 순서와 반대로 수행된다. 20 | 21 | public class EventExpirationCheckInterceptor extends HandlerInterceptorAdaptor { 22 | @Override 23 | public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { 24 | // 처리 로직 수행 25 | return true; 26 | } 27 | } 28 | 29 | 위 코드처럼 HandlerInterceptor 를 구현한다. 30 | 31 | 이제 인터셉터 클래스까지 구현을 했으면 설정 파일에 등록을 해보자. 사용할 HandlerMapping 빈 안에서 interceptors 프로퍼티 설정을 통해서 등록할 수 있다. 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /0401/07.예외처리.md: -------------------------------------------------------------------------------- 1 | # 13. 예외처리 # 2 | 3 | 컨트롤러 클래스의 @RequestMapping 메서드는 모든 타입의 예외를 발생 시킬 수 있다. 4 | 예외타입에 따라 스프링 MVC 와 연동된 뷰를 이용해서 에러 페이지를 출력하고 싶다면 스프링에서 제공하는 HandlerExceptionResolver 인터페이스를 사용하면 된다. 5 | 6 | public interface HandlerExceptionResolver { 7 | ModelAndView resolveException( 8 | HttpServletRequest request, HttpServletResponse response, 9 | Object handler, Exception ex); 10 | } 11 | 12 | ==> Object handler(컨트롤러 객체) / Exception ex (발생한 예외) 13 | 14 | - AnnotationMethodHandlerExceptionResolver
15 | @ExceptionHandler 어노테이션이 적용된 메서드를 이용해서 예외를 처리한다. 16 | - DefaultHandlerExceptionResolver
17 | NoSuchRequestHandlingMethodException고 같이 스프링 관련 예외 타입을 처리해 준다. 18 | - SimpleMappingExceptionResolver
19 | 예외 타입 별로 뷰 이름을 지정할 때 사용된다. 20 | 21 | **01. @ExceptionHandler 어노테이션을 이용한 예외처리** 22 | @Controller 어노테이션이 적용된 클래스에서 @ExceptionHandler 어노테이션이 적용된 메서드를 이용해서 예외처리 한다. 23 | 24 | @ExceptionHandler(NullPointerException.class) 25 | public String handleNullPointerException(NullPointerException ex) { 26 | return "error/nullException"; 27 | } 28 | 29 | **02. SimpleMappingExceptionResolver 클래스를 이용한 에러 페이지 지정** 30 | SimpleMappingExceptionResolver 클래스는 예외 타입 이름과 특정 뷰 이름을 매핑할때 사용한다. 31 | 32 | 34 | 35 | 36 | 37 | error/mathException 38 | 39 | 40 | error/exception 41 | 42 | 43 | 44 | 45 | 46 | 위 코드는 ArithmeticException 예외가 발생할 경우 error/mathException 을 뷰로 사용하고 Exception 예외가 발생할 경우 error/exceptiondmf 뷰로 사용한다. 47 | 48 | **03. 캐시 옵션설정** 49 | 50 | 51 | 52 | 53 | cacheSeconds 프로퍼티의 값이 0 인경우 캐시를 하지 않도록 해더를 생성하고 -1인 경우는 캐시 관련 해더를 생성하지 않는다. 54 | cacheSeconds 프로퍼티의 값이 1 이상인 경우 지정한 시간만큼 캐시하도록 해더를 설정한다. 55 | 56 | **04. 서블릿 관련 코드에서 WebApplicationContext 직접 접근하기** 57 | 58 | 자바코드를 작성하다 보면 스프링 컨테이너를 통해서 관리되지 않는 객체에서 스프링 빈을 사용하고 싶은 경우 스프링에서 제공하는 WebApplicationContextUtils 클래스를 이용해서 WebApplicationContext 에 접근할 수 있다. 59 | 60 | WebApplicationContext dispatcherContext = 61 | WebApplicationContextUtils.getWebApplicationContext(getServletContext()), 62 | "org.springframework.web.servlet.FrameworkServlet.CONTEXT.dispatcher"); 63 | 64 | Object hellController = dispatcherContext.getBean("helloController"); 65 | 66 | WebApplicationContextUtils.getWebApplicationContext() 메서드는 첫번째 파라미터로 ServletContext를 필요로 하기 때문에, 67 | JSP나 서블릿, 커스텀 테그 등 ServletContext에 접근할 수 있는 코드에서 스프링 컨테이너에 등록된 빈을 필요로 할때 주로 사용된다. 68 | 69 | **05 DelegatingFilterProxy를 이용한 서블릿 필터 등록** 70 | ApplicationContextUtils 클래스를 사용해도 되지만 , 서블릿 필터 자체를 스프링 컨테이너 빈으로 등록해서 스피링에서 제공하는 DI 통해 다른 빈을 사용해도 된다. 71 | 72 | web.xml 파일에 DelegatingFilterProxy를 서블릿 필터로 등록해주면 된다. 73 | 74 | 75 | profileFilter 76 | org.springframework.web.filter.DelegatingFilterProxy 77 | 78 | targetBeanName 79 | webProfileBean 80 | 81 | 82 | contextAttribute 83 | 84 | org.springframework.web.servlet.FrameworkServlet.CONTEXT.dispatcher 85 | 86 | 87 | 88 | targetFilterLifecycle 89 | true 90 | 91 | 92 | 93 | **06 @RequestBody, @ResponseBody 어노테이션 사용** 94 | 95 | 96 | 97 | @RequestMapping(value = "/test/simpleTest.do", method = RequestMethod.POST) 98 | @ResponseBody 99 | public String simpleTest(@RequestBody String body) { 100 | return body; 101 | } 102 | 103 | 104 | 105 | 이름:
106 | 나이: 107 | 108 | 109 | 110 | 111 | name=hckoo&age=34 112 | 113 | @RequestBody 어노테이션 => HTTP 요청 몸체를 자바 객체로 전달
114 | @ResponseBody 어노테이션 => 자바 객체를 HTTP 응답 몸체로 변한해 주는데 사용된다. 115 | 116 | **07. HttpMessageConverter를 이용한 변환처리**
117 | **08. Content-type과 Accept 해더 기반의 변환처리** 118 | 119 | AnnotationMethodHandlerAdapter 클래스는 @RequestBody 어노테이션이 적용된 파라미터나 @ResponseBody 어노테이션이 적용된 메서드에 대해 HttpMessageConverter를 사용해서 변환을 처리한다. 120 | 121 | xml or json 형대 변환 122 | 123 | **09. HandlerMapping,HandlerAdapter 컨트롤러 구현체** 124 | 125 | DispatcherServlet 은 HandlerMapping을 이용해서 컨트롤러 구현체를 찾는다. @Controller 어노테이션이 적용된 클래스 뿐만 아니라 Controller 인터페이스를 구현한 클래스나 HttpRequestHanler 인터페이스를 구현한 클래스도 컨트롤러 구현체가 될 수 있다. 126 | 127 | 128 | - SimpleUrlhandlerMapping 을 이용한 컨트롤러 매핑
129 | SimpleUrlhandlerMapping은 Controller 인터페이스를 구현한 컨트롤러 빈을 매핑할 때 주로 사용되는 HandlerMapping 으로서 패텀 매칭을 이용하여 다양한 URL 경로를 컨트롤러에 매핑 시켜준다. 130 | 131 | 132 | 133 | 134 | 135 | 136 | contentController 137 | helloController 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | - BeanNameUrlHandlerMapping 을 이용한 컨트롤러 매핑
146 | URL과 매칭되는 이름을 갖는 빈을 컨트롤러로 사용하는 HandlerMapping 이다. 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | - DispatcherServlet의 기본 HandlerMapping 구현 클래스와 HandlerMapping 적용순서 156 | 157 | 1. BeannameUrlHandlerMapping 158 | 2. DefaultAnnotationHandlerMapping 159 | 160 | 동시에 한개 이상의 HandlerMapping 이 등록되어 있을경우 order 프로퍼티를 이용해 사용 순서를 결정한다. 161 | 162 | 163 | 164 | 165 | order 가 낮은 순서가 먼저 의뢰된다. order 프로퍼티가 존재하지 않을 경우 설정파일에 입력한 순서에 따라 매핑 되는 컨트롤러를 검색하게 된다. 166 | 167 | 168 | **10. HandlerAdapter 의 주요 구현 클래스** 169 | 170 | DispatcherServlet은 HandlerMapping을 통해서 URL과 매칭되는 컨트롤러 객체를 찾은 뒤에는 컨트롤러 객체를 실행할 때 사용될 HandlerAdapter를 선택한다. 각 컨트롤러 객체마다 타입이나 적용된 어노테이션이 다를 수 있는데 HandlerAdpter는 각 컨트롤러의 실행 결과를 ModelAndView로 변환해주는 기능을 제공한다. 171 | 172 | - HttpRequestHandlerAdapter => HttpRequestHandler 인터페이스를 구현한 클래스를 처리 173 | - SimpleControllerHandlerAdapter => Controller 인터페이스를 구현한 클래스를 처리 174 | - AnnotationMethodHandlerAdapter => @Controller 어노테이션과 @RequestMapping 어노테이션이 적용된 클래스를 처리 175 | 176 | **11. ParameterizableViewController, UrlFilenameViewController를 이용한 정적 view 매핑** 177 | DB 연동과 같은 처리가 필요없고 뷰 페이지를 바로 보여주는 경우에 사용된다. 178 | 179 | 설정은 307 page 참조 180 | -------------------------------------------------------------------------------- /0401/07.예외처리.md: -------------------------------------------------------------------------------- 1 | # 13. 예외처리 # 2 | 3 | 컨트롤러 클래스의 @RequestMapping 메서드는 모든 타입의 예외를 발생 시킬 수 있다. 4 | 예외타입에 따라 스프링 MVC 와 연동된 뷰를 이용해서 에러 페이지를 출력하고 싶다면 스프링에서 제공하는 HandlerExceptionResolver 인터페이스를 사용하면 된다. 5 | 6 | public interface HandlerExceptionResolver { 7 | ModelAndView resolveException( 8 | HttpServletRequest request, HttpServletResponse response, 9 | Object handler, Exception ex); 10 | } 11 | 12 | ==> Object handler(컨트롤러 객체) / Exception ex (발생한 예외) 13 | 14 | - AnnotationMethodHandlerExceptionResolver
15 | @ExceptionHandler 어노테이션이 적용된 메서드를 이용해서 예외를 처리한다. 16 | - DefaultHandlerExceptionResolver
17 | NoSuchRequestHandlingMethodException고 같이 스프링 관련 예외 타입을 처리해 준다. 18 | - SimpleMappingExceptionResolver
19 | 예외 타입 별로 뷰 이름을 지정할 때 사용된다. 20 | 21 | **01. @ExceptionHandler 어노테이션을 이용한 예외처리** 22 | @Controller 어노테이션이 적용된 클래스에서 @ExceptionHandler 어노테이션이 적용된 메서드를 이용해서 예외처리 한다. 23 | 24 | @ExceptionHandler(NullPointerException.class) 25 | public String handleNullPointerException(NullPointerException ex) { 26 | return "error/nullException"; 27 | } 28 | 29 | **02. SimpleMappingExceptionResolver 클래스를 이용한 에러 페이지 지정** 30 | SimpleMappingExceptionResolver 클래스는 예외 타입 이름과 특정 뷰 이름을 매핑할때 사용한다. 31 | 32 | 34 | 35 | 36 | 37 | error/mathException 38 | 39 | 40 | error/exception 41 | 42 | 43 | 44 | 45 | 46 | 위 코드는 ArithmeticException 예외가 발생할 경우 error/mathException 을 뷰로 사용하고 Exception 예외가 발생할 경우 error/exceptiondmf 뷰로 사용한다. 47 | 48 | **03. 캐시 옵션설정** 49 | 50 | 51 | 52 | 53 | cacheSeconds 프로퍼티의 값이 0 인경우 캐시를 하지 않도록 해더를 생성하고 -1인 경우는 캐시 관련 해더를 생성하지 않는다. 54 | cacheSeconds 프로퍼티의 값이 1 이상인 경우 지정한 시간만큼 캐시하도록 해더를 설정한다. 55 | 56 | **04. 서블릿 관련 코드에서 WebApplicationContext 직접 접근하기** 57 | 58 | 자바코드를 작성하다 보면 스프링 컨테이너를 통해서 관리되지 않는 객체에서 스프링 빈을 사용하고 싶은 경우 스프링에서 제공하는 WebApplicationContextUtils 클래스를 이용해서 WebApplicationContext 에 접근할 수 있다. 59 | 60 | WebApplicationContext dispatcherContext = 61 | WebApplicationContextUtils.getWebApplicationContext(getServletContext()), 62 | "org.springframework.web.servlet.FrameworkServlet.CONTEXT.dispatcher"); 63 | 64 | Object hellController = dispatcherContext.getBean("helloController"); 65 | 66 | WebApplicationContextUtils.getWebApplicationContext() 메서드는 첫번째 파라미터로 ServletContext를 필요로 하기 때문에, 67 | JSP나 서블릿, 커스텀 테그 등 ServletContext에 접근할 수 있는 코드에서 스프링 컨테이너에 등록된 빈을 필요로 할때 주로 사용된다. 68 | 69 | **05 DelegatingFilterProxy를 이용한 서블릿 필터 등록** 70 | ApplicationContextUtils 클래스를 사용해도 되지만 , 서블릿 필터 자체를 스프링 컨테이너 빈으로 등록해서 스피링에서 제공하는 DI 통해 다른 빈을 사용해도 된다. 71 | 72 | web.xml 파일에 DelegatingFilterProxy를 서블릿 필터로 등록해주면 된다. 73 | 74 | 75 | profileFilter 76 | org.springframework.web.filter.DelegatingFilterProxy 77 | 78 | targetBeanName 79 | webProfileBean 80 | 81 | 82 | contextAttribute 83 | 84 | org.springframework.web.servlet.FrameworkServlet.CONTEXT.dispatcher 85 | 86 | 87 | 88 | targetFilterLifecycle 89 | true 90 | 91 | 92 | 93 | **06 @RequestBody, @ResponseBody 어노테이션 사용** 94 | 95 | 96 | 97 | @RequestMapping(value = "/test/simpleTest.do", method = RequestMethod.POST) 98 | @ResponseBody 99 | public String simpleTest(@RequestBody String body) { 100 | return body; 101 | } 102 | 103 | 104 |
105 | 이름:
106 | 나이: 107 | 108 |
109 | 110 | 111 | name=hckoo&age=34 112 | 113 | @RequestBody 어노테이션 => HTTP 요청 몸체를 자바 객체로 전달
114 | @ResponseBody 어노테이션 => 자바 객체를 HTTP 응답 몸체로 변한해 주는데 사용된다. 115 | 116 | **07. HttpMessageConverter를 이용한 변환처리**
117 | **08. Content-type과 Accept 해더 기반의 변환처리** 118 | 119 | AnnotationMethodHandlerAdapter 클래스는 @RequestBody 어노테이션이 적용된 파라미터나 @ResponseBody 어노테이션이 적용된 메서드에 대해 HttpMessageConverter를 사용해서 변환을 처리한다. 120 | 121 | xml or json 형대 변환 122 | 123 | **09. HandlerMapping,HandlerAdapter 컨트롤러 구현체** 124 | 125 | DispatcherServlet 은 HandlerMapping을 이용해서 컨트롤러 구현체를 찾는다. @Controller 어노테이션이 적용된 클래스 뿐만 아니라 Controller 인터페이스를 구현한 클래스나 HttpRequestHanler 인터페이스를 구현한 클래스도 컨트롤러 구현체가 될 수 있다. 126 | 127 | 128 | - SimpleUrlhandlerMapping 을 이용한 컨트롤러 매핑
129 | SimpleUrlhandlerMapping은 Controller 인터페이스를 구현한 컨트롤러 빈을 매핑할 때 주로 사용되는 HandlerMapping 으로서 패텀 매칭을 이용하여 다양한 URL 경로를 컨트롤러에 매핑 시켜준다. 130 | 131 | 132 | 133 | 134 | 135 | 136 | contentController 137 | helloController 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | - BeanNameUrlHandlerMapping 을 이용한 컨트롤러 매핑
146 | URL과 매칭되는 이름을 갖는 빈을 컨트롤러로 사용하는 HandlerMapping 이다. 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | - DispatcherServlet의 기본 HandlerMapping 구현 클래스와 HandlerMapping 적용순서 156 | 157 | 1. BeannameUrlHandlerMapping 158 | 2. DefaultAnnotationHandlerMapping 159 | 160 | 동시에 한개 이상의 HandlerMapping 이 등록되어 있을경우 order 프로퍼티를 이용해 사용 순서를 결정한다. 161 | 162 | 163 | 164 | 165 | order 가 낮은 순서가 먼저 의뢰된다. order 프로퍼티가 존재하지 않을 경우 설정파일에 입력한 순서에 따라 매핑 되는 컨트롤러를 검색하게 된다. 166 | 167 | 168 | **10. HandlerAdapter 의 주요 구현 클래스** 169 | 170 | DispatcherServlet은 HandlerMapping을 통해서 URL과 매칭되는 컨트롤러 객체를 찾은 뒤에는 컨트롤러 객체를 실행할 때 사용될 HandlerAdapter를 선택한다. 각 컨트롤러 객체마다 타입이나 적용된 어노테이션이 다를 수 있는데 HandlerAdpter는 각 컨트롤러의 실행 결과를 ModelAndView로 변환해주는 기능을 제공한다. 171 | 172 | - HttpRequestHandlerAdapter => HttpRequestHandler 인터페이스를 구현한 클래스를 처리 173 | - SimpleControllerHandlerAdapter => Controller 인터페이스를 구현한 클래스를 처리 174 | - AnnotationMethodHandlerAdapter => @Controller 어노테이션과 @RequestMapping 어노테이션이 적용된 클래스를 처리 175 | 176 | **11. ParameterizableViewController, UrlFilenameViewController를 이용한 정적 view 매핑** 177 | DB 연동과 같은 처리가 필요없고 뷰 페이지를 바로 보여주는 경우에 사용된다. 178 | 179 | 설정은 307 page 참조 180 | -------------------------------------------------------------------------------- /0408/0408.md: -------------------------------------------------------------------------------- 1 | # 뷰 2 | * Controller는 ModelAndView 객체를 리턴 3 | * DispatcherServlet은 ViewResolver를 통해서 View객체로 내용 생성 4 | 5 | 6 | 7 | ## ViewResolver 8 | 9 | ### 설정 10 | #### Controller 11 | ``` 12 | @Controller 13 | public class HelloController { 14 | @RequestMapping("/hello.do") 15 | public String hello() { 16 | return "hello"; 17 | } 18 | } 19 | ``` 20 | 21 | ViewResolver 구현 클래스 | 설명 22 | :----:|:----: 23 | InternalResourceViewResolver | View 객체 리턴 24 | VelocityViewResolver | Velocity View 객체 리턴 25 | VelocityLayoutViewResolver | VVR + 레이아웃 기능 제공 26 | BeanNameViewResolver | 뷰 이름과 동일한 빈 객체를 View 객체로 사용 27 | ResourceBundleViewResolver | 뷰 이름과 View 객체간의 매핑정보 properties 사용 28 | XmlViewResolver | 뷰 이름과 View 객체간의 매핑정보 XML 사용 29 | 30 | #### ViewResolver Interface 31 | #### View Interface 32 | #### InternalResourceViewResolver 33 | ``` 34 | 37 | 38 | ``` 39 | 40 | ``` 41 | ModelAndView mav = new ModelAndView("hello"); 42 | return mav; 43 | 44 | /WEB-INF/views/ + hello + .jsp 45 | 46 | /WEB-INF/views/hello.jsp 47 | ``` 48 | #### BeanNameViewResolver 49 | * 뷰 이름과 같은 이름의 빈을 뷰 객체로 사용 50 | * Custom View 클래스 51 | * 파일 다운로드 52 | 53 | ``` 54 | @RequestMapping("/download.do") 55 | public ModelAndView download(HttpServletRequest request, 56 | HttpServletResponse) { 57 | File downloadFile = getFile(request); 58 | return new ModelAndView("download", "downloadFile", downloadFile); 59 | } 60 | ``` 61 | 62 | ``` 63 | 65 | 66 | 68 | ``` 69 | 70 | #### XmlViewResolver 71 | p314 72 | 73 | ``` 74 | 77 | ``` 78 | 79 | #### ResourceBundleViewresolver 80 | ``` 81 | 84 | 85 | ``` 86 | ``` 87 | views/views.properties 88 | 89 | download.class=madvirus.spring.chap07.view.DownloadView 90 | ``` 91 | * Locale 이용가능 92 | * DI, AOP 적용할 수 없음 93 | 94 | 95 | 96 | #### 멀티 viewResolver 순서 97 | 참고: [http://www.mkyong.com/spring-mvc/configure-multiple-view-resolvers-priority-in-spring-mvc/]() 98 | 99 | 100 | ## defaultHtmlEscape 101 | * <> to \<\> 102 | 103 | 104 | ``` 105 | web.xml 106 | 107 | defaultHtmlEscape 108 | false 109 | 110 | 111 | ``` 112 | ## JSP 뷰 113 | ### 114 | 115 | ``` 116 | <%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> 117 | <spring:message code="login.form.title"/> 118 | 119 | log.form.title=Login Form 120 | ``` 121 | 122 | ``` 123 | greeing=\uc804 {0} \uc785\ub2c8\ub2e4. {1} 124 | 125 | 126 | ``` 127 | 128 | ### 129 | ``` 130 | <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> 131 | ``` 132 | 133 | #### 134 | 135 | ``` 136 | 137 | 138 | 139 | ``` 140 | 141 | #### , , 142 | 143 | ``` 144 | 145 | 회원 ID 146 | 147 | 148 | 149 | ``` 150 | 151 | ``` 152 |
153 | 154 | 155 | 156 | ``` 157 | 158 | 159 | #### , , 160 | LoginController.java 161 | 162 | ``` 163 | @ModelAttribute("loginTypes") 164 | protected List referenceData() throws Exception { 165 | List loginTypes = new ArrayList(); 166 | loginTypes.add("일반회원"); 167 | loginTypes.add("기업회원"); 168 | loginTypes.add("헤드헌터회원"); 169 | return loginTypes; 170 | } 171 | ``` 172 | 173 | ``` 174 | 175 | 176 | 177 | 178 | ``` 179 | 180 | #### , 181 | 182 | #### , 183 | 184 | #### 185 | 186 | #### CSS, HTML 태그 공통 속성 187 | 188 | * cssClass 189 | * cssErrorClass 190 | * cssStyle 191 | 192 | * id, title, dir 193 | * disabled, tabindex 194 | * onfocus, onblur, onchange 195 | * onclick, ondblclick 196 | * onkeydown, onkeypress, onkeyup 197 | * onmousedown, onmousemove, onmouseup 198 | * onmouseout, onmouseover 199 | 200 | ### Validator Tag 201 | 202 | ``` 203 | 204 | ``` 205 | 206 | ### 207 | ``` 208 | 209 | ``` 210 | 211 | ### RESTful 212 | * 스프링 MVC는 HTTP의 GET, POST, PUT, DELETE 지원 213 | 214 | ArticleController.java 215 | 216 | ``` 217 | @RequestMapping(value="/article/{id}", method=RequestMethod.GET) 218 | public String read(@PathVariable("id") Integer id, Model model) { 219 | return "article/read"; 220 | } 221 | 222 | @RequestMapping(value="/article/{id}", method=RequestMethod.DELETE) 223 | public String delete(@PathVariable("id") Integer id, Model model) { 224 | return "article/delete"; 225 | } 226 | 227 | @RequestMapping(value="/article/{id}", method=RequestMethod.PUT) 228 | public String modify(@PathVariable("id") Integer id, Model model) { 229 | return "article/modify"; 230 | } 231 | 232 | @RequestMapping(value="/article", method=RequestMethod.POST) 233 | public String write(@PathVariable("id") Integer id, Model model) { 234 | return "article/write"; 235 | } 236 | 237 | ``` 238 | 239 | 240 | 241 | 242 | 243 | ## Tiles 뷰 244 | p338 245 | * 동일한 페이지 구성, 상단과 하단 246 | * Tiles 템플릿 라이브러리 247 | 248 | 249 | ### Tiles 2 연동 250 | * TilesConfigurer 이용 Tiles 2 레이아웃 설정 명시 251 | * UrlBasedViewResolver TileView 252 | 253 | _dispatcher-Tiles2-servlet.xml_ 254 | 255 | ``` 256 | 258 | 259 | 260 | /WEB-INF/tiles2def/tilesdef.xml 261 | 262 | 263 | 265 | 266 | 267 | 269 | 271 | 272 | 273 | ``` 274 | 275 | 276 | 277 | ## Velocity 뷰 278 | 279 | * 템플릿 엔진 280 | * 템플릿 파일 쉽게 작성 281 | * 자바 객체 사용 가능 282 | 283 | ### VelocityViewResolver 284 | 285 | _dispatcherVm-servlet.xml_ 286 | 287 | ``` 288 | 290 | 291 | 292 | 293 | EUC-KR 294 | EUC-KR 295 | 296 | 297 | 298 | 299 | 303 | 304 | ``` 305 | 306 | #### Velocity Tools 307 | * dateToolAttribute 308 | * numberToolAttribute 309 | 310 | _time.vm_ 311 | 312 | ``` 313 | #springMessage("label.currentTime") 314 | : $dateTool.format("yyyy-MM-dd HH:mm:ss", $time) 315 | ``` 316 | 317 | #### request, session 속성 318 | * exposeRequestAttributes 319 | * exposeSessionAttributes 320 | 321 | ``` 322 | 329 | ``` 330 | 331 | ### 스프링 Velocity 매크로 332 | p347 333 | #### springMessage, springMessageText 334 | * \#springMessage(code) 335 | * \#springMessageText(code, text) 336 | 337 | ``` 338 | 339 | #springMessageText("login.form.id.help", "도움말이 없습니다.") 340 | ``` 341 | 342 | #### springBind, springBindEscape 343 | ``` 344 | #springBind("login.loginType") 345 | 354 | ``` 355 | 356 | #### springFormInput 매크로 357 | 358 | #### springFormSingleSelect, springFormMultiSelect 매크로 359 | 360 | #### springFormCheckboxes, springFormCheckbox 매크로 361 | 362 | #### springFormRadioButtons 매크로 363 | 364 | #### springFormTextarea 매크로 365 | 366 | #### springShowErrors 매크로 367 | 368 | #### VelocityLayoutViewResolver 템플릿 적용 369 | 370 | _dispatcherVmLayout-servlet.xml_ 371 | * 차이점 layoutUrl 프로퍼티로 템플릿 파일 지정 372 | 373 | #### Velocity Layout 파일 생성 374 | $screen_content 375 | 376 | 377 | ## 다운로드, 엑셀, PDF 378 | 379 | ### 파일 다운로드 커스텀 View 380 | _DownloadController.java_ 381 | 382 | ``` 383 | @RequestMapping("/file") 384 | public ModelAndView download() throws Exception { 385 | File downloadFile = getFile(); 386 | return new ModelAndView("download", "downloadFile", downloadFile); 387 | } 388 | 389 | private File getFile() { 390 | String path = context.getServletContext().getRealPath( 391 | "/WEB-INF/desc.txt"); 392 | return new File(path); 393 | } 394 | ``` 395 | 396 | _DownloadView.java_ 397 | 398 | ``` 399 | public class DownloadView extends AbstractView { 400 | 401 | public DownloadView() { 402 | setContentType("application/download; charset=utf-8"); 403 | } 404 | 405 | @Override 406 | protected void renderMergedOutputModel(Map model, 407 | HttpServletRequest request, HttpServletResponse response) 408 | throws Exception { 409 | File file = (File) model.get("downloadFile"); 410 | 411 | response.setContentType(getContentType()); 412 | response.setContentLength((int) file.length()); 413 | 414 | String userAgent = request.getHeader("User-Agent"); 415 | boolean ie = userAgent.indexOf("MSIE") > -1; 416 | String fileName = null; 417 | if (ie) { 418 | fileName = URLEncoder.encode(file.getName(), "utf-8"); 419 | } else { 420 | fileName = new String(file.getName().getBytes("utf-8"), 421 | "iso-8859-1"); 422 | } 423 | response.setHeader("Content-Disposition", "attachment; filename=\"" 424 | + fileName + "\";"); 425 | response.setHeader("Content-Transfer-Encoding", "binary"); 426 | OutputStream out = response.getOutputStream(); 427 | FileInputStream fis = null; 428 | try { 429 | fis = new FileInputStream(file); 430 | FileCopyUtils.copy(fis, out); 431 | } finally { 432 | if (fis != null) 433 | try { 434 | fis.close(); 435 | } catch (IOException ex) { 436 | } 437 | } 438 | out.flush(); 439 | } 440 | 441 | ``` 442 | 443 | ### Excel 다운로드 구현 444 | * AbstractExcelView - POI api 445 | * AbstractJExcelView - jexcel api 446 | 447 | _PageRanksView.java_ 448 | 449 | ### PDF 다운로드 구현 450 | 451 | * iText api 이용 452 | 453 | _PageReportView.java_ 454 | 455 | ``` 456 | @RequestMapping("/pageReport") 457 | public ModelAndView pdfReport() { 458 | List pageRanks = new ArrayList(); 459 | pageRanks.add(new PageRank(1, "/bbs/mir2/list")); 460 | pageRanks.add(new PageRank(2, "/bbs/mir3/list")); 461 | pageRanks.add(new PageRank(3, "/bbs/changchun2/list")); 462 | return new ModelAndView("pageReport", "pageRanks", pageRanks); 463 | } 464 | 465 | ``` 466 | 467 | ### XML 응답 생성 468 | * MarshallingView 이용 469 | * OXM 470 | 471 | _PageRank_ 472 | 473 | ``` 474 | @XmlAccessorType(XmlAccessType.FIELD) 475 | @XmlType(name = "", propOrder = { "rank", "page" }) 476 | public class PageRank { 477 | 478 | ``` 479 | 480 | ``` 481 | @RequestMapping("/pageXmlReport") 482 | public ModelAndView xmlReport() { 483 | List pageRanks = new ArrayList(); 484 | pageRanks.add(new PageRank(1, "/bbs/mir2/list")); 485 | pageRanks.add(new PageRank(2, "/bbs/mir3/list")); 486 | pageRanks.add(new PageRank(3, "/bbs/changchun2/list")); 487 | return new ModelAndView("pageXmlReport", "report", new PageRankReport( 488 | pageRanks)); 489 | } 490 | 491 | ``` 492 | 493 | ### JSON 응답 생성 494 | * MappingJacksonJsonView 이용 495 | * [http://jackson.codehaus.org/]() 496 | 497 | ``` 498 | @RequestMapping("/pageJsonReport") 499 | public ModelAndView jsonReport() { 500 | List pageRanks = new ArrayList(); 501 | pageRanks.add(new PageRank(1, "/bbs/mir2/list")); 502 | pageRanks.add(new PageRank(2, "/bbs/mir3/list")); 503 | pageRanks.add(new PageRank(3, "/bbs/changchun2/list")); 504 | return new ModelAndView("pageJsonReport", "report", new PageRankReport( 505 | pageRanks)); 506 | } 507 | ``` 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | ## 지역별 처리 516 | 517 | ### LocaleResolver 인터페이스 518 | 519 | ### LocaleResolver 종류 520 | 521 | ### Locale 변경 522 | 523 | ### LocaleChangeInterceptor 524 | * 웹 요청 파라미터를 통해서 손쉽게 Locale 변경 525 | 526 | ``` 527 | 530 | 531 | 533 | 534 | 535 | 536 | 537 | 538 | 539 | ``` 540 | 541 | ``` 542 | http://localhost:8080/chap07/jsp/login/login.do?language=en 543 | ``` 544 | 545 | 546 | ==== 547 | 548 | # 데이터베이스와 JDBC 549 | 550 | 551 | ## 스프링의 데이터베이스 552 | 553 | ## DataSource 554 | 555 | ## 스프링 JDBC 556 | 557 | -------------------------------------------------------------------------------- /0408/chap08.md: -------------------------------------------------------------------------------- 1 | #Chapter 08 데이터베이스 연동 지원과 JDBC 지원 2 | ##1. 스프링의 데이터베이스 연동 지원 3 | 4 | 1.1 데이터베이스 연동 5 | 6 | - 데이터베이스 연동을 구현하는 전형적인 방법 7 | - 데이터 베이스에 접근하는 클래스와 서비스 로직을 구현한 클래스를 구분. 8 | - 데이터베이스 접근을 위한 DAO(Data Access Object)를 만들고, 서비스 클래스에서는 DAO를 호출해서 데이터에 대한 CRUD를 처리. 9 | 10 | 스프링의 데이터베이스 연동 지원 11 | 12 | JDBC, 하이버네이트, iBATIS 등의 다양한 기술을 이용해서 손쉽게 DAO 클래스를 구현할 수 있도록 지원. 13 | 14 | - 템플릿 클래스를 통한 데이터 접근 지원. 15 | - DaoSupport 클래스를 이용한 DAO 클래스 구현. 16 | - 의미 있는 예외 클래스 제공. 17 | 18 | 데이터베이스 연동을 위한 템플릿 클래스 19 | - 데이터에 접근하는 코드는 거의 동일한 코드 구성. 20 | - JDBC를 사용해서 특정 테이블에서 데이터를 로딩하는 코드 형식 21 | 22 | 23 | 24 | ```java 25 | Connection conn = null; 26 | PreparedStatement pstmt = null; 27 | ResultSet rs = null; 28 | 29 | try { 30 | conn = getConnection(); 31 | pstmt = conn.prepareStatement("select * from message where guestBookId = ?"); 32 | pstmt.setInt(1, guestBookId); 33 | 34 | rs = pstmt.executeQuery(); 35 | 36 | if(rs.next()) { 37 | do { 38 | Message message = new Message(); 39 | message.setContent(rs.getString("content")); 40 | ... 41 | } while(rs.next()); 42 | } 43 | } catch(SQLException ex) { 44 | // 알맞은 예외 처리. 45 | } finally { 46 | if(rs != null) rs.close(); 47 | if(pstmt != null) pstmt.close(); 48 | if(conn != null) conn.close(); 49 | } 50 | ``` 51 | - Connection을 생성하고 PreparedStatement, ResultSet, 그리고 Connection 등의 자원을 반환하는 코드는 거의 모든 JDBC 코드에서 중복되는 코드. 52 | - PreparedStatement를 구하고 ResultSet으로부터 데이터를 읽어와 자바빈 객체에 저장하는 코드 역시 동일한 형식 53 | 54 | 55 | 56 | 1.2 스프링의 예외 지원 57 | - 데이터베이스 처리 과정에서 발생한 예외가 왜 발생했는지를 좀 더 구체적으로 확인하기 위해 데이터베이스 처리와 관련된 예외 클래스 제공. 58 | - OptimisticLockingFailureException 이나 DataRetrievalFailureException과 같이 보다 구체적인 실패 원인을 설명해 주는 예외 클래스 제공. 59 | - 스프링이 제공하는 템플릿 클래스는 내부적으로 발생하는 예외 클래스를 스프링이 제공하는 예외 클래스로 알맞게 변환해서 예외를 발생시킴. 60 | - 데이터베이스 연동을 위해 사용하는 기술에 상관없이 동일한 방식으로 예외 처리. 61 | 62 | DAO에서 사용하는 기본적인 기능을 제공, 상속받아서 사용 63 | 64 | 올바르지 않은 SQL 쿼리를 실행하는 경우 JdbcTemplate은 BadSqlGrammarException 구체적인 예외(Exception)를 발생 시킴. 65 | 66 | ```java 67 | JdbcTemplate jdbcTemplate = getJdbcTemplate(); 68 | List list = jdbcTemplate.query( 69 | "select * from GUESTBOOK_MESSAGE order by GUESTBOOK_MESSAGE_ID desc limited ?,?", 70 | // 잘못된 SQL 입력시 71 | ... 72 | ); 73 | ``` 74 | 75 | 필요한 경우에만 try-catch 블록을 이용하여 예외 처리를 하면 됨 76 | 77 | ##2. DataSource 설정 78 | 2.1. 커넥션 풀을 이용한 Datasource 설정 79 | ```xml 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | ``` 97 | 98 | ```xml 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | ``` 116 | dataSource : DAO가 데이터에 접근하기 위한 포인트 117 | DBCP(Jakarta Commons Database Connection Pool) API(connection library) 이용 118 | 119 | 2.2. JNDI를 이용한 DataSource 설정 120 | 121 | ```xml 122 | 123 | 124 | 131 | 132 | 133 | 134 | 135 | 137 | 138 | ``` 139 | 140 | JEE App server, Tomcat, Resin 등 web container 사용하는 경우 태그 사용하여 JNDI에 등록된 객체명 명시 141 | 142 | 2.3. DriverManager를 이용한 DataSource 설정 143 | ```xml 144 | 150 | ``` 151 | 152 | 2.4. DataSource로부터 Connection 구하기 153 | ```java 154 | public class JdbcMessageDao omplements MessageDao { 155 | private DataSource dataSource; 156 | public void setDataSource(DataSource dataSource) { 157 | this.dataSource=dataSource; 158 | } 159 | @Override 160 | public int selectCount() { 161 | Connection conn=null; 162 | try { 163 | // conn=dataSource.getConnection(); 164 | conn=DataSourceUils.getConnection(dataSource); 165 | } finally { 166 | // JdbcUtils.closeConnection(conn); 167 | datasourceutils.releaseConnection(conn,dataSource); 168 | } 169 | } 170 | } 171 | ``` 172 | DataSource로부터 Connection 구하기 173 | getConnection() 활용하면 되나 스프링이 제공하는 트랜잭션 관리 기능 활용할 수 없음 174 | 이를 방지하기 위해 DataSourceUtils 클래스를 이용하여 connection을 구하고 반환함. 175 | 176 | 177 | 178 | ##3. 스프링 JDBC 지원 179 | 180 | ###JDBC를 위한 템플릿 클래스 181 | - Connection을 구하고, try-catch-finally로 자원을 관리하는 등의 중복된 코드를 제거. 182 | - JDBC를 위한 세 개의 템플릿 클래스 183 | ####JdbcTemplate 184 | - 기본적인 JDBC 템플릿 클래스. 185 | - JDBC를 이용해서 데이터에 대한 접근을 제공. 186 | ###NamedParameterJdbcTemplate 187 | - PreparedStatement에서 인덱스 기반의 파라미터가 아닌 이름을 가진 파라미터를 사용할 수 있도록 지원하는 템플릿 클래스. 188 | ###SimpleJdbcTemplate 189 | - 자바 5의 가변 인자를 이용해서 쿼리를 실행할 때 사용되는 데이터를 전달할 수 있는 템플릿 클래스. 190 | - 자바 1.4 이하의 버전에서는 사용할 수 없음. 191 | 192 | ###JdbcTemplate 클래스를 이용한 JDBC 프로그래밍 193 | JdbcTemplate 클래스 194 | - DataSource를 필요로 함. 195 | - 설정 파일을 이용해서 JdbcTemplate의 dataSource 프로퍼티에 DataSource를 전달. 196 | 197 | ```xml 198 | 203 | 204 | 206 | ``` 207 | ###DAO 클래스에서 JdbcTemplate 사용 208 | - set 메서드나 생성자를 통해서 JdbcTemplate을 전달 받을 수 있도록 함. 209 | 210 | ```java 211 | public class JdbcTemplateMessageDao implements MessageDao { 212 | private JdbcTemplate jdbcTemplate; 213 | 214 | public void setJdbcTemplate(JdbcTemplate jdbcTemplate) { 215 | this.jdbcTemplate = jdbcTemplate; 216 | } 217 | ... 218 | } 219 | ``` 220 | 221 | - 설정 파일에서는 DAO에 JdbcTemplate을 전달하도록 설정. 222 | 223 | ```xml 224 | 227 | ``` 228 | 229 | - 설정 완료 후 JdbcTemplate이 제공하는 기능 사용 가능. 230 | - JdbcTemplate 클래스를 이용해서 SELECT 쿼리 실행 231 | - query() 메서드 사용. 232 | - List query(String sql, Object[] args, RowMapper rowMapper) 233 | - PreparedStatement를 이용해서 SELECT 쿼리를 실행할 때 사용. 234 | -List query(String sql, RowMapper rowMapper) 235 | - 정적 SQL을 이용해서 SELECT 쿼리를 실행할 때 사용. 236 | - query() 메서드 237 | - sql 파라미터로 전달받은 SELECT 쿼리를 실행한 뒤 결과를 리턴. 238 | - PreparedStatement를 사용할 경우 쿼리에 포함된 물음표(인덱스 파라미터)에 전달할 값을 Object[]로 전달. 239 | ###RowMapper 240 | - ResultSet에서 값을 가져와 원하는 타입으로 매핑할 때 사용. 241 | - 인터페이스 정의 242 | 243 | ```java 244 | public interface RowMapper { 245 | Object mapRow(ResultSet rs, int rowNum) throws SQLException; 246 | } 247 | ``` 248 | 249 | ### mapRow() 메서드 250 | - ResultSet 에서 읽어온 값을 이용해서 원하는 타입의 객체를 생성한 뒤 리턴. 251 | - rowNum은 행번호를 의미. (0부터 시작) 252 | 253 | 254 | ■ query() 메서드 실행 예 255 | 256 | ```java 257 | private static final String SELECT_LIST_SQL = 258 | "select * from GUESTBOOK_MESSAGE order by GUESTBOOK_MESSAGE_ID desc limit ?, ?"; 259 | 260 | @SuppressWarnings("unchecked") 261 | @Override 262 | public List selectList(int page, int pageSize) { 263 | int startRow = (page - 1) * pageSize; 264 | List list = jdbcTemplate.query(SELECT_LIST_SQL, 265 | new Object[] {startRow, pageSize}, 266 | new RowMapper() { 267 | @Override 268 | public Object mapRow(ResultSet rs, int rowNum) throws SQLException { 269 | Message message = new Message(); 270 | message.setId(rs.getInt("GUESTBOOK_MESSAGE_ID")); 271 | message.setGuestName(rs.getString("GUEST_NAME")); 272 | message.setContent(rs.getString("CONTENT")); 273 | 274 | return message; 275 | } 276 | } 277 | ); 278 | 279 | return list; 280 | } 281 | ``` 282 | 283 | - query() 메서드에 RowMapper의 구현 객체를 전달할 때는 임의의 클래스(Anonymous Class)를 주로 사용. 284 | ■ RowMapper 구현 클래스 285 | - 여러 메서드에서 공통으로 사용되는 코드가 있다면 RowMapper 구현 클래스를 별도로 구현해서 재사용. 286 | 287 | ```java 288 | import org.springframework.jdbc.core.RowMapper; 289 | 290 | public class MemberRowMapper implements RowMapper { 291 | @Override 292 | public Object mapRow(ResultSet rs, int rowNum) throws SQLException { 293 | Message message = new Message(); 294 | message.setId(rs.getInt("GUESTBOOK_MESSAGE_ID")); 295 | message.setGuestName(rs.getString("GUEST_NAME")); 296 | message.setContent(rs.getString("CONTENT")); 297 | 298 | return message; 299 | } 300 | } 301 | 302 | ``` 303 | 304 | ■ queryForObject() 메서드 305 | ○ 정의 306 | - 쿼리 실행 결과 행의 개수가 한 개인 경우에 사용. 307 | ○ 종류 308 | - public Object queryForObject(String sql, RowMapper rowMapper) 309 | - public Object queryForObject(String sql, Object[] args, RowMapper rowMapper) 310 | ○ 특징 311 | - 전달되는 각각의 파라미터는 query() 메서드와 동일. 312 | - 차이점 : List 대신 한 개의 객체를 리턴. 313 | - 쿼리 실행 결과의 행 개수가 한 개가 아닌 경우에는 IncorrectResultSizeDataAccessException 예외를 발생. 314 | ■ JdbcTemplate 에서 제공하는 메서드 315 | - Object가 아닌 int나 long 타입을 결과를 구할 때 사용할 수 있는 메서드. 316 | ○ 종류 317 | - public int queryForInt(String sql) 318 | - public int queryForInt(String sql, Object[] args) 319 | - public long queryForLong(String sql) 320 | - public long queryForLong(String sql, Object[] args) 321 | ○ 특징 322 | - 결과 행 개수가 한 개가 아닌 경우 예외 발생. 323 | ○ 사용 예 324 | 325 | ```java 326 | private String SELECT_COUNT_SQL = "select count(*) form GUESTBOOK_MESSAGE"; 327 | 328 | public int selectCount() { 329 | return jdbcTemplate.queryForInt(SELECT_COUNT_SQL); 330 | } 331 | 332 | ``` 333 | 334 | ■ update() 메서드 335 | - INSERT, UPDATE, DELETE 쿼리를 실행할 때에 사용. 336 | - query() 메서드와 마찬가지로 인덱스 파라미터를 위한 값을 전달받는 메서드와 그렇지 않은 메서드로 구분. 337 | ○ 종류 338 | - public int update(String sql) 339 | - public int update(String sql, Object[] args) 340 | ○ 특징 341 | - 쿼리 실행 결과 변경된 행의 개수를 리턴. 342 | ○ 사용 예 343 | 344 | ```java 345 | private static final String INSERT_SQL = "insert into GUESTBOOK_MESSAGE " + 346 | "(GUEST_NAME, CONTENT) values (?, ?)" ; 347 | 348 | @Override 349 | public void insert(Message message) { 350 | jdbcTemplate.update(INSERT_SQL, 351 | new Object[] {message.getGuestName(), message.getContent()} 352 | ); 353 | } 354 | ``` 355 | 356 | ■ execute() 메서드 357 | - Connection 을 직접 사용해야 할 경우 사용. 358 | - 파라미터로 전달받은 ConnectionCallback 인터페이스 구현 객체의 doInConnection() 메서드를 호출하는데 이때 Connection을 359 | doInConnection()에 전달 해야함. 360 | - 커넥션 생성과 종료는 JdbcTemplate이 처리하므로 Connection 종료할 필요가 없음 361 | 362 | ```java 363 | jdbcTemplate.execute(new ConnectionCallback() { 364 | @Override 365 | public Object doInConnection(Connection con) throws SQLException, DataAccessException { 366 | Statement stmt = null; 367 | 368 | try { 369 | stmt = con.createStatement(); 370 | ... 371 | } finally { 372 | JdbcUtils.closeStatement(stmt); 373 | } 374 | 375 | return someValue; 376 | } 377 | } 378 | ``` 379 | 380 | □ NamedParameterJdbcTemplate 클래스를 이용한 JDBC 프로그래밍 381 | ■ NamedParameterJdbcTemplate 클래스 382 | - 인덱스 기반의 파라미터가 아닌 이름 기반의 파라미터를 설정할 수 있도록 해주는 템플릿 클래스. 383 | - 인덱스 기반의 파라미터를 전달받는 물음표를 사용하지 않고 이름 기반의 파라미터를 쿼리에서 사용할 수 있도록 지원. 384 | 385 | 386 | select * from GUESTBOOK_MESSAGE order by GUESTBOOK_MESSAGE_ID desc limit :startRow, :fetchSize 387 | 388 | ■ 설정 389 | - 생성자를 이용해서 DataSource를 전달 받음. 390 | 391 | ```xml 392 | 394 | 395 | 396 | 397 | 398 | ``` 399 | 400 | ■ 메서드 401 | - JdbcTemplate 과 비슷한 메서드 제공. 402 | - Object[] 대신에 Map을 전달 받음. 403 | ○ 종류 404 | - public List query(String sql, Map paramMap, RowMapper rowMapper) 405 | - public Object queryForObject(String sql, Map paramMap, RowMapper rowMapper) 406 | - public int queryForInt(String sql, Map paramMap) 407 | - public List queryForList(String sql, Map paramMap) 408 | - public int update(String sql, Map paramMap) 409 | ○ paramMap 파라미터 410 | - 이름 기반의 파라미터에 삽입될 값을 설정하기 위한 Map. 411 | - 쿼리에서 사용되는 이름과 값을 저장. 412 | ○ 사용 예 413 | 414 | ```java 415 | private static final String SELECT_LIST_SQL = "select * from GUESTBOOK_MESSAGE " + 416 | "order by GUESTBOOK_MESSAGE_ID desc limit :startRow, :fetchSize"; 417 | 418 | @SuppressWarnings("unchecked") 419 | @Override 420 | public List selectList(int page, int pageSize) { 421 | int startRow = (page - 1) * pageSize; 422 | Map params = new HashMap(); 423 | params.put("startRow", startRow); 424 | params.put("fetchSize", pageSize); 425 | 426 | List list = jdbcTemplate.query(SELECT_LIST_SQL, params, 427 | new RowMapper() { 428 | @Override 429 | public Object mapRow(ResultSet rs, int rowNum) throws SQLException { 430 | Message message = new Message(); 431 | message.setId(rs.getInt("GUESTBOOK_MESSAGE_ID")); 432 | message.setGuestName(rs.getString("GUEST_NAME")); 433 | message.setContent(rs.getString("CONTENT")); 434 | 435 | return message; 436 | } 437 | } 438 | ); 439 | 440 | return list; 441 | } 442 | 443 | ``` 444 | 445 | ○ 이름 기반의 파라미터를 갖지 않는 쿼리를 실행하는 경우 446 | - 아무 값도 갖지 않는 Map 객체를 사용. 447 | 448 | ```java 449 | private String SELECT_COUNT_SQL = "select count(*) form GUESTBOOK_MESSAGE"; 450 | 451 | @SuppressWarnings("unchecked") 452 | @Override 453 | public int selectCount() { 454 | Map paramMap = Collections.emtpyMap(); 455 | 456 | return jdbcTemplate.queryForInt(SELECT_COUNT_SQL, paramMap); 457 | } 458 | ``` 459 | 460 | □ SimpleJdbcTemplate 클래스를 이용한 JDBC 프로그래밍 461 | ■ SimpleJdbcTemplate 클래스 462 | - NamedParameterJdbcTemplate이 제공하는 기능을 제공하면서 더불어 JdbcTemplate과 비슷한 기능을 제공. 463 | ■ 메서드 464 | - Map을 전달받는 메서드와 가변 인자를 전달받는 메서드를 제공. 465 | ○ public List query(String sql, ParameterizedRowMapper rm, Map args) 466 | - 이름 기반의 파라미터를 사용하는 쿼리를 실행할 때 사용. 467 | ○ public List query(String sql, ParameterizedRowMapper rm, Object... args) 468 | - 일반 JDBC와 마찬가지로 인덱스 기반의 파라미터를 사용하는 쿼리를 실행할 때 사용. 469 | - 각 물음표에 삽입될 값은 Object 배열이 아닌 가변 인자를 통해서 입력하기 때문에 코드 작성이 좀더 간단해짐. 470 | ■ 사용 예 471 | 472 | ```java 473 | 474 | private static final String SELECT_LIST_SQL = "select * from GUECTBOOK_MESSAGE " + 475 | "order by GUESTBOOK_MESSAGE_ID desc limit ?,?"; 476 | 477 | @Override 478 | public List selectList(int page, int pageSize) { 479 | int startRow = (page - 1) * pageSize; 480 | List list = jdbcTemplate.query(SELECT_LIST_SQL, 481 | new ParameterizedRowMapper() { 482 | @Override 483 | public Message mapRow(ResultSet rs, int rowNum) throws SQLException { 484 | Message message = new Message(); 485 | message.setId(rs.getInt("GUESTBOOK_MESSAGE_ID")); 486 | message.setGuestName(rs.getString("GUEST_NAME")); 487 | message.setContent(rs.getString("CONTENT")); 488 | 489 | return message; 490 | } 491 | }, 492 | startRow, pageSize // 가변 인수로 파라미터 값 지정. 493 | ); 494 | 495 | return list; 496 | } 497 | ``` 498 | 499 | ■ 설정 500 | - 생성자를 통해서 DataSource를 설정. 501 | 502 | ```xml 503 | 505 | 506 | 507 | 508 | 509 | ``` 510 | 511 | □ DaoSupport 클래스를 이용한 JDBC 기반의 DAO 구현 512 | ■ DaoSupport 클래스 513 | - 스프링 DAO 클래스를 구현할 때 상위 클래스로 사용. 514 | - 각각의 템플릿 클래스 별로 존재. 515 | ■ JDBC 기반의 DAO 클래스 구현 516 | ○ 종류 517 | ● JdbcDaoSupport 518 | - JdbcTemplate을 지원하는 DaoSupport 클래스. 519 | ● NamedParameterJdbcDaoSupport 520 | - NamedParameterJdbcTemplate을 지원하는 DaoSupport 클래스. 521 | ● SimpleJdbcDaoSupport 522 | - SimpleJdbcTemplate을 지원하는 DaoSupport 클래스. 523 | ○ 특징 524 | - 해당하는 템플릿 클래스를 구할 수 있는 메서드를 제공. 525 | ■ 설정 526 | - DaoSource를 프로퍼티로 전달 받음. 527 | 528 | ```xml 529 | 534 | 535 | 537 | ``` 538 | 539 | - NamedParameterJdbcDaoSupport 클래스와 SimpleJdbcDaoSupport 클래스를 상속받은 클래스에서도 동일하게 dataSource 540 | 프로퍼티에 DataSource를 전달. 541 | ■ JdbcDaoSupport 클래스를 상속받은 클래스 542 | - getJdbcTemplate() 메서드를 사용해서 JdbcTemplate를 구함. 543 | 544 | ```java 545 | 546 | public class JdbcMessageDao extends JdbcDaoSupport implements MessageDao { 547 | private String SELECT_COUNT_SQL = "select count(*) form GUESTBOOK_MESSAGE"; 548 | 549 | @Override 550 | public int selectCount() { 551 | return getJdbcTemplate().queryForInt(SELECT_COUNT_SQL); 552 | } 553 | } 554 | ``` 555 | 556 | ■ NamedParameterJdbcDaoSupport 클래스를 상속받은 클래스 557 | 558 | ```java 559 | public class NamedMessageDao extends NamedParameterJdbcDaoSupport implements MessageDao { 560 | ... 561 | @Override 562 | public void insert(Message message) { 563 | Map params = new HashMap(); 564 | params.put("guestName", message.getGuestName()); 565 | params.put("content", message.getContent()); 566 | getNamedParameterJdbcTemplate().update(INSERT_SQL, params); 567 | } 568 | ... 569 | } 570 | ``` 571 | 572 | ■ SimpleJdbcDaoSupport 클래스를 상속받은 클래스 573 | 574 | ```java 575 | 576 | public class SimpleTemplateMessageDao extends SimpleJdbcDaoSupport 577 | implements MessageDao { 578 | ... 579 | @Override 580 | public void insert(Message message) { 581 | Map params = new HashMap(); 582 | params.put("guestName", message.getGuestName()); 583 | params.put("content", message.getContent()); 584 | 585 | getSimpleJdbcTemplate().update(INSERT_SQL, 586 | new Object[] {message.getGuestName(), message.getContent()} 587 | ); 588 | } 589 | ... 590 | } 591 | ``` 592 | 593 | -------------------------------------------------------------------------------- /0408_jdbc: -------------------------------------------------------------------------------- 1 | Chapter 08 데이터베이스 연동 지원과 JDBC 지원 2 | 1. 스프링의 데이터베이스 연동 지원 3 | 2. DataSource 설정 4 | 3. 스프링 JDBC 지원 5 | -------------------------------------------------------------------------------- /0415.md: -------------------------------------------------------------------------------- 1 | http://blog.naver.com/azanghs/90171232785 2 | -------------------------------------------------------------------------------- /0422/0422.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kenu/springstudy2013/42b4c5a0b3afae99a52d80b907f372a98a057c4e/0422/0422.md -------------------------------------------------------------------------------- /0422/0422_chap12.md: -------------------------------------------------------------------------------- 1 | #12장 작업실행과 스케쥴링 2 | 3 | ##차례 4 | 1. 작업실행과 스케쥴링 5 | 2. 어노테이션을 이용한 작업 실행설정 6 | 7 | ##작업 실행과 스케쥴링 8 |
9 | ###작업실행 10 | - 핵심인터페이스 : TaskExecutor 11 | - 스프링 3버전은 TaskExecutor 빈을 손쉽게 설정할 수 있는 태그제공 12 | 13 | ###과정 14 | 1. 태그를 이용한 TaskExecutor 빈 설정 15 | 2. TaskExecutor 빈 객체에 Runnable rngus rorcpfmf wjsekfgotj wkrdjqtlfgod 16 | 3. 로 생성한 빈 종료. 17 | 18 | 19 | 20 |

1. TaskExecutor 빈 생성

21 | 사용시 task네임스페이스를 등록하여야 한다. 22 | java.util.concurrent.ThreadPoolExecutor를 이용해서 작업실행 23 | 24 | ex) 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 42 | 43 | 44 | 45 | 50 | 51 | 52 | 53 | 56 | 57 | 58 | 59 | 82 | 83 |
속성설명
id생성할 빈의 식별 값 지정
pool-size 38 | 쓰레드 풀의 개수를 지정
39 | ex) 10-20, 15
40 | Default) 1-Integer.MAX_VALUE 41 |
queue-capacity 46 | 작업이 저장되는 큐의 최대크기
47 | 풀에 쓰레드가 모두 작업중인 경우 큐에서 대기
48 | Default) Integer.MAX_VALUE 49 |
keep-alive 54 | 풀에 들어있는 쓰레드의 최대 유휴 시간 55 |
rejection-policy 60 | 큐에 다차서 작업이 불가능할 때의 작업처리 지정

61 | 설정할 수 있는 값 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 |
ABORT작업을 거부하고 예외 발생
CALLER_RUNS호출한 쓰레드를 이용해서 실행
DISCARD작업을 실행하지 않고 무시
DISCARD_OLDEST큐의 헤드에서 하나를 제거하고 작업을 추가한다.
80 | 기본값 : ABORT 81 |
84 | 85 |
ThreadPoolExecutor가 풀의 크기를 관리하는 규칙 - P473 참고
86 |
87 |

2. TaskExecutor / AsyncTaskExecutor 인터페이스를 이용한 작업실행

88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 |
void execute(Runnable task)task 실행
void execute(Runnable task, long startTimeout)task를 실행, 지정한 시간 내에 실행되지 않으면 TaskRejectedException 예외 발생
Future submit(Runnable task)task 실행, Future를 통해 작업완료시 처리결과 확인가능
Future submit(Callable task)위의 작업 + return 값 확인가능
106 | 107 | **유의사항**
108 | - 위의 메서드는 비동기로 작업되므로 작업실행 여부와 관계없이 즉시 리턴한다. 109 | 110 |

3. 빈의 종료처리

111 | ThreadPoolTaskExecutor.shutdown(); 112 | 113 | 태그 이용시 TaskExecutorFactoryBean 클래스를 이용해서 ThreadPoolTaskExecutor객체를 생성
114 | 스프링 - FactoryBean에 의해 생성된 빈 객체의 라이프사이클 처리를 하지 않음
115 | 그러므로 스프링 컨테이너를 종료해도 JVM프로세스가 종료되지 않는다. 116 | 117 | 그래서 shutdown() 메서드를 호출하여 쓰레드 풀을 종료 해주어야 한다. 118 | ( showdown() - 내부적으로 쓰레드풀을 종료하며, 이때 관련된 쓰레드도 함께 종료된다.) 119 |


120 | **shutdown을 사용하지 않고 자동으로 종료하는 방법** 121 | 직접 ThreadPoolTaskExecutor 클래스를 사용해서 빈을 생성하시오… 122 | -------------------------------------------------------------------------------- /0422/0422_chap12_2.md: -------------------------------------------------------------------------------- 1 | ### 를 이용한 스케줄러 사용 2 | 스프링 3 버전에 스케쥴링을 위한 TaskScheduler 인터페이스 추가됨 3 | 4 | ####과정 5 | 1. 를 이용한 스케줄러 생성
6 | - 다음과 같이 생성 7 | - pool-size의 기본값 : 1 8 | 9 | 2. TaskScheduler 인터페이스를 이용한 작업 스케줄링 (메서드 : P479참조) 10 | - TaskScheduler 인터페이스가 제공하는 메서드를 이용해서 작업실행을 스케줄링 가능 11 | - 모든메서드 리턴타입 : ScheduledFuture 12 | 13 | 14 | ### 를 이용한 작업스케쥴링 15 | 16 | 17 | 18 | 19 | 태그는 한 개 이상의 를 가질 수 있으며 태그는 스케줄러를 통해서 실행될 작업을 설정한다. 20 | 21 | 속성 22 | 1. scheduler : 작업을 실행할 스케줄러 빈을 설정 23 | 2. ref : 실행할 빈 객체 24 | 3. method : 실행할 메서드 25 | 26 | 태그의 작업실행 관련 속성 ( **반드시 하나의 속성을 사용** ) 27 | 1. cron : cron표현식을 이용해서 실행시간 표현 28 | 2. fixed-delay : 지정된 시간 간격으로 작업을 실행 29 | 3. fixed-rate : 지정한 시간 주기로 작업을 실행 30 | ###어노테이션을 이용한 작업 실행 설정 31 | ** 밑의 두 어노테이션을 사용하려면 태그를 이용해 작업을 실행할 TaskExecutor와 TaskScheduler를 지정해주어야 한다. 32 | ex) 4 | MailSender 와 JavaMailSender를 이용한 메일 발송 5 | MimeMessageHelper를 이용한 파일 첨부 6 | 7 | MailSender 인터페이스를 제공 8 | 9 | ```java 10 | public interface MailSender { 11 | 12 | /** 13 | * Send the given simple mail message. 14 | * @param simpleMessage message to send 15 | * @throws MailException in case of message, authentication, or send errors 16 | */ 17 | public void send(SimpleMailMessage simpleMessage) throws MailException; 18 | 19 | /** 20 | * Send the given array of simple mail messages in batch. 21 | * @param simpleMessages messages to send 22 | * @throws MailException in case of message, authentication, or send errors 23 | */ 24 | public void send(SimpleMailMessage[] simpleMessages) throws MailException; 25 | 26 | } 27 | ``` 28 | 29 | 30 | JavaMailSender: 31 | ```java 32 | public interface JavaMailSender extends MailSender { 33 | 34 | /** 35 | * Create a new JavaMail MimeMessage for the underlying JavaMail Session 36 | * of this sender. Needs to be called to create MimeMessage instances 37 | * that can be prepared by the client and passed to send(MimeMessage). 38 | * @return the new MimeMessage instance 39 | * @see #send(MimeMessage) 40 | * @see #send(MimeMessage[]) 41 | */ 42 | public MimeMessage createMimeMessage(); 43 | 44 | /** 45 | * Send the given JavaMail MIME message. 46 | * The message needs to have been created with createMimeMessage. 47 | * @param mimeMessage message to send 48 | * @throws MailException in case of message, authentication, or send errors 49 | * @see #createMimeMessage 50 | */ 51 | public void send(MimeMessage mimeMessage) throws MailException; 52 | 53 | /** 54 | * Send the given array of JavaMail MIME messages in batch. 55 | * The messages need to have been created with createMimeMessage. 56 | * @param mimeMessages messages to send 57 | * @throws MailException in case of message, authentication, or send errors 58 | * @see #createMimeMessage 59 | */ 60 | public void send(MimeMessage[] mimeMessages) throws MailException; 61 | 62 | /** 63 | * Send the JavaMail MIME message prepared by the given MimeMessagePreparator. 64 | * Alternative way to prepare MimeMessage instances, instead of createMimeMessage 65 | * and send(MimeMessage) calls. Takes care of proper exception conversion. 66 | * @param mimeMessagePreparator the preparator to use 67 | * @throws MailException in case of message, authentication, or send errors 68 | */ 69 | public void send(MimeMessagePreparator mimeMessagePreparator) throws MailException; 70 | 71 | /** 72 | * Send the JavaMail MIME messages prepared by the given MimeMessagePreparators. 73 | * Alternative way to prepare MimeMessage instances, instead of createMimeMessage 74 | * and send(MimeMessage[]) calls. Takes care of proper exception conversion. 75 | * @param mimeMessagePreparators the preparator to use 76 | * @throws MailException in case of message, authentication, or send errors 77 | */ 78 | public void send(MimeMessagePreparator[] mimeMessagePreparators) throws MailException; 79 | 80 | } 81 | 82 | ``` 83 | 84 | Basic MailSender and SimpleMailMessage usage 85 | ```java 86 | import org.springframework.mail.MailException; 87 | import org.springframework.mail.MailSender; 88 | import org.springframework.mail.SimpleMailMessage; 89 | 90 | public class SimpleOrderManager implements OrderManager { 91 | 92 | private MailSender mailSender; 93 | private SimpleMailMessage templateMessage; 94 | 95 | public void setMailSender(MailSender mailSender) { 96 | this.mailSender = mailSender; 97 | } 98 | 99 | public void setTemplateMessage(SimpleMailMessage templateMessage) { 100 | this.templateMessage = templateMessage; 101 | } 102 | 103 | public void placeOrder(Order order) { 104 | 105 | // Do the business calculations... 106 | 107 | // Call the collaborators to persist the order... 108 | 109 | // Create a thread safe "copy" of the template message and customize it 110 | SimpleMailMessage msg = new SimpleMailMessage(this.templateMessage); 111 | msg.setTo(order.getCustomer().getEmailAddress()); 112 | msg.setText( 113 | "Dear " + order.getCustomer().getFirstName() 114 | + order.getCustomer().getLastName() 115 | + ", thank you for placing order. Your order number is " 116 | + order.getOrderNumber()); 117 | try{ 118 | this.mailSender.send(msg); 119 | } 120 | catch(MailException ex) { 121 | // simply log it and go on... 122 | System.err.println(ex.getMessage()); 123 | } 124 | } 125 | } 126 | ``` 127 | 128 | ```xml 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | ``` 145 | ```java 146 | Using the JavaMailSender and the MimeMessagePreparator 147 | 148 | import javax.mail.Message; 149 | import javax.mail.MessagingException; 150 | import javax.mail.internet.InternetAddress; 151 | import javax.mail.internet.MimeMessage; 152 | 153 | import javax.mail.internet.MimeMessage; 154 | import org.springframework.mail.MailException; 155 | import org.springframework.mail.javamail.JavaMailSender; 156 | import org.springframework.mail.javamail.MimeMessagePreparator; 157 | 158 | public class SimpleOrderManager implements OrderManager { 159 | 160 | private JavaMailSender mailSender; 161 | 162 | public void setMailSender(JavaMailSender mailSender) { 163 | this.mailSender = mailSender; 164 | } 165 | 166 | public void placeOrder(final Order order) { 167 | 168 | // Do the business calculations... 169 | 170 | // Call the collaborators to persist the order... 171 | 172 | MimeMessagePreparator preparator = new MimeMessagePreparator() { 173 | 174 | public void prepare(MimeMessage mimeMessage) throws Exception { 175 | 176 | mimeMessage.setRecipient(Message.RecipientType.TO, 177 | new InternetAddress(order.getCustomer().getEmailAddress())); 178 | mimeMessage.setFrom(new InternetAddress("mail@mycompany.com")); 179 | mimeMessage.setText( 180 | "Dear " + order.getCustomer().getFirstName() + " " 181 | + order.getCustomer().getLastName() 182 | + ", thank you for placing order. Your order number is " 183 | + order.getOrderNumber()); 184 | } 185 | }; 186 | try { 187 | this.mailSender.send(preparator); 188 | } 189 | catch (MailException ex) { 190 | // simply log it and go on... 191 | System.err.println(ex.getMessage()); 192 | } 193 | } 194 | } 195 | 196 | ``` 197 | 198 | 199 | Using the JavaMail MimeMessageHelper 200 | 201 | ```java 202 | // of course you would use DI in any real-world cases 203 | JavaMailSenderImpl sender = new JavaMailSenderImpl(); 204 | sender.setHost("mail.host.com"); 205 | 206 | MimeMessage message = sender.createMimeMessage(); 207 | MimeMessageHelper helper = new MimeMessageHelper(message); 208 | helper.setTo("test@host.com"); 209 | helper.setText("Thank you for ordering!"); 210 | 211 | sender.send(message); 212 | 213 | ``` 214 | 215 | Sending attachments and inline resources 216 | 217 | ```java 218 | // of course you would use DI in any real-world cases 219 | JavaMailSenderImpl sender = new JavaMailSenderImpl(); 220 | sender.setHost("mail.host.com"); 221 | 222 | MimeMessage message = sender.createMimeMessage(); 223 | MimeMessageHelper helper = new MimeMessageHelper(message); 224 | helper.setTo("test@host.com"); 225 | helper.setText("Thank you for ordering!"); 226 | 227 | sender.send(message); 228 | ``` 229 | Sending attachments and inline resources 230 | 231 | 232 | Attachments 233 | 234 | ```java 235 | 236 | JavaMailSenderImpl sender = new JavaMailSenderImpl(); 237 | sender.setHost("mail.host.com"); 238 | 239 | MimeMessage message = sender.createMimeMessage(); 240 | 241 | // use the true flag to indicate you need a multipart message 242 | MimeMessageHelper helper = new MimeMessageHelper(message, true); 243 | helper.setTo("test@host.com"); 244 | 245 | helper.setText("Check out this image!"); 246 | 247 | // let's attach the infamous windows Sample file (this time copied to c:/) 248 | FileSystemResource file = new FileSystemResource(new File("c:/Sample.jpg")); 249 | helper.addAttachment("CoolImage.jpg", file); 250 | 251 | sender.send(message); 252 | ``` 253 | 254 | A Velocity-based example 255 | 256 | ```html 257 | # in the com/foo/package 258 | 259 | 260 |

Hi ${user.userName}, welcome to the Chipping Sodbury On-the-Hill message boards!

261 | 262 |
263 | Your email address is ${user.emailAddress}. 264 |
265 | 266 | 267 | 268 | ``` 269 | 270 | ```java 271 | package com.foo; 272 | 273 | import org.apache.velocity.app.VelocityEngine; 274 | import org.springframework.mail.javamail.JavaMailSender; 275 | import org.springframework.mail.javamail.MimeMessageHelper; 276 | import org.springframework.mail.javamail.MimeMessagePreparator; 277 | import org.springframework.ui.velocity.VelocityEngineUtils; 278 | 279 | import javax.mail.internet.MimeMessage; 280 | import java.util.HashMap; 281 | import java.util.Map; 282 | 283 | public class SimpleRegistrationService implements RegistrationService { 284 | 285 | private JavaMailSender mailSender; 286 | private VelocityEngine velocityEngine; 287 | 288 | public void setMailSender(JavaMailSender mailSender) { 289 | this.mailSender = mailSender; 290 | } 291 | 292 | public void setVelocityEngine(VelocityEngine velocityEngine) { 293 | this.velocityEngine = velocityEngine; 294 | } 295 | 296 | public void register(User user) { 297 | 298 | // Do the registration logic... 299 | 300 | sendConfirmationEmail(user); 301 | } 302 | 303 | private void sendConfirmationEmail(final User user) { 304 | MimeMessagePreparator preparator = new MimeMessagePreparator() { 305 | public void prepare(MimeMessage mimeMessage) throws Exception { 306 | MimeMessageHelper message = new MimeMessageHelper(mimeMessage); 307 | message.setTo(user.getEmailAddress()); 308 | message.setFrom("webmaster@csonth.gov.uk"); // could be parameterized... 309 | Map model = new HashMap(); 310 | model.put("user", user); 311 | String text = VelocityEngineUtils.mergeTemplateIntoString( 312 | velocityEngine, "com/dns/registration-confirmation.vm", model); 313 | message.setText(text, true); 314 | } 315 | }; 316 | this.mailSender.send(preparator); 317 | } 318 | } 319 | 320 | ``` 321 | 322 | ```xml 323 | 324 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | resource.loader=class 342 | class.resource.loader.class=org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader 343 | 344 | 345 | 346 | 347 | 348 | ``` 349 | 350 | -------------------------------------------------------------------------------- /0429/SpringJms.md: -------------------------------------------------------------------------------- 1 | 1. What is Message ? - Message란 무엇이고 왜 사용 하는가 2 | 2. JMS Architecture - JMS의 전반적인 아키텍쳐와 사용 모델 3 | 3. JMS Element - JMS에서 사용하는 엘리먼트들에 대한 설명 4 | 5 | 6 | ##1. What is Message? - Message란 무엇이고 왜 사용 하는가 7 | 8 | 메시지은 느슨하게 결합된 분산 통신의 한 형태이다. 9 | 여기서 통신이란 용어는 소프트웨어 컴포넌트들 간의 구성요소들 사이에 메시지의 교환으로 이해할 수 있다. 10 | 메시지 지향 테크놀로지들은 강하게 결합된 통신(TCP network socekst, CORBA or RMI 같은)을 중간요소의 11 | 도입을 통해 완하하려고 시도한다. 후자의 접근 방식을 통해 소프트웨어 컴포넌트가 서로 '간접적으로' 통신 할 수 있다. 12 | 이러한 메시지의 장점은 보내는 쪽에서 받는 쪽에 대한 정확한 정보가 없어도 가능 하다는 점이다. 13 | 14 | 15 | 16 | ![screenshot](http://cfile25.uf.tistory.com/image/171199524E0AB5AA2DC047) 17 | ####안좋은 예 18 | 19 | 간단한 메시징 시스템의 예로 콜센터를 들 수 있다. 20 | 고객과 콜센터 직원이 라인을 통해 다이렉트로 통화를 할 경우, 21 | 고객이 늘어날 경우에 콜센터 직원이 통화가 가능 할 때까지 22 | 계속 다시 전화를 걸어서 통화가 가능한 라인을 찾아야 한다. 23 | 24 | ![screenshot](http://cfile27.uf.tistory.com/image/17435D544E0AB64C0B118B) 25 | ####기존에 시스템에 경유할수 있는 경유지를 두는 경우 26 | 27 | 위 그림처럼 중간에 경유할 수 있는 Voice Mail Box 같은 장치를 두면, 28 | 고객의 요청은 box에 쌓이게 되고 직원은 이것을 확인하고 처리 하면 되기 29 | 때문에 고객은 계속 전화를 걸어 통화가 가능한 라인이 있는지 확인할 필요 없이 처리 할 수 있다. 30 | 31 | JMS는 응용프로그램 개발자가 같은 API로 다른 시스템들에 사용할 수 있는 JDBC와 유사 하다. 32 | 만약 JMS를 준수하여 서비스를 할 경우, JMS API를 통해 공급자가 해당 업체에 메시지를 주고 받을 수 있다. 33 | 예를 들어 SonicMQ를 사용하여 메시지를 보내고 IBM WebSphere MQ를 통해 메시지를 처리 할 수 있다. 34 | 35 | ##Spring Jms 36 | 37 | http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/jms.html 38 | 39 | 40 | ```java 41 | import javax.jms.ConnectionFactory; 42 | import javax.jms.JMSException; 43 | import javax.jms.Message; 44 | import javax.jms.Queue; 45 | import javax.jms.Session; 46 | 47 | import org.springframework.jms.core.MessageCreator; 48 | import org.springframework.jms.core.JmsTemplate; 49 | 50 | public class JmsQueueSender { 51 | 52 | private JmsTemplate jmsTemplate; 53 | private Queue queue; 54 | 55 | public void setConnectionFactory(ConnectionFactory cf) { 56 | this.jmsTemplate = new JmsTemplate(cf); 57 | } 58 | 59 | public void setQueue(Queue queue) { 60 | this.queue = queue; 61 | } 62 | 63 | public void simpleSend() { 64 | this.jmsTemplate.send(this.queue, new MessageCreator() { 65 | public Message createMessage(Session session) throws JMSException { 66 | return session.createTextMessage("hello queue world"); 67 | } 68 | }); 69 | } 70 | } 71 | 72 | ``` 73 | 스프링이 제고하는 JmsTemplate클래스를 사용할려면 2가지를 설정해야 함 74 | 75 | ###JMS 서버와의 연결을 제공하는 ConnectionFactory 76 | ###메시지의 목저지 설정 (Queue또는topic) 77 | 78 | Javax.jms.ConnectionFactory를 설정해야함 79 | 80 | activeMQ를 메시지 브로커로 사용시 81 | 82 | ActiveMQConnectionFactory 클래스를 사용해서 ConnectionFacotry를 설정한다 83 | 84 | 85 | ```xml 86 | 87 | 88 | tcp://localhost:61616 89 | 90 | 91 | 92 | ``` 93 | 94 | 메시지 목적지 설정 95 | 96 | ```xml 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | ``` 107 | 108 | #JmsTemplate이용한 메시지 송수신 109 | 110 | 1.JMS이용할때 자원이 처리를 위해 반복되는 코드를 제거 111 | 2.JMS연동 과정에서 발생하는 javax.jms.JMSException 타입및 하위 타입의 예외로 알맞게 제공해 주는 기능도 제공 112 | 3.불필요한 try-catch 제거 113 | 114 | ```xml 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | ``` 130 | jms를 이용해서 메시지를 송신하거나 수신할 빈은 JmsTemplate과 큐나 토픽 목적지를 설정한 빈을 전달 받아 사용하면 된다 131 | 132 | 133 | ## JMS message보내기 134 | 135 | send() 메서드는 메시지를 전송할 목적지를 의미하는 Destination 객체와 136 | MessageCreator()메서드를 이용해서 전송할 message객체를 생성한다. 137 | 138 | 139 | ```java 140 | package madvirus.spring.chap14.domain; 141 | 142 | import javax.jms.Destination; 143 | import javax.jms.JMSException; 144 | import javax.jms.MapMessage; 145 | import javax.jms.Message; 146 | import javax.jms.Session; 147 | 148 | import org.springframework.jms.core.JmsTemplate; 149 | import org.springframework.jms.core.MessageCreator; 150 | 151 | public class ArticleMessageSender { 152 | 153 | private JmsTemplate jmsTemplate; 154 | private Destination destination; 155 | 156 | public void setJmsTemplate(JmsTemplate jmsTemplate) { 157 | this.jmsTemplate = jmsTemplate; 158 | } 159 | 160 | public void setDestination(Destination destination) { 161 | this.destination = destination; 162 | System.out.println("목적지:"+destination); 163 | } 164 | 165 | public void sendMessage(final Article article) { 166 | jmsTemplate.send(destination, new MessageCreator() { 167 | 168 | //Message Creator 169 | 170 | @Override 171 | 172 | public Message createMessage(Session session) throws JMSException { 173 | MapMessage mapMessage = session.createMapMessage(); 174 | mapMessage.setString("subject", article.getSubject()); 175 | 176 | return mapMessage; 177 | //javax.jms.Message객체를 리턴 178 | } 179 | }); 180 | // jmsTemplate.convertAndSend(destination, article); 181 | } 182 | 183 | } 184 | 185 | ``` 186 | 187 | JmsTemplate.send()메서드는 createMessage()메서드가 리턴한 message객체를 목적지에 전송한다. 188 | messageCreator 인터페이스 구현객체의 createMessage()메서드는 메시지를 전송하기 189 | 위한 알맞은 타입의 javax.jms.Message객체를 리턴하면 된다. 190 | 191 | 192 | ##JMS message 받기 193 | 194 | ```java 195 | package madvirus.spring.chap14.domain; 196 | 197 | import javax.jms.Destination; 198 | import javax.jms.JMSException; 199 | import javax.jms.MapMessage; 200 | 201 | import org.springframework.jms.core.JmsTemplate; 202 | import org.springframework.jms.support.JmsUtils; 203 | 204 | public class ArticleMessageReceiver { 205 | 206 | private JmsTemplate jmsTemplate; 207 | private Destination destination; 208 | 209 | public void setJmsTemplate(JmsTemplate jmsTemplate) { 210 | this.jmsTemplate = jmsTemplate; 211 | } 212 | 213 | public void setDestination(Destination destination) { 214 | this.destination = destination; 215 | } 216 | 217 | public Article receive() { 218 | MapMessage mapMessage = (MapMessage) jmsTemplate.receive(destination); 219 | //receive 메서드에 메시지를 전달 받을 목적지를 지정 220 | Article article = new Article(); 221 | try { 222 | article.setSubject(mapMessage.getString("subject")); 223 | System.out.println(mapMessage.getString("subject")); 224 | return article; 225 | //해당 목적지의로 부터 message 를 리턴 226 | } catch (JMSException e) { 227 | throw JmsUtils.convertJmsAccessException(e); 228 | } 229 | // Article article = (Article)jmsTemplate.receiveAndConvert(destination); 230 | // return article; 231 | } 232 | } 233 | 234 | ``` 235 | JmsTemplate의 기본 목적지 설정 236 | 237 | JmsTemplate 클래스의 send()메서드와 recive()메서드를 호출할때 목적지를 정정하지 않으면 defaultDestnition프로퍼티로 설정한다. 238 | ```xml 239 | 240 | 241 | 242 | 243 | 245 | 246 | ``` 247 | 248 | 위와 같이 설정시 249 | 250 | 목적지 지정하지 않고 rend() receive() 가능 251 | 252 | ```java 253 | jmsTemplate.send(destination, new MessageCreator() { 254 | @Override 255 | ..... 256 | } 257 | 258 | jmsTemplate.send(new MessageCreator() { 259 | @Override 260 | ..... 261 | } 262 | 263 | 264 | MapMessage mapMessage = (MapMessage) jmsTemplate.receive(destination); 265 | MapMessage mapMessage = (MapMessage) jmsTemplate.receive(); 266 | ``` 267 | 268 | 269 | webshpere 270 | 271 | http://www.ibm.com/developerworks/java/library/wa-spring4/index.html 272 | 273 | IBM WebSphere MQ 관련 자료 274 | 275 | http://windowx.tistory.com/entry/RabbitMQ-Spring-AMQP-setup-and-tester-example 276 | 277 | RabbitMq 관련 자료 278 | 279 | ```java 280 | 281 | package madvirus.spring.chap14.domain; 282 | 283 | import org.apache.activemq.ActiveMQConnectionFactory; 284 | 285 | import javax.jms.Connection; 286 | import javax.jms.DeliveryMode; 287 | import javax.jms.Destination; 288 | import javax.jms.ExceptionListener; 289 | import javax.jms.JMSException; 290 | import javax.jms.Message; 291 | import javax.jms.MessageConsumer; 292 | import javax.jms.MessageProducer; 293 | import javax.jms.Session; 294 | import javax.jms.TextMessage; 295 | 296 | /** 297 | * Hello world! 298 | */ 299 | public class App { 300 | 301 | public static void main(String[] args) throws Exception { 302 | thread(new HelloWorldProducer(), false); 303 | thread(new HelloWorldProducer(), false); 304 | thread(new HelloWorldConsumer(), false); 305 | Thread.sleep(1000); 306 | thread(new HelloWorldConsumer(), false); 307 | thread(new HelloWorldProducer(), false); 308 | thread(new HelloWorldConsumer(), false); 309 | thread(new HelloWorldProducer(), false); 310 | Thread.sleep(2000); 311 | thread(new HelloWorldConsumer(), false); 312 | thread(new HelloWorldProducer(), false); 313 | thread(new HelloWorldConsumer(), false); 314 | thread(new HelloWorldConsumer(), false); 315 | thread(new HelloWorldProducer(), false); 316 | thread(new HelloWorldProducer(), false); 317 | Thread.sleep(3000); 318 | thread(new HelloWorldProducer(), false); 319 | thread(new HelloWorldConsumer(), false); 320 | thread(new HelloWorldConsumer(), false); 321 | thread(new HelloWorldProducer(), false); 322 | thread(new HelloWorldConsumer(), false); 323 | thread(new HelloWorldProducer(), false); 324 | thread(new HelloWorldConsumer(), false); 325 | thread(new HelloWorldProducer(), false); 326 | thread(new HelloWorldConsumer(), false); 327 | thread(new HelloWorldConsumer(), false); 328 | thread(new HelloWorldProducer(), false); 329 | } 330 | 331 | public static void thread(Runnable runnable, boolean daemon) { 332 | Thread brokerThread = new Thread(runnable); 333 | brokerThread.setDaemon(daemon); 334 | brokerThread.start(); 335 | } 336 | 337 | public static class HelloWorldProducer implements Runnable { 338 | public void run() { 339 | try { 340 | // Create a ConnectionFactory 341 | ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://localhost:61616"); 342 | 343 | // Create a Connection 344 | Connection connection = connectionFactory.createConnection(); 345 | connection.start(); 346 | 347 | // Create a Session 348 | Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); 349 | 350 | // Create the destination (Topic or Queue) 351 | Destination destination = session.createQueue("TEST.FOO"); 352 | 353 | // Create a MessageProducer from the Session to the Topic or Queue 354 | MessageProducer producer = session.createProducer(destination); 355 | producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT); 356 | 357 | // Create a messages 358 | String text = "Hello world! From: " + Thread.currentThread().getName() + " : " + this.hashCode(); 359 | TextMessage message = session.createTextMessage(text); 360 | 361 | // Tell the producer to send the message 362 | System.out.println("Sent message: "+ message.hashCode() + " : " + Thread.currentThread().getName()); 363 | producer.send(message); 364 | 365 | // Clean up 366 | session.close(); 367 | connection.close(); 368 | } 369 | catch (Exception e) { 370 | System.out.println("Caught: " + e); 371 | e.printStackTrace(); 372 | } 373 | } 374 | } 375 | 376 | public static class HelloWorldConsumer implements Runnable, ExceptionListener { 377 | public void run() { 378 | try { 379 | 380 | // Create a ConnectionFactory 381 | ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://localhost:61616"); 382 | 383 | // Create a Connection 384 | Connection connection = connectionFactory.createConnection(); 385 | connection.start(); 386 | 387 | connection.setExceptionListener(this); 388 | 389 | // Create a Session 390 | Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); 391 | 392 | // Create the destination (Topic or Queue) 393 | Destination destination = session.createQueue("TEST.FOO"); 394 | 395 | // Create a MessageConsumer from the Session to the Topic or Queue 396 | MessageConsumer consumer = session.createConsumer(destination); 397 | 398 | // Wait for a message 399 | Message message = consumer.receive(1000); 400 | 401 | if (message instanceof TextMessage) { 402 | TextMessage textMessage = (TextMessage) message; 403 | String text = textMessage.getText(); 404 | System.out.println("Received: " + text); 405 | } else { 406 | System.out.println("Received: " + message); 407 | } 408 | 409 | consumer.close(); 410 | session.close(); 411 | connection.close(); 412 | } catch (Exception e) { 413 | System.out.println("Caught: " + e); 414 | e.printStackTrace(); 415 | } 416 | } 417 | 418 | public synchronized void onException(JMSException ex) { 419 | System.out.println("JMS Exception occured. Shutting down client."); 420 | } 421 | } 422 | } 423 | 424 | 425 | ``` 426 | 427 | jms activeMq 테스트 428 | -------------------------------------------------------------------------------- /0506/0506.md: -------------------------------------------------------------------------------- 1 | # 15 JMX 연동 2 | 3 | ## MBean 등록 4 | 5 | ![MBean](imgs/jmx-example.png 'jmx-example') 6 | 7 | ```java 8 | /************************************************************************* 9 | * Compilation: javac HelloWorld.java 10 | * Execution: java HelloWorld 11 | * 12 | * Prints "Hello, World". By tradition, this is everyone's first program. 13 | * 14 | * % java HelloWorld 15 | * Hello, World 16 | * 17 | * These 17 lines of text are comments. They are not part of the program; 18 | * they serve to remind us about its properties. The first two lines tell 19 | * us what to type to compile and test the program. The next line describes 20 | * the purpose of the program. The next few lines give a sample execution 21 | * of the program and the resulting output. We will always include such 22 | * lines in our programs and encourage you to do the same. 23 | * 24 | *************************************************************************/ 25 | 26 | public class HelloWorld { 27 | 28 | public static void main(String[] args) { 29 | System.out.println("Hello, World"); 30 | } 31 | 32 | } 33 | ``` 34 | -------------------------------------------------------------------------------- /0506/SpringJms.md: -------------------------------------------------------------------------------- 1 | Spring Jms 2 | 3 | http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/jms.html 4 | 5 | ```java 6 | import javax.jms.ConnectionFactory; 7 | import javax.jms.JMSException; 8 | import javax.jms.Message; 9 | import javax.jms.Queue; 10 | import javax.jms.Session; 11 | 12 | import org.springframework.jms.core.MessageCreator; 13 | import org.springframework.jms.core.JmsTemplate; 14 | 15 | public class JmsQueueSender { 16 | 17 | private JmsTemplate jmsTemplate; 18 | private Queue queue; 19 | 20 | public void setConnectionFactory(ConnectionFactory cf) { 21 | this.jmsTemplate = new JmsTemplate(cf); 22 | } 23 | 24 | public void setQueue(Queue queue) { 25 | this.queue = queue; 26 | } 27 | 28 | public void simpleSend() { 29 | this.jmsTemplate.send(this.queue, new MessageCreator() { 30 | public Message createMessage(Session session) throws JMSException { 31 | return session.createTextMessage("hello queue world"); 32 | } 33 | }); 34 | } 35 | } 36 | 37 | ``` 38 | 39 | 40 | -------------------------------------------------------------------------------- /0506/imgs/jmx-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kenu/springstudy2013/42b4c5a0b3afae99a52d80b907f372a98a057c4e/0506/imgs/jmx-example.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | springstudy2013 2 | =============== 3 | --------------------------------------------------------------------------------