resubmit
resubmit is a progressive anti-duplicate submission framework designed for java.
Recommended reading:
Interviewer: How did you prevent duplicate submissions in your project?
Introduction to resubmit Progressive Anti-Duplicate Submission Framework
creative purpose
Sometimes it is very troublesome to manually add to prevent repeated submissions, and manual writing every time is not conducive to reuse.
So I hope to realize a tool from simple to complex, which is convenient for daily use.
characteristic
- Progressive implementation, can be used independently of spring
- Based on annotation + bytecode, flexible configuration
- Support for programmatic calls
- Support annotation, perfect integration of spring
- Support integration of spring-boot
Changelog
quick start
maven import
<dependency>
<group>com.github.houbb</group>
<artifact>resubmit-core</artifact>
<version>1.0.0</version>
</dependency>
coding
- UserService.java
@Resubmit
The corresponding attributes are as follows:
Attributes | illustrate | Defaults |
---|---|---|
value() | How long to suppress duplicate submissions, in milliseconds. | 60000 |
@Resubmit(5000)
public void queryInfo(final String id) {
System.out.println("query info: " + id);
}
- test code
If the request is repeated within the specified time difference, an exception ResubmitException will be thrown
@Test(expected = ResubmitException.class)
public void errorTest() {
UserService service = ResubmitProxy.getProxy(new UserService());
service.queryInfo("1");
service.queryInfo("1");
}
If the same parameter is submitted twice directly, an error will be reported.
- Test Scenario 2
If the wait exceeds the specified 5s, no error will be reported.
@Test
public void untilTtlTest() {
UserService service = ResubmitProxy.getProxy(new UserService());
service.queryInfo("1");
DateUtil.sleep(TimeUnit.SECONDS, 6);
service.queryInfo("1");
}
customize
ResubmitProxy.getProxy(new UserService());
You can get the proxy corresponding to UserService.
Equivalent to:
ResubmitBs resubmitBs = ResubmitBs.newInstance()
.cache(new CommonCacheServiceMap())
.keyGenerator(new KeyGenerator())
.tokenGenerator(new HttpServletRequestTokenGenerator());
UserService service = ResubmitProxy.getProxy(new UserService(), resubmitBs);
Among them, ResubmitBs is used as a guide class, and the corresponding policies support customization.
Attributes | illustrate | Defaults |
---|---|---|
cache() | Cache Implementation Strategy | The default is a memory-based cache implementation based on the ConcurrentHashMap implementation |
keyGenerator() | The key implementation strategy is used to uniquely identify a method + parameters to determine whether it is the same submission | md5 strategy |
tokenGenerator() | The token implementation strategy is used to uniquely identify a user. | Obtained from the header attribute in HttpServletRequest resubmit_token |
Spring integration
maven import
<dependency>
<group>com.github.houbb</group>
<artifact>resubmit-spring</artifact>
<version>1.0.0</version>
</dependency>
code writing
- UserService.java
@Service
public class UserService {
@Resubmit(5000)
public void queryInfo(final String id) {
System.out.println("query info: " + id);
}
}
- SpringConfig.java
@ComponentScan("com.github.houbb.resubmit.test.service")
@EnableResubmit
@Configuration
public class SpringConfig {
}
@EnableResubmit annotation description
@EnableResubmit
, the user can specify the corresponding implementation strategy, which is convenient to adapt to the business scenario more flexibly.
One-to-one correspondence with the attributes that support custom in ResubmitBs
.
Attributes | illustrate | Defaults |
---|---|---|
cache() | Cache Implementation Strategy | The default is a memory-based cache implementation based on the ConcurrentHashMap implementation |
keyGenerator() | The key implementation strategy is used to uniquely identify a method + parameters to determine whether it is the same submission | md5 strategy |
tokenGenerator() | The token implementation strategy is used to uniquely identify a user. | Obtained from the header attribute in HttpServletRequest resubmit_token |
test code
@ContextConfiguration(classes = SpringConfig.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class ResubmitSpringTest {
@Autowired
private UserService service;
@Test(expected = ResubmitException.class)
public void queryTest() {
service.queryInfo("1");
service.queryInfo("1");
}
}
Integrate spring-boot
maven import
<dependency>
<groupId>com.github.houbb</groupId>
<artifactId>resubmit-springboot-starter</artifactId>
<version>1.0.0</version>
</dependency>
Code
- UserService.java
The implementation of this method is the same as before.
@Service
public class UserService {
@Resubmit(5000)
public void queryInfo(final String id) {
System.out.println("query info: " + id);
}
}
- Application.java
boot entry
@SpringBootApplication
public class ResubmitApplication {
public static void main(String[] args) {
SpringApplication.run(ResubmitApplication.class, args);
}
}
test code
@ContextConfiguration(classes = ResubmitApplication.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class ResubmitSpringBootStarterTest {
@Autowired
private UserService service;
@Test(expected = ResubmitException.class)
public void queryTest() {
service.queryInfo("1");
service.queryInfo("1");
}
}
custom strategy
The policies mentioned above in @EnableResubmit
support customization.
Here we only take cache as an example. For simplicity, the default is a cache implementation based on local memory.
If you are not a single point application, then a redis-based cache is more suitable
custom cache
Implement caching
You only need to implement the ICommonCacheService
interface.
public class MyDefineCache extends CommonCacheServiceMap {
// 这里只是作为演示,实际生产建议使用 redis 作为统一缓存
@Override
public synchronized void set(String key, String value, long expireMills) {
System.out.println("------------- 自定义的设置实现");
super.set(key, value, expireMills);
}
}
specified in core
In non-spring projects, we can specify the cache we define in the bootstrap class.
ResubmitBs resubmitBs = ResubmitBs.newInstance()
.cache(new MyDefineCache());
UserService service = ResubmitProxy.getProxy(new UserService(), resubmitBs);
Other usage remains unchanged.
specified in spring
In the spring project, we need to adjust the configuration, other things remain unchanged.
@ComponentScan("com.github.houbb.resubmit.test.service")
@Configuration
@EnableResubmit(cache = "myDefineCache")
public class SpringDefineConfig {
@Bean("myDefineCache")
public ICommonCacheService myDefineCache() {
return new MyDefineCache();
}
}
@EnableResubmit(cache = "myDefineCache")
Specify our custom cache policy name.
Redis' built-in caching strategy
In order to facilitate reuse, the cache strategy based on redis has been implemented, and there is time to explain it later.
Redis-Config
open source address
In order to facilitate everyone's learning and use, the current anti-duplicate submission framework has been open sourced.
Welcome to fork+star, encourage the old horse~
https://github.com/houbb/resubmit
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。