https://cchoimin.tistory.com/entry/Valid-%EC%84%B8%ED%8C%85-%EB%B0%8F-%EC%82%AC%EC%9A%A9%EB%B2%95
https://www.daleseo.com/lombok-useful-annotations/
- @NonNull
롬복에서 제공하는 @NonNull은 메서드의 인자에 사용하면 null이 들어올 시 NullPointerException을 발생시킨다.
https://www.baeldung.com/a-guide-to-java-enums
https://javacan.tistory.com/entry/114
https://nhj12311.tistory.com/469
https://hianna.tistory.com/546
https://mangkyu.tistory.com/14
https://jeong-pro.tistory.com/222
-
Servlet Context vs Root Web Application Context
https://www.inflearn.com/questions/71872
https://velog.io/@sixhustle/Jasypt
Details
@Configuration
@EnableConfigurationProperties(JasyptConfigProperties.class)
@RequiredArgsConstructor
public class JasyptConfig {
private final JasyptConfigProperties jasyptConfigProperties;
@Bean(name = "jasyptStringEncryptor")
public StringEncryptor stringEncryptor() {
PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor();
SimpleStringPBEConfig config = new SimpleStringPBEConfig();
config.setPassword(System.getProperty("jasypt.encryptor.password"));
config.setAlgorithm(jasyptConfigProperties.getAlgorithm());
config.setKeyObtentionIterations(jasyptConfigProperties.getKeyObtentionIterations());
config.setIvGeneratorClassName(jasyptConfigProperties.getIvGeneratorClassName());
config.setPoolSize(jasyptConfigProperties.getPoolSize());
config.setSaltGeneratorClassName(jasyptConfigProperties.getSaltGeneratorClassName());
config.setStringOutputType(jasyptConfigProperties.getStringOutputType());
encryptor.setConfig(config);
return encryptor;
}
}
@Component
@ConfigurationProperties(value = "jasypt.encryptor")
@Data
public class JasyptConfigProperties {
private String algorithm = "PBEWITHHMACSHA512ANDAES_256";
private String keyObtentionIterations = "1000";
private String poolSize = "1";
private String saltGeneratorClassName = "org.jasypt.salt.RandomSaltGenerator";
private String stringOutputType = "base64";
private String ivGeneratorClassName = "org.jasypt.iv.RandomIvGenerator";
}
bootRun {
if ( project.hasProperty('jvmArgs') ) {
jvmArgs = (project.jvmArgs.split("\\s+") as List)
}
}
jasypt:
encryptor:
bean: jasyptStringEncryptor
password: password. # VM Options을 통해서 실행하면 생략가능.
# Way1. Application run (Add VM Options)
$ java -jar xxx.jar -Djasypt.encrpytor.password=password
# Way2. gradle bootrun
$ gradle bootRun -PjvmArgs="-Djasypt.encryptor.password=password"
https://blog.outsider.ne.kr/710
pom.xml
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>31.0.1-jre</version>
<!-- or, for Android: -->
<version>31.0.1-android</version>
</dependency>
build.gradle
dependencies {
// Pick one:
// 1. Use Guava in your implementation only:
implementation("com.google.guava:guava:31.0.1-jre")
// 2. Use Guava types in your public API:
api("com.google.guava:guava:31.0.1-jre")
// 3. Android - Use Guava in your implementation only:
implementation("com.google.guava:guava:31.0.1-android")
// 4. Android - Use Guava types in your public API:
api("com.google.guava:guava:31.0.1-android")
}
https://recordsoflife.tistory.com/474
pom.xml
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
build.gradle
implementation 'org.apache.commons:commons-lang3:3.12.0'
https://giljae.medium.com/graalvm-%EC%86%8C%EA%B0%9C-84ac547f8df2
https://meetup.toast.com/posts/273
Details
#################################
# How to install Java in Ubuntu #
#################################
# 기존 버전 설치 여부 확인
$ java -version
$ dpkg -l | grep openjdk
$ whereis java
$ which java
# https://www.azul.com/downloads/zulu/
# Openjdk-8-jdk Local Install
$ sudo su
$ tar -xzvf java-8-openjdk-amd64.tar.gz -C /usr/lib/jvm
# /usr/bin/java 라는 파일이 없을 때, 심볼릭 링크 생성
$ ln -s /usr/lib/java-openjdk-8-amd64/bin/java /usr/bin/java
$ update-alternatives --install "/usr/bin/java" "java" "/usr/lib/jdk/java-openjdk-8-amd64/bin/java" 1
# 운영체제 Architecture 확인 방법 (amd vs arm)
$ dpkg --print-architecture
# 설치 확인
$ java -version
Details
$ git clone https://github.com/rolroralra/InstallCert
$ javac InstallCert.java
$ ls InstallCert*.class
InstallCert$SavingTrustManager.class InstallCert.class
# Download Cetificate from "plugins.jetbrains.com"
# And then Import Certificate to "$JAVA_HOME/jre/lib/security/cacerts"
$ java InstallCert plugins.jetbrains.com
$ cd $JAVA_HOME/jre/lib/security
$ keystore -exportcert -alias plugins.jetbrains.com -keystore ./cacerts -storepass changeit -file plugins.jetbrains.com.cer
$ keytool -keystore ${JAVA_HOME}\jre\lib\security\cacerts -importcert -alias sds -file C:\Users\SDS\SDS.crt
Details
# get $JAVA_HOME
$ which java
$ whereis java
$ cd $JAVA_HOME/jre/lib/security;
$ keytool -import -file ${CERT_FILE_PATH} -keystore ./cacerts -storepass "changeit"
Detail
public final class RequestWrapper extends HttpServletRequestWrapper {
public RequestWrapper(HttpServletRequest request) {
super(request);
}
public String[] getParameterValues(String parameter) {
String[] values = super.getParameterValues(parameter);
if (values == null) {
return null;
}
int count = values.length;
String[] encodeValues = new String[count];
for (int i = 0; i < count; i++) {
encodeValues[i] = cleanXSS(values[i]);
}
return encodeValues;
}
public String getParameter(String parameter) {
String value = super.getParameter(parameter);
if (value == null) {
return null;
}
return cleanXSS(value);
}
public String getHeader(String name) {
String value = super.getHeader(name);
if (value == null) {
return null;
}
return cleanXSS(value);
}
private String cleanXSS(String value) {
value = value.replaceAll("<", "& lt;").replaceAll(">", "& gt;");
value = value.replaceAll("\\(", "& #40;").replaceAll("\\),", "& 41;");
value = value.replaceAll("'", "& #39;");
value = value.replaceAll("eval\\((.*)\\)", "");
value = value.replaceAll("[\\\"\\'][\\s]*javascript:", "\"\"");
value = value.replaceAll("[\\\"\\'][\\s]*vbscript:", "\"\"");
value = value.replaceAll("script", "");
value = value.replaceAll("document.cookie", "<document.cookie");
value = value.replaceAll("<iframe", "<iframe");
value = value.replaceAll("<object", "<object");
value = value.replaceAll("<embed", "<embed");
value = value.replaceAll("onload", "no_onload");
value = value.replaceAll("expression", "no_expression");
value = value.replaceAll("onmouseover", "no_onmouseover");
value = value.replaceAll("onmouseout", "no_onmouseout");
value = value.replaceAll("onclick", "no_onclick");
return value;
}
}
Spring Getting Started: https://spring.io/guides/gs/gateway/
Detail
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.RemoveRequestHeaderGatewayFilterFactory;
import org.springframework.cloud.gateway.filter.factory.RemoveResponseHeaderGatewayFilterFactory;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
@Configuration
public class RouteConfig {
@Autowired
private Properties properties;
@Bean
public RouteLocator myRoute(RouteLocatorBuilder builder) {
GatewayFilter requestFilter = new RequestHistoryFilter();
GatewayFilter addRequestHeaderApiServiceFilter = new AddRequestHeaderApiServiceFilter();
GatewayFilter removeRequestHeaderAuthKeyFilter =
BeanUtil.getBean(RemoveRequestHeaderGatewayFilterFactory.class).apply(c->c.setName("Authorization"));
GatewayFilter removeRequestHeaderCookieFilter =
BeanUtil.getBean(RemoveRequestHeaderGatewayFilterFactory.class).apply(c->c.setName("Cookie"));
GatewayFilter removeResponseHeaderCookieFilter =
BeanUtil.getBean(RemoveResponseHeaderGatewayFilterFactory.class).apply(c->c.setName("cookie"));
String[] url = {
"/get", "/login", "/logout", "/home"
};
return builder.routes().route(p -> p
.path(url)
.uri("http://endpoint:port")
.build();
}
}
class AddRequestHeaderApiServiceFilter implements GatewayFilter {
private static final Logger LOGGER = LoggerFactory.getLogger(AddRequestHeaderApiServiceFilter.class);
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest().mutate().header("HEADER_NAME", "xxx").build();
return chain.filter(exchange.mutate().request(request).build());
}
}
https://www.baeldung.com/java-inputstream-to-outputstream
Detail
void copy(InputStream source, OutputStream target) throws IOException {
byte[] buf = new byte[8192];
int length;
while ((length = source.read(buf)) > 0) {
target.write(buf, 0, length);
}
}
@Test
public void givenUsingJavaNine_whenCopyingInputStreamToOutputStream_thenCorrect() throws IOException {
String initialString = "Hello World!";
try (InputStream inputStream = new ByteArrayInputStream(initialString.getBytes());
ByteArrayOutputStream targetStream = new ByteArrayOutputStream()) {
inputStream.transferTo(targetStream);
assertEquals(initialString, new String(targetStream.toByteArray()));
}
}
- https://www2.slideshare.net/madvirus/8-35205661
- https://www2.slideshare.net/gyumee/java-8-lambda-35352385
Detail
// Method Reference
// 1. static method reference
IntStream.range(-10,10).map(Math::abs)
// IntStream.range(-10,10).map(x -> Math.abs(x));
IntStream.range(1,32).mapToObj(Integer::toHexString);
//IntStream.range(1,32).map(x -> Ingeger.toHexString(x));
// 2. object instance method reference
IntStream.range(1,10).forEach(System.out::println);
// IntStream.range(1,10).forEach(x -> System.out.println(x);
// 3. class instance method reference
someOperation(XClass:double)
someOperation((x, y) -> x.double(y))
Detail
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
}
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
}
@FunctionalInterface
public interface Supplier<T> {
T get();
}
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
}
@FunctionalInterface
public interface BiConsumer<T, U> {
void accept(T t, U u);
}
@FunctionalInterface
public interface BiFunction<T, U, R> {
R apply(T t, U u);
}
// Bifunction<T, T, T>
@FunctionalInterface
public interface BinaryOperator<T> {
T apply(T t1, T t2);
}
/// Function<T, T>
@FunctionalInterface
public interface UnaryOperator<T> {
T apply(T t);
}
// IntFunction<R>
@FunctionalInterface
public interface IntFunction<R> {
R apply(int t);
}
// LongFunction<R>
@FunctionalInterface
public interface LongFunction<R> {
R apply(long t);
}
// DoubleFunction<R>
@FunctionalInterface
public interface DoubleFunction<R> {
R apply(double t);
}
// ToIntFunction<T>
@FunctionalInterface
public interface ToIntFunction<T> {
int applyAsInt(T t);
}
// ToLongFunction<T>
@FunctionalInterface
public interface ToLongFunction<T> {
long applyAsLong(T t);
}
// ToDoubleFunction<T>
@FunctionalInterface
public interface ToDoubleFunction<T> {
double applyAsDouble(T t);
}
// ToIntBiFunction<T, U>
@FunctionalInterface
public interface ToIntBiFunction<T, U> {
int applyAsInt(T t, U u);
}
// ToLongBiFunction<T, U>
@FunctionalInterface
public interface ToLongBiFunction<T, U> {
long applyAsLong(T t, U u);
}
// ToDoubleBiFunction<T, U>
@FunctionalInterface
public interface ToDoubleBiFunction<T, U> {
double applyAsDouble(T t, U u);
}
추가된 인터페이스의 하위 호환성 보장
Class의 method가 Interface의 default method보다 우선 순위
Diamond Problem (Interface01.super.method()) 기본적으로 Java는 다중 상속(extends) 허용치 않음. 인터페이스 다중 구현은 허용.
Detail
interface MyInterface1{
public static int num = 100;
public default void display() {
System.out.println("display method of MyInterface1");
}
}
interface MyInterface2{
public static int num = 1000;
public default void display() {
System.out.println("display method of MyInterface2");
}
}
public class InterfaceExample implements MyInterface1, MyInterface2{
public void display() {
MyInterface1.super.display();
//or,
MyInterface2.super.display();
}
public static void main(String args[]) {
InterfaceExample obj = new InterfaceExample();
obj.display();
}
}
Interface에 필요한 Utility Class 따로 만들 필요 없음.
Detail
import static org.junit.Assert.*;
import static java.lang.Math.*;
http://iloveulhj.github.io/posts/java/java-stream-api.html
Detail
카테고리 | Method Type | Method |
---|---|---|
Stream | static | of(T...), of(T), empty(), ofNullable(T) |
Stream | static | generate(Supplier), iterate(T, UnaryOperator) |
Stream | static | concat(Stream, Stream) |
Collection 객체 | instance | stream(), parallelStream() |
Arrays | static | stream(*) |
IntStream, LongStream | static | range(int startInclusive, int endExclusive), rangeClosed(int startInclusive, int endInclusive) |
Resource 객체 (BufferedReader) | instance | lines() |
// Stream<String> --> IntStream
Stream<String> stream = ...;
IntStream result = stream.mapToInt(String::length);
// IntStream --> Stream<Integer>
IntStream.range(0,5).mapToObj(Integer::valueOf);
Method | Return |
---|---|
Stream.filter(Predicate) | Stream |
Stream.map(Function<T,R>) | Stream |
Stream.flatMap(Function<T,Stream>) | Stream |
Stream.limit(long) | Stream |
Stream.skip(long) | Stream |
Stream.peek(Consumer) | Stream |
Method | Return |
---|---|
Stream.sorted() | Stream |
Stream.distinct() | Stream |
Method | Return |
---|---|
Stream.count() | long |
Stream.max(Comparator) | Optional |
Stream.min(Comparator) | Optional |
Stream.findFirst() | Optional |
Stream.findAny() | Optional |
Stream.allMatch(Predicate) | boolean |
Stream.anyMath(Predicate) | boolean |
Stream.noneMatch(Predicate) | boolean |
Stream.forEach(Consumer action) | void |
Stream.forEachOrdered(Consumer action) | void |
Stream.reduce(BinaryOperator accumulator) | Optional |
Stream.reduce(T identity, BinaryOperator accumulator) | T |
Stream.reduce(U identity, BiFunction<U, T, U> accumulator, BinaryOperator combiner) | U |
Stream.collect(Collector<T, A, R> collector) | R |
Stream.collect(Supplier supplier, BiConsumer<R, T> accumulator, BiConsumer<R, R> combiner) | R |
/**
* @param <T> the type of input elements to the reduction operation
* @param <A> the mutable accumulation type of the reduction operation (often
* hidden as an implementation detail)
* @param <R> the result type of the reduction operation
*/
public interface Collector<T, A, R> {
Supplier<A> supplier;
BiConsumer<A, T> accumulator;
BinaryOperator<A> combiner;
Function<A, R> finisher;
}
List<String> result = stream.collect(Collectors.toList());
Set<String> result = stream.collect(Collectors.toSet());
// Collectors.toCollection(Supplier<C>) : return Collector<T, ?, C>
TreeSet<String> result = stream.collect(Collectors.toCollection(TreeSet::new));
String result = stream.collect(Collectors.joining());
String result = stream.collect(Collectors.joining(", "));
String result = stream.collect(Collectors.joining(", ", "{", "}"));
// Collectors.toMap(Function<T,K> keyMapper, Function<T,U> valueMapper, BinaryOperator<U> mergeFunction, Supplier<M> mapFactory) => return Collector<T, ?, M>
Collector | Type |
---|---|
Collectors.toList() | Collector<<T, ?, List>> |
Collecotrs.toSet() | Collector<T, ?, Set> |
Collectors.toCollection(Supplier) | Collector<T, ?, C> |
Collectors.joining() | Collector<Charsequence, ?, String> |
Collectors.joining(Charsequence delimiter) | Collector<Charsequence, ?, String> |
Collectors.joining(Charsequence delimiter, Charsequence prefix, Charsequence suffix) | Collector<Charsequence, ?, String> |
Collectors.toMap(Function<T, K> keyMapper, Function<T, U> valueMapper) | Collector<T, ?, Map<K, U>> |
Collectors.toMap(Function<T, K> keyMapper, Function<T, U> valueMapper, BinaryOperator mergeFunction) | Collector<T, ?, Map<K, U>> |
Collectors.toMap(Function<T, K> keyMapper, Function<T, U> valueMapper, BinaryOperator mergeFunction, Supplier mapFactory) | Collector<T, ?, M> |
Collectors.partitioningBy(Predicate predicate) | Collector<T, ?, Map<Boolean, List>> |
Collectors.partitioningBy(Predicate predicate, Collector<T, A, D> downstream) | Collector<T, ?, Map<Boolean, D>> |
Collectors.groupingBy(Function<T, K> classifier) | Collector<T, ?, Map<K, List>> |
Collectors.groupingBy(Function<T, K> classifier, Collector<T, A, D> downstream) | Collector<T, ?, Map<K, D>> |
Collectors.groupingBy(Function<T, K> classifier, Supplier mapFactory, Collector<T, A, D> downstream) | Collector<T, ?, M> |
http://www.tcpschool.com/java/java_stream_optional
Detail
Method | Return |
---|---|
Optional.of(T) | Optional |
Optional.empty() | Optional |
Optional.ofNullable(T) | Optional |
Optional.get() | T |
Optional.isPresent | boolean |
Optional.isEmpty | boolean |
Optional.ifPresent(Consumer) | void |
Optional.ifPresentOrElse(Consumer, Runnable) | void |
Optional.map(Function<T,R>) | Optional |
Optional.flatMap(Function<T, Optional>) | Optional |
Optional.or(Supplier<Optional>) | Optional |
Optional.orElse(T) | R |
Optional.orElseGet(Supplier) | T |
Optional.orElseThrow() | T |
Optional.orElseThrow(Supplier) | T throws X |
https://jistol.github.io/java/2019/11/17/spliterator/
Detail
@SuppressWarnings("all") : 모든 경고를 억제
@SuppressWarnings("cast") : 캐스트 연산자 관련 경고 억제
@SuppressWarnings("dep-ann") : 사용하지 말아야 할 주석 관련 경고 억제
@SuppressWarnings("deprecation") : 사용하지 말아야 할 메소드 관련 경고 억제
@SuppressWarnings("fallthrough") : switch문에서의 break 누락 관련 경고 억제
@SuppressWarnings("finally") : 반환하지 않는 finally 블럭 관련 경고 억제
@SuppressWarnings("null") : null 분석 관련 경고 억제
@SuppressWarnings("rawtypes") : 제네릭을 사용하는 클래스 매개 변수가 불특정일 때의 경고 억제
@SuppressWarnings("unchecked") : 검증되지 않은 연산자 관련 경고 억제
@SuppressWarnings("unused") : 사용하지 않는 코드 관련 경고 억제
Detail
Set<String> countries = new HashSet<String>() {
{
add("India");
add("USSR");
add("USA");
}
};
assertTrue(countries.contains("India"));
Detail
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence"
version="2.2">
<persistence-unit name="test">
<properties>
<!-- 필수 속성 -->
<property name="javax.persistence.jdbc.driver" value="org.h2.Driver"/>
<property name="javax.persistence.jdbc.user" value="sa"/>
<property name="javax.persistence.jdbc.password" value=""/>
<property name="javax.persistence.jdbc.url" value="jdbc:h2:tcp://localhost/~/Workspace/h2-test/test"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>
<!-- 옵션 -->
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.format_sql" value="true"/>
<property name="hibernate.use_sql_comments" value="true"/>
<property name="hibernate.jdbc.batch_size" value="10"/>
<!-- <property name="hibernate.hbm2ddl.auto" value="create"/>-->
</properties>
</persistence-unit>
</persistence>
Details
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.LongAdder;
public class ConcurrentHashMapTest {
public static void main(String[] args) {
int loopSize = 30;
CountDownLatch countDownLatch = new CountDownLatch(loopSize);
ExecutorService tp = Executors.newFixedThreadPool(10);
// Map<String, LongAdder> testMap = new HashMap<>();
Map<String, LongAdder> testMap = new ConcurrentHashMap<>();
for(int i = 0; i < loopSize; i++) {
int selector = (i % 3);
String type = (selector == 0) ? "HR" : (selector == 1) ? "SALES" : "IT";
tp.submit(new Runner(type , countDownLatch, testMap));
}
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(testMap.toString());
tp.shutdown();
}
static class Runner implements Runnable {
private final String runnerNo;
private final CountDownLatch countDownLatch;
private Map<String, LongAdder> testMap;
Runner(String runnerNo, CountDownLatch countDownLatch, Map<String, LongAdder> testMap){
this.runnerNo = runnerNo;
this.countDownLatch = countDownLatch;
this.testMap = testMap;
}
@Override
public void run() {
try {
testMap.computeIfAbsent(runnerNo, (value) -> new LongAdder()).increment();
} catch (Exception e){
e.printStackTrace();
} finally {
countDownLatch.countDown();
}
}
}
}
https://0yumin.tistory.com/16
https://github.com/rolroralra/java-rmi
https://github.com/rolroralra/helloJMX
https://blog.naver.com/kkson50/220564273220
https://madplay.github.io/post/why-java-serialization-is-bad
http://wiki.gurubee.net/display/SWDEV/PipedInputStream%2C+PipedOutputStream
Details
- ExecutorService
- SingleThreadExecutor
- ScheduledThreadPoolExecutor
- Future
- CompletableFuture
- Callable
https://codechacha.com/ko/java-executors/
https://codechacha.com/ko/java-scheduled-thread-pool-executor/
https://codechacha.com/ko/java-synchronized-keyword/
https://bamdule.tistory.com/35
Details
Annotation | Description |
---|---|
@NotNull | Null 불가 |
@Null | Null만 입력 가능 |
@NotEmpty | Null, 빈 문자열 불가 |
@NotBlank | Null, 빈 문자열, 스페이스만 있는 문자열 불가 |
@Size(min=,max=) | 문자열, 배열 등의 크기가 만족하는가? |
@Pattern(regex=) | Regular Expression을 만족하는가? |
@Max(숫자) | 지정 값 이하인가? |
@Min(숫자) | 지정 값 이상인가? |
@Positive | 양수만 가능 |
@PositiveOrZero | 양수와 0만 가능 |
@Negative | 음수만 가능 |
@NegativeOrZero | 음수와 0만 가능 |
@Digits(integer=,fraction=) | 대상 수가 지정된 정수와 소수 자리 수 보다 작은가? |
@DecimalMax(value=) | 지정된 값(실수) 이하인가? |
@DecimalMin(value=) | 지정된 값(실수) 이상인가? |
이메일 형식만 가능 | |
@Future | 현재보다 미래인가? |
@Past | 현재보다 과거인가? |
@AssertFalse | false인가? |
@AssertTrue | true인가? |