From eef5a370187c20744fd4129d52b5ce22307e2258 Mon Sep 17 00:00:00 2001 From: weihu Date: Sat, 16 Mar 2024 10:41:47 +0800 Subject: [PATCH] :tada: add DistributedLock --- pom.xml | 3 +- spring-boot-nebula-dependencies/pom.xml | 13 ++ spring-boot-nebula-distribute-lock/pom.xml | 19 ++- .../annotation/NebulaDistributedLock.java | 65 ++++++++++ ...aDistributedLockAnnotationInterceptor.java | 113 ++++++++++++++++++ ...ebulaDistributedLockAutoConfiguration.java | 35 ++++++ .../distribute/lock/core/DistributedLock.java | 16 +++ .../core/NebulaDistributedLockTemplate.java | 67 +++++++++++ .../core/RedissonDistributedLockTemplate.java | 74 ++++++++++++ .../exception/DistributedLockException.java | 13 ++ .../main/resources/META-INF/spring.factories | 2 + spring-boot-nebula-web-common/pom.xml | 34 ++++++ .../NebulaApplicationContextAware.java | 21 ++++ .../web/common/utils/ExpressionUtil.java | 48 ++++++++ .../web/common/utils/SpringBeanUtils.java | 41 +++++++ .../main/resources/META-INF/spring.factories | 2 + 16 files changed, 564 insertions(+), 2 deletions(-) create mode 100644 spring-boot-nebula-distribute-lock/src/main/java/com/nebula/distribute/lock/annotation/NebulaDistributedLock.java create mode 100644 spring-boot-nebula-distribute-lock/src/main/java/com/nebula/distribute/lock/aop/NebulaDistributedLockAnnotationInterceptor.java create mode 100644 spring-boot-nebula-distribute-lock/src/main/java/com/nebula/distribute/lock/autoconfigure/NebulaDistributedLockAutoConfiguration.java create mode 100644 spring-boot-nebula-distribute-lock/src/main/java/com/nebula/distribute/lock/core/DistributedLock.java create mode 100644 spring-boot-nebula-distribute-lock/src/main/java/com/nebula/distribute/lock/core/NebulaDistributedLockTemplate.java create mode 100644 spring-boot-nebula-distribute-lock/src/main/java/com/nebula/distribute/lock/core/RedissonDistributedLockTemplate.java create mode 100644 spring-boot-nebula-distribute-lock/src/main/java/com/nebula/distribute/lock/exception/DistributedLockException.java create mode 100644 spring-boot-nebula-distribute-lock/src/main/resources/META-INF/spring.factories create mode 100644 spring-boot-nebula-web-common/pom.xml create mode 100644 spring-boot-nebula-web-common/src/main/java/com/nebula/web/common/autoconfigure/NebulaApplicationContextAware.java create mode 100644 spring-boot-nebula-web-common/src/main/java/com/nebula/web/common/utils/ExpressionUtil.java create mode 100644 spring-boot-nebula-web-common/src/main/java/com/nebula/web/common/utils/SpringBeanUtils.java create mode 100644 spring-boot-nebula-web-common/src/main/resources/META-INF/spring.factories diff --git a/pom.xml b/pom.xml index bda7b27..5d26f7c 100644 --- a/pom.xml +++ b/pom.xml @@ -18,7 +18,8 @@ spring-boot-nebula-mybatis spring-boot-nebula-aop-base spring-boot-nebula-distribute-lock - + spring-boot-nebula-web-common + spring-boot-nebula spring-boot-common diff --git a/spring-boot-nebula-dependencies/pom.xml b/spring-boot-nebula-dependencies/pom.xml index ae34132..79f22b7 100644 --- a/spring-boot-nebula-dependencies/pom.xml +++ b/spring-boot-nebula-dependencies/pom.xml @@ -28,6 +28,7 @@ 4.4 6.6.5 3.5.5 + 3.17.3 @@ -65,6 +66,12 @@ ${revision} + + io.github.weihubeats + spring-boot-nebula-web-common + ${revision} + + com.google.guava guava @@ -89,6 +96,12 @@ ${mybatis-plus-boot-starter.version} + + org.redisson + redisson + ${redission.version} + + diff --git a/spring-boot-nebula-distribute-lock/pom.xml b/spring-boot-nebula-distribute-lock/pom.xml index fa3dacb..d480b5c 100644 --- a/spring-boot-nebula-distribute-lock/pom.xml +++ b/spring-boot-nebula-distribute-lock/pom.xml @@ -16,12 +16,29 @@ 11 UTF-8 - + + io.github.weihubeats spring-boot-nebula-aop-base + + + org.redisson + redisson + + + + io.github.weihubeats + spring-boot-nebula-common + + + + io.github.weihubeats + spring-boot-nebula-web-common + + \ No newline at end of file diff --git a/spring-boot-nebula-distribute-lock/src/main/java/com/nebula/distribute/lock/annotation/NebulaDistributedLock.java b/spring-boot-nebula-distribute-lock/src/main/java/com/nebula/distribute/lock/annotation/NebulaDistributedLock.java new file mode 100644 index 0000000..46ab458 --- /dev/null +++ b/spring-boot-nebula-distribute-lock/src/main/java/com/nebula/distribute/lock/annotation/NebulaDistributedLock.java @@ -0,0 +1,65 @@ +package com.nebula.distribute.lock.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.concurrent.TimeUnit; + +/** + * @author : wh + * @date : 2024/3/13 13:49 + * @description: + */ +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface NebulaDistributedLock { + + /** + * 锁名字 + */ + String lockName() default ""; + + /** + * 锁前缀 + */ + String lockNamePre() default ""; + + /** + * 锁后缀 + */ + String lockNamePost() default ""; + + /** + * 锁前后缀拼接分隔符 + */ + String separator() default "_"; + + /** + * 是否使用公平锁 + */ + boolean fairLock() default false; + + /** + * 是否使用尝试锁 + */ + boolean tryLock() default false; + + /** + * 尝试锁最长等待时间 + */ + long tryWaitTime() default 30L; + + /** + * 锁超时时间,超时自动释放锁 + */ + long outTime() default 20L; + + /** + * 时间单位 默认秒 + */ + TimeUnit timeUnit() default TimeUnit.SECONDS; + +} diff --git a/spring-boot-nebula-distribute-lock/src/main/java/com/nebula/distribute/lock/aop/NebulaDistributedLockAnnotationInterceptor.java b/spring-boot-nebula-distribute-lock/src/main/java/com/nebula/distribute/lock/aop/NebulaDistributedLockAnnotationInterceptor.java new file mode 100644 index 0000000..b0e656a --- /dev/null +++ b/spring-boot-nebula-distribute-lock/src/main/java/com/nebula/distribute/lock/aop/NebulaDistributedLockAnnotationInterceptor.java @@ -0,0 +1,113 @@ +package com.nebula.distribute.lock.aop; + +import com.nebula.base.utils.DataUtils; +import com.nebula.distribute.lock.annotation.NebulaDistributedLock; +import com.nebula.distribute.lock.core.DistributedLock; +import com.nebula.distribute.lock.core.NebulaDistributedLockTemplate; +import com.nebula.web.common.utils.ExpressionUtil; +import java.lang.reflect.Method; +import java.util.Objects; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import lombok.extern.slf4j.Slf4j; +import org.aopalliance.intercept.MethodInterceptor; +import org.aopalliance.intercept.MethodInvocation; + +/** + * @author : wh + * @date : 2024/3/15 13:34 + * @description: + */ +@Slf4j +public class NebulaDistributedLockAnnotationInterceptor implements MethodInterceptor { + + private final NebulaDistributedLockTemplate lock; + + public NebulaDistributedLockAnnotationInterceptor(NebulaDistributedLockTemplate lock) { + if (DataUtils.isEmpty(lock)) { + throw new RuntimeException("DistributedLockTemplate is null"); + } + this.lock = lock; + } + + @Nullable + @Override + public Object invoke(@Nonnull MethodInvocation methodInvocation) { + Method method = methodInvocation.getMethod(); + NebulaDistributedLock annotation = method.getAnnotation(NebulaDistributedLock.class); + Object[] args = methodInvocation.getArguments(); + String lockName = getLockName(annotation, args, method); + if (log.isDebugEnabled()) { + log.debug("lockName: {}", lockName); + } + boolean fairLock = annotation.fairLock(); + if (annotation.tryLock()) { + return lock.tryLock(new DistributedLock<>() { + @Override + public Object process() { + return proceed(methodInvocation); + } + + @Override + public String lockName() { + return lockName; + } + }, annotation.tryWaitTime(), annotation.outTime(), annotation.timeUnit(), fairLock); + } else { + return lock.lock(new DistributedLock<>() { + @Override + public Object process() { + return proceed(methodInvocation); + } + + @Override + public String lockName() { + return lockName; + } + }, annotation.outTime(), annotation.timeUnit(), fairLock); + } + } + + public Object proceed(MethodInvocation methodInvocation) { + try { + return methodInvocation.proceed(); + } catch (Throwable e) { + throw new RuntimeException(e); + } + + } + + /** + * 获取锁名字,优先获取注解中锁名 + * + * @param nebulaDistributedLock + * @param args + * @param method + * @return + */ + private String getLockName(NebulaDistributedLock nebulaDistributedLock, Object[] args, Method method) { + if (DataUtils.isNotEmpty(nebulaDistributedLock.lockName())) { + return nebulaDistributedLock.lockName(); + } + String lockNamePre = nebulaDistributedLock.lockNamePre(); + String lockNamePost = nebulaDistributedLock.lockNamePost(); + String separator = nebulaDistributedLock.separator(); + + if (ExpressionUtil.isEl(lockNamePre)) { + lockNamePre = (String) ExpressionUtil.parse(lockNamePre, method, args); + } + if (ExpressionUtil.isEl(lockNamePost)) { + lockNamePost = Objects.requireNonNull(ExpressionUtil.parse(lockNamePost, method, args)).toString(); + } + + StringBuilder sb = new StringBuilder(); + if (DataUtils.isNotEmpty(lockNamePre)) { + sb.append(lockNamePre); + } + sb.append(separator); + if (DataUtils.isNotEmpty(lockNamePost)) { + sb.append(lockNamePost); + } + return sb.toString(); + } +} diff --git a/spring-boot-nebula-distribute-lock/src/main/java/com/nebula/distribute/lock/autoconfigure/NebulaDistributedLockAutoConfiguration.java b/spring-boot-nebula-distribute-lock/src/main/java/com/nebula/distribute/lock/autoconfigure/NebulaDistributedLockAutoConfiguration.java new file mode 100644 index 0000000..57ce410 --- /dev/null +++ b/spring-boot-nebula-distribute-lock/src/main/java/com/nebula/distribute/lock/autoconfigure/NebulaDistributedLockAutoConfiguration.java @@ -0,0 +1,35 @@ +package com.nebula.distribute.lock.autoconfigure; + +import com.nebula.aop.base.NebulaBaseAnnotationAdvisor; +import com.nebula.distribute.lock.annotation.NebulaDistributedLock; +import com.nebula.distribute.lock.aop.NebulaDistributedLockAnnotationInterceptor; +import com.nebula.distribute.lock.core.NebulaDistributedLockTemplate; +import com.nebula.distribute.lock.core.RedissonDistributedLockTemplate; +import org.redisson.api.RedissonClient; +import org.springframework.aop.Advisor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.Order; + +/** + * @author : wh + * @date : 2024/3/15 13:39 + * @description: + */ +@Configuration(proxyBeanMethods = false) +public class NebulaDistributedLockAutoConfiguration { + + @Bean + public RedissonDistributedLockTemplate redissonDistributedLockTemplate(RedissonClient redissonClient) { + RedissonDistributedLockTemplate template = new RedissonDistributedLockTemplate(redissonClient); + return template; + } + + @Bean + @Order(1) + public Advisor distributedLockAnnotationAdvisor(NebulaDistributedLockTemplate nebulaDistributedLockTemplate) { + NebulaDistributedLockAnnotationInterceptor advisor = new NebulaDistributedLockAnnotationInterceptor(nebulaDistributedLockTemplate); + return new NebulaBaseAnnotationAdvisor(advisor, NebulaDistributedLock.class); + } + +} diff --git a/spring-boot-nebula-distribute-lock/src/main/java/com/nebula/distribute/lock/core/DistributedLock.java b/spring-boot-nebula-distribute-lock/src/main/java/com/nebula/distribute/lock/core/DistributedLock.java new file mode 100644 index 0000000..4e392de --- /dev/null +++ b/spring-boot-nebula-distribute-lock/src/main/java/com/nebula/distribute/lock/core/DistributedLock.java @@ -0,0 +1,16 @@ +package com.nebula.distribute.lock.core; + +/** + * @author : wh + * @date : 2024/3/15 13:35 + * @description: + */ +public interface DistributedLock { + + /** + * 分布式锁逻辑 代码块 + */ + T process(); + + String lockName(); +} diff --git a/spring-boot-nebula-distribute-lock/src/main/java/com/nebula/distribute/lock/core/NebulaDistributedLockTemplate.java b/spring-boot-nebula-distribute-lock/src/main/java/com/nebula/distribute/lock/core/NebulaDistributedLockTemplate.java new file mode 100644 index 0000000..441d387 --- /dev/null +++ b/spring-boot-nebula-distribute-lock/src/main/java/com/nebula/distribute/lock/core/NebulaDistributedLockTemplate.java @@ -0,0 +1,67 @@ +package com.nebula.distribute.lock.core; + +import java.util.concurrent.TimeUnit; + +/** + * @author : wh + * @date : 2024/3/15 13:35 + * @description: + */ +public interface NebulaDistributedLockTemplate { + + /** + * 默认超时锁释放时间 + */ + long DEFAULT_OUT_TIME = 5; + /** + * 默认尝试加锁时间 + */ + long DEFAULT_TRY_OUT_TIME = 30; + /** + * 默认时间单位 + */ + TimeUnit DEFAULT_TIME_UNIT = TimeUnit.SECONDS; + + /** + * 加锁 + * @param distributedLock + * @param fairLock 是否使用公平锁 + * @param + * @return + */ + T lock(DistributedLock distributedLock, boolean fairLock); + + /** + * + * @param distributedLock + * @param outTime 锁超时时间。超时后自动释放锁 + * @param timeUnit 时间单位 + * @param fairLock 是否使用公平锁 + * @param + * @return + */ + T lock(DistributedLock distributedLock, long outTime, TimeUnit timeUnit, boolean fairLock); + + /** + * 尝试加锁 + * @param distributedLock + * @param fairLock 是否使用公平锁 + * @param + * @return + */ + T tryLock(DistributedLock distributedLock, boolean fairLock); + + /** + * + * @param distributedLock + * @param tryOutTime 尝试获取锁时间 + * @param outTime 锁超时时间 + * @param timeUnit 时间单位 + * @param fairLock 是否使用公平锁 + * @param + * @return + */ + T tryLock(DistributedLock distributedLock, long tryOutTime, long outTime, TimeUnit timeUnit, boolean fairLock); + + +} diff --git a/spring-boot-nebula-distribute-lock/src/main/java/com/nebula/distribute/lock/core/RedissonDistributedLockTemplate.java b/spring-boot-nebula-distribute-lock/src/main/java/com/nebula/distribute/lock/core/RedissonDistributedLockTemplate.java new file mode 100644 index 0000000..2c8652a --- /dev/null +++ b/spring-boot-nebula-distribute-lock/src/main/java/com/nebula/distribute/lock/core/RedissonDistributedLockTemplate.java @@ -0,0 +1,74 @@ +package com.nebula.distribute.lock.core; + +import com.nebula.distribute.lock.exception.DistributedLockException; +import java.util.concurrent.TimeUnit; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.redisson.api.RLock; +import org.redisson.api.RedissonClient; + +/** + * @author : wh + * @date : 2024/3/15 13:36 + * @description: + */ +@Slf4j +@RequiredArgsConstructor +public class RedissonDistributedLockTemplate implements NebulaDistributedLockTemplate { + + private final RedissonClient redisson; + + @Override + public T lock(DistributedLock distributedLock, boolean fairLock) { + return lock(distributedLock, DEFAULT_OUT_TIME, DEFAULT_TIME_UNIT, fairLock); + } + + @Override + public T lock(DistributedLock distributedLock, long outTime, TimeUnit timeUnit, boolean fairLock) { + RLock lock = getLock(distributedLock.lockName(), fairLock); + lock.lock(outTime, timeUnit); + try { + return distributedLock.process(); + } finally { + if (lock.isLocked()) { + lock.unlock(); + } + } + } + + @Override + public T tryLock(DistributedLock distributedLock, boolean fairLock) { + return tryLock(distributedLock, DEFAULT_TRY_OUT_TIME, DEFAULT_OUT_TIME, DEFAULT_TIME_UNIT, fairLock); + } + + @Override + public T tryLock(DistributedLock distributedLock, long tryOutTime, long outTime, TimeUnit timeUnit, + boolean fairLock) { + String lockName = distributedLock.lockName(); + RLock lock = getLock(lockName, fairLock); + try { + log.info("try acquire lock {}", lockName); + if (lock.tryLock(tryOutTime, outTime, timeUnit)) { + log.info("lock acquired {}", lockName); + try { + return distributedLock.process(); + } finally { + // isHeldByCurrentThread 防止锁过期再释放锁导致报错 + if (lock.isLocked() && lock.isHeldByCurrentThread()) { + lock.unlock(); + log.info("lock released {}", lockName); + } + } + } + } catch (InterruptedException ignored) { + log.warn("锁中断..."); + log.info("can not acquire lock {}", lockName); + } + throw new DistributedLockException("lock fail"); + } + + private RLock getLock(String lockName, boolean fairLock) { + return fairLock ? redisson.getFairLock(lockName) : redisson.getLock(lockName); + } + +} diff --git a/spring-boot-nebula-distribute-lock/src/main/java/com/nebula/distribute/lock/exception/DistributedLockException.java b/spring-boot-nebula-distribute-lock/src/main/java/com/nebula/distribute/lock/exception/DistributedLockException.java new file mode 100644 index 0000000..9924446 --- /dev/null +++ b/spring-boot-nebula-distribute-lock/src/main/java/com/nebula/distribute/lock/exception/DistributedLockException.java @@ -0,0 +1,13 @@ +package com.nebula.distribute.lock.exception; + +/** + * @author : wh + * @date : 2024/3/15 13:41 + * @description: + */ +public class DistributedLockException extends RuntimeException { + + public DistributedLockException(String message) { + super(message); + } +} diff --git a/spring-boot-nebula-distribute-lock/src/main/resources/META-INF/spring.factories b/spring-boot-nebula-distribute-lock/src/main/resources/META-INF/spring.factories new file mode 100644 index 0000000..a1e8656 --- /dev/null +++ b/spring-boot-nebula-distribute-lock/src/main/resources/META-INF/spring.factories @@ -0,0 +1,2 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ + com.nebula.distribute.lock.autoconfigure.NebulaDistributedLockAutoConfiguration diff --git a/spring-boot-nebula-web-common/pom.xml b/spring-boot-nebula-web-common/pom.xml new file mode 100644 index 0000000..cc96855 --- /dev/null +++ b/spring-boot-nebula-web-common/pom.xml @@ -0,0 +1,34 @@ + + + 4.0.0 + + io.github.weihubeats + spring-boot-nebula + ${revision} + + + spring-boot-nebula-web-common + + + 11 + 11 + UTF-8 + + + + + + org.springframework.boot + spring-boot-starter-web + + + + io.github.weihubeats + spring-boot-nebula-common + + + + + \ No newline at end of file diff --git a/spring-boot-nebula-web-common/src/main/java/com/nebula/web/common/autoconfigure/NebulaApplicationContextAware.java b/spring-boot-nebula-web-common/src/main/java/com/nebula/web/common/autoconfigure/NebulaApplicationContextAware.java new file mode 100644 index 0000000..354297b --- /dev/null +++ b/spring-boot-nebula-web-common/src/main/java/com/nebula/web/common/autoconfigure/NebulaApplicationContextAware.java @@ -0,0 +1,21 @@ +package com.nebula.web.common.autoconfigure; + +import com.nebula.web.common.utils.SpringBeanUtils; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * @author : wh + * @date : 2024/3/16 10:33 + * @description: + */ +@Configuration(proxyBeanMethods = false) +public class NebulaApplicationContextAware { + + @Bean + public SpringBeanUtils springBeanUtils() { + return new SpringBeanUtils(); + } + + +} diff --git a/spring-boot-nebula-web-common/src/main/java/com/nebula/web/common/utils/ExpressionUtil.java b/spring-boot-nebula-web-common/src/main/java/com/nebula/web/common/utils/ExpressionUtil.java new file mode 100644 index 0000000..90c4faf --- /dev/null +++ b/spring-boot-nebula-web-common/src/main/java/com/nebula/web/common/utils/ExpressionUtil.java @@ -0,0 +1,48 @@ +package com.nebula.web.common.utils; + +import com.nebula.base.utils.DataUtils; +import java.lang.reflect.Method; +import java.util.Objects; +import org.springframework.core.LocalVariableTableParameterNameDiscoverer; +import org.springframework.expression.ExpressionParser; +import org.springframework.expression.spel.standard.SpelExpressionParser; +import org.springframework.expression.spel.support.StandardEvaluationContext; + +/** + * @author : wh + * @date : 2024/3/16 10:20 + * @description: + */ +public class ExpressionUtil { + + /** + * el表达式解析 + * + * @param expressionString 解析值 + * @param method 方法 + * @param args 参数 + * @return + */ + public static Object parse(String expressionString, Method method, Object[] args) { + if (DataUtils.isEmpty(expressionString)) { + return null; + } + //获取被拦截方法参数名列表 + LocalVariableTableParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer(); + String[] paramNameArr = discoverer.getParameterNames(method); + //SPEL解析 + ExpressionParser parser = new SpelExpressionParser(); + StandardEvaluationContext context = new StandardEvaluationContext(); + for (int i = 0; i < Objects.requireNonNull(paramNameArr).length; i++) { + context.setVariable(paramNameArr[i], args[i]); + } + return parser.parseExpression(expressionString).getValue(context); + } + + public static boolean isEl(String param) { + if (DataUtils.isEmpty(param)) { + return false; + } + return Objects.equals(param.substring(0, 1), "#"); + } +} diff --git a/spring-boot-nebula-web-common/src/main/java/com/nebula/web/common/utils/SpringBeanUtils.java b/spring-boot-nebula-web-common/src/main/java/com/nebula/web/common/utils/SpringBeanUtils.java new file mode 100644 index 0000000..509063c --- /dev/null +++ b/spring-boot-nebula-web-common/src/main/java/com/nebula/web/common/utils/SpringBeanUtils.java @@ -0,0 +1,41 @@ +package com.nebula.web.common.utils; + +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; + +/** + * @author : wh + * @date : 2024/3/16 10:23 + * @description: + */ +public class SpringBeanUtils implements ApplicationContextAware { + + private static ApplicationContext applicationContext; + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + SpringBeanUtils.applicationContext = applicationContext; + } + + private static ApplicationContext getApplicationContext() { + return applicationContext; + } + + public static Object getBean(String name) { + return getApplicationContext().getBean(name); + } + + public static T getBean(Class clazz) { + return getApplicationContext().getBean(clazz); + } + + public static T getBean(String name, Class clazz) { + return getApplicationContext().getBean(name, clazz); + } + + public static boolean containsBean(Class clazz) { + return getApplicationContext().getBeanNamesForType(clazz).length > 0; + } + +} diff --git a/spring-boot-nebula-web-common/src/main/resources/META-INF/spring.factories b/spring-boot-nebula-web-common/src/main/resources/META-INF/spring.factories new file mode 100644 index 0000000..ca5b352 --- /dev/null +++ b/spring-boot-nebula-web-common/src/main/resources/META-INF/spring.factories @@ -0,0 +1,2 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ + com.nebula.web.common.autoconfigure.NebulaApplicationContextAware