https://mangkyu.tistory.com/173
https://www.baeldung.com/spring-cache-tutorial
https://shinsunyoung.tistory.com/50
https://www.baeldung.com/spring-retry
https://www.hanumoka.net/2020/07/02/springBoot-20200702-sringboot-async-service/
https://junhyunny.github.io/information/spring-boot/javascript/content-type-and-spring-annotation/
https://docs.spring.io/spring-boot/docs/current/reference/html/application-properties.html
https://velog.io/@sa833591/Spring-Security-5-Spring-Security-Filter-%EC%A0%81%EC%9A%A9
https://www.baeldung.com/spring-boot-dispatcherservlet-web-xml
https://eunbc-2020.tistory.com/200
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"
@ConstraintValidator
@Constraint(validatedBy = ...)
https://cheese10yun.github.io/ConstraintValidator/
https://docs.spring.io/spring-boot/docs/current/reference/html/application-properties.html
https://exhibitlove.tistory.com/302
https://logical-code.tistory.com/118
https://pomo0703.tistory.com/100
https://github.com/rolroralra/hello-spring/tree/master/src/main/java/sample/dao/impl
Details
package com.example.controller;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.example.domain.Result;
import com.example.domain.Stock;
import com.example.service.StockService;
@RestController
@CrossOrigin()
@RequestMapping("/api/v1/stock")
public class StockController {
private static final Logger LOGGER = LoggerFactory.getLogger(StockController.class);
@Autowired
private StockService stockService;
@GetMapping("/alt1/{productId}")
public ResponseEntity<Result<Stock>> findWithResponseEntity(@PathVariable String productId) {
Result<Stock> result = new Result<Stock>();
result.add(linkTo(methodOn(StockController.class).find(productId)).withRel("find"));
result.add(linkTo(methodOn(StockController.class).register(result.getResult())).withRel("register"));
result.add(linkTo(methodOn(StockController.class).modify(result.getResult())).withRel("modify"));
result.add(linkTo(methodOn(StockController.class).remove(productId)).withRel("remove"));
return new ResponseEntity<Result<Stock>>(result, HttpStatus.ACCEPTED);
}
@GetMapping("/{productId}")
public Result<Stock> find(@PathVariable String productId) {
LOGGER.info("find {}", productId);
Result<Stock> result = new Result<>();
Stock stock = stockService.find(productId);
if (stock == null) {
result.setErrorCode(HttpStatus.NOT_FOUND.value());
result.setErrorMessage("Not found.");
} else {
result.setResult(stock);
}
return result;
}
@PostMapping()
public Result<String> register(@RequestBody Stock newStock) {
LOGGER.info("register {}", newStock);
Result<String> result = new Result<>();
Stock resultStock = stockService.register(newStock);
if (resultStock == null) {
result.setErrorCode(HttpStatus.BAD_REQUEST.value());
result.setErrorMessage("Already Exists.");
} else {
result.setResult(resultStock.getProductId());
}
return result;
}
@PutMapping()
public Result<String> modify(@RequestBody Stock newStock) {
LOGGER.info("modify {}", newStock);
Result<String> result = new Result<>();
Stock resultStock = stockService.modify(newStock);
if (resultStock == null) {
result.setErrorCode(HttpStatus.NOT_FOUND.value());
result.setErrorMessage("Not found.");
} else {
result.setResult(resultStock.getProductId());
}
return result;
}
@DeleteMapping("/{productId}")
public Result<Stock> remove(@PathVariable String productId) {
LOGGER.info("remove {}", productId);
stockService.remove(productId);
return new Result<Stock>();
}
}
https://www.baeldung.com/spring-expression-language
https://www.concretepage.com/spring-5/spring-value-default#List
Single Text Oriented Message Protocol
https://supawer0728.github.io/2018/03/30/spring-websocket/
https://yonguri.tistory.com/82
- Spring Gemfire https://spring.io/guides/gs/caching-gemfire/
- Hazelcast, Memcached
https://junhyunny.github.io/spring-mvc/quartz-in-spring-mvc/
https://mangkyu.tistory.com/76
https://mangkyu.tistory.com/77
https://www.youtube.com/watch?v=4x1QRyMIjGU
https://veluxer62.github.io/translate/spring-webflux/
https://www.thymeleaf.org/doc/articles/standarddialect5minutes.html
https://spring.io/guides/gs/spring-boot-docker/
https://goddaehee.tistory.com/154
https://escapefromcoding.tistory.com/248
Details
method | description |
---|---|
allOf | matches if all matchers match (short circuits) |
anyOf | matches if any matchers match (short circuits) |
not | matches if the wrapped matcher doesn’t match and vice |
equalTov | test object equality using the equals method |
is | decorator for equalTo to improve readability |
hasToString | test Object.toString |
instanceOf, isCompatibleType | test type |
notNullValue, nullValue | test for null |
sameInstance | test object identity |
hasEntry, hasKey, hasValue | test a map contains an entry, key or value |
hasItem, hasItems | test a collection contains elements |
hasItemInArray | test an array contains an element |
closeTo | test floating point values are close to a given value |
greaterThan, greaterThanOrEqualTo, lessThan, lessThanOrEqualTo | compare number |
equalToIgnoringCase | test string equality ignoring case |
equalToIgnoringWhiteSpace | test string equality ignoring differences in runs of whitespace |
containsString, endsWith, startsWith | test string matching |
ApiDocumentUtil
package com.sds.nexledger.smartPass.controller.common;
import org.springframework.restdocs.operation.preprocess.OperationRequestPreprocessor;
import org.springframework.restdocs.operation.preprocess.OperationResponsePreprocessor;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.*;
public interface ApiDocumentUtils {
static OperationRequestPreprocessor getDocumentRequestPreprocessor() {
return preprocessRequest(
modifyUris()
.scheme("http")
.host("localhost")
.port(8080),
prettyPrint());
}
static OperationResponsePreprocessor getDocumentResponsePreprocessor() {
return preprocessResponse(prettyPrint());
}
}
CommonControllerTests
package com.sds.nexledger.smartPass.controller.common;
import io.restassured.RestAssured;
import io.restassured.builder.RequestSpecBuilder;
import io.restassured.specification.RequestSpecification;
import org.junit.Before;
import org.junit.Rule;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.restdocs.JUnitRestDocumentation;
import static org.springframework.restdocs.restassured3.RestAssuredRestDocumentation.documentationConfiguration;
public class CommonControllerTests implements ApiDocumentUtils {
@Rule
public JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation();
@LocalServerPort
int port;
public RequestSpecification spec;
@Before
public void before() {
this.spec = new RequestSpecBuilder()
.addFilter(documentationConfiguration(this.restDocumentation))
.build();
RestAssured.baseURI = "http://localhost";
RestAssured.port = this.port;
}
}
MockMVC
package com.sds.nexledger.smartPass.controller;
import com.google.gson.Gson;
import com.google.gson.internal.LinkedTreeMap;
import com.google.gson.reflect.TypeToken;
import com.sds.nexledger.smartPass.controller.common.ApiDocumentUtils;
import com.sds.nexledger.smartPass.controller.dto.offchain.DataDTO;
import com.sds.nexledger.smartPass.controller.dto.offchain.DataRequestDTO;
import com.sds.nexledger.smartPass.controller.dto.offchain.OwnershipDTO;
import com.sds.nexledger.smartPass.controller.dto.offchain.OwnershipRequestDTO;
import com.sds.nexledger.smartPass.util.gson.DateDeserializer;
import lombok.extern.slf4j.Slf4j;
import org.junit.FixMethodOrder;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.runners.MethodSorters;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureWebMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders;
import org.springframework.restdocs.payload.JsonFieldType;
import org.springframework.restdocs.snippet.Attributes;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.ResultActions;
import java.util.ArrayList;
import java.util.List;
import java.util.StringJoiner;
import java.util.stream.IntStream;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.*;
import static org.springframework.restdocs.payload.PayloadDocumentation.*;
import static org.springframework.restdocs.request.RequestDocumentation.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.multipart;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
@AutoConfigureWebMvc
@AutoConfigureRestDocs
@ExtendWith(SpringExtension.class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Slf4j
public class ControllerMockMvcTests {
@Autowired
public MockMvc mockMvc;
@Autowired
public Gson gson;
private final String BASE_URL = "/offchain/v1";
private final String POSTFIX_REST_DOC = "_MockMvc";
// @MockBean
// public OffchainService offchainService;
@AfterEach
public void tearDown() {
}
@Test
public void getDataAll() {
try {
String restDocumentName = "getDataAll" + POSTFIX_REST_DOC;
// Given
// Mockito.when(offchainService.getAllData()).thenReturn(Collections.singletonList(new DataDTO()));
// When
String url = BASE_URL+ "/data/all/metadata";
ResultActions resultActions = mockMvc.perform(
get(url).accept(MediaType.APPLICATION_JSON)
);
// Then
MvcResult mvcResult = resultActions
.andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andDo(document(
restDocumentName,
ApiDocumentUtils.getDocumentRequestPreprocessor(),
ApiDocumentUtils.getDocumentResponsePreprocessor(),
responseFields(
fieldWithPath("[].dataId").type(JsonFieldType.STRING).description("dataId"),
fieldWithPath("[].name").type(JsonFieldType.STRING).description("name"),
fieldWithPath("[].type").type(JsonFieldType.STRING).description("type"),
fieldWithPath("[].fileId").type(JsonFieldType.STRING).description("fileId"),
fieldWithPath("[].description").type(JsonFieldType.STRING).description("description").optional(),
fieldWithPath("[].hash").type(JsonFieldType.STRING).description("hash"),
fieldWithPath("[].creator").type(JsonFieldType.STRING).description("creator"),
fieldWithPath("[].updater").type(JsonFieldType.STRING).description("updater"),
fieldWithPath("[].timestamp").type(JsonFieldType.STRING).attributes(Attributes.key("format").value(DateDeserializer.DEFAULT_DATE_FORMAT)).description("timestamp"),
fieldWithPath("[].properties").type(JsonFieldType.OBJECT).description("properties").optional(),
fieldWithPath("[].version").type(JsonFieldType.NUMBER).description("version"),
fieldWithPath("[].status").type(JsonFieldType.STRING).description("status"),
fieldWithPath("[].expirationTime").type(JsonFieldType.NUMBER).description("expirationTime").optional()))
)
.andReturn();
// Return JSON type Check
String responseString = mvcResult.getResponse().getContentAsString();
List<DataDTO> list = gson.fromJson(responseString, TypeToken.getParameterized(List.class, DataDTO.class).getType());
Assertions.assertNotNull(list);
} catch (Throwable e) {
Assertions.fail(e);
}
}
}
RestAssured
package com.sds.nexledger.smartPass.controller;
import com.google.gson.Gson;
import com.google.gson.internal.LinkedTreeMap;
import com.google.gson.reflect.TypeToken;
import com.sds.nexledger.smartPass.controller.common.ApiDocumentUtils;
import com.sds.nexledger.smartPass.controller.common.CommonControllerTests;
import com.sds.nexledger.smartPass.controller.dto.offchain.DataDTO;
import com.sds.nexledger.smartPass.controller.dto.offchain.DataRequestDTO;
import com.sds.nexledger.smartPass.controller.dto.offchain.OwnershipDTO;
import com.sds.nexledger.smartPass.controller.dto.offchain.OwnershipRequestDTO;
import com.sds.nexledger.smartPass.util.gson.DateDeserializer;
import io.restassured.RestAssured;
import io.restassured.http.ContentType;
import io.restassured.path.json.config.JsonPathConfig;
import io.restassured.response.Response;
import lombok.extern.slf4j.Slf4j;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.jupiter.api.Assertions;
import org.junit.runner.RunWith;
import org.junit.runners.MethodSorters;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.restdocs.payload.JsonFieldType;
import org.springframework.restdocs.snippet.Attributes;
import org.springframework.test.context.junit4.SpringRunner;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.IntStream;
import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.equalTo;
import static org.springframework.restdocs.payload.PayloadDocumentation.*;
import static org.springframework.restdocs.request.RequestDocumentation.*;
import static org.springframework.restdocs.restassured3.RestAssuredRestDocumentation.document;
@AutoConfigureRestDocs
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Slf4j
public class OffchainControllerRestAssuredTests extends CommonControllerTests {
private final String BASE_URL = "/offchain/v1";
@Autowired
private Gson gson;
private final JsonPathConfig jsonPahConfig = JsonPathConfig.jsonPathConfig()
.gsonObjectMapperFactory((type, s) -> this.gson.newBuilder().create());
@Test
public void getDataAll() {
try {
String restDocumentName = "getDataAll";
String url = BASE_URL+ "/data/all/metadata";
Response response =
given(this.spec)
.accept(ContentType.JSON)
.filter(document(
restDocumentName,
ApiDocumentUtils.getDocumentRequestPreprocessor(),
ApiDocumentUtils.getDocumentResponsePreprocessor(),
// pathParameters(),
// requestFields(),
responseFields(
fieldWithPath("[].dataId").type(JsonFieldType.STRING).description("dataId"),
fieldWithPath("[].name").type(JsonFieldType.STRING).description("name"),
fieldWithPath("[].type").type(JsonFieldType.STRING).description("type"),
fieldWithPath("[].fileId").type(JsonFieldType.STRING).description("fileId"),
fieldWithPath("[].description").type(JsonFieldType.STRING).description("description").optional(),
fieldWithPath("[].hash").type(JsonFieldType.STRING).description("hash"),
fieldWithPath("[].creator").type(JsonFieldType.STRING).description("creator"),
fieldWithPath("[].updater").type(JsonFieldType.STRING).description("updater"),
fieldWithPath("[].timestamp").type(JsonFieldType.STRING).attributes(Attributes.key("format").value(DateDeserializer.DEFAULT_DATE_FORMAT)).description("timestamp"),
fieldWithPath("[].properties").type(JsonFieldType.OBJECT).description("properties").optional(),
fieldWithPath("[].version").type(JsonFieldType.NUMBER).description("version"),
fieldWithPath("[].status").type(JsonFieldType.STRING).description("status"),
fieldWithPath("[].expirationTime").type(JsonFieldType.NUMBER).description("expirationTime").optional()
))
)
.when()
.log().all()
.port(RestAssured.port)
.get(url)
.then()
.assertThat()
.statusCode(HttpStatus.OK.value())
.contentType(ContentType.JSON)
.log().all()
.extract().response();
// ResponseBody JSON type Check
String responseString = response.getBody().asString();
List<DataDTO> list = gson.fromJson(responseString, TypeToken.getParameterized(List.class, DataDTO.class).getType());
Assertions.assertNotNull(list);
} catch (Exception e) {
Assertions.fail(e);
}
}
}
https://code.sdsdev.co.kr/blockchain
[https://pjh3749.tistory.com/260](https://pjh3749.tistory.com/260
Details
import io.micrometer.core.instrument.util.StringUtils;
import io.swagger.annotations.ApiOperation;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.ClassUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
// TODO : [LEGO-4663] - 옵션 제공 추가
@Profile({"default", "local", "dev"})
@Aspect
@Component
public class LegoControllerLoggingAOP {
@Pointcut("within(@org.springframework.stereotype.Controller *) || within(@org.springframework.web.bind.annotation.RestController *)")
public void pointcutController() {}
@Pointcut("execution(* com.sds.lego..rest..*(..))")
public void pointcutLegoRestPackage() {}
@Around("pointcutController() && pointcutLegoRestPackage()")
public Object aroundLegoRestController(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
HttpServletRequest request = findCurrentHttpServletRequest();
Class<?> targetClass = AopUtils.getTargetClass(proceedingJoinPoint.getTarget());
Method targetMethod = findTargetMethod(proceedingJoinPoint);
if (ObjectUtils.allNotNull(request, targetClass, targetMethod)) {
loggingRequestIfPossible(request, targetClass, targetMethod, proceedingJoinPoint.getArgs());
}
return proceedingJoinPoint.proceed();
}
private void loggingRequestIfPossible(HttpServletRequest request, Class<?> targetClass, Method targetMethod, Object[] args) {
Logger logger = LoggerFactory.getLogger(targetClass);
if (!logger.isDebugEnabled()) {
return;
}
// ex) [GET] /rest/v2/blah/blah... (methodName) : methodDescription
// ex) [GET] /rest/v2/blah/blah... (methodName)
logger.debug("[{}] {} ({}){}",
request.getMethod(),
request.getRequestURI(),
targetMethod.getName(),
getApiOperationDescription(targetMethod));
if (args != null && args.length > 0) {
List<String> params = new ArrayList<>();
for (Object arg : args) {
if (arg == null) {
params.add("null");
}
else if (ClassUtils.isPrimitiveOrWrapper(arg.getClass())) {
params.add(arg.toString());
}
else {
params.add("{" + arg.getClass().getSimpleName() + "}");
}
}
logger.debug("Parameters : {}", params);
}
}
// region [Privates - Etc]
private Method findTargetMethod(ProceedingJoinPoint proceedingJoinPoint) {
Signature signature = proceedingJoinPoint.getSignature();
if (signature instanceof MethodSignature) {
return ((MethodSignature) signature).getMethod();
}
return null;
}
private HttpServletRequest findCurrentHttpServletRequest() {
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
if (requestAttributes instanceof ServletRequestAttributes) {
return ((ServletRequestAttributes) requestAttributes).getRequest();
}
return null;
}
private String getApiOperationDescription(Method targetMethod) {
ApiOperation apiOperation = findAnnotation(targetMethod, ApiOperation.class);
if (apiOperation == null) {
return "";
}
String value = apiOperation.value();
if (StringUtils.isBlank(value)) {
return "";
}
return " : " + value;
}
@SuppressWarnings({"unchecked", "SameParameterValue"})
private <T extends Annotation> T findAnnotation(Method targetMethod, Class<T> clazz) {
Annotation[] annotations = targetMethod.getAnnotations();
for (Annotation annotation : annotations) {
if (clazz.isAssignableFrom(annotation.getClass())) {
return (T) annotation;
}
}
return null;
}
// endregion
}
https://ksshlee.github.io/spring/springboot/swagger/
https://www.baeldung.com/swagger-2-documentation-for-spring-rest-api
https://velog.io/@gillog/Swagger-UI-Annotation-%EC%84%A4%EB%AA%85
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인가? |