alturkovic / distributed-lock Goto Github PK
View Code? Open in Web Editor NEWDistributed locking with Spring
License: MIT License
Distributed locking with Spring
License: MIT License
Like this.
@EventListener
@Async
@RedisLocked
public void sendEmail(XXXEvent event) {
// do something
}
It is recommended not to use the setBeforeExistingAdvisors method,You can detect the presence of AsyncAnnotationBeanPostProcessor.
Thanks for certifying the project on the latest version of the Spring Boot framework. Unfortunately, I am unable to pull the latest version for any of the packages. If I try to pull the following as specified by Jitpack, then I get an auth error.
<dependency>
<groupId>com.github.alturkovic</groupId>
<artifactId>distributed-lock</artifactId>
<version>1.3.0</version>
</dependency>
I am still able to pull 1.2.2 without issue.
For example I didn't realize that distributedLock.acquire(keyList, STORE_ID, expiration)
could actually return null
until it did.
Nullability annotations would make it nicer to work with this library from Kotlin, for example.
Of course not a deal breaker 😄 .
I'd like to be able to try to execute a method, but if the lock can't be acquired it should simply fail and never retry. Is it possible out of the box?
Hi,
We have a use case where we run a cron on a Spring service spawned in multiple instances and we need to run it only on one instance. For this we use @RedisLocked
and we expect that all of the locks will fail BUT the winning one. This is working now as expected, but the failing ones throw DistributedLockException
in LockMethodInterceptor.invoke
from method executeLockedMethod
.
This is not the ideal case for us, as these exceptions are hard to catch in spring (@RedisLocked
is on the same level as @Scheduled
) and are not welcome in our usecase.
It would be nice to have an option on @RedisLocked
annotation to suppress this exception and log it in warn instead. For example @RedisLocked(logFailedLock = true)
or @RedisLocked(suppressFailure = true)
.
Cron is not the only usecase, this option can help any usecase that needs to lock across multiple instances without raising exceptions.
I am prepared to create PR for you.
I was wondering why line 65 in the code block below is returning null instead of returning the result of the invocation.proceed() call.
I assumed that the throwing
option was added to call the target method rather than throwing a DistributedLockException
when there's a problem with redis or a network failure, but seeing that it returns null, I'm wondering if my assumption is wrong.
Hi, the recent changes to the SQL query for MySql is causing PSQL syntax errors.
public static final String ACQUIRE_FORMATTED_QUERY = "INSERT INTO `%s` (lock_key, token, expireAt) VALUES (?, ?, ?);";
public static final String RELEASE_FORMATTED_QUERY = "DELETE FROM `%s` WHERE lock_key = ? AND token = ?;";
public static final String DELETE_EXPIRED_FORMATTED_QUERY = "DELETE FROM `%s` WHERE expireAt < ?;";
public static final String REFRESH_FORMATTED_QUERY = "UPDATE `%s` SET expireAt = ? WHERE lock_key = ? AND token = ?;";
It seems to be that the inclusion of " ` " is breaking PSQL syntax.
org.postgresql.util.PSQLException: ERROR: syntax error at or near "`"
Not too sure on a fix, but it looks like MySql and PSQL syntax are conflicting, would appreciate if you could look into this.
Thanks.
Hello, according to this, lock
is a reserved keyword in mysql.
Whereas the readme, as attached, says for jdbc use, we should create a table named lock
Is there a way to modify the code, to use a different named table name for the locks
Hi!
Recently I decided to create a complete environment of Microservices architecture and implement some important concepts like Distributed Lock, with the goal to be a sandbox for everyone who wants to play with Microservices.
So, I created a repository simulating a Hotel Booking System.
After some researches I decided to use your lib to handle my distributed lock, but today one thread started around Distributed Lock Manager and I think your opinion will be really important for the community.
Here is the link: juliofalbo/complete-microservices-env#1
Thank you very much!
Hi @alturkovic ,
Want to ask regarding the manually controlled locks, i saw the sample code as below but the method lock.acquire(keys, storeId, expiration, retry, timeout) is missing. I cant find acquire method with 5 params. Is it missing method on it ?
And May i know does it possible have dedicated method to check whether it is locked instead of use the lock.acquired method to check on return token value whether it is empty ? Appreciate your help. Thanks
@component
public class Example {
@Autowired
@Qualifier("simpleRedisLock")
private Lock lock;
// other fields...
private void manuallyLocked() {
// code before locking...
final String token = lock.acquire(keys, storeId, expiration, retry, timeout);
// check if you acquired a token
if (StringUtils.isEmpty(token)) {
throw new IllegalStateException("Lock not acquired!");
}
// code after locking...
lock.release(keys, token, storeId);
// code after releasing the lock...
}
}
Currently a single Thread of execution cannot call the same @Locked
method twice (or more) with the same parameter values. I think maybe this should be allowed as the Thread already 'owns' the first lock? Java's synchronized
allows this.
It's happening to us where we override a @Locked
method in a subclass and call super.method()
, and it looks like distributed-lock's advice is run twice - first for the subclass's method call and then again when it calls its parent class's method with super.method()
.
Hi!
I'm using a master/replica scheme with "read from replica" client property enabled.
var masterReplicaConfiguration = new RedisStaticMasterReplicaConfiguration(masterNode.getHost(), masterNode.getPort());
redisProperties
.getReplicas()
.forEach(
replica -> {
RedisNode node = RedisNode.fromString(replica.getHost());
masterReplicaConfiguration.addNode(node.getHost(), node.getPort());
});
LettuceClientConfiguration.LettuceClientConfigurationBuilder clientConfigBuilder =
LettuceClientConfiguration.builder().readFrom(ReadFrom.REPLICA_PREFERRED);
It seems that "execute" is treated as a read operation by default, so it throws:
c.github.alturkovic.lock.exception.DistributedLockException: Unable to acquire lock with expression: #key
at com.github.alturkovic.lock.advice.LockMethodInterceptor.executeLockedMethod(LockMethodInterceptor.java:77)
at com.github.alturkovic.lock.advice.LockMethodInterceptor.invoke(LockMethodInterceptor.java:61)
... 3 frames excluded
...
Caused by: <#029e4a23> org.springframework.data.redis.RedisSystemException: Error in execution; nested exception is io.lettuce.core.RedisReadOnlyException: READONLY You can't write against a read only replica. script: 15df23044ffd4f2723d9689555be4a5cccac4154, on @user_script:1.
at o.s.data.redis.connection.lettuce.LettuceExceptionConverter.convert(LettuceExceptionConverter.java:54)
Is there a way to set the Lock to not retry ?
I'm trying to lock a couple of methods across jvm instances using Jdbc and Postgres.
So what I'm doing:
@EnableJdbcDistributedLock
lock
table:CREATE TABLE lock
(
id SERIAL,
key VARCHAR(255) UNIQUE,
token VARCHAR(255),
expireAt TIMESTAMP
);
@JdbcLocked
But it doesn't work: Unable to acquire lock with expression: #executionPath
.
I'd like to use simply the name of the method as a key, for start.
But I'm not really sure how to move from here 😅
Hi @alturkovic and co.,
Thank you for creating this project - I'm a recently graduated student and am looking to learn more about locks and concurrency for an upcoming project I'm doing. This has been very inspiring and cool!
I have been having some issues getting this setup and in understanding how to run the examples and test cases in the project.
I have cloned the repo, and attempted to use "mvn spring-boot:run" to run the examples but I get a host of errors. I am not sure exactly how to run the examples or test cases and would appreciate any guidance you might be able to give. I.e. does the project contain a dummy or example distributed system that this locking tool is used on?
Thank you!
I would love to use this library in one of our projects, but unfortunately there is no software license available in the repository. Therefore I can not use it. Would you be open to publishing this code under Apache 2.0, MIT or something similar?
I was about to start using this library for a project at the company I work for. But the first link on Google points to an issue in the Gradle repo where it is argued against using JitPack due to multiple security concerns. This is a show stopper for my company at least. And I imagine for many others as well. I can see that previous version were published on Maven Central. So the question is why stop using it?
Is there an easy way to test annotated methods in SpringBootTest
?
I'd like to disable distributed-lock
entirely, I tried with:
@Profile("!test")
@Configuration
@EnableJdbcDistributedLock
class DistributedLockConfiguration
But this fails at runtime
Hi,
when acquiring a lock (manually), there seems to be some confusion as to what should happen when the lock is not acquired.
The documentation on the Lock interface states, that null will be returned from acquire(), but both implementations seem to be throwing an exception (LockNotAvailableException).
So I am not sure what to do: If I rely in the interface, my code will fail. If I rely on the implementation, it may change and fail, just in reverse.
Am I missing something?
Any plans to release a version that upgrades the Spring Boot dependency to 2.2?
I have created SQL table using the following script from the documentation:
CREATE TABLE lock (
id int NOT NULL AUTO_INCREMENT PRIMARY KEY,
lock_key varchar(255) UNIQUE,
token varchar(255),
expireAt TIMESTAMP,
PRIMARY KEY(`id`)
);
After that I'm using the following code:
@JdbcLocked(prefix = "accountId", expression = "#accountId", storeId = "`lock`")
This result in the following exception:
java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'lock WHERE expireAt < '2022-08-21 19:20:56.016'' at line 1
...
com.github.alturkovic.lock.jdbc.service.SimpleJdbcLockSingleKeyService.acquire(SimpleJdbcLockSingleKeyService.java:51)
at com.github.alturkovic.lock.jdbc.service.SimpleJdbcLockSingleKeyService$$FastClassBySpringCGLIB$$7d90f809.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
...
I manage to fix this in my code by overriding storeId:
@JdbcLocked(prefix = "accountId", expression = "#accountId", storeId = "`lock`")
In MySql 'lock' is keyword so if you are using this for table name you need to escape it. So every SQL query in SimpleJdbcLockSingleKeyService
need to be scaped with `
like this:
public static final String ACQUIRE_FORMATTED_QUERY = "INSERT INTO `%s` (lock_key, token, expireAt) VALUES (?, ?, ?);";
public static final String RELEASE_FORMATTED_QUERY = "DELETE FROM `%s` WHERE lock_key = ? AND token = ?;";
public static final String DELETE_EXPIRED_FORMATTED_QUERY = "DELETE FROM `%s` WHERE expireAt < ?;";
public static final String REFRESH_FORMATTED_QUERY = "UPDATE `%s` SET expireAt = ? WHERE lock_key = ? AND token = ?;";
I will create a pull request for this
The Problem
Using one of the out-of-the-box lock implementations, e.g. JDBC, BUT importing the whole library as per the readme, i.e.
<dependencies>
<dependency>
<groupId>com.github.alturkovic.distributed-lock</groupId>
<artifactId>distributed-lock-redis</artifactId>
<version>[insert latest version here]</version>
</dependency>
</dependencies>
causes Spring Boot auto wiring issues during startup for the other implementations, e.g.1
"Spring Data MongoDB - Could not safely identify store assignment for repository candidate interface uk.co.news.applenews.respository.AppleNewsFailedReceiptRepository. If you want this repository to be a MongoDB repository, consider annotating your entities with one of these annotations: org.springframework.data.mongodb.core.mapping.Document (preferred), or consider extending one of the following types with your repository: org.springframework.data.mongodb.repository.MongoRepository."
and e.g.2
"Spring Data Redis - Could not safely identify store assignment for repository candidate interface uk.co.news.applenews.respository.AppleNewsFailedReceiptRepository. If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository."
Suggested Solution
Exclude the out-of-the-box lock implementations that aren't needed, e.g.
implementation('com.github.alturkovic:distributed-lock:1.5.5'){
exclude module: 'distributed-lock-example'
exclude module: 'distributed-lock-mongo'
exclude module: 'distributed-lock-redis'
}
In the example of manual usage:
lock.release(keys, token, storeId);
There should be
lock.release(keys, storeId, token);
I tried to make PR, but it's too long way to do.
Hello,
I am getting this weird issue where scriptExecutor
of RedisTempate
when using the lock is null and hence creation of the lock is giving me a NPE. Here is how I create the lock
Lock Config
@Configuration
@EnableRedisDistributedLock
public class LockConfiguration {
@Bean
public Lock lock(@Value("${redis.lock.secret}") String secret) {
return new SimpleRedisLock(() -> secret, new StringRedisTemplate());
}
}
Cache Config
@Log4j2
@Configuration
public class CacheConfiguration {
@Bean
JedisConnectionFactory jedisConnectionFactory(@Value("${redis.host}") String host, @Value("${redis.port}") int port) {
log.info("Connecting to Redis at: " + host);
return new JedisConnectionFactory(new RedisStandaloneConfiguration(host, port));
}
/**
* Configures template for serialization and deserialization of cache entries.
* @return redis template
*/
@Bean
public RedisTemplate<String, List<String>> redisTemplate(@Value("${redis.host}") String host, @Value("${redis.port}") int port) {
RedisTemplate<String, List<String>> template = new RedisTemplate<>();
template.setValueSerializer(RedisSerializer.json());
template.setKeySerializer(RedisSerializer.string());
template.setConnectionFactory(jedisConnectionFactory(host, port));
return template;
}
}
Usage of lock
@Log4j2
@Repository
@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
public class EventKeysCache {
private final RedisTemplate<String, String> redisTemplate;
private final StringRedisTemplate stringRedisTemplate;
private final Lock lock;
@Value("${redis.lock.expiration}")
private Integer expiration;
@Value("${redis.lock.secret}")
private String lockSecret;
@Value("${redis.lock.collection}")
private String lockCollection;
public void createLock(String eventFilter) {
log.info("Creating lock for eventFilter: " + eventFilter);
try {
lock.acquire(Collections.singletonList(eventFilter), lockCollection, expiration);
} catch (Exception e) {
log.info("Failed to acquire lock for eventFilter: " + eventFilter + e.getMessage() + e.getStackTrace().toString());
}
}
public void releaseLock(String eventFilter) {
log.info("Releasing lock for eventFilter: " + eventFilter);
lock.release(Collections.singletonList(eventFilter), lockCollection, lockSecret);
}
}
The weird thing is this was working before, so not sure what I could be doing wrong. Any help will be highly appreciated.
Thanks
Hello!
I'm trying to use manually acquired and released locks, for a project that needs to release locks in other threads and async methods.
But, with the example code of acquiring :
@Qualifier("simpleMongoLock")
@Autowired
private final Lock lock;
private String acquireLock(String id) {
final String token = lock.acquire(
Collections.singletonList(id),
"locks",
LOCK_EXPIRATION
);
if (StringUtils.isEmpty(token)) {
throw new IllegalStateException("Lock not acquired for id" + id);
}
return token;
}
public void releaseLock(String id, String token) {
lock.release(
Collections.singletonList(id),
token,
"locks"
);
}
And configuration :
import com.github.alturkovic.lock.mongo.configuration.EnableMongoDistributedLock;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableMongoDistributedLock
public class LockConfiguration {
}
I'm able to use distributed locking with annotations @MongoLocked, but not manually released, the lock never gets deleted from database, and even if it's expired, I'm not able to acquire it again !
And in the logs I see that when I try to release the lock just after acquiring it. The acquire is successful, but not the release!
c.g.a.lock.mongo.impl.SimpleMongoLock : Remove query did not affect any records for key
Does anybody ever tried this ?
Thank you!!
Hi,
Thanks for the implementation but I am wondering when the "expired" lock document are removed in the following code
Am I missing something?
By the way, I think I'll adapt your implementation to use ReactiveMongoTemplate.
Hi , can i know does any release version to support Spring Boot 3 ?
It seemed to me that the main purpose of the @locked annotation was to prevent a method from being executing if the previous call to it had not yet finished.
And after this change, acquire lock will be retried until finally a null token is returned instead of an exception being thrown (consequently the annotated method will be called as if lock were not there).
I used the following code stripped form the LockMethodInterceptor to get the retry/timeout functionality with a simple redis lock. It sure would be nice if it was part of the library.
`
private String getLockToken(List keys, String storeId, int retry, long timeOut, long expiration) {
String token = null;
try {
token = constructRetryTemplate(retry, timeOut).execute(context -> {
final String attemptedToken = lock.acquire(keys, storeId, expiration);
if (StringUtils.isEmpty(attemptedToken)) {
throw new LockNotAvailableException(
String.format("Lock not available for keys: %s in store %s", keys, storeId));
}
return attemptedToken;
});
} catch (final Exception e) {
throw new DistributedLockException(String.format("Unable to acquire lock for keys: %s in store %s", keys, storeId), e);
}
return token;
}
private RetryTemplate constructRetryTemplate(int retryPeriodMs, long timeOutMs) {
// how long to sleep between retries
final FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy();
fixedBackOffPolicy.setBackOffPeriod(retryPeriodMs);
// when to timeout the whole operation
final TimeoutRetryPolicy timeoutRetryPolicy = new TimeoutRetryPolicy();
timeoutRetryPolicy.setTimeout(timeOutMs);
// what exceptions to retry; only LockNotAvailableException, all other exceptions are unexpected and locking should fail
final SimpleRetryPolicy simpleRetryPolicy = new SimpleRetryPolicy(Integer.MAX_VALUE, Collections.singletonMap(LockNotAvailableException.class, true));
// combine policies
final CompositeRetryPolicy compositeRetryPolicy = new CompositeRetryPolicy();
compositeRetryPolicy.setPolicies(new RetryPolicy[]{timeoutRetryPolicy, simpleRetryPolicy});
// construct the template
final RetryTemplate retryTemplate = new RetryTemplate();
retryTemplate.setRetryPolicy(compositeRetryPolicy);
retryTemplate.setBackOffPolicy(fixedBackOffPolicy);
return retryTemplate;
}
`
Hi,
I'm struggling to test the annotated locks. I've tried everything under the sun to get it to work but I cannot seem to get anything working.
It doesn't seem the annotation is working what so ever under test conditions (as evidenced by the fact, even when changing the storeId to one that doesn't exist nothing happens).
I've tried annotating the test class with @EnableJdbcDistributedLock
even though my Application file already has this annotation. When the method is called it is not triggering the annotation at all.
I've tried annotating the method that I would like locked with both @Locked
and @JdbcLocked
of which neither made any difference.
I tried to create a very simple test to remove any doubt:
@JdbcLocked(expression = "#p0")
public void fakeMethod(UUID id) {
log.debug("Fake method called! {}", id);
}
@Test
public void simpleTest() {
testService.fakeMethod(UUID.randomUUID());
}
This should fail, since I don't even have a DB table available for this lock, yet it is not doing anything.
Any advice for testing the annotations would be greatly appreciated.
Spring Boot version 3.1.9 (tested with 3.1.5 too just in case)
Distributed Lock Version: 'com.github.alturkovic.distributed-lock:distributed-lock-jdbc:2.1.0'
Dear Alen,
I've made a distributed-lock spring boot starter, replaced Lock implementation to redisson, but when i integrated it to my example project, it failed with an error which confused me a lot.
It seems that if LockBeanPostProcessor is instantiated before WebMvcAutoConfiguration, then this error would occur. And also, I've tried excluding AutoConfiguration from spring.factories files and using @EnableXXX, then it will be OK, i.e. LockBeanPostProcessor instance should be instantiated after WebMvcAutoConfiguration loaded.
From you doc, @EnableJdbcDistributedLock would be annotated in project, i wonder if this is the same situation.
I wanna if you could help me find a solution. Thx.
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'resourceHandlerMapping' defined in class path resource [org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.web.servlet.HandlerMapping]: Factory method 'resourceHandlerMapping' threw exception; nested exception is java.lang.IllegalStateException: No ServletContext set
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:657) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:637) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1176) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:556) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:516) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:324) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:322) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:897) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:879) ~[spring-context-5.2.12.RELEASE.jar:5.2.12.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:551) ~[spring-context-5.2.12.RELEASE.jar:5.2.12.RELEASE]
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:143) ~[spring-boot-2.3.7.RELEASE.jar:2.3.7.RELEASE]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:758) [spring-boot-2.3.7.RELEASE.jar:2.3.7.RELEASE]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:750) [spring-boot-2.3.7.RELEASE.jar:2.3.7.RELEASE]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:405) [spring-boot-2.3.7.RELEASE.jar:2.3.7.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:315) [spring-boot-2.3.7.RELEASE.jar:2.3.7.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1237) [spring-boot-2.3.7.RELEASE.jar:2.3.7.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1226) [spring-boot-2.3.7.RELEASE.jar:2.3.7.RELEASE]
at cn.hrfax.redisson.RedissonApplication.main(RedissonApplication.java:17) [classes/:na]
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.web.servlet.HandlerMapping]: Factory method 'resourceHandlerMapping' threw exception; nested exception is java.lang.IllegalStateException: No ServletContext set
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:185) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:652) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
... 20 common frames omitted
Caused by: java.lang.IllegalStateException: No ServletContext set
at org.springframework.util.Assert.state(Assert.java:76) ~[spring-core-5.2.12.RELEASE.jar:5.2.12.RELEASE]
at org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport.resourceHandlerMapping(WebMvcConfigurationSupport.java:534) ~[spring-webmvc-5.2.12.RELEASE.jar:5.2.12.RELEASE]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_261]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_261]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_261]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_261]
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
... 21 common frames omitted
Sincerely
Selwyn
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.