Category Archives: SpringFramwork
스프링 프레임워크 위키 – http://ko-wikipedia-orgwiki스프링_프레임워크
스프링 주요 어노테이션 정리
이름 | 설명 |
---|---|
@Controller | 해당 클래스가 Controller임을 나타내기 위한 어노테이션 |
@RequestMapping | 요청에 대해 어떤 Controller, 어떤 메소드가 처리할지를 맵핑하기 위한 어노테이션 |
@RequestParam | Controller 메소드의 파라미터와 웹요청 파라미터와 맵핑하기 위한 어노테이션 |
@ModelAttribute | Controller 메소드의 파라미터나 리턴값을 Model 객체와 바인딩하기 위한 어노테이션 |
@SessionAttributes | Model 객체를 세션에 저장하고 사용하기 위한 어노테이션 |
@RequestPart | Multipart 요청의 경우, 웹요청 파라미터와 맵핑가능한 어노테이션(egov 3.0, Spring 3.1.x부터 추가) |
@CommandMap | Controller메소드의 파라미터를 Map형태로 받을 때 웹요청 파라미터와 맵핑하기 위한 어노테이션(egov 3.0부터 추가) |
@ControllerAdvice | Controller를 보조하는 어노테이션으로 Controller에서 쓰이는 공통기능들을 모듈화하여 전역으로 쓰기 위한 어노테이션(egov 3.0, Spring 3.2.X부터 추가) |
스프링 싱글톤 설명
스프링은 왜 싱글톤으로 빈을 만드는가??
에러 org.springframework.jdbc.BadSqlGrammarException
org.springframework.jdbc.BadSqlGrammarException
칼럼명을 잘못 기술 하였을 경우 나타나는 에러
삽질의 끝은 어딜까요…흐흑 ㅠㅠ
스프링 어노테이션 annotation 정리
관련 문서
목차
- @Component
- @Required
- @Autowired
- @Qualifier
- @Resource
- @Scope
- @PostConstruct
- @PreDestroy
- @Inject
- @Service
- @Repository
- @Controller
- @RequestMapping
- @RequestParam
- @SessionAttributes
- @InitBinder
- @ModelAttribute
- @RequestBody
- @ResponseBody
- @PathVariable
개발단계에서 사용빈도가 높은 어노테이션 위주로 정리.
목차에 없는 항목은 API 문서를 참고할 것. 구글링하는게속편한건함정
@Component
패키지: org.springframework.stereotype
버전: spring 2.5
설정 위치: 클래스 선언부 앞
<context:component-scan> 태그를 설정파일에 추가하면 해당 어노테이션이 적용된 클래스를 빈으로 등록하게 된다. 범위는 디폴트로 singleton이며 @Scope를 사용하여 지정할 수 있다.
사용하려면 XML 설정파일에 <context:component-scan>을 정의하고 적용할 기본 패키지를 base-package 속성으로 등록한다.
context:annotation-config 태그는 어노테이션과 관련해서 다음의 BeanPostProcessor를 함께 등록 한다.
- @Required(RequiedAnnotationBeanPostProcessor)
- @Autowired(AutowiredAnnotationBeanPostProcessor)
- @Resource, @PostConstruct, @PreDestory(CommonAnnotationBeanPostProcessor)
- @Configuration(ConfigurationClassPostProcessor)
- 그 외 Repository, Service, Controller 포함
예를 들어 다음처럼 설정하면:
<context:component-scan base-package=”xxx”/>
|
cs |
xxx 패키지 하위에 @Component로 선언된 클래스를 bean으로 자동 등록한다. bean의 이름은 해당 클래스명(첫글자는 소문자)이 사용된다.
<context:component-scan /> 요소에는 scoped-proxy 속성이 존재 한다. scoped-proxy는 <aop:scoped-poxy/>처럼 WebApplicationContext 에서만 유효하며 “session”, “globalSession”, “request” 이외의 scope는 무시 되며 아래의 3가지 값을 설정 할 수 있다.
- no: proxy를 생성하지 않는다.(기본값)
- interfaces: JDK Dynamic Proxy를 이용한 Proxy 생성
- targetClass: 클래스에 대해 프록시를 생성(CGLIB를 이용한 Proxy 생성)
@Component
@Scope(“prototype”) // 생략하면 싱글톤
public class Test {
…..
}
|
cs |
CGLIB
기존의 자바 클래스파일로부터 자바의 소스코드를 동적으로 생성하는 라이브러리(자바 소스 변경)
http://sourceforge.net/projects/cglib/
스캔 대상 클래스 범위 지정하기
<context:include-filter> 태그와 <context:exclude-filter> 태그를 사용하면 자동 스캔 대상에 포함시킬 클래스와 포함시키지 않을 클래스를 구체적으로 명시할 수 있다.
<context:component-scan base-package=”spring.demo” scoped-proxy=”no”>
<context:include-filter type=”regex” expression=”*HibernateRepository”/>
<context:exclude-filter type=”aspectj” expression=”..*IBatisRepository”/>
</context:component-scan>
|
cs |
위와 같이 <context:include-filter> 태그와 <context:exclude-filter> 태그는 각각 type 속성과 expresseion 속성을 갖는데, type 속성에 따라 expression 속성에 올 수 있는 값이 달라진다. type 속성에 입력가능한 값은 다음과 같다:
- annotation: 클랙스에 지정한 어노테이션이 적용됐는지의 여부. expression 속성에서는 “org.example.SomeAnnotation”와 같은 어노테이션 이름을 입력한다.
- assignable: 클래스가 지정한 타입으로 할당 가능한지의 여부. expression 속성에는 “org.exampleSomeClass” 와 같은 타입 이름을 입력한다.
- regex: 클래스 이름이 정규 표현식에 매칭되는 지의 여부. expression 속성에는 “org\.example\.Default.*” 와 같이 정규표현식을 입력한다.
- aspectj: 클래스 이름이 AspectJ 의 표현식에 매칭되는 지의 여부. expression 속성에는 “org.example..*Service+” 와 같이 AspectJ 의 표현식을 입력한다.
@Required
패키지: org.springframework.beans.factory.annotation
버전: spring 2.0
설정 위치: setter 메서드 앞
Required 어노테이션은 필수 프로퍼티임을 명시하는 것으로 필수 프로퍼티를 설정하지 않을 경우 빈 생성시 예외를 발생시킨다.
import org.springframework.beans.factory.annotation.Required
public class TestBean {
@Required
private TestDao testDao;
public void setTestDao(TestDao testDao) {
this.testDao = testDao;
}
}
|
cs |
<bean class=”org.springframework.beans.factory.annotation.RequiredAnnotationBeanpostProcessor”/>
<bean name=”testBean” class=”han.test.TestBean”>
<property name=”testDao” ref=”testDao”/>
<!– @Required 어노테이션을 적용하였으므로 설정하지 않으면 예외를 발생시킨다. –>
</bean>
|
cs |
RequiredAnnotationBeanPostProcessor 클래스는 스프링 컨테이너에 등록된 bean 객체를 조사하여 @Required 어노테이션으로 설정되어 있는 프로퍼티의 값이 설정되어 있는지 검사한다.
사용하려면 <bean class=”org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor” /> 클래스를 빈으로 등록시켜줘야 하지만 이를 대신하여 <context:annotation-config> 태그를 사용해도 된다:
<beans xmlns=”http://www.springframework.org/schema/beans”
xmlns:context=”http://www.springframework.org/schema/context”
xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”
xsi:schemaLocation=”http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd”>
<context:annotation-config/>
</beans>
|
cs |
@Autowired
패키지: org.springframework.beans.factory.annotation
버전: spring 2.5
설정 위치: 생성자, 필드, 메서드(setter메서드가 아니여도 된다) 앞
의존관계를 자동설정할 때 사용하며 타입을 이용하여 의존하는 객체를 삽입해 준다. 그러므로 해당 타입의 빈객체가 존재하지 않거나 또는 2개 이상 존재할 경우 스프링은 예외를 발생시키게 된다.
options:
- required: Autowired 어노테이션을 적용한 프로퍼티 중 반드시 설정할 필요가 없는 경우에 false값을 주어 프로퍼티가 존재하지 않더라도 스프링이 예외를 발생하지 않도록 한다. 기본값은 TRUE. ex) @Autowired(required=false)
사용하려면 <bean class=”org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor” /> 클래스를 빈으로 등록시켜줘야 한다. 해당 설정 대신에 <context:annotation-config> 태그를 사용해도 된다.
@Autowired를 적용할 때 같은 타입의 빈이 2개 이상 존재하게 되면 예외가 발생하는데, Autowired도 이러한 문제가 발생한다. 이럴 때 @Qualifier를 사용하면 동일한 타입의 빈 중 특정 빈을 사용하도록 하여 문제를 해결할 수 있다.
@Autowired
@Qualifier(“test”)
private Test test;
|
cs |
@Qualifier
패키지: org.springframework.beans.factory.annotation
버전: spring 2.5
설정 위치: @Autowired 어노테이션과 함께 사용된다.
qualifier 어노테이션은 @Autowired의 목적에서 동일 타입의 빈객체가 존재시 특정빈을 삽입할 수 있게 설정한다. @Qualifier(“mainBean”)의 형태로 @Autowired와 같이 사용하며 해당 <bean>태그에 <qualifire value=”mainBean” /> 태그를 선언해주어야 한다. 메서드에서 두개이상의 파라미터를 사용할 경우는 파라미터 앞에 선언해야한다.
options:
- name: alias명
사용하려면 동일타입의 빈객체 설정에서 <qualifier value=”[alias명]” />를 추가해 준다.
<bean id=”user2″ class=”com.sp4.UserImpl”>
<property name=”name” value=”스프링”/>
<property name=”age” value=”20″/>
<property name=”tel” value=”000-0000-0000″/>
</bean>
<bean id=”userService1″ class=”com.sp4.UserService”/>
|
cs |
public class UserService {
@Autowired
@Qualifier(“user2”)
private User user;
public String result() {
return user.getData();
}
}
|
cs |
@Resource
자바 6 및 JEE5에 추가된 것으로 어플리케이션에서 필요로 하는 자원을 자동 연결할 때 사용 한다. 스프링 2.5 부터 지원하는 어노테이션으로 스프링에서는 의존하는 빈 객체를 전달할 때 사용한다.
@Autowired와 흡사하지만 @Autowired는 타입으로(by type), @Resource는 이름으로(by name)으로 연결한다는 점이 다르다.
options:
- name: 자동으로 연결될 빈객체의 이름을 입력한다. ex) @Resource(name=”testDao”)
사용하려면 <bean class=”org.springframework.beans.factory.annotation.CommonAnnotationBeanPostProcessor”/> 클래스를 빈으로 등록시켜줘야 한다. 해당 설정 대신에 <context:annotation-config> 태그를 사용해도 된다.
<beans>
<!– 기타 설정 생략 –>
<context:annotation-config/>
<bean id=”user2″ class=”com.test.UserImpl” p:data=”65536″/>
</beans>
|
cs |
public class UserService {
@Resource(name=”user2″)
private User user;
//UserImpl user2 = new UserImpl();
//User user = user2;
public void setUser(User user) {
this.user = user;
}
public String result() {
return user.getData();
}
}
|
cs |
@Scope
패키지: org.springframework.beans.factory.annotation
설정: prototype, singleton, request, session, globalSession
스프링은 기본적으로 빈의 범위를 “singleton” 으로 설정한다. “singleton” 이 아닌 다른범위를 지정하고 싶다면 @Scope 어노테이션을 이용하여 범위를 지정한다.
@Component
@Scope(value=”prototype”)
public class Worker { }
|
cs |
@Component
@Scope(value=”prototype”, proxyMode=ScopedProxyMode.TARGET_CLASS)
public class Worker { }
|
cs |
@PostConstruct
패키지: javax.annotation
버전: jdk1.6, spring 2.5
설정 위치: 초기화 작업 수행 메서드 앞
의존하는 객체를 설정한 이후에 초기화 작업을 수행하기 위해 사용한다.
사용하려면 CommonAnnotationBeanPostProcessor 클래스를 빈으로 등록시켜줘야 한다. <context:annotation-config> 태그로 대신할 수 있다.
@PostConstruct
public void init() {
System.out.println(“객체 생성 후 내가 먼저 실행된다.”);
}
|
cs |
@PreDestroy
패키지: javax.annotation
버전: jdk1.6, spring 2.5
설정 위치: 해당 작업 메서드 앞
컨테이너에서 객체를 제거하기 전에 해야할 작업을 수행하기 위해 사용한다.
사용하려면 CommonAnnotationBeanPostProcessor 클래스를 빈으로 등록시켜줘야 한다. <context:annotation-config> 태그로 대신할 수 있다.
@Inject
SR-330 표준 Annotation으로 Spring 3 부터 지원하는 Annotation이다. 특정 Framework에 종속되지 않은 어플리케이션을 구성하기 위해서는 @Inject를 사용할 것을 권장한다. @Inject를 사용하기 위해서는 클래스 패스 내에 JSR-330 라이브러리인 javax.inject-x.x.x.jar 파일이 추가되어야 함에 유의해야 한다.
@Service
@Service를 적용한 Class는 비지니스 로직이 들어가는 Service로 등록이 된다. Controller에 있는 @Autowired는 @Service(“xxxService”)에 등록된 xxxService와 변수명이 같아야 하며 Service에 있는 @Autowired는 @Repository(“xxxDao”)에 등록된 xxDao와 변수명이 같아야 한다.
@Service(“helloService”)
public class HelloServiceImpl implements HelloService {
@Autowired
private HelloDao helloDao;
public void hello() {
System.out.println(“HelloServiceImpl :: hello()”);
helloDao.selectHello();
}
}
|
cs |
helloDao.selectHello(); 와 같이 @Autowired를 이용한 객체를 이용하여 Dao 객체를 호출한다:
@Service(“test2.testService”)
//괄호 속 문자열은 식별자를 의미한다.
//괄호를 생략할 경우 클래스명 그대로 사용한다.
//따라서 ,같은 클래스명이 존재 할 시 같은 식별자가 생성되기때문에 에러가 발생한다.
public class TestService {
public String result(int num1, int num2, String oper) {
String str = null;
if (oper.equals(“+”)) {
//…
return str;
}
}
}
|
cs |
@Resouce로 연결
@Resource(name=”test2.testService”)
//name에 필요한 것은 @Service(“test2.testService”) <- 여기서 괄호 속 문자열, 즉 식별자
private TestService service;
//TestService service = new TestService(); 라고 하는것과 같은 식
@RequestMapping(value=”/test2/oper.action”, method={RequestMethod.GET})
public String form() throws Exception {
return “test2/write”;
}
|
cs |
@Repository
패키지: org.springframework.stereotype
버전: spring 2.0
@Repository는 일반적으로 DAO에 사용되며 DB Exception을 DataAccessException으로 변환한다.
@Repository(“bbs.boardDAO”)
public class BoardDAO {
private SqlSession sqlSession;
public int insertBoard(Board dto) throws Exception {
…
}
}
|
cs |
public class BoardServiceImpl implements BoardService {
@Resource(name=”bbs.boardDAO”)
private BoardDAO dao;
public int insertBoard(Board dto){}
}
|
cs |
@Controller
http://noritersand.tistory.com/474
@RequestMapping
http://noritersand.tistory.com/475
@RequestParam
http://noritersand.tistory.com/357
@SessionAttributes
SessionAttribute annotation은 세션상에서 model의 정보를 유지하고 싶을 경우 사용한다.
@Controller
@SessionAttributes(“blog”)
public class BlogController {
// 중간생략
@RequestMapping(“/createBlog”)
public ModelMap createBlogHandler() {
blog = new Blog();
blog.setRegDate(new Date());
return new ModelMap(blog);
}
// 중간생략
}
|
cs |
@InitBinder
WebDataBinder를 초기화하는 method를 지정 할 수 있는 설정을 제공한다.
일반적으로 WebDataBinder는 annotation handler 메서드의 command 와 form 객체 인자를 조작하는데 사용된다.
InitBinder 메서드가 필수적으로 반환값을 가질 필요는 없으며, 일반적으로 이런 경우에 void를 선언한다. 특별한 인자는 WebdataBinder와 WebRequest또는 Locale의 조합으로 이루어지며, 이러한 조건이 만족되면 context-specific editors를 등록하는것이 허용된다.
WebdataBinder: WebDataBinder는 web request parameter를 javaBean 객체에 바인딩하는 특정한 DataBinder이다. WebDataBinder는 웹 환경이 필요하지만, Servlet API에 의존적이지 않다. servlet API에 의존적인 ServletRequestDataBinder와 같이 특정한 DaraBinder를 위한 더많은 base class를 제공한다.
RequestMapping: RequestMapping annotation은 web request를 특정한 handler class와 handler method에 mapping하는 역활을 수행한다. 대응하는 handlerMapping(for type level annotation)과 HandlerAdapter(for method level annotation)가 dispatch에 존재한다면, @RequestMapping이 처리된다.
WebRequest: WebRequest는 웹 요청에 대한 Generic interface이다. 주로 일반 request metadata에 generic web request interceptors의 접근을 허용하여 metadata에 대한 처리를 하기 위한 것이지 request 자체를 처리하기 위한 것은 아니다.
Annotation 기반 Controller 에서 ServletContext 구하기:
@Controller
@RequestMapping(“/common/download”)
public class DownloadController {
@Autowired
private ServletContext sc;
@RequestMapping
public ModelAndView download(@RequestParam(“filePath”) String filePath) throws Exception {
String path = sc.getRealPath(filePath);
return new ModelAndView(“common.download”, “downloadFile”, new File(path));
}
}
|
cs |
@ModelAttribute
http://noritersand.tistory.com/365
@RequestBody
@RequestBody 어노테이션이 적용된 파라미터는 HTTP Request body의 내용이 전달된다.
참고: http://java.ihoney.pe.kr/283
@RequestMapping(value=”/test”)
public void penaltyInfoDtlUpdate(@RequestBody String body,
HttpServletRequest req, HttpServletResponse res,
Model model, HttpSession session) throws Exception {
System.out.println(body);
}
|
cs |
@ResponseBody
서버가 응답할 값 혹은 객체를 HTTP Response body에 쓴다.
참고: http://ismydream.tistory.com/140
클라이언트에 JSON 형식의 값을 응답할 때 유용하다. 메서드에 @ResponseBody를 적용한 후 객체를 리턴하면 그 값은 아니라 HTTP ResponseBody에 직접 쓰여진다.
@RequestMapping(“/getVocTypeList”)
@ResponseBody
public ArrayList<Object> getVocTypeList() throws Exception {
HashMap<String, Object> vocData = gvocInf.searchVocTypeList();
return (ArrayList<Object>) vocData.get(“data”);
}
|
cs |
@ResponseBody가 적용된 컨트롤러 메서드는 설정된 resolver를 통하지 않고 값을 그대로 전달한다.
@PathVariable
URL의 일부를 파라미터 혹은 변수로 사용한다.
package com.sp.ex;
@Controller(“ex.exController”)
public class ExController{
@RequestMapping(value=”/blog/{userId}/main.action”, method=RequestMethod.GET)
public String main(HttpServletRequest req
, @PathVariable String userId) throws Exception {
req.setAttribute(“userId”, userId);
return “restful/result”;
}
}
|
cs |
SPRING에서 JSON 객체를 파라메터로 넘겼을 경우
SPRING에서 PARAMETER로 JSON을 던져서 처리하는 것을 연습삼아 하게 되었다.
삽질의 연속이었다.-_-
JSP파일에서는 JQUERY 의 $.ajax를 사용하였다.
data 속성에 담아서 보냈는데 data에 담을 경우 알아서 queryString으로 파싱을 하게 된다.
(queryString : type=sfa&name=3234&id=1324142 형식)
그래서 JSON.stringify 함수를 통해서 JSON문자열로 만든 후 이를 data에 담아서 보냈다.
그리고 Spring에 JSON으로 보낸다는 것을 인식시키기 위해 contentType을 application/json;charset=UTF-8 로 보내야 한다.
그렇지 않을 경우 415 415 unsupported media type 에러를 받아볼 수 있을 것이다.(제길-_-)
소스
var jsonStr = JSON.stringify(pObj);
console.log(“jsonStr : ” + jsonStr);
$.ajax({
url : ‘/app/adm/menu/modify’
, method : “post”
, dataType : ‘json’
, data : jsonStr
, processData : true /*querySTring make false*/
, contentType : “application/json; charset=UTF-8”
, success : function(data, stat, xhr) {
alert(“success”);
}
, error : function(xhr, stat, err) {
alert(“error”);
console.log(err);
}
});
spring셋팅에서 @annoatation-drriven 을 사용하였기에 MessageConvert에 자동으로 JacksonJson 이 추가된다고 알고 있었다.
근데 이것도 pom.xml 에 jacksonJson mapper lib가 추가되었을 경우 해당한다.
(jackson json lib 버젼의 경우 1.9.x 도 되는듯 하나 그냥 최신버젼으로 하였다.)
(스프링 doc에 3.1.2 부터 가능하다고 하는 이야기도 있다. 안될 경우 spring버젼을 3.1.2 이상으로 하자.)
pom.xml
spring 의 controller 부분에서는 paremeter 로 json을 받기 위해 controller의 arg 부분에 @requestBody 를 추가하였다.
또한 return 을 json으로 하기 위해 @responseBody를 추가하였다.
controller의 argument, return 값이 대한 설명 참고 url
http://springsource.tistory.com/13
또한 json 으로 넘겨받은 것을 jackson json lib 가 자동으로 convert 시 리턴값을 List
(나같은 경우 json 기준 [{}, {}, {}] 구조임)
여기서도 domain 객체로 받는 것으로 처리해놨다가 엄청 삽질하고 구글링 통해 포기했다.-ㅅ-;;
controller 소스
@RequestMapping(“modify”)
@ResponseBody
public String modify(@RequestBody List
ArrayList
Spring Framework의 spring data redis이용하기
spring에서 제공하는 RedisTemplate을 함께 사용하는 방법이 있다.
RedisTemplate을 사용하면 redis client 라이브러리의 종류에 상관없이 사용할 수 있어 좋고,
redis가 지원하는 자료구조를 사용하기 좋게 랩핑을 해놓아 좋다.
jedis를 그냥 써도 좋지만, byte[] 타입으로 컨버팅 노가다를 해야 한다.
어려운 길을 가지말고 RedisTemplate을 사용하기로 해보자.
pom.xml에 추가
1
2
3
4
5
6
7
8
9
10
11
12
|
<!-- jedis --> < dependency > < groupId >redis.clients</ groupId > < artifactId >jedis</ artifactId > < version >2.2.1</ version > </ dependency > <!-- spring data redis --> < dependency > < groupId >org.springframework.data</ groupId > < artifactId >spring-data-redis</ artifactId > < version >1.2.0.RELEASE</ version > </ dependency > |
root-context.xml에 추가
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
<? xml version = "1.0" encoding = "UTF-8" ?> xsi:schemaLocation="http://www.springframework.org/schema/beans > <!-- Root Context: defines shared resources visible to all other web components --> < mvc:annotation-driven conversion-service = "conversionService" /> < bean id = "jedisConnectionFactory" class = "org.springframework.data.redis.connection.jedis.JedisConnectionFactory" p:use-pool = "true" p:host-name = "172.20.44.209" p:port = "6379" /> <!-- redis template definition --> < bean id = "redisTemplate" class = "org.springframework.data.redis.core.RedisTemplate" p:connection-factory-ref = "jedisConnectionFactory" /> </ beans > |
다양하게 사용해 보자
1. 가장 쉽게 사용해 보기
사용할 타입을 RedisTemplate과 자료구조에 지정하여 사용
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
/** * Handles requests for the application home page. */ @Controller @RequestMapping (value= "/home" ) public class HomeController { private static final Logger logger = LoggerFactory.getLogger(HomeController. class ); @Autowired RedisTemplate<String, String> redisTemplate; @Resource (name= "redisTemplate" ) private ValueOperations<String, String> valueOps; @RequestMapping (value= "/jedis2" ) public String jedis2(){ valueOps.set( "foo" , "bar" ); logger.info( "Welcome jedis user {}." ,valueOps.get( "foo" )); return "home" ; } } |
2. Custom class 이용해 보기
Serializable 를 구현한 User class를 넣어보자
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
public class User implements Serializable { @NotNull String name; @Min ( 0 ) @Max ( 100 ) int id; Level level; public void setLevel(Level _level) { level = _level; } public Level getLevel() { return level; } public void setId( int _id) { id = _id; } public int getId() { return id; } public void setName(String _name) { name = _name; } public String getName() { return name; } } |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
/** * Handles requests for the application home page. */ @Controller @RequestMapping (value= "/home" ) public class HomeController { private static final Logger logger = LoggerFactory.getLogger(HomeController. class ); @Autowired RedisTemplate<String, User> redisTemplate; @Resource (name= "redisTemplate" ) private ValueOperations<String, User> valueOps; @RequestMapping (value= "/jedis2" ) public String jedis2(){ User user = new User(); user.setId( 4 ); user.setName( "warwick" ); user.setLevel(Level.GOLD); valueOps.set( "foo" , user); return "home" ; } @RequestMapping (value= "/jedis3" ) public String jedis3(){ User savedUser = valueOps.get( "foo" ); logger.info( "Welcome jedis user " + savedUser.id + " name : " + savedUser.name + " level : " + savedUser.level); return "home" ; } } |
결과
INFO : com.yakolla.mvctest.HomeController – Welcome jedis user 4 name : warwick level : GOLD
3. valueOperations 없이 사용해보기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
public class HomeController { private static final Logger logger = LoggerFactory.getLogger(HomeController. class ); @Autowired RedisTemplate<String, Object> redisTemplate; @RequestMapping (value= "/jedis2" ) public String jedis2(){ User user = new User(); user.setId( 6 ); user.setName( "warwick2" ); user.setLevel(Level.SILVER); redisTemplate.opsForValue().set( "foo" , user); return "home" ; } @RequestMapping (value= "/jedis3" ) public String jedis3(){ User savedUser = (User)redisTemplate.opsForValue().get( "foo" ); logger.info( "Welcome jedis user " + savedUser.id + " name : " + savedUser.name + " level : " + savedUser.level); return "home" ; } } |
결과
INFO : com.yakolla.mvctest.HomeController – Welcome jedis user 6 name : warwick2 level : SILVER
[Spring3.0] 다중파일 업로드(MultiFile upload)
단일 파일 업로드는 많이 해봤지만
다중으로 여러건 파일 업로드 처리해야할 경우들이 있습니다.
배열속성으로 처리하는 방법도 있지만
오늘은 동일한 name값으로 처리하는 방법을 알아봅시다
*-servlet.xml
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver" > <property name="maxUploadSize" value="30720000" /> </bean>
form.jsp
<form name="uploadForm" method="post" enctype="multipart/form-data" action="file.spring?action=upload"> <input type="file" name="upFile"> <input type="file" name="upFile"> <input type="file" name="upFile"> </form>
Controller.java
public ModelAndView addRoomType(HttpServletRequest request, HttpServletResponse response){ MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest)request; //다중파일 업로드 List<MultipartFile> files = multipartRequest.getFiles("upFile"); return new ModelAndView("redirect:*.spring?action=list"); }
핵심은 multipartRequest 오는 getFiles에 name명을 찾아서 처리하는 걸로 ~ 끝
Spirng Batch 로 Batch Job 만들어보기
이번에 일일 집계를 만들면서 spring batch를 처음 써봤다.
인터넷에 batch에 대한 이론 자료는 많으므로 여기엔 딱히 쓰진 않겠다.
spring batch는 스케줄 등록하는 기능이 없다.
그래서 Jenkins로 시간을 설정해서 빌드하고 batch는 spring batch를 썼다.
하루동안 사람들이 쓴 글의 총 수를 TEXT 테이블에서 셀렉트 해온 후
그 데이터를 AGGT(집계) 테이블에다가 넣는 예제이다.
(1) 비지니스 로직 없이 select 후 바로 insert 하기
1. XML에 JOB을 설정한다.
<job id="aggt" incrementer="incrementer" xmlns="http://www.springframework.org/schema/batch"> <step id="txt_cnt"> -- 한 job에 스텝 여러개 추가 가능 <tasklet> <chunk reader="aggtReader" writer="aggtWriter" commit-interval="1" /> </tasklet> </step> </job> <bean id="aggtReader" class="org.springframework.batch.item.database.IbatisPagingItemReader"> <property name="queryId" value="aggt.selectTextCnt" /> -- query의 namespace+id <property name="sqlMapClient" ref="dataSqlMapClient" /> -- database 선택, select, insert 하는 곳이 서로 다른 DB일 때 설정해놓은 id를 이렇게 불러서 쓰면 된다! </bean> <bean id="aggtWriter" class="org.springframework.batch.item.database.IbatisPagingItemWriter"> <property name="queryId" value="aggt.insertTextCnt" /> <property name="sqlMapClient" ref="aggtSqlMapClient" /> </bean>
JAVA에서 아무 조작 없이 셀렉트 해온 값을 Map에 들고있다가 바로 Insert 하게 된다.
* commit-interval 은 단위인데..
만약 commit-interval이 5고, 총 100건을 돌린다 할 때
5개 단위로 가고, 중간에 실패하면 5 단위로 된 곳부터 시작한다고 함.
2. query.xml (iBatis를 쓴다)
<sqlMap namespace="aggt"> <resultMap id="textAggt" class="객체 위치"> <result property="aggtDate" column="AGGT_DATE" /> <result property="textCnt" column="TEXT_CNT" /> </resultMap> <select id="selectTextCnt" resultMap="textAggt"> /* 셀렉트 해오는 쿼리 */ </select> <insert id="insertTextCnt"> /* 위에서 셀렉트 한 값을 인서트하는 쿼리 */ </insert>
(2) 비지니스 로직 삽입
1. XML에 JOB 설정
bean 설정 대신 직접 구현한다.
<job id="aggt" incrementer="incrementer" xmlns="http://www.springframework.org/schema/batch"> <step id="txt_cnt"> <tasklet> <chunk reader="aggtReader" writer="aggtWriter" commit-interval="1" /> </tasklet> </step> </job>
때에 따라서 reader, writer 사이에 processor=”클래스명” 속성을 넣어줄 수도 있다.
reader 이후 비지니스가 필요할 때 processor에서 데이터를 가공하면 된다.
2. reader 구현
내 경우랑은 좀 달라서 테스트는 안해봤는데
다른 예제들을 찾아보면 bean에서 파라미터들을 넘겨주는 경우가 있다.
그 때 파라미터 개수만큼 reader가 호출되는 듯 하다.
그래서 전역변수를 설정해놓고 리턴할 값이 null인지를 체크해야 하며,
null이 리턴되면 reader는 끝난다.
파라미터 없는 경우는 한번불리고 끝나는거 같다.
public class AggtReader implements ItemReader<TextAggt> { ... 중략 public TextAggt read() { TextAggt textAggt = new TextAggt(); // TODO : null 체크 하여 null을 리턴하던지 writer에 쓸 객체를 리턴할 것 TextCntService result = textCntService.selectTextCnt(); return result; // 리스트를 리턴하지 말고 객체 하나를 리턴할 것 } }
3.
중간에 프로세서가 있다면 프로세서를 구현하고, 나는 프로세서를 생략하겠다.
라이터는 데이터를 받아올 때 리스트로 받는다.
처음할 때 리더에서 리스트 보냈다가 라이터에서 List> 받으려 하다가 쌩고생함..
public class AggtWriter implements ItemWriter<TextAggt> { .... public void write(List<? extends TextAggt> list) { textCntService.insertTextCnt(list); } }
스프링 배치 어드민 튜토리얼
http://docs.spring.io/spring-batch-admin/reference/reference.pdf
다운로드
http://docs.spring.io/downloads/nightly/release-download.php?project=BATCH
https://tedwon.atlassian.net/wiki/display/SE/Spring+Batch (최종 여기서 다운받아서 설치 진행)