티스토리 뷰

>> SpringBoot 프로젝트 생성 및 설정


> Project Wizard로 Spring Starter Project 생성

( maven 및 spring eclipse plugin 최신버전으로 update)


> pom.xml

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

<modelVersion>4.0.0</modelVersion>


<groupId>lyj</groupId>

<artifactId>sample-springBoot</artifactId>

<version>0.0.1-SNAPSHOT</version>

<packaging>jar</packaging>


<name>sample-springBoot</name>

<description>Demo project for Spring Boot</description>


<parent>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-parent</artifactId>

<version>1.3.5.RELEASE</version>

<relativePath/> <!-- lookup parent from repository -->

</parent>


<properties>

<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

<java.version>1.7</java.version>

</properties>


<dependencies>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-web</artifactId>

</dependency>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-test</artifactId>

<scope>test</scope>

</dependency>

</dependencies>

<build>

<plugins>

<plugin>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-maven-plugin</artifactId>

</plugin>

</plugins>

</build>

</project>


> Spring Boot Application 클래스 작성

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;


@SpringBootApplication

public class SampleSpringBootApplication {


public static void main(String[] args) throws Exception{

SpringApplication.run(SampleSpringBootApplication.class, args);

}

}


> spring boot Application 실행방법

1. run as > java application : application클래스 메인메소드

2. run as > Spring boot App : spring eclipse plugin 설치된 경우

3. run as > maven build... : goal은 spring-boot:run 입력, 또는 $> mvn spring-boot:run 명령어 실행


> 정상실행 되고, http://localhost:8080 페이지 접속하면 아래와 같이 표시됨.

Whitelabel Error Page

This application has no explicit mapping for /error, so you are seeing this as a fallback.

Mon May 30 10:25:13 KST 2016
There was an unexpected error (type=Not Found, status=404).

No message available


> Jar파일 packaging 및 실행

1. mvn clean package 실행 --> /target 디렉토리 jar파일 생성 (ex: sample-springBoot-0.0.1-SNAPSHOT.jar)

2. java -jar sample-springBoot-0.0.1-SNAPSHOT.jar 명령어 실행


* Maven 설치

1. maven 다운로드 및 압축풀기 : (https://maven.apache.org/download.cgi)

2. PATH 환경변수에 %MAVEN_HOME%/bin 디렉토리 추가

3. JAVA_HOME 환경변수 설정필수

4. $> mvn -v 명령어로 확인

Apache Maven 3.3.9 (bb52d8502b132ec0a5a3f4c09453c07478323dc5; 2015-11-11T01:41:47+09:00)

Maven home: C:\Dev\apache-maven-3.3.9\bin\..

Java version: 1.7.0_80, vendor: Oracle Corporation

Java home: C:\Program Files\Java\jdk1.7.0_80\jre

Default locale: ko_KR, platform encoding: MS949

OS name: "windows 7", version: "6.1", arch: "x86", family: "windows"


>> RESTful 서비스 개발

> Controller 및 Service 예제

* Controller 소스

package lyj.sample.web;


import java.math.BigInteger;

import java.util.Collection;

import java.util.HashMap;

import java.util.Map;


import javax.annotation.Resource;


import lyj.sample.model.Greeting;

import lyj.sample.service.GreetingService;


import org.springframework.http.HttpStatus;

import org.springframework.http.MediaType;

import org.springframework.http.ResponseEntity;

import org.springframework.web.bind.annotation.PathVariable;

import org.springframework.web.bind.annotation.RequestBody;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestMethod;

import org.springframework.web.bind.annotation.RestController;


@RestController

public class GreetingController {

@Resource

private GreetingService greetingService;

@RequestMapping(value="/api/greetings", method=RequestMethod.GET, produces=MediaType.APPLICATION_JSON_VALUE)

public ResponseEntity<Collection<Greeting>> getGreetings(){

Collection<Greeting> greetings = greetingService.findAll();

return new ResponseEntity<Collection<Greeting>>(greetings, HttpStatus.OK);

}

@RequestMapping(value="/api/greetings/{id}", method=RequestMethod.GET, produces=MediaType.APPLICATION_JSON_VALUE)

public ResponseEntity<Greeting> getGreeting(@PathVariable("id") BigInteger id){

Greeting greeting = greetingService.findOne(id);

if (greeting == null){

return new ResponseEntity<Greeting>(HttpStatus.NOT_FOUND);

}

return new ResponseEntity<Greeting>(greeting, HttpStatus.OK);

}

@RequestMapping(value="/api/greetings", method=RequestMethod.POST, consumes=MediaType.APPLICATION_JSON_VALUE, produces=MediaType.APPLICATION_JSON_VALUE)

public ResponseEntity<Greeting> createGreeting(@RequestBody Greeting greeting){

Greeting savedGreeting = greetingService.create(greeting);

return new ResponseEntity<Greeting>(savedGreeting, HttpStatus.CREATED);

}

@RequestMapping(value="/api/greetings/{id}", method=RequestMethod.PUT, consumes = MediaType.APPLICATION_JSON_VALUE, produces=MediaType.APPLICATION_JSON_VALUE)

public ResponseEntity<Greeting> upateGreeting(@RequestBody Greeting greeting){

Greeting updatedGreeting = greetingService.update(greeting);

if (updatedGreeting == null){

return new ResponseEntity<Greeting>(HttpStatus.INTERNAL_SERVER_ERROR);

}

return new ResponseEntity<Greeting>(updatedGreeting, HttpStatus.OK);

}

@RequestMapping(value="/api/greetings/{id}", method=RequestMethod.DELETE, consumes=MediaType.APPLICATION_JSON_VALUE)

public ResponseEntity<Greeting> deleteGreeting(@PathVariable("id") BigInteger id, @RequestBody Greeting greeting){

greetingService.delete(id);

return new ResponseEntity<Greeting>(HttpStatus.NO_CONTENT);

}

}


> Service 소스 예제

package lyj.sample.service;


import java.math.BigInteger;

import java.util.Collection;

import java.util.HashMap;

import java.util.Map;


import lyj.sample.model.Greeting;


import org.springframework.stereotype.Service;


@Service

public class GreetingService {

private static BigInteger nextId;

private static Map<BigInteger, Greeting> greetingMap;

private static Greeting save(Greeting greeting){

if (greetingMap == null){

greetingMap = new HashMap<BigInteger, Greeting>();

nextId = BigInteger.ONE;

}

// If Update.....

if (greeting.getId() != null){

Greeting oldGreeting = greetingMap.get(greeting.getId());

if (oldGreeting == null){

return null;

}

greetingMap.remove(greeting.getId());

greetingMap.put(greeting.getId(), greeting);

return greeting;

}

// If Create.....

greeting.setId(nextId);

nextId = nextId.add(BigInteger.ONE);

greetingMap.put(greeting.getId(), greeting);

return greeting;

}

private static boolean remove(BigInteger id){

Greeting deletedGreeging = greetingMap.remove(id);

if (deletedGreeging == null){

return false;

}

return true;

}

static {

Greeting g1 = new Greeting();

g1.setText("Hello World!");

save(g1);

Greeting g2 = new Greeting();

g2.setText("Hola Mundo!");

save(g2);

}

public Collection<Greeting> findAll(){

Collection<Greeting> greetings = greetingMap.values();

return greetings;

}

public Greeting findOne(BigInteger id){

Greeting greeting = greetingMap.get(id);

return greeting;

}

public Greeting create(Greeting greeting){

Greeting savedGreeting = save(greeting);

return savedGreeting;

}

public Greeting update(Greeting greeting){

Greeting updatedGreeting = save(greeting);

return updatedGreeting;

}

public void delete(BigInteger id){

remove(id);

}

}


>> Persistance Layer with JPA

> depenency 추가

<dependency>

<groupId>org.springframework.boot</groupId>

      <artifactId>spring-boot-starter-data-jpa</artifactId>

</dependency>

<dependency>

<groupId>org.hsqldb</groupId>

<artifactId>hsqldb</artifactId>

<scope>runtime</scope>

</dependency>


> Repository 인터페이스 작성

package lyj.sample.repository;


import java.math.BigInteger;

import lyj.sample.model.Greeting;

import org.springframework.data.jpa.repository.JpaRepository;

import org.springframework.stereotype.Repository;


@Repository

public interface GreetingRepository extends JpaRepository<Greeting, BigInteger> {


}


> 서비스 메소드 repository 사용

@Service

public class GreetingService {

@Autowired

GreetingRepository greetingRepository;


public Collection<Greeting> findAll(){

Collection<Greeting> greetings = greetingRepository.findAll();

return greetings;

}

public Greeting findOne(BigInteger id){

Greeting greeting = greetingRepository.findOne(id);

return greeting;

}

public Greeting create(Greeting greeting){

if (greeting.getId() != null){

// Cannot create Greeting with specified ID value

return null;

}

Greeting savedGreeting = greetingRepository.save(greeting);

return savedGreeting;

}

public Greeting update(Greeting greeting){

Greeting greetingPersisted = findOne(greeting.getId());

if (greetingPersisted == null){

// Cannot update Greeting that hasn't been persisted.

return null;

}

Greeting updatedGreeting = greetingRepository.save(greeting);

return updatedGreeting;

}

public void delete(BigInteger id){

greetingRepository.delete(id);

}

}


> 도메인 클래스 @Entity 어노테이션

@Entity

public class Greeting {

@Id

@GeneratedValue

private BigInteger id;

private String text;


> hsqldb script 파일 작성

* src\main\resources\data\hsqldb\schema.sql

DROP TABLE Greeting IF EXISTS;


CREATE TABLE Greeting(

id NUMERIC(19) GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1) NOT NULL,

text VARCHAR(100) NOT NULL,

PRIMARY KEY (id)

)

* src\main\resources\data\hsqldb\data.sql

INSERT INTO Greeting(text) VALUES ('Hello World!');

INSERT INTO Greeting(text) VALUES ('Hola Mundo!');


> application.propertis 에 data source config 추가

# Hibernate

spring.jpa.hibernate.naming.strategy=org.hibernate.cfg.DefaultNamingStrategy

spring.jpa.hibernate.ddl-auto=validate


# Initialization

spring.datasource.schema=classpath:/data/hsqldb/schema.sql

spring.datasource.data=classpath:/data/hsqldb/data.sql


>> Transaction Management

> spring boot main class에  @EnableTransactionManagement 어노테이션 추가

@SpringBootApplication

@EnableTransactionManagement

public class SampleSpringBootApplication {


public static void main(String[] args) throws Exception{

SpringApplication.run(SampleSpringBootApplication.class, args);

}

}


> Service 클래스 및 메소드에 @Transactional 어노테이션 추가. 의도적 rollback 상황 exception 로직 추가

@Service

@Transactional(propagation=Propagation.SUPPORTS, readOnly=true)

public class GreetingService {

.....

  @Transactional(propagation=Propagation.REQUIRED, readOnly=false)

public Greeting create(Greeting greeting){

if (greeting.getId() != null){

// Cannot create Greeting with specified ID value

return null;

}

Greeting savedGreeting = greetingRepository.save(greeting);

// Illustrate Tx rollback

if (savedGreeting.getId() == BigInteger.valueOf(4L)){

throw new RuntimeException("Roll me back!!");

}

return savedGreeting;

}

.....



>> Cache Management

> Main Configuration Class에 @EnableCaching 어노테이션 및 CacheManager Bean생성

@SpringBootApplication

@EnableTransactionManagement

@EnableCaching

public class SampleSpringBootApplication {


public static void main(String[] args) throws Exception{

SpringApplication.run(SampleSpringBootApplication.class, args);

}

@Bean

public CacheManager cacheManager(){

ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager("greetings");

return cacheManager;

}

}


> Service method에 @Cacheable, @CachePut, @CacheEvic 어노테이션 적용

@Cacheable(value="greetings", key="#id")

public Greeting findOne(BigInteger id){


@CachePut(value="greetings", key="#result.id")

public Greeting create(Greeting greeting){


@CachePut(value="greetings", key="#greeting.id")

public Greeting update(Greeting greeting){


@CacheEvict(value="greetings", key="#id")

public void delete(BigInteger id){


> Google guava cache 사용하기

> pom.xml dependency 추가

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-context-support</artifactId>

</dependency>

<!-- http://mvnrepository.com/artifact/com.google.guava/guava -->

<dependency>

   <groupId>com.google.guava</groupId>

   <artifactId>guava</artifactId>

   <version>19.0</version>

</dependency>


> main configuration class에 cachemanager bean 변경

       @Bean

public CacheManager cacheManager(){

GuavaCacheManager cacheManager = new GuavaCacheManager("greetings");

return cacheManager;

}



>> Scheduled Process

> application main configure class 에 @@EnableScheduling 어노테이션 추가

import org.springframework.scheduling.annotation.EnableScheduling;


@SpringBootApplication

@EnableTransactionManagement

@EnableCaching

@EnableScheduling

public class SampleSpringBootApplication {



> schedule bean class 작성

package lyj.sample.batch;


import java.util.Collection;


import lyj.sample.model.Greeting;

import lyj.sample.service.GreetingService;


import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.scheduling.annotation.Scheduled;

import org.springframework.stereotype.Component;


@Component

public class GreetingBatchBean {


private Logger logger = LoggerFactory.getLogger(this.getClass());

@Autowired

private GreetingService greetingService;

@Scheduled(cron="0,30 * * * * *")

public void cronJob(){

logger.info("> cronJob");

// Add scheduled logic here

Collection<Greeting> greetings = greetingService.findAll();

logger.info("There are {} greetings in the data store.", greetings.size());

logger.info("< cronJob");

}


@Scheduled(initialDelay = 5000

, fixedRate = 15000 //이전 수행 시작 기준. 이전 배치가 완료되어야 다음 배치 실행됨.

//, fixedDelay = 15000 //이전 수행 종료 기준.

)

public void fixedRateJobWithInitialDelay(){

logger.info("> fixedRateJobWithInitialDelay");

long pause = 5000;

long start = System.currentTimeMillis();

do{

if (start + pause < System.currentTimeMillis()){

break;

}

}while(true);

logger.info("Processing time was {} seconds.", pause / 1000);

logger.info("< fixedRateJobWithInitialDelay");

}

}



>> Asynchronous Process

> application main confugure class  에 @EnableAsync 어노테이션 추가


> AsyncResponse class 작성

package lyj.sample.util;


import java.util.concurrent.CancellationException;

import java.util.concurrent.ExecutionException;

import java.util.concurrent.Future;

import java.util.concurrent.TimeUnit;

import java.util.concurrent.TimeoutException;


public class AsyncResponse<V> implements Future<V> {

private V value;

private Exception executionException;

private boolean isCompletedExceptionally = false;

private boolean isCancelled = false;

private boolean isDone = false;

private long checkCompletedInterval = 100;

public AsyncResponse() {

}

public AsyncResponse(V val){

this.value = val;

this.isDone = true;

}

public AsyncResponse(Throwable ex){

this.executionException = new ExecutionException(ex);

this.isCompletedExceptionally = true;

this.isDone = true;

}

@Override

public boolean cancel(boolean mayInterruptIfRunning) {

this.isCancelled = true;

this.isDone = true;

return false;

}


@Override

public boolean isCancelled() {

return this.isCancelled;

}

public boolean isCompletedExceptionally(){

return this.isCompletedExceptionally;

}

@Override

public boolean isDone() {

return this.isDone;

}


@Override

public V get() throws InterruptedException, ExecutionException {

block(0);

if (isCancelled()){

throw new CancellationException();

}

if (isCompletedExceptionally()){

throw new ExecutionException(this.executionException);

}

if (isDone()){

return this.value;

}

throw new InterruptedException();

}


@Override

public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {

long timeoutInMillis = unit.toMillis(timeout);

block(timeoutInMillis);

if (isCancelled()){

throw new CancellationException();

}

if (isCompletedExceptionally()){

throw new ExecutionException(this.executionException);

}

if (isDone()){

return this.value;

}

throw new InterruptedException();

}

public boolean complete(V val){

this.value = val;

this.isDone = true;

return true;

}

public boolean completeExceptionally(Throwable ex){

this.value = null;

this.executionException = new ExecutionException(ex);

this.isCompletedExceptionally = true;

this.isDone = true;

return true;

}

public void setCheckCompletedInterval(long millis){

this.checkCompletedInterval = millis;

}

private void block(long timeout) throws InterruptedException{

long start = System.currentTimeMillis();

// Block until done, cancelled, or the timeout is exceeded

while(!isDone() && !isCancelled()){

if (timeout > 0){

long now = System.currentTimeMillis();

if (now > start + timeout){

break;

}

}

Thread.sleep(checkCompletedInterval);

}

}

}


> Async Service 작성

package lyj.sample.service;


import java.util.concurrent.Future;


import lyj.sample.model.Greeting;

import lyj.sample.util.AsyncResponse;


import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.scheduling.annotation.Async;

import org.springframework.stereotype.Service;


@Service

public class EmailService {

private Logger logger = LoggerFactory.getLogger(this.getClass());

public Boolean send(Greeting greeting){

logger.info("> send");

Boolean success = Boolean.FALSE;

// Simulate method execution time

long pause = 5000;

try{

Thread.sleep(pause);

} catch(Exception e){

// do nothing

}

logger.info("Processing time was {} seconds.", pause/1000);

success = Boolean.TRUE;

logger.info("< send");

return success;

}

@Async

public void sendAsync(Greeting greeting){

logger.info("> sendAsync");

try{

send(greeting);

} catch(Exception e){

logger.warn("Excecution caught sending asynchronous mail.", e);

}

logger.info("< sendAsync");

}

@Async

public Future<Boolean> sendAsyncWithResult(Greeting greeting){

logger.info("> sendAsyncWithResult");

AsyncResponse<Boolean> response = new AsyncResponse<Boolean>();

try{

Boolean success = send(greeting);

response.complete(success);

} catch(Exception e){

logger.warn("Excecution caught sending asynchronous mail.", e);

response.completeExceptionally(e);

}

logger.info("< sendAsyncWithResult");

return response;

}

}


> Controller에서 호출

       @RequestMapping(value="/api/greeting/{id}/send", method=RequestMethod.POST, produces=MediaType.APPLICATION_JSON_VALUE)

public ResponseEntity<Greeting> sendGreeting(@PathVariable("id") BigInteger id,

@RequestParam(value="wait", defaultValue="false") boolean waitForAsyncResult){

logger.info("> sendGreeting : wait=" + waitForAsyncResult);

Greeting greeting = null;

try{

greeting = greetingService.findOne(id);

if (greeting == null){

return new ResponseEntity<Greeting>(HttpStatus.NOT_FOUND);

}

if (waitForAsyncResult){

Future<Boolean> asyncResponse = emailService.sendAsyncWithResult(greeting);

boolean emailSent = asyncResponse.get();

logger.info("- greeting email sent? {}", emailSent);

} else{

emailService.sendAsync(greeting);

}

} catch(Exception e){

logger.error("A problem occured sending the Greeting.", e);

return new ResponseEntity<Greeting>(HttpStatus.INTERNAL_SERVER_ERROR);

}

logger.info("<sendGreeting");

return new ResponseEntity<Greeting>(greeting, HttpStatus.OK);

}


테스트 결과

http://localhost:8080/api/greeting/2/send?wait=true 호출시

2016-05-31 18:13:12.381  INFO 4800 --- [nio-8080-exec-5] lyj.sample.web.GreetingController        : > sendGreeting : wait=true

2016-05-31 18:13:12.383  INFO 4800 --- [cTaskExecutor-5] lyj.sample.service.EmailService          : > sendAsyncWithResult

2016-05-31 18:13:12.383  INFO 4800 --- [cTaskExecutor-5] lyj.sample.service.EmailService          : > send

2016-05-31 18:13:17.397  INFO 4800 --- [cTaskExecutor-5] lyj.sample.service.EmailService          : Processing time was 5 seconds.

2016-05-31 18:13:17.398  INFO 4800 --- [cTaskExecutor-5] lyj.sample.service.EmailService          : < send

2016-05-31 18:13:17.398  INFO 4800 --- [cTaskExecutor-5] lyj.sample.service.EmailService          : < sendAsyncWithResult

2016-05-31 18:13:17.398  INFO 4800 --- [nio-8080-exec-5] lyj.sample.web.GreetingController        : - greeting email sent? true

2016-05-31 18:13:17.398  INFO 4800 --- [nio-8080-exec-5] lyj.sample.web.GreetingController        : < sendGreeting


http://localhost:8080/api/greeting/2/send?wait=false 호출시

2016-05-31 18:12:38.014  INFO 4800 --- [nio-8080-exec-4] lyj.sample.web.GreetingController        : > sendGreeting : wait=false

2016-05-31 18:12:38.016  INFO 4800 --- [nio-8080-exec-4] lyj.sample.web.GreetingController        : < sendGreeting

2016-05-31 18:12:38.020  INFO 4800 --- [cTaskExecutor-4] lyj.sample.service.EmailService          : > sendAsync

2016-05-31 18:12:38.020  INFO 4800 --- [cTaskExecutor-4] lyj.sample.service.EmailService          : > send

2016-05-31 18:12:43.020  INFO 4800 --- [cTaskExecutor-4] lyj.sample.service.EmailService          : Processing time was 5 seconds.

2016-05-31 18:12:43.020  INFO 4800 --- [cTaskExecutor-4] lyj.sample.service.EmailService          : < send

2016-05-31 18:12:43.020  INFO 4800 --- [cTaskExecutor-4] lyj.sample.service.EmailService          : < sendAsync



댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/05   »
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