<!-- 레이어 division-->
<div class="layer" id="new_layer" style="width:800px;display:none;z-index:100 ">
<div class="layer_header">
<strong class="tit">HEADER</strong>
</div>
<div class="layer_content">
<div class="table" id="table" >
<table cellspacing="0">
<col width="20%">
<col width="35%">
<col width="10%">
<col width="35%">
<thead>
<tr>
<th>Colume 1</th>
<th>Colume 2</th>
<th>Colume 3</th>
<th>Colume 4</th>
</tr>
</thead>
<tbody id="tempatePlace">
</tbody>
</table>
</div>
</div>
<div class="layer_footer">
<a href="javascript:;" class="btn_layer_close"><span>닫기</span></a>
</div>
</div>
<!-- handlebars template -->
<script id="layer-template" type="text/x-handlebars-template">
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</script>
/** 레이어 닫기 버튼 초기화 */
$('.btn_layer_close').bind("click", function(e) {
resetLayer();
});
resetLayer : function() {
$("#new_layer").hide();
$("#tempatePlace").html("");
},
/** 클릭 링크마다 생성 */
var clickFunction = 'clickText("' + sequence + '")';
return '<a href="javascript:;" id="layer_"' + sequence + '" onclick="' + clickFunction + '" ><span>항목</span></a>';
/** 링크 클릭시 각 항목을 ajax를 통해 가져온다.*/
clickText : function (sequence, e) {
var url = AJAX_URL + "/" + sequence;
util.requestAjax(url, "GET", null, responseLayerAjax);
},
/** 응답받은 결과로 레이어의 데이터를 구성한다. */
responseLayerAjax : function (result) {
resetLayer();
var source = $("#layer-template").html();
var template = Handlebars.compile(source);
// 데이터 형식
var data = {
"dataList" : [
{column1:"data1", column2:"data2", column3:"data3", column4:"data4"}
]
}
var html = template(data); // result.data
$("#tempatePlace").append(html);
$("#new_layer").show();
// 클릭한 위치에 따라 레이어의 위치를 변경해준다.
var offset = $("#layer_" + result.sequence).offset();
$("#new_layer").offset({top : offset.top + 6, left : offset.left + 25});
},
<table cellspacing="0" border="1" style="width:100%;">
<colgroup>
<col width="130px" />
<col width="*" />
</colgroup>
<tbody>
<tr>
<th>1 DEPTH <input type="checkbox" name="1depth" checked="checked" class="1depth"></th>
<tr>
<th>2 DEPTH <input type="checkbox" checked="checked" class="itemList 2depth"></th>
<td colspan="6">
<input type="checkbox" checked="checked" class="item 3depth" value="value1"> 1번
<input type="checkbox" checked="checked" class="item 3depth" value="value2"> 2번
<input type="checkbox" checked="checked" class="item 3depth" value="value3"> 3번
<input type="checkbox" checked="checked" class="item 3depth" value="value4"> 4번
<input type="checkbox" checked="checked" class="item 3depth" value="value5"> 5번
</td>
</tr>
</tbody>
</table>
_initCheckBox : function () {
var checkBoxList = [
{upperClass : ".1depth", subClass: [".2depth", ".3depth"]}
];
// underscore
_.each(checkBoxList, function (classInfo) {
this.initCheckBoxEvent(classInfo);
})
},
initCheckBoxEvent : function (classInfo) {
/** 상위 체크박스 이벤트 설정 */
$(classInfo.upperClass).on('click', classInfo, onClickUpperCheckBoxEvent);
/** 하위 체크박스 이벤트 설정 (underscore) */
_.each(classInfo.subClass, function (subClass) {
$('#form').on('click', subClass, {"upperClass" : classInfo.upperClass, "subClass" : subClass}, bill.common.util.onClickSubCheckBoxEvent);
});
},
/**
* 상위 체크박스 선택시 하위체크 박스 모두 변경
*/
onClickUpperCheckBoxEvent : function(event) {
// (underscore)
_.each(event.data.subClass, function (subClass) {
$(subClass).prop("checked", event.target.checked);
})
},
/**
* 하위 체크박스 클릭시 상위체크박스 변경
* (하위 체크박스 전체 선택 여부에 따른 상위체크 박스의 선택 여부 변경)
**/
onClickSubCheckBoxEvent : function(event) {
$(event.data.upperClass).prop("checked", ($(event.data.subClass).length == $(event.data.subClass + ":checked").length) ? true : false);
},
다른 클래스 내부에 정의되는 클래스를 중첩클래스라고 한다. 중첩 클래스는 독립적으로 오브젝트로 만들어질 수 있는 스태틱 클래스(static class)와 자신이 정의된 클래스의 오브젝트 안에서만 만들어질 수 있는 내부클래스(inner class)로 구분된다.
내부 클래스는 다시 범위에 따라 세가지로 구분된다. 멤버필드처럼 오브젝트 레벨에 정의되는 멤버 내부 클래스 (member inner class) 메소드 레벨에 정의되는 로컬 클래스 (local class) 이름을 갖지 않는 익명 내부 클래스 (anonymous inner class)이다. 익명 내부 클래스의 범위는 선언된 위치에 따라 다르다.
클래스 선언과 오브젝트 생성이 결합된 형태로 만들어지며, 상속할 클래스나 구현할 인터페이스를 생성자 대신 사용해서 다음과 같은 형태로 만들어 사용한다. 클래스를 재사용할 필요가 없고, 구현한 인터페이스 타입으로만 사용할 경우에 유용하다.
new 인터페이스명() {
클래스 본문
}
class OuterClass {
// 스태칙클래스
static class StaticNestedClass {
}
// 멤버내부클래스
class MemberInnerClass {
}
void method(SomeObject someObject) {
// 선언된 메소드 내에서만 사용가능함
// 자신이 선언된 곳의 정보에 접근이 가능하다.
class LocalClass implements InterfaceAAA {
public String implement() {
return someObject.getTest();
}
}
InterfaceAAA test = new LocalClass();
}
void anonyMethod(SomeObject someObject) {
// 익명 내부 클래스
InterfaceAAA test = new InterfaceAAA() {
public String implement() {
return someObject.getTest();
}
}
}
}
// 익명클래스를 위한 인터페이스 정의
interface InterfaceAAA {
String implements();
}
package com.dasolute;
public class EnumConstants {
private static Map<String, Map<String, String>> enumMap = Maps.newHashMap();
static {
enumMap.put(ENUM1.class.getSimpleName(), getTextEnumMap(ENUM1.class));
enumMap.put(ENUM2.class.getSimpleName(), getTextEnumMap(ENUM2.class));
enumMap.put(ENUM3.class.getSimpleName(), getTextEnumMap(ENUM3.class));
enumMap.put(ENUM4.class.getSimpleName(), getTextEnumMap(ENUM4.class));
MapUtils.unmodifiableMap(enumMap);
}
private static Map<String, String> getTextEnumMap(Class<? extends Enum<?>> clz) {
Map<String,String> nameMap = Maps.newHashMap();
for(Object obj : clz.getEnumConstants()) {
TextEnum textEnum = (TextEnum) obj; // Enum implements TextEnum
Enum en = (Enum) obj;
nameMap.put(en.name(), textEnum.getText());
}
return nameMap;
}
public static Map<String, Map<String, String>> getEnumMap() {
return enumMap;
}
}
<#assign JacksonUtil=statics["com.dasolute.JacksonUtil"]>
<#assign EnumConstants=statics["com.dasolute.EnumConstants"]>
<script type="text/javascript">
if (typeof com == "undefined"){ bill = {};}
if (typeof com.dasolute == "undefined"){ com.dasolute = {};}
com.dasolute.data = ${(JacksonUtil.toJson(EnumConstants.getEnumMap()))!};
</script>
if (typeof com == "undefined"){ com = {};}
if (typeof com.dasolute == "undefined"){ com.dasolute = {};}
com.dasolute.formatter = {
code : {
getText : function(codeTypeName, code) {
if(com.dasolute.data[codeTypeName] && com.dasolute.data[codeTypeName][code]) {
return com.dasolute.data[codeTypeName][code];
}
return "";
}
}
}
var text = com.dasolute.formatter.code.getText('Enum1', 'code');
URL : https://github.com/ktoso/maven-git-commit-id-plugin
<project>
<pluginRepositories>
<pluginRepository>
<id>sonatype-snapshots</id>
<name>Sonatype Snapshots</name>
<url>https://oss.sonatype.org/content/repositories/snapshots/</url>
</pluginRepository>
</pluginRepositories>
<build>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
</resource>
<plugins>
<!-- git 정보 가져오기 -->
<plugin>
<groupId>pl.project13.maven</groupId>
<artifactId>git-commit-id-plugin</artifactId>
<version>2.2.1</version>
<executions>
<execution>
<id>get-the-git-infos</id>
<goals>
<goal>revision</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
deploy.phase=beta
deploy.domain=https://application.com/
# ${git.branch} 에 branch 정보가 들어온다.
git.branch=${git.branch}
1.5 버젼때 만들어진 객체.
Thread를 하나하나 생성하는 것 자체가 비용이다. 그래서 Thread Pool을 만들어 놓고 Thread를 재활용하여 비용을 줄이는 기법 Executors.newCachedThreadPool() => 만들어진 Thread를 GC 하지 않고 그대로 남겨놓음
ExecutorService의 submit()를 사용하면 다른 Thread의 결과 값을 받을 수 있다. Callable 인터페이스는 예외 발생 시 Exception을 던지고 결과객체도 리턴할 수 있다. Runnable 인터페이스는 예외를 안에서 모두 해결해야 하고 결과객체 리턴 불가.
future.get() - blocking method, 결과를 리턴해 줄 수 있을 때까지 대기한다.
ExecutorService es = Executors.newCachedThreadPool();
FutureTask<String> futureTask = new FutureTask<String>(() -> {
Thread.sleep(2000L);
return "done";
}) {
// 익명클래스를 정의한다.
@Override
protected void done() {
System.out.println(get());
}
}
es.execute(futureTask);
es.shutdown(); // 비동기 작업들이 모두 끝날때까지 대기하다가 종료하라
@Bean
ThreadPoolTaskExecutor tp() {
ThreadPoolTaskExecutor te = new ThreadPoolTaskExecutor();
te.setCorePoolSize(10); // ThreadPool을 기본적으로 4개를 만들어둔다. (첫 요청 시점에)
te.setMaxPoolSize(100); // pool이 꽉찼는데 100개까지 만든다 는 아니다. queue만큼 차면 늘리는 개념이다.
te.setQueueCapacity(200); // Queue가 꽉 차면 MaxPoolSize까지 늘린다.
te.setThreadNamePrefix("myThread");
te.initialize();
return te;
}
AsyncRestTemplate rt = new AsyncRestTemplate();
@GetMapping("test")
public ListenableFuture<ResponseEntity<String>> rest (int idx) {
ListenableFuture<ResponseEntity<String>> res = rt.getForEntity(URL, String.class, "hello");
return res; // 비동기적으로 처리한다. 즉시 리턴을 한다.
}
// 내부적으로 100번 외부 호출에 대해서 100 개의 스레드를 만든다.
// 서블릿 스레드를 100개 쓰는게 낫지 별도의 스레드 100개를 만드는 작업은 원치 않는다.
// 외부 API 를 호출하기 위해서 Netty를 통해 Call한다.
// 이때 Netty로도 Thread 1개로 통신한다.
AsyncRestTemplate rt = new AsyncRestTemplate(new Netty4ClientHttpRequestFactory(new NioEventLoopGroup(1)));
@GetMapping("test")
public ListenableFuture<ResponseEntity<String>> rest (int idx) {
ListenableFuture<ResponseEntity<String>> res = rt.getForEntity(URL, String.class, "hello");
return res; // 비동기적으로 처리한다. 즉시 리턴을 한다.
}
@GetMapping("test")
public DeferedResult<String> rest (int idx) {
// DR 선언
DeferedResult<String> dr = new DeferedResult<>();
ListenableFuture<ResponseEntity<String>> res = rt.getForEntity(URL, String.class, "hello");
// callback 세팅
res.addCallback(s-> {
dr.setResult(s.getBody() + "/work"); // API의 응답값을 결과에 써 준다.
}, e-> {
dr.setErrorResult(e.getMessage()) // 에러를 세팅하면 Spring Controller가 처리한다.
})
// 언제가 될지 모르지만 DR에 값을 써주면 그 값을 응답값으로 처리해주겠다 라는 것
return dr;
}
@GetMapping("test")
public DeferedResult<String> rest(int idx) {
// DR 선언
DeferedResult<String> dr = new DeferedResult<>();
ListenableFuture<ResponseEntity<String>> res1 = rt.getForEntity(URL, String.class, "hello");
res1.addCallback(s-> {
ListenableFuture<ResponseEntity<String>> res2 = rt.getForEntity(URL2, String.class, "hello");
res2.addCallback(s2-> {
ListenableFuture<ResponseEntity<String>> res3 = rt.getForEntity(URL3, String.class, "hello");
res3.addCallback(s3-> {
dr.setResult(s3.getBody())
}, e-> {
dr.setErrorResult(e.getMessage()) // 에러를 세팅하면 Spring Controller가 처리한다.
})
}, e-> {
dr.setErrorResult(e.getMessage()) // 에러를 세팅하면 Spring Controller가 처리한다.
})
}, e-> {
dr.setErrorResult(e.getMessage()) // 에러를 세팅하면 Spring Controller가 처리한다.
})
// 언제가 될지 모르지만 DR에 값을 써주면 그 값을 응답값으로 처리해주겠다 라는 것
return dr;
}