In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-04-02 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/01 Report--
Today, I would like to share with you the relevant knowledge about how to achieve automatic idempotency of the interface with Spring Boot. The content is detailed and the logic is clear. I believe most people still know too much about this, so share this article for your reference. I hope you can get something after reading this article. Let's learn about it together.
Preface
In actual development projects, an exposed interface is often faced with many requests. Let's explain the idempotent concept: the impact of any number of execution is the same as that of a single execution. According to this meaning, the ultimate meaning is that the impact on the database can only be one-off, can not be repeated processing. How to ensure its idempotency, there are usually the following means:
The establishment of a unique index in the database can ensure that only one piece of data is eventually inserted into the database.
Token mechanism: get a token before each API request, then add the token to the header body of the request next time, and verify it in the background. If the verification passes the deletion of the token, the token will be judged again in the next request.
Pessimistic lock or optimistic lock can guarantee that other sql cannot update data every time for update (when the database engine is innodb, the condition of select must be a unique index to prevent locking the whole table)
First query and then judge, first by querying whether there is data in the database, if the existence proof has been requested, directly reject the request, if it does not exist, it is proved to be the first time to come in and let it go directly.
Schematic diagram of redis to achieve automatic idempotency:
First, build the redis service Api1: first, build the redis server. 2: introduce the stater of redis in springboot, or the jedis encapsulated by Spring. The api mainly used later is its set method and exists method. Here we use the encapsulated redisTemplate of springboot.
/ * *
* redis tool class
* /
@ Component
Public class RedisService {
@ Autowired
Private RedisTemplate redisTemplate
/ * *
* write cache
* @ param key
* @ param value
* @ return
* /
Public boolean set (final String key, Object value) {
Boolean result = false
Try {
ValueOperations operations = redisTemplate.opsForValue ()
Operations.set (key, value)
Result = true
} catch (Exception e) {
E.printStackTrace ()
}
Return result
}
/ * *
* write cache set aging time
* @ param key
* @ param value
* @ return
* /
Public boolean setEx (final String key, Object value, Long expireTime) {
Boolean result = false
Try {
ValueOperations operations = redisTemplate.opsForValue ()
Operations.set (key, value)
RedisTemplate.expire (key, expireTime, TimeUnit.SECONDS)
Result = true
} catch (Exception e) {
E.printStackTrace ()
}
Return result
}
/ * *
* determine whether there is a corresponding value in the cache
* @ param key
* @ return
* /
Public boolean exists (final String key) {
Return redisTemplate.hasKey (key)
}
/ * *
* read cache
* @ param key
* @ return
* /
Public Object get (final String key) {
Object result = null
ValueOperations operations = redisTemplate.opsForValue ()
Result = operations.get (key)
Return result
}
/ * *
* Delete the corresponding value
* @ param key
* /
Public boolean remove (final String key) {
If (exists (key)) {
Boolean delete = redisTemplate.delete (key)
Return delete
}
Return false
}
}
Second: custom annotation AutoIdempotent customize an annotation, the main purpose of defining this annotation is to add it to the method that needs to achieve idempotency, whenever a method annotates it, it will be automatically idempotent. If the background uses reflection to scan this annotation, it will process the method to achieve automatic idempotency, using meta-annotation ElementType.METHOD to indicate that it can only be placed on the method, and etentionPolicy.RUNTIME to indicate that it is at run time.
@ Target ({ElementType.METHOD})
@ Retention (RetentionPolicy.RUNTIME)
Public @ interface AutoIdempotent {
}
3: token create and verify 1:token service interface We create a new interface and create a token service, which mainly consists of two methods, one for creating token and the other for verifying token. The creation of token mainly produces a string, and the main purpose of verifying token is to convey the request object, so why pass the request object? The main function is to get the token in the header, then verify it, and get the specific error information back to the front end through the thrown Exception.
Public interface TokenService {
/ * *
* create token
* @ return
* /
Public String createToken ()
/ * *
* verify token
* @ param request
* @ return
* /
Public boolean checkToken (HttpServletRequest request) throws Exception
}
2:token 's service implementation class token refers to the redis service. Create token to generate random uuid strings using a random algorithm tool class, and then put them into redis (to prevent redundant retention of data, the expiration time is set to 10000 seconds, depending on the business). If the placement is successful, the token value is returned. The checkToken method is to get the value of token from header (or from paramter if you can't get it from header), and throw an exception if it doesn't exist. This exception information can be captured by the interceptor and returned to the front end.
@ Service
Public class TokenServiceImpl implements TokenService {
@ Autowired
Private RedisService redisService
/ * *
* create token
*
* @ return
* /
@ Override
Public String createToken () {
String str = RandomUtil.randomUUID ()
StrBuilder token = new StrBuilder ()
Try {
Token.append (Constant.Redis.TOKEN_PREFIX) .append (str)
RedisService.setEx (token.toString (), token.toString (), 10000L)
Boolean notEmpty = StrUtil.isNotEmpty (token.toString ())
If (notEmpty) {
Return token.toString ()
}
} catch (Exception ex) {
Ex.printStackTrace ()
}
Return null
}
/ * *
* verify token
*
* @ param request
* @ return
* /
@ Override
Public boolean checkToken (HttpServletRequest request) throws Exception {
String token = request.getHeader (Constant.TOKEN_NAME)
Token does not exist in if (StrUtil.isBlank (token)) {/ / header
Token = request.getParameter (Constant.TOKEN_NAME)
Token also does not exist in if (StrUtil.isBlank (token)) {/ / parameter
Throw new ServiceException (Constant.ResponseCode.ILLEGAL_ARGUMENT, 100)
}
}
If (! redisService.exists (token)) {
Throw new ServiceException (Constant.ResponseCode.REPETITIVE_OPERATION, 200)
}
Boolean remove = redisService.remove (token)
If (! remove) {
Throw new ServiceException (Constant.ResponseCode.REPETITIVE_OPERATION, 200)
}
Return true
}
}
4: the interceptor's configuration 1:web configuration class implements WebMvcConfigurerAdapter. The main function is to add autoIdempotentInterceptor to the configuration class, so that we can only take effect when we go to the interceptor. Note the @ Configuration annotation, so that it can be added to the context when the container starts.
@ Configuration
Public class WebConfiguration extends WebMvcConfigurerAdapter {
@ Resource
Private AutoIdempotentInterceptor autoIdempotentInterceptor
/ * *
* add interceptor
* @ param registry
* /
@ Override
Public void addInterceptors (InterceptorRegistry registry) {
Registry.addInterceptor (autoIdempotentInterceptor)
Super.addInterceptors (registry)
}
}
2: intercept processor: the main function is to intercept the scanned AutoIdempotent to the annotated method, and then call the checkToken () method of tokenService to verify whether the token is correct. If an exception is caught, the exception information is rendered as json and returned to the front end.
/ * *
* interceptor
* /
@ Component
Public class AutoIdempotentInterceptor implements HandlerInterceptor {
@ Autowired
Private TokenService tokenService
/ * *
* Pretreatment
*
* @ param request
* @ param response
* @ param handler
* @ return
* @ throws Exception
* /
@ Override
Public boolean preHandle (HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
If (! (handler instanceof HandlerMethod)) {
Return true
}
HandlerMethod handlerMethod = (HandlerMethod) handler
Method method = handlerMethod.getMethod ()
/ / scan marked by ApiIdempotment
AutoIdempotent methodAnnotation = method.getAnnotation (AutoIdempotent.class)
If (methodAnnotation! = null) {
Try {
Return tokenService.checkToken (request); / / idempotent check. If the check passes, it is released. If the check fails, an exception is thrown, and a friendly prompt is returned through unified exception handling.
} catch (Exception ex) {
ResultVo failedResult = ResultVo.getFailedResult (101, ex.getMessage ())
WriteReturnJson (response, JSONUtil.toJsonStr (failedResult))
Throw ex
}
}
/ / true must be returned, otherwise all requests will be intercepted
Return true
}
@ Override
Public void postHandle (HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@ Override
Public void afterCompletion (HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
/ * *
* returned json value
* @ param response
* @ param json
* @ throws Exception
* /
Private void writeReturnJson (HttpServletResponse response, String json) throws Exception {
PrintWriter writer = null
Response.setCharacterEncoding ("UTF-8")
Response.setContentType ("text/html; charset=utf-8")
Try {
Writer = response.getWriter ()
Writer.print (json)
} catch (IOException e) {
} finally {
If (writer! = null)
Writer.close ()
}
}
}
Five: test case 1: simulate the business request class first we need to get the specific token through the getToken () method through the / get/token path, and then we call the testIdempotence method, which is annotated with @ AutoIdempotent, and the interceptor will intercept all requests. When it is determined that the processed method has the annotation, the checkToken () method in TokenService will be called. If an exception is caught, the exception will be thrown to the caller. Let's simulate the request:
@ RestController
Public class BusinessController {
@ Resource
Private TokenService tokenService
@ Resource
Private TestService testService
@ PostMapping ("/ get/token")
Public String getToken () {
String token = tokenService.createToken ()
If (StrUtil.isNotEmpty (token)) {
ResultVo resultVo = new ResultVo ()
ResultVo.setCode (Constant.code_success)
ResultVo.setMessage (Constant.SUCCESS)
ResultVo.setData (token)
Return JSONUtil.toJsonStr (resultVo)
}
Return StrUtil.EMPTY
}
@ AutoIdempotent
@ PostMapping ("/ test/Idempotence")
Public String testIdempotence () {
String businessResult = testService.testIdempotence ()
If (StrUtil.isNotEmpty (businessResult)) {
ResultVo successResult = ResultVo.getSuccessResult (businessResult)
Return JSONUtil.toJsonStr (successResult)
}
Return StrUtil.EMPTY
}
}
2: use the postman request to first access the get/token path to get specific to token:
By getting the token and putting the specific request into the header, you can see that the first request is successful, and then we request the second time:
The second request is returned to the repetitive operation. It can be seen that the repetitive verification has passed. When we make repeated requests, we will only make it successful for the first time and fail for the second time:
These are all the contents of the article "how to achieve interface auto-idempotency in Spring Boot". Thank you for reading! I believe you will gain a lot after reading this article. The editor will update different knowledge for you every day. If you want to learn more knowledge, please pay attention to the industry information channel.
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.