roleIds;
+
/**
* 权限列表
* @since 3.14.0
*/
private List permissionList;
+
/**
- * 用户实体对象
- *
- * 注意,这个有没有值根据下面条件:
- * 如果stateless=false,那么是使用session方式,这个一定有实体对象
- * 如果stateless=true,那么是无状态token认证方式,该值默认为null,如果想要有值,可实现:
+ * 用户实体对象,它的值根据以下条件确定:
+ * 1.如果stateless=false,那么使用的是session方式,这个一定有实体对象
+ * 2.如果stateless=true,那么是无状态token认证方式,该值默认为null,如果想要有值,可实现:
*
* public class XXXDetailsService extends CrustUserDetailsService {
* protected CrustEntity findEntityById(String uid) {
@@ -108,8 +114,6 @@ public class CrustUserInfo implements Serializable {
*/
private Class> permClass;
-
-
public CrustUserInfo(Serializable uid, String username, String token, List roleIds, T entity) {
this.uid = uid;
this.username = username;
@@ -145,6 +149,10 @@ public Long getUidLong() {
*/
@SuppressWarnings("unchecked")
public T getEntity() {
+ Crust crust = CrustContext.get();
+ if (crust != null && crust.getProps().isEnableLoadEntityLazy() && this.entity == null) {
+ this.entity = crust.loadEntity(this.getUid());
+ }
if (this.entity instanceof Map) {
if (this.getEntityClass() == null) {
return this.entity;
@@ -157,6 +165,10 @@ public T getEntity() {
@SuppressWarnings("unchecked")
public void setPermissionList(List permissionList) {
if (!CollectionUtils.isEmpty(permissionList)) {
+ // user has no permissions!
+ if (permissionList.get(0) == null) {
+ return;
+ }
boolean isMap = permissionList.get(0) instanceof Map;
if (isMap && this.permClass != null) {
this.permissionList = (List
) JSONUtil.parseList(JSONUtil.serialize(permissionList), this.permClass);
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/crust/EnableCrust.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/crust/EnableCrust.java
index 6ca769f8..30abe8c9 100644
--- a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/crust/EnableCrust.java
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/crust/EnableCrust.java
@@ -36,9 +36,9 @@
*
* Create at 2019/11/11 15:14
*/
-@Import(CrustConfig.class)
+@Import({CrustConfig.class, CrustAutoConfigurer.class})
+@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
@EnableWebSecurity
-@EnableGlobalMethodSecurity(prePostEnabled = true)
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/crust/EnableCrustMicro.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/crust/EnableCrustMicro.java
new file mode 100644
index 00000000..b5f05ab7
--- /dev/null
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/crust/EnableCrustMicro.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2022 yizzuide All rights Reserved.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package com.github.yizzuide.milkomeda.crust;
+
+import org.springframework.context.annotation.Import;
+
+import java.lang.annotation.*;
+
+/**
+ * Enable crust used in microservice that not need spring security environment, it just parses token to login info.
+ *
+ * @since 3.15.0
+ * @author yizzuide
+ *
+ * Create at 2022/12/07 00:09
+ */
+@Import(CrustMicroConfig.class)
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Inherited
+public @interface EnableCrustMicro {
+}
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/fusion/FusionRegistration.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/fusion/FusionRegistration.java
index d920ee3d..fed2a1a9 100644
--- a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/fusion/FusionRegistration.java
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/fusion/FusionRegistration.java
@@ -43,6 +43,7 @@
*
* Create at 2020/05/05 16:23
*/
+@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
@Slf4j
public class FusionRegistration {
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/halo/HaloContext.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/halo/HaloContext.java
index 4194494a..1eb0bdf4 100644
--- a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/halo/HaloContext.java
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/halo/HaloContext.java
@@ -51,12 +51,14 @@ public class HaloContext implements ApplicationListener {
private static Map> tableNameMap = new HashMap<>();
- private static final Map> preTableNameMap = new HashMap<>();
+ private static Map> preTableNameMap;
- private static final Map> postTableNameMap = new HashMap<>();
+ private static Map> postTableNameMap;
@Override
public void onApplicationEvent(@NonNull ContextRefreshedEvent event) {
+ preTableNameMap = new HashMap<>();
+ postTableNameMap = new HashMap<>();
tableNameMap = SpringContext.getHandlerMetaData(HaloHandler.class, HaloListener.class, (annotation, handlerAnnotation, metaData) -> {
HaloListener haloListener = (HaloListener) annotation;
// 设置其它属性方法的值
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/halo/HaloInterceptor.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/halo/HaloInterceptor.java
index 2fef61e3..e9d7a91e 100644
--- a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/halo/HaloInterceptor.java
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/halo/HaloInterceptor.java
@@ -21,6 +21,10 @@
package com.github.yizzuide.milkomeda.halo;
+import com.github.yizzuide.milkomeda.universe.aop.invoke.args.ArgumentDefinition;
+import com.github.yizzuide.milkomeda.universe.aop.invoke.args.ArgumentMatchType;
+import com.github.yizzuide.milkomeda.universe.aop.invoke.args.ArgumentSources;
+import com.github.yizzuide.milkomeda.universe.aop.invoke.args.MethodArgumentBinder;
import com.github.yizzuide.milkomeda.universe.metadata.HandlerMetaData;
import com.github.yizzuide.milkomeda.util.MybatisUtil;
import lombok.extern.slf4j.Slf4j;
@@ -37,10 +41,10 @@
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
-import org.apache.ibatis.session.defaults.DefaultSqlSession;
import org.apache.ibatis.type.TypeHandlerRegistry;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
+import org.springframework.util.ReflectionUtils;
import java.lang.reflect.Method;
import java.time.Instant;
@@ -56,10 +60,11 @@
*
* @author yizzuide
* @since 2.5.0
- * @version 3.11.4
+ * @version 3.15.0
*
* Create at 2020/01/30 20:38
*/
+@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
@Slf4j
@Intercepts({
@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
@@ -158,7 +163,7 @@ private Object warpIntercept(Invocation invocation, MappedStatement mappedStatem
}
// Mybatis map参数获取不存在时会抛异常,转为普通map
- if (param instanceof DefaultSqlSession.StrictMap || param instanceof MapperMethod.ParamMap) {
+ if (/*param instanceof DefaultSqlSession.StrictMap || */param instanceof MapperMethod.ParamMap) {
param = new HashMap((Map) param);
}
@@ -204,26 +209,18 @@ private void invokeHandler(String tableName, HandlerMetaData handlerMetaData, St
Object target = handlerMetaData.getTarget();
// 获取参数类型
Class>[] parameterTypes = method.getParameterTypes();
+ ReflectionUtils.makeAccessible(method);
if (parameterTypes.length == 1 && parameterTypes[0] == HaloMeta.class) {
HaloMeta haloMeta = new HaloMeta(sqlCommandType, tableName, param, result);
method.invoke(target, haloMeta);
} else if (parameterTypes.length > 1) {
- // 检测参数是否有SqlCommandType
- if (parameterTypes[0] == SqlCommandType.class) {
- if (result == null) {
- method.invoke(target, sqlCommandType, param);
- } else {
- method.invoke(target, sqlCommandType, param, result);
- }
- } else {
- if (result == null) {
- method.invoke(target, param, sqlCommandType);
- } else {
- method.invoke(target, param, result, sqlCommandType);
- }
- }
- } else {
- method.invoke(target, param);
+ // invoke and bind args dynamically
+ ArgumentSources argumentSources = new ArgumentSources();
+ argumentSources.add(new ArgumentDefinition(ArgumentMatchType.BY_NAME_PREFIX,"param", Object.class, param));
+ argumentSources.add(new ArgumentDefinition(ArgumentMatchType.BY_TYPE,null, SqlCommandType.class, sqlCommandType));
+ argumentSources.add(new ArgumentDefinition(ArgumentMatchType.Residual,null, Object.class, result));
+ Object[] args = MethodArgumentBinder.bind(argumentSources, method);
+ method.invoke(target, args);
}
} catch (Exception e) {
log.error("Halo invoke handler [{}] error: {}, with stmt id: {} and sql: {}", handlerMetaData.getTarget(), e.getMessage(), mappedStatement.getId(), sql, e);
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/hydrogen/filter/ServletContextListener.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/hydrogen/filter/ServletContextListener.java
index 04f61301..cc338ee8 100644
--- a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/hydrogen/filter/ServletContextListener.java
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/hydrogen/filter/ServletContextListener.java
@@ -36,6 +36,7 @@
*
* Create at 2020/04/01 18:18
*/
+@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
public class ServletContextListener implements ServletContextInitializer {
@Autowired
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/hydrogen/filter/TomcatFilterLoader.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/hydrogen/filter/TomcatFilterLoader.java
index f6809c15..b346ae21 100644
--- a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/hydrogen/filter/TomcatFilterLoader.java
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/hydrogen/filter/TomcatFilterLoader.java
@@ -48,6 +48,7 @@
*
* Create at 2020/04/01 18:19
*/
+@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
@Slf4j
public class TomcatFilterLoader extends AbstractFilterLoader {
/**
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/hydrogen/i18n/I18nConfig.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/hydrogen/i18n/I18nConfig.java
index 2f2d85bc..9579180d 100644
--- a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/hydrogen/i18n/I18nConfig.java
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/hydrogen/i18n/I18nConfig.java
@@ -22,6 +22,7 @@
package com.github.yizzuide.milkomeda.hydrogen.i18n;
import com.github.yizzuide.milkomeda.hydrogen.core.HydrogenHolder;
+import com.github.yizzuide.milkomeda.universe.metadata.BeanIds;
import com.github.yizzuide.milkomeda.universe.polyfill.SpringMvcPolyfill;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
@@ -87,7 +88,7 @@ static class ExtendedConfig implements InitializingBean {
@Autowired
private I18nMessages i18nMessages;
- @Qualifier("requestMappingHandlerMapping")
+ @Qualifier(BeanIds.REQUEST_MAPPING_HANDLER_MAPPING)
@Autowired
private RequestMappingHandlerMapping requestMappingHandlerMapping;
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/hydrogen/interceptor/InterceptorConfig.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/hydrogen/interceptor/InterceptorConfig.java
index 52825136..8af712ea 100644
--- a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/hydrogen/interceptor/InterceptorConfig.java
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/hydrogen/interceptor/InterceptorConfig.java
@@ -21,7 +21,9 @@
package com.github.yizzuide.milkomeda.hydrogen.interceptor;
+import com.github.yizzuide.milkomeda.universe.metadata.BeanIds;
import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@@ -48,7 +50,8 @@ public class InterceptorConfig {
@Bean
@ConditionalOnClass(name = "org.springframework.web.servlet.HandlerInterceptor")
- public InterceptorLoader interceptorHandler(InterceptorProperties interceptorProperties, RequestMappingHandlerMapping requestMappingHandlerMapping) {
+ public InterceptorLoader interceptorHandler(InterceptorProperties interceptorProperties,
+ @Qualifier(BeanIds.REQUEST_MAPPING_HANDLER_MAPPING) RequestMappingHandlerMapping requestMappingHandlerMapping) {
return new WebMvcInterceptorLoader(interceptorProperties, requestMappingHandlerMapping);
}
}
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/hydrogen/transaction/TransactionConfig.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/hydrogen/transaction/TransactionConfig.java
index d8fef822..4b56fb19 100644
--- a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/hydrogen/transaction/TransactionConfig.java
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/hydrogen/transaction/TransactionConfig.java
@@ -35,6 +35,7 @@
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionManager;
+import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource;
import org.springframework.transaction.interceptor.RollbackRuleAttribute;
import org.springframework.transaction.interceptor.RuleBasedTransactionAttribute;
@@ -55,32 +56,50 @@
*/
@Aspect
@Configuration
-@EnableConfigurationProperties(TransactionProperties.class)
+@EnableTransactionManagement
@AutoConfigureAfter(TransactionAutoConfiguration.class)
+@EnableConfigurationProperties(TransactionProperties.class)
@ConditionalOnProperty(prefix = "milkomeda.hydrogen.transaction", name = "enable", havingValue = "true")
public class TransactionConfig {
@Autowired
private TransactionProperties props;
+ // PlatformTransactionManager是事务规范接口(实现有JDBC的DataSourceTransactionManager等),事务由具体数据库来现实,
+ // 而TransactionDefinition和TransactionStatus这两个接口分别是事务的定义和运行状态。
+ // Spring的事务通过AOP动态代理:TransactionInterceptor.invoke() -> TransactionAspectSupport.invokeWithinTransaction()
@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
@Bean
public TransactionInterceptor txAdvice(PlatformTransactionManager transactionManager) {
RuleBasedTransactionAttribute txAttr_REQUIRED = new RuleBasedTransactionAttribute();
- // 设置传播行为:若当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这是默认值。
+ // 设置传播行为:
+ // PROPAGATION_REQUIRED:若当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
+ // PROPAGATION_REQUIRED_NEW:如果当前没有事务,创建一个新的事务;如果当前存在事务,则把当前事务挂起,再创建新事务,使执行相互独立。
+ // 事务回滚原则:事务A调用事务B,事务B抛出异常回滚,由于没捕获被事务A监听到而导致事务A也回滚
+ // PROPAGATION_NESTED:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于PROPAGATION_REQUIRED。
+ // 事务回滚原则:外部主事务回滚的话,子事务也会回滚,而内部子事务可以单独回滚而不影响外部主事务和其他子事务。
txAttr_REQUIRED.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
// 抛出异常后执行切点回滚
txAttr_REQUIRED.setRollbackRules(props.getRollbackWhenException()
.stream().map(RollbackRuleAttribute::new).collect(Collectors.toList()));
+ // 设置隔离级别
+ // ISOLATION_READ_UNCOMMITTED:读未提交,可能会产生脏读、不可重复读(同一事务多次读取记录值时不一样,重点在修改)、幻读(多次读取记录条数不一样,重在插入和删除)。
+ // ISOLATION_READ_COMMITTED:读已提交,可能会产生不可重复读、幻读。
+ // ISOLATION_REPEATABLE_READ:可重复读,可能会产生幻读。
+ // InnoDB存储引擎在 REPEATABLE-READ(可重读)事务隔离级别下使用的是 Next-Key Lock 锁(记录锁+ Cap锁),且不会造成任何性能上的损失,因此可以避免幻读的产生。
+ // ISOLATION_SERIALIZABLE:串行化,完全遵行ACID,解决所有问题,但性能会大幅下降。
+ txAttr_REQUIRED.setIsolationLevel(props.getIsolationLevel().value());
// 设置超时
txAttr_REQUIRED.setTimeout((int) props.getRollbackWhenTimeout().getSeconds());
+ // 为什么要有只读事务?
+ // 由于MySQL默认对每一个新建立的连接都启用了autocommit模式。在该模式下,每一个发送到 MySQL 服务器的sql语句都会在一个单独的事务中进行处理,执行结束后会自动提交事务。
+ // 如果不加@Transactional,每条sql会开启一个单独的事务,中间被其它事务改了数据,都会实时读取到最新值,这样会导致数据不一致。
RuleBasedTransactionAttribute txAttr_REQUIRED_READONLY = new RuleBasedTransactionAttribute();
txAttr_REQUIRED_READONLY.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
txAttr_REQUIRED_READONLY.setReadOnly(true);
NameMatchTransactionAttributeSource source = new NameMatchTransactionAttributeSource();
- // 开启只读, 提高数据库访问性能
if (!CollectionUtils.isEmpty(props.getReadOnlyPrefix())) {
for (String prefix : props.getReadOnlyPrefix()) {
source.addTransactionalMethod(prefix, txAttr_REQUIRED_READONLY);
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/hydrogen/transaction/TransactionProperties.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/hydrogen/transaction/TransactionProperties.java
index 681c5131..41d63708 100644
--- a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/hydrogen/transaction/TransactionProperties.java
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/hydrogen/transaction/TransactionProperties.java
@@ -24,11 +24,11 @@
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.convert.DurationUnit;
+import org.springframework.transaction.annotation.Isolation;
import java.time.Duration;
import java.time.temporal.ChronoUnit;
import java.util.Arrays;
-import java.util.Collections;
import java.util.List;
/**
@@ -42,27 +42,39 @@
@Data
@ConfigurationProperties("milkomeda.hydrogen.transaction")
public class TransactionProperties {
+
/**
- * 启用AOP事务
+ * 启用AOP事务(只有作用到 public 方法上事务才生效,不推荐在接口上使用)
*/
private boolean enable = false;
+
/**
* 切点表达式
*/
private String pointcutExpression = "execution(* com..service.*.*(..))";
+
/**
- * 事务超时回滚(默认单位:s。-1:不设置超时回滚)
+ * Transaction isolation level.
+ * @since 3.15.0
+ */
+ private Isolation isolationLevel = Isolation.DEFAULT;
+
+ /**
+ * 事务超时回滚,单位s(默认-1:不设置超时回滚)
*/
@DurationUnit(ChronoUnit.SECONDS)
private Duration rollbackWhenTimeout = Duration.ofSeconds(-1);
+
/**
- * 指定异常类回滚
+ * 指定异常类回滚(默认为RuntimeException和Error)
*/
- private List> rollbackWhenException = Collections.singletonList(Exception.class);
+ private List> rollbackWhenException = Arrays.asList(RuntimeException.class, Error.class);
+
/**
* 只读事务方法前辍
*/
private List readOnlyPrefix = Arrays.asList("get*", "query*", "find*", "select*", "list*", "count*", "is*");
+
/**
* 追加只读事务方法前辍
*/
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/hydrogen/uniform/ResultVO.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/hydrogen/uniform/ResultVO.java
index b209a228..8e85bce5 100644
--- a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/hydrogen/uniform/ResultVO.java
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/hydrogen/uniform/ResultVO.java
@@ -21,10 +21,21 @@
package com.github.yizzuide.milkomeda.hydrogen.uniform;
+import com.github.yizzuide.milkomeda.comet.core.CometResponseInterceptor;
+import com.github.yizzuide.milkomeda.universe.parser.yml.YmlResponseOutput;
+
+import java.util.HashMap;
import java.util.Map;
/**
- * View Object interface.
+ * This interface is specification as a response. The default implementation is {@link UniformResult}, its used with
+ * {@link CometResponseInterceptor} of comet module which default implementation is {@link UniformResponseInterceptor}.
+ * If used this type to declare response, must config the follow:
+ *
+ * 1. Enable response wrapper with config: milkomeda.comet.enable-read-response-body=true
.
+ * 2. Add response interceptor with config: milkomeda.comet.response-interceptors.uniform.enable=true
.
+ * 3. (Optional) If you need change the response field name, such as change `message` to `msg` with config: milkomeda.hydrogen.uniform.response.200.message[msg]=""
.
+ *
*
* @since 3.14.0
* @author yizzuide
@@ -33,7 +44,7 @@
*/
public interface ResultVO {
/**
- * code field type.
+ * Code field type.
*/
enum CodeType {
INT,
@@ -57,8 +68,14 @@ enum CodeType {
T getData();
/**
- * Convert to map.
+ * Convert to standard response map with filed: code, message, data.
* @return Map
*/
- Map toMap();
+ default Map toMap() {
+ Map map = new HashMap<>(8);
+ map.put(YmlResponseOutput.CODE, getCode());
+ map.put(YmlResponseOutput.MESSAGE, getMessage());
+ map.put(YmlResponseOutput.DATA, getData());
+ return map;
+ }
}
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/hydrogen/uniform/UniformConfig.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/hydrogen/uniform/UniformConfig.java
index 39749f8d..4f78314a 100644
--- a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/hydrogen/uniform/UniformConfig.java
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/hydrogen/uniform/UniformConfig.java
@@ -25,14 +25,26 @@
import com.github.yizzuide.milkomeda.universe.context.ApplicationContextHolder;
import com.github.yizzuide.milkomeda.universe.polyfill.SpringMvcPolyfill;
import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.autoconfigure.web.ServerProperties;
+import org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration;
+import org.springframework.boot.autoconfigure.web.servlet.error.ErrorViewResolver;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.boot.web.servlet.error.ErrorAttributes;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
+import org.springframework.lang.NonNull;
+import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerExceptionResolver;
+import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+import java.util.stream.Collectors;
/**
* UniformConfig
@@ -47,14 +59,13 @@
* Create at 2020/03/25 22:46
*/
@Import(MilkomedaContextConfig.class)
-@EnableConfigurationProperties(UniformProperties.class)
+@AutoConfigureBefore(ErrorMvcAutoConfiguration.class)
+@EnableConfigurationProperties({UniformProperties.class, ServerProperties.class})
@ConditionalOnProperty(prefix = "milkomeda.hydrogen.uniform", name = "enable", havingValue = "true")
@Configuration
public class UniformConfig {
- // 注入需要使用的ApplicationContext(让MilkomedaContextConfig先配置)
- @SuppressWarnings("unused")
@Autowired
- private ApplicationContextHolder applicationContextHolder;
+ private ServerProperties serverProperties;
@Bean
public UniformHandler uniformHandler() {
@@ -66,9 +77,21 @@ public UniformResponseInterceptor uniformResponseInterceptor() {
return new UniformResponseInterceptor();
}
+ @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
+ @Bean
+ public UniformErrorController uniformErrorController(ErrorAttributes errorAttributes,
+ ObjectProvider errorViewResolvers) {
+ return new UniformErrorController(errorAttributes, this.serverProperties.getError(),
+ errorViewResolvers.orderedStream().collect(Collectors.toList()));
+ }
+
@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
@Configuration
static class ExtendedConfig implements InitializingBean {
+ // 注入需要使用的ApplicationContext(让MilkomedaContextConfig先配置)
+ @SuppressWarnings("unused")
+ @Autowired
+ private ApplicationContextHolder applicationContextHolder;
@Autowired
private UniformProperties props;
@@ -84,7 +107,22 @@ public void afterPropertiesSet() throws Exception {
// 动态添加异常切面
private void configExceptionAdvice() {
- SpringMvcPolyfill.addDynamicExceptionAdvice(handlerExceptionResolver, ApplicationContextHolder.get(), "uniformHandler");
+ SpringMvcPolyfill.addDynamicExceptionAdvice(handlerExceptionResolver, applicationContextHolder.getApplicationContext(), "uniformHandler");
+ }
+ }
+
+ @Configuration(proxyBeanMethods = false)
+ @EnableConfigurationProperties(UniformProperties.class)
+ public static class RequestMappingConfigurer implements WebMvcConfigurer {
+
+ @Autowired
+ private UniformProperties props;
+
+ @Override
+ public void configurePathMatch(@NonNull PathMatchConfigurer configurer) {
+ if (StringUtils.hasText(props.getRequestPathPrefix())) {
+ configurer.addPathPrefix(props.getRequestPathPrefix(), p -> true);
+ }
}
}
}
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/hydrogen/uniform/UniformErrorController.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/hydrogen/uniform/UniformErrorController.java
new file mode 100644
index 00000000..5f533213
--- /dev/null
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/hydrogen/uniform/UniformErrorController.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2022 yizzuide All rights Reserved.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package com.github.yizzuide.milkomeda.hydrogen.uniform;
+
+import com.github.yizzuide.milkomeda.universe.lang.Tuple;
+import com.github.yizzuide.milkomeda.universe.parser.yml.YmlResponseOutput;
+import com.github.yizzuide.milkomeda.util.JSONUtil;
+import org.jetbrains.annotations.NotNull;
+import org.springframework.boot.autoconfigure.web.ErrorProperties;
+import org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController;
+import org.springframework.boot.autoconfigure.web.servlet.error.ErrorViewResolver;
+import org.springframework.boot.web.servlet.error.ErrorAttributes;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Request mapping handle for 404, 406.
+ *
+ * @since 3.15.0
+ * @author yizzuide
+ *
+ * Create at 2022/12/07 18:49
+ */
+@Controller
+@RequestMapping("${server.error.path:${error.path:/error}}")
+public class UniformErrorController extends BasicErrorController {
+
+ public UniformErrorController(ErrorAttributes errorAttributes, ErrorProperties errorProperties) {
+ super(errorAttributes, errorProperties);
+ }
+
+ public UniformErrorController(ErrorAttributes errorAttributes, ErrorProperties errorProperties, List errorViewResolvers) {
+ super(errorAttributes, errorProperties, errorViewResolvers);
+ }
+
+ @Override
+ public ResponseEntity> error(HttpServletRequest request) {
+ HttpStatus status = getStatus(request);
+ if(UniformHandler.tryMatch(status.value())) {
+ Tuple> mapTuple = uniformMatchResult(request, status);
+ return ResponseEntity.status(mapTuple.getT1()).body(mapTuple.getT2());
+ }
+ return super.error(request);
+ }
+
+ // handle http content-type not match
+ @Override
+ public ResponseEntity mediaTypeNotAcceptable(HttpServletRequest request) {
+ HttpStatus status = getStatus(request);
+ if(UniformHandler.tryMatch(status.value())) {
+ Tuple> mapTuple = uniformMatchResult(request, status);
+ return ResponseEntity.status(mapTuple.getT1()).body(JSONUtil.serialize(mapTuple.getT2()));
+ }
+ return super.mediaTypeNotAcceptable(request);
+ }
+
+ @NotNull
+ private Tuple> uniformMatchResult(HttpServletRequest request, HttpStatus status) {
+ Map source = new HashMap<>();
+ Map originalMap = getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.ALL));
+ String error = (String) originalMap.get("error");
+ source.put(YmlResponseOutput.MESSAGE, error);
+ Tuple, Map> mapTuple = UniformHandler.matchStatusResult(status.value(), source);
+ int resolveStatus = Integer.parseInt(mapTuple.getT1().get(YmlResponseOutput.STATUS).toString());
+ Map body = mapTuple.getT2();
+ return Tuple.build(HttpStatus.resolve(resolveStatus), body);
+ }
+}
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/hydrogen/uniform/UniformExceptionDataAssert.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/hydrogen/uniform/UniformExceptionDataAssert.java
index 440ced05..771e6f8d 100644
--- a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/hydrogen/uniform/UniformExceptionDataAssert.java
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/hydrogen/uniform/UniformExceptionDataAssert.java
@@ -24,7 +24,6 @@
import java.text.MessageFormat;
/**
- * UniformExceptionAssert
* 统一异步断言,最终需要被枚举类实现
*
* @author yizzuide
@@ -45,7 +44,7 @@ default UniformException newException(Throwable t, Object... args) {
}
/**
- * Subclasses can override and implement different formatting message, {@link MessageFormat} is used by default
+ * Subclasses can override and implement a different formatting message, {@link MessageFormat} is used by default.
* @param msg exception message
* @param args exception args
* @return format message
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/hydrogen/uniform/UniformHandler.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/hydrogen/uniform/UniformHandler.java
index 3a9e7e82..e351f466 100644
--- a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/hydrogen/uniform/UniformHandler.java
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/hydrogen/uniform/UniformHandler.java
@@ -22,10 +22,10 @@
package com.github.yizzuide.milkomeda.hydrogen.uniform;
import com.github.yizzuide.milkomeda.universe.context.ApplicationContextHolder;
-import com.github.yizzuide.milkomeda.universe.context.WebContext;
import com.github.yizzuide.milkomeda.universe.lang.Tuple;
import com.github.yizzuide.milkomeda.universe.parser.yml.YmlParser;
import com.github.yizzuide.milkomeda.universe.parser.yml.YmlResponseOutput;
+import com.github.yizzuide.milkomeda.util.DataTypeConvertUtil;
import com.github.yizzuide.milkomeda.util.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
@@ -37,6 +37,7 @@
import org.springframework.http.ResponseEntity;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
+import org.springframework.util.StringUtils;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
@@ -58,22 +59,25 @@
/**
* UniformHandler
*
- * @author yizzuide
- * @since 3.0.0
- * @version 3.14.1
* @see org.springframework.boot.SpringApplication#run(java.lang.String...)
* #see org.springframework.boot.SpringApplication#registerLoggedException(java.lang.Throwable)
* #see org.springframework.boot.SpringBootExceptionHandler.LoggedExceptionHandlerThreadLocal#initialValue()
* @see org.springframework.boot.SpringApplication#setRegisterShutdownHook(boolean)
* @see org.springframework.context.support.AbstractApplicationContext#registerShutdownHook()
+ * @author yizzuide
+ * @since 3.0.0
+ * @version 3.15.0
*
* Create at 2020/03/25 22:47
*/
+@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
@Slf4j
// 可以用于定义@ExceptionHandler、@InitBinder、@ModelAttribute, 并应用到所有@RequestMapping中
//@ControllerAdvice // 这种方式默认就会扫描并加载到Ioc,不好动态控制是否加载,但好处是外部API对未来版本的兼容性强
public class UniformHandler extends ResponseEntityExceptionHandler {
+ public static final int REQUEST_BEFORE_EXCEPTION_CODE = 5000;
+
@Autowired
private UniformProperties props;
@@ -115,7 +119,9 @@ public void init() {
}
private Class> createExceptionClass(Object clazz) {
- if (!(clazz instanceof String)) return null;
+ if (!(clazz instanceof String)) {
+ return null;
+ }
Class> expClazz = null;
try {
expClazz = Class.forName(clazz.toString());
@@ -140,8 +146,12 @@ private Class> createExceptionClass(Object clazz) {
public ResponseEntity constraintViolationException(ConstraintViolationException e) {
ConstraintViolation> constraintViolation = e.getConstraintViolations().iterator().next();
String value = String.valueOf(constraintViolation.getInvalidValue());
- String message = WebContext.getRequestNonNull().getRequestURI() +
- " [" + constraintViolation.getPropertyPath() + "=" + value + "] " + constraintViolation.getMessage();
+ String message;
+ if (props.isIgnoreAddFieldOnValidFail()) {
+ message = constraintViolation.getMessage();
+ } else {
+ message = "[" + constraintViolation.getPropertyPath() + "=" + value + "] " + constraintViolation.getMessage();
+ }
log.warn("Hydrogen uniform valid response exception with msg: {} ", message);
ResponseEntity responseEntity = handleExceptionResponse(e, HttpStatus.BAD_REQUEST.value(), message);
return responseEntity == null ? ResponseEntity.status(HttpStatus.BAD_REQUEST.value()).body(null) : responseEntity;
@@ -194,9 +204,11 @@ public ResponseEntity handleException(Throwable e) {
private ResponseEntity handleValidBeanExceptionResponse(Exception ex, BindingResult bindingResult) {
ObjectError objectError = bindingResult.getAllErrors().get(0);
String message = objectError.getDefaultMessage();
- if (objectError.getArguments() != null && objectError.getArguments().length > 0) {
- FieldError fieldError = (FieldError) objectError;
- message = WebContext.getRequestNonNull().getRequestURI() + " [" + fieldError.getField() + "=" + fieldError.getRejectedValue() + "] " + message;
+ if (!props.isIgnoreAddFieldOnValidFail()) {
+ if (objectError.getArguments() != null && objectError.getArguments().length > 0) {
+ FieldError fieldError = (FieldError) objectError;
+ message = "[" + fieldError.getField() + "=" + fieldError.getRejectedValue() + "] " + message;
+ }
}
log.warn("Hydrogen uniform valid response exception with msg: {} ", message);
return handleExceptionResponse(ex, HttpStatus.BAD_REQUEST.value(), message);
@@ -248,20 +260,35 @@ private ResponseEntity handleInnerErrorExceptionResponse(Exception ex, M
}
/**
- * Used for external match with status code to get response result.
- * @param response response object
+ * Try match response resolve with code before write.
+ * @param code response status code
+ * @return true if matched
+ * @since 3.15.0
+ */
+ public static boolean tryMatch(int code) {
+ UniformProperties props = Binder.get(ApplicationContextHolder.get().getEnvironment()).bind(UniformProperties.PREFIX, UniformProperties.class).get();
+ if (props == null) {
+ return false;
+ }
+ Map, ?> resolveMap = (Map, ?>) props.getResponse().get(String.valueOf(code));
+ return resolveMap != null;
+ }
+
+ /**
+ * Used for external match with status code to get the response result.
+ * @param statusCode response status
* @param source replace data
* @return tuple(yml node map, response content)
- * @since 3.14.0
+ * @since 3.15.0
*/
@SuppressWarnings("unchecked")
- public static Tuple, Map> matchStatusResult(HttpServletResponse response, Map source) {
+ public static Tuple, Map> matchStatusResult(int statusCode, Map source) {
UniformProperties props = Binder.get(ApplicationContextHolder.get().getEnvironment()).bind(UniformProperties.PREFIX, UniformProperties.class).get();
Map resolveMap;
if (props == null) {
resolveMap = createInitResolveMap();
} else {
- resolveMap = (Map) props.getResponse().get(String.valueOf(response.getStatus()));
+ resolveMap = (Map) props.getResponse().get(String.valueOf(statusCode));
if (resolveMap == null) {
resolveMap = createInitResolveMap();
}
@@ -269,20 +296,38 @@ public static Tuple, Map> matchStatusResult(
Map result = new HashMap<>();
// status == 200?
- if (response.getStatus() == HttpStatus.OK.value()) {
+ if (statusCode == HttpStatus.OK.value()) {
YmlParser.parseAliasMapPath(resolveMap, result, YmlResponseOutput.CODE, null, source);
YmlParser.parseAliasMapPath(resolveMap, result, YmlResponseOutput.MESSAGE, null, source);
YmlParser.parseAliasMapPath(resolveMap, result, YmlResponseOutput.DATA, null, source);
resultFilter(result);
} else { // status != 200
+ // 源Code字段为空或已配置了Code的值,就使用配置的值
+ Object code = source.get(YmlResponseOutput.CODE);
+ Object configCode = resolveMap.get(YmlResponseOutput.CODE);
+ if (code == null || (configCode != null && StringUtils.hasText(configCode.toString()))) {
+ source.put(YmlResponseOutput.CODE, resolveMap.get(YmlResponseOutput.CODE));
+ }
YmlResponseOutput.output(resolveMap, result, source, null, false);
}
return Tuple.build(resolveMap, result);
}
+ /**
+ * Used for external match with status code to get the response result.
+ * @param response response object
+ * @param source replace data
+ * @return tuple(yml node map, response content)
+ * @since 3.14.0
+ */
+
+ public static Tuple, Map> matchStatusResult(HttpServletResponse response, Map source) {
+ return matchStatusResult(response.getStatus(), source);
+ }
+
@NotNull
private static Map createInitResolveMap() {
- Map resolveMap = new HashMap<>(7);
+ Map resolveMap = new HashMap<>(8);
resolveMap.put(YmlResponseOutput.STATUS, HttpStatus.OK.value());
resolveMap.put(YmlResponseOutput.CODE, "0");
resolveMap.put(YmlResponseOutput.MESSAGE, "");
@@ -290,6 +335,27 @@ private static Map createInitResolveMap() {
return resolveMap;
}
+ /**
+ * Used for external match with status code to write.
+ * @param response response object
+ * @param status http status code
+ * @param e exception
+ * @throws IOException if an input or output exception occurred
+ * @since 3.15.0
+ */
+ public static void matchStatusToWrite(HttpServletResponse response, Integer status, Exception e) throws IOException {
+ response.setStatus(status == null ? REQUEST_BEFORE_EXCEPTION_CODE : status);
+ ResultVO> source;
+ if (e != null) {
+ Map exMap = DataTypeConvertUtil.beanToMap(e);
+ Object code = exMap.get(YmlResponseOutput.CODE);
+ source = UniformResult.error(String.valueOf(code != null ? code : response.getStatus()), e.getMessage());
+ } else {
+ source = UniformResult.error(String.valueOf(response.getStatus()), "");
+ }
+ UniformHandler.matchStatusToWrite(response, source.toMap());
+ }
+
/**
* Used for external match with status code to write.
* @param response response object
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/hydrogen/uniform/UniformProperties.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/hydrogen/uniform/UniformProperties.java
index 77988c76..3986c0cc 100644
--- a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/hydrogen/uniform/UniformProperties.java
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/hydrogen/uniform/UniformProperties.java
@@ -45,12 +45,24 @@ public class UniformProperties {
*/
private boolean enable = false;
+ /**
+ * 添加统一请求访问路径前缀
+ * @since 3.15.0
+ */
+ private String requestPathPrefix;
+
/**
* Response code type.
* @since 3.14.0
*/
private ResultVO.CodeType codeType = ResultVO.CodeType.INT;
+ /**
+ * Ignore add field to message when valid fail.
+ * @since 3.15.0
+ */
+ private boolean ignoreAddFieldOnValidFail = false;
+
/**
* 响应数据
*/
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/hydrogen/uniform/UniformQueryData.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/hydrogen/uniform/UniformQueryData.java
index 28588f84..4238283a 100644
--- a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/hydrogen/uniform/UniformQueryData.java
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/hydrogen/uniform/UniformQueryData.java
@@ -39,12 +39,28 @@ public class UniformQueryData {
* 模型实体
*/
private T entity;
+
/**
* 开始时间
*/
private Date startDate;
+
/**
* 结束时间
*/
private Date endDate;
+
+ public Long getStartUnixTime() {
+ if (getStartDate() == null) {
+ return null;
+ }
+ return getStartDate().getTime() / 1000;
+ }
+
+ public Long getEndUnixTime() {
+ if (getEndDate() == null) {
+ return null;
+ }
+ return getEndDate().getTime() / 1000;
+ }
}
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/hydrogen/uniform/UniformResponseInterceptor.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/hydrogen/uniform/UniformResponseInterceptor.java
index c525a8ea..2ef27d86 100644
--- a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/hydrogen/uniform/UniformResponseInterceptor.java
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/hydrogen/uniform/UniformResponseInterceptor.java
@@ -21,64 +21,37 @@
package com.github.yizzuide.milkomeda.hydrogen.uniform;
-import com.github.yizzuide.milkomeda.comet.core.CometResponseInterceptor;
-import com.github.yizzuide.milkomeda.universe.lang.Tuple;
+import com.github.yizzuide.milkomeda.comet.core.AbstractResponseInterceptor;
+import com.github.yizzuide.milkomeda.universe.extend.annotation.Alias;
import com.github.yizzuide.milkomeda.universe.parser.yml.YmlResponseOutput;
-import com.github.yizzuide.milkomeda.util.JSONUtil;
import lombok.extern.slf4j.Slf4j;
-import org.springframework.core.Ordered;
-import org.springframework.util.FastByteArrayOutputStream;
import javax.servlet.http.HttpServletResponse;
-import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
/**
- * Uniform impl of response wrapper interceptor.
+ * Uniform implementation of response interceptor. It works when response type is subclass of {@link ResultVO}.
*
* @since 3.14.0
+ * @version 3.15.0
* @author yizzuide
*
* Create at 2022/10/10 17:57
*/
@Slf4j
-public class UniformResponseInterceptor implements CometResponseInterceptor {
+@Alias("uniform")
+public class UniformResponseInterceptor extends AbstractResponseInterceptor {
@Override
- public boolean writeToResponse(FastByteArrayOutputStream outputStream, HttpServletResponse wrapperResponse, HttpServletResponse rawResponse, Object body) {
+ protected Object doResponse(HttpServletResponse response, Object body) {
if (body instanceof ResultVO) {
ResultVO> resultVO = (ResultVO>) body;
- Map source = new HashMap<>(5);
+ Map source = new HashMap<>(8);
source.put(YmlResponseOutput.CODE, resultVO.getCode());
source.put(YmlResponseOutput.MESSAGE, resultVO.getMessage());
source.put(YmlResponseOutput.DATA, resultVO.getData());
- try {
- Tuple, Map> mapTuple = UniformHandler.matchStatusResult(rawResponse, source);
- // has config 200?
- if (mapTuple.getT1() == null || mapTuple.getT1().size() == 0) {
- return false;
- }
- Map result = mapTuple.getT2();
- String content = JSONUtil.serialize(result);
- // reset content and length
- byte[] bytes = content.getBytes(StandardCharsets.UTF_8);
- wrapperResponse.resetBuffer();
- wrapperResponse.setContentLength(bytes.length);
- outputStream.write(bytes);
- rawResponse.setContentLength(outputStream.size());
- // write to response
- outputStream.writeTo(rawResponse.getOutputStream());
- return true;
- } catch (Exception e) {
- log.error("uniform response error with msg: {}", e.getMessage(), e);
- return false;
- }
+ return UniformHandler.matchStatusResult(response, source).getT2();
}
- return false;
- }
-
- @Override
- public int getOrder() {
- return Ordered.HIGHEST_PRECEDENCE;
+ return null;
}
}
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/hydrogen/uniform/UniformResult.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/hydrogen/uniform/UniformResult.java
index c5b64edc..ab779343 100644
--- a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/hydrogen/uniform/UniformResult.java
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/hydrogen/uniform/UniformResult.java
@@ -21,37 +21,34 @@
package com.github.yizzuide.milkomeda.hydrogen.uniform;
-import com.github.yizzuide.milkomeda.universe.parser.yml.YmlResponseOutput;
import lombok.Data;
-import java.util.HashMap;
-import java.util.Map;
-
/**
- * Uniformed result view object.
+ * Uniformed result response object. The usage document see {@link ResultVO}.
*
* @since 3.14.0
+ * @version 3.15.0
* @author yizzuide
*
* Create at 2022/10/10 16:24
*/
@Data
public class UniformResult implements ResultVO {
-
+ /**
+ * Response code.
+ */
private String code;
+ /**
+ * Response message.
+ */
private String message;
+ /**
+ * Response data.
+ */
private T data;
- public Map toMap() {
- Map map = new HashMap<>(5);
- map.put(YmlResponseOutput.CODE, getCode());
- map.put(YmlResponseOutput.MESSAGE, getMessage());
- map.put(YmlResponseOutput.DATA, getData());
- return map;
- }
-
/**
* Return success.
* @param data success data
@@ -64,6 +61,22 @@ public static ResultVO ok(T data) {
return resultVo;
}
+ /**
+ * Return success with code and empty message.
+ * @param code result code
+ * @param data success data
+ * @param data type
+ * @return ResultVO
+ * @since 3.15.0
+ */
+ public static ResultVO ok(String code, T data) {
+ UniformResult resultVo = new UniformResult<>();
+ resultVo.setCode(code);
+ resultVo.setMessage("");
+ resultVo.setData(data);
+ return resultVo;
+ }
+
/**
* Return failure.
* @param code failure code
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/hydrogen/validator/ValidatorConfig.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/hydrogen/validator/ValidatorConfig.java
index 70107f56..19b0fce7 100644
--- a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/hydrogen/validator/ValidatorConfig.java
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/hydrogen/validator/ValidatorConfig.java
@@ -22,6 +22,7 @@
package com.github.yizzuide.milkomeda.hydrogen.validator;
import com.github.yizzuide.milkomeda.hydrogen.core.HydrogenHolder;
+import lombok.Cleanup;
import org.hibernate.validator.HibernateValidator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
@@ -66,6 +67,7 @@ public MethodValidationPostProcessor methodValidationPostProcessor() {
@Bean
public Validator validator() {
+ @Cleanup
ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class )
.configure()
// 设置validator模式为快速失败(只要有一个校验不通过就不立即返回错误)
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/ice/DelayJob.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/ice/DelayJob.java
index 7e15b421..9fd6482d 100644
--- a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/ice/DelayJob.java
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/ice/DelayJob.java
@@ -41,10 +41,12 @@
@Data
@NoArgsConstructor
public class DelayJob implements Serializable {
+
private static final long serialVersionUID = -1408197881231593037L;
/**
- * 延迟任务的唯一标识(长度最好小于62位,因为存储重试次数多占了两位,用于Redis的 ziplist 内存存储优化)
+ * 延迟任务的唯一标识(当长度都小于64字节时,可用于Redis ZSet数据结构SkipList到ZipList的存储优化)
+ * 内部存储格式:jodId#retryCount
*/
private String jodId;
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/ice/DelayJobHandler.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/ice/DelayJobHandler.java
index 94c09b96..05cbb3b1 100644
--- a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/ice/DelayJobHandler.java
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/ice/DelayJobHandler.java
@@ -22,6 +22,7 @@
package com.github.yizzuide.milkomeda.ice;
import com.github.yizzuide.milkomeda.ice.inspector.JobInspector;
+import com.github.yizzuide.milkomeda.universe.extend.env.Environment;
import com.github.yizzuide.milkomeda.universe.metadata.HandlerMetaData;
import com.github.yizzuide.milkomeda.universe.polyfill.RedisPolyfill;
import com.github.yizzuide.milkomeda.util.RedisUtil;
@@ -106,7 +107,9 @@ public void run() {
// 延迟桶处理锁住资源,防止同一桶索引分布式并发执行时出现相同记录问题
if (props.isEnableJobTimerDistributed()) {
boolean hasObtainLock = RedisUtil.setIfAbsent(this.lockKey, props.getJobTimerLockTimeoutSeconds().getSeconds(), redisTemplate);
- if (!hasObtainLock) return;
+ if (!hasObtainLock) {
+ return;
+ }
}
DelayJob delayJob = null;
@@ -144,7 +147,7 @@ public void run() {
processDelayJob(delayJob, job);
}
} catch (Exception e) {
- log.error("Ice Timer处理延迟Job {} 异常:{}", delayJob != null ?
+ log.error("Ice Timer处理延迟Job[{}]异常:{}", delayJob != null ?
delayJob.getJodId() : "[任务数据获取失败]", e.getMessage(), e);
} finally {
if (props.isEnableJobTimerDistributed()) {
@@ -160,11 +163,11 @@ public void run() {
@SuppressWarnings({"unchecked", "rawtypes"})
private void processTtrJob(DelayJob delayJob, Job> job) {
if (delayJob.getRetryCount() > Integer.MAX_VALUE - 1) {
- log.error("Ice处理TTR的Job {}, 重试次数超过Integer.MAX_VALUE,放弃重试", delayJob.getJodId());
+ log.error("Ice处理TTR的Job[{}], 重试次数超过Integer.MAX_VALUE,放弃重试", delayJob.getJodId());
return;
}
int currentRetryCount = delayJob.getRetryCount() + 1;
- log.warn("Ice处理TTR的Job {},当前重试次数为{}", delayJob.getJodId(), currentRetryCount);
+ log.warn("Ice处理TTR的Job[{}],当前重试次数为{}", delayJob.getJodId(), currentRetryCount);
// 检测重试次数过载
boolean overload = delayJob.getRetryCount() >= job.getRetryCount();
@@ -185,7 +188,7 @@ private void processTtrJob(DelayJob delayJob, Job> job) {
}
if (overload) {
- log.error("Ice检测到 Job {} 的TTR超过预设的{}次!", job.getId(), job.getRetryCount());
+ log.error("Ice检测到Job[{}]的TTR超过预设的{}次!", job.getId(), job.getRetryCount());
// 调用TTR Overload监听器
List ttrOverloadMetaDataList = IceContext.getTopicTtrOverloadMap().get(job.getTopic());
if (!CollectionUtils.isEmpty(ttrOverloadMetaDataList)) {
@@ -213,7 +216,7 @@ private void processTtrJob(DelayJob delayJob, Job> job) {
delayJob.setRetryCount(0);
// 添加dead queue
deadQueue.add(operations, delayJob);
- log.warn("Ice处理TTR Overload的 Job {} 进入Dead queue", job.getId());
+ log.warn("Ice处理TTR Overload的Job[{}]进入Dead queue", job.getId());
}
// Record job
@@ -262,7 +265,9 @@ private void processTtrJob(DelayJob delayJob, Job> job) {
* 处理延时任务
*/
private void processDelayJob(DelayJob delayJob, Job> job) {
- log.info("Ice正在处理延迟的Job {},当前状态为:{}", delayJob.getJodId(), job.getStatus());
+ if(Environment.isShowLog()) {
+ log.info("Ice正在处理延迟的Job[{}],当前状态为:{}", delayJob.getJodId(), job.getStatus());
+ }
RedisUtil.batchOps((operations) -> {
// 修改任务池状态
job.setStatus(JobStatus.READY);
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/ice/EnableIce.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/ice/EnableIce.java
index 4f1fac3d..eafde8c6 100644
--- a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/ice/EnableIce.java
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/ice/EnableIce.java
@@ -21,6 +21,7 @@
package com.github.yizzuide.milkomeda.ice;
+import com.github.yizzuide.milkomeda.universe.config.RedisGlobalConfig;
import org.springframework.context.annotation.Import;
import org.springframework.scheduling.annotation.EnableScheduling;
@@ -96,6 +97,6 @@
@Target({ElementType.TYPE})
@Inherited
@EnableScheduling
-@Import({IceConfig.class, IceScheduleConfig.class})
+@Import({RedisGlobalConfig.class, IceConfig.class, IceScheduleConfig.class})
public @interface EnableIce {
}
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/ice/EnableIceBasic.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/ice/EnableIceBasic.java
index 64cb2648..722e6f91 100644
--- a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/ice/EnableIceBasic.java
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/ice/EnableIceBasic.java
@@ -21,6 +21,7 @@
package com.github.yizzuide.milkomeda.ice;
+import com.github.yizzuide.milkomeda.universe.config.RedisGlobalConfig;
import org.springframework.context.annotation.Import;
import java.lang.annotation.*;
@@ -50,6 +51,6 @@
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Inherited
-@Import(IceBasicConfig.class)
+@Import({RedisGlobalConfig.class, IceBasicConfig.class})
public @interface EnableIceBasic {
}
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/ice/EnableIceClient.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/ice/EnableIceClient.java
index e176743d..a830e309 100644
--- a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/ice/EnableIceClient.java
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/ice/EnableIceClient.java
@@ -21,6 +21,7 @@
package com.github.yizzuide.milkomeda.ice;
+import com.github.yizzuide.milkomeda.universe.config.RedisGlobalConfig;
import org.springframework.context.annotation.Import;
import org.springframework.scheduling.annotation.EnableScheduling;
@@ -41,6 +42,6 @@
@Target({ElementType.TYPE})
@Inherited
@EnableScheduling
-@Import({IceClientConfig.class, IceScheduleConfig.class})
+@Import({RedisGlobalConfig.class, IceClientConfig.class, IceScheduleConfig.class})
public @interface EnableIceClient {
}
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/ice/EnableIceServer.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/ice/EnableIceServer.java
index 25035bba..e594fe6d 100644
--- a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/ice/EnableIceServer.java
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/ice/EnableIceServer.java
@@ -21,6 +21,7 @@
package com.github.yizzuide.milkomeda.ice;
+import com.github.yizzuide.milkomeda.universe.config.RedisGlobalConfig;
import org.springframework.context.annotation.Import;
import org.springframework.scheduling.annotation.EnableScheduling;
@@ -41,6 +42,6 @@
@Target({ElementType.TYPE})
@Inherited
@EnableScheduling
-@Import(IceServerConfig.class)
+@Import({RedisGlobalConfig.class, IceServerConfig.class})
public @interface EnableIceServer {
}
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/ice/IceKeys.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/ice/IceKeys.java
index a25c1202..7626f16a 100644
--- a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/ice/IceKeys.java
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/ice/IceKeys.java
@@ -51,7 +51,7 @@ public class IceKeys {
*/
public static Map resolve(JobWrapper jobWrapper) {
String applicationName = IceHolder.getApplicationName();
- Map keyMap = new HashMap<>(3);
+ Map keyMap = new HashMap<>(8);
keyMap.put("jobPool", JOB_POOL_KEY_PREFIX.concat(":" + applicationName));
switch (jobWrapper.getQueueType()) {
case DelayQueue:
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/ice/IceProperties.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/ice/IceProperties.java
index c0b50c1b..5d060db5 100644
--- a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/ice/IceProperties.java
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/ice/IceProperties.java
@@ -50,7 +50,7 @@ public class IceProperties {
public final static String MERGE_ID_SEPARATOR = "-";
/**
- * 实例名(用于多产品隔离,否则不要修改)
+ * 服务实例名(用于多产品隔离)
*/
private String instanceName = DEFAULT_INSTANCE_NAME;
@@ -145,8 +145,7 @@ public class IceProperties {
private boolean multiTopicListenerPerHandler = false;
/**
- * TTR超时后是否放入到Dead queue
- * @see IceTtrOverloadListener
+ * TTR超时后是否放入到DeadQueue(设置为false,则超时由开发者处理通过监听器 {@link IceTtrOverloadListener} 处理)
*/
private boolean enableRetainToDeadQueueWhenTtrOverload = false;
@@ -160,7 +159,7 @@ public class IceProperties {
@Data
public static class Introspect {
/**
- * Enable export job introspection api.
+ * Enable export job introspection data.
*/
private boolean enable = false;
@@ -170,7 +169,7 @@ public static class Introspect {
private JobInspector.IndexType indexType = JobInspector.IndexType.UPDATE_TIME;
/**
- * Select strategy which for job inspection store.
+ * Select strategy for job inspection store.
*/
private JobInspector.InspectorType inspectorType = JobInspector.InspectorType.REDIS;
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/ice/RedisIce.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/ice/RedisIce.java
index c934e89e..1b0df304 100644
--- a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/ice/RedisIce.java
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/ice/RedisIce.java
@@ -46,7 +46,6 @@
import java.util.stream.Collectors;
/**
- * RedisIce
* 基于Redis的延迟队列实现
*
* @author yizzuide
@@ -55,6 +54,7 @@
*
* Create at 2019/11/16 15:20
*/
+@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
@Slf4j
public class RedisIce implements Ice, ApplicationListener {
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/ice/inspector/AbstractJobInspector.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/ice/inspector/AbstractJobInspector.java
index bf57b0a6..ff6d990e 100644
--- a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/ice/inspector/AbstractJobInspector.java
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/ice/inspector/AbstractJobInspector.java
@@ -31,7 +31,7 @@
import java.util.stream.Collectors;
/**
- * Abstract job inspector with common code.
+ * Abstract job inspector for persistence storage.
*
* @author yizzuide
* @since 3.14.0
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/ice/inspector/JobInspector.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/ice/inspector/JobInspector.java
index 4e0aca19..d0a0e5eb 100644
--- a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/ice/inspector/JobInspector.java
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/ice/inspector/JobInspector.java
@@ -29,7 +29,7 @@
import java.util.function.Consumer;
/**
- * Inspect accessor for job.
+ * Inspect accessor for job.
*
* @author yizzuide
* @since 3.14.0
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/ice/inspector/JobStatInfo.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/ice/inspector/JobStatInfo.java
index 35cd68c8..835c7ee5 100644
--- a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/ice/inspector/JobStatInfo.java
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/ice/inspector/JobStatInfo.java
@@ -26,7 +26,7 @@
import java.util.Map;
/**
- * JobStatInfo
+ * Job stat record info.
*
* @since 3.14.0
* @author yizzuide
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/ice/inspector/JobWrapper.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/ice/inspector/JobWrapper.java
index 2c102b77..954ba6f0 100644
--- a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/ice/inspector/JobWrapper.java
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/ice/inspector/JobWrapper.java
@@ -31,7 +31,7 @@
import java.io.Serializable;
/**
- * Job introspect info with {@link Job}
+ * Job introspect info with {@link Job}.
*
* @author yizzuide
* @since 3.14.0
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/ice/inspector/MongoJobInspector.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/ice/inspector/MongoJobInspector.java
index 88fddebe..2c3cfb99 100644
--- a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/ice/inspector/MongoJobInspector.java
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/ice/inspector/MongoJobInspector.java
@@ -46,6 +46,7 @@
*
* Create at 2022/09/26 23:02
*/
+@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
public class MongoJobInspector extends AbstractJobInspector {
@Autowired
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/ice/inspector/domain/JobInspection.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/ice/inspector/domain/JobInspection.java
index d34d77ba..0f9263c2 100644
--- a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/ice/inspector/domain/JobInspection.java
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/ice/inspector/domain/JobInspection.java
@@ -38,23 +38,53 @@
public class JobInspection implements Serializable {
private static final long serialVersionUID = -71079477949653608L;
+ /**
+ * Job id.
+ */
private Long id;
+ /**
+ * Job topic name.
+ */
private String topic;
+ /**
+ * Which application does the job come from.
+ */
private String applicationName;
+ /**
+ * Job into what queue type.
+ */
private Integer queueType;
+ /**
+ * Which bucket index does the job come from.
+ */
private Integer bucketIndex;
+ /**
+ * Number of retries already happened.
+ */
private Integer hadRetryCount;
+ /**
+ * The job has need to re-push.
+ */
private Integer needRePush;
+ /**
+ * Next execution time with job.
+ */
private Date executionTime;
+ /**
+ * Job push time.
+ */
private Date pushTime;
+ /**
+ * Latest update time executed.
+ */
private Date updateTime;
}
\ No newline at end of file
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/ice/inspector/domain/JobInspectionDocument.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/ice/inspector/domain/JobInspectionDocument.java
index 434331d6..67a3c570 100644
--- a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/ice/inspector/domain/JobInspectionDocument.java
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/ice/inspector/domain/JobInspectionDocument.java
@@ -31,9 +31,10 @@
import java.io.Serializable;
/**
- * JobInspectionDocument
+ * Job inspection document for mongodb.
*
* @author yizzuide
+ * @since 3.14.0
*
* Create at 2022/09/26 23:14
*/
@@ -44,35 +45,65 @@ public class JobInspectionDocument implements Serializable {
public static final String COLLECTION_NAME = "job_inspection";
+ /**
+ * Job id.
+ */
@MongoId
private String id;
+ /**
+ * Job topic name.
+ */
@Field("topic")
private String topic;
+ /**
+ * Which application does the job come from.
+ */
@Field("application_name")
private String applicationName;
+ /**
+ * Job into what queue type.
+ */
@Field("queue_type")
private Integer queueType;
+ /**
+ * Which bucket index does the job come from.
+ */
@Field("bucket_index")
private Integer bucketIndex;
+ /**
+ * Number of retries already happened.
+ */
@Field("hadRetry_count")
private Integer hadRetryCount;
+ /**
+ * The job has need to re-push.
+ */
@Field("need_re_push")
private Integer needRePush;
+ /**
+ * Next execution time with job.
+ */
@Field(name = "execution_time", targetType = FieldType.TIMESTAMP)
private Long executionTime;
+ /**
+ * Job push time.
+ */
// create index named 'push_time.index', and created in the background.
@Indexed(name = "index", background = true)
@Field(name = "push_time", targetType = FieldType.TIMESTAMP)
private Long pushTime;
+ /**
+ * Latest update time executed.
+ */
// create index named 'update_time.index', and created in the background.
@Indexed(name = "index", background = true)
@Field(name = "update_time", targetType = FieldType.TIMESTAMP)
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/ice/inspector/mapper/JobInspectionMapper.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/ice/inspector/mapper/JobInspectionMapper.java
index ee730ad3..a4c7df24 100644
--- a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/ice/inspector/mapper/JobInspectionMapper.java
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/ice/inspector/mapper/JobInspectionMapper.java
@@ -29,7 +29,7 @@
import java.util.List;
/**
- * Job Inspection mybatis mapper
+ * Job Inspection mybatis mapper.
*
* @author yizzuide
* @since 3.14.0
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/jupiter/AbstractJupiterRuleEngine.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/jupiter/AbstractJupiterRuleEngine.java
index 46883fba..741ac135 100644
--- a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/jupiter/AbstractJupiterRuleEngine.java
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/jupiter/AbstractJupiterRuleEngine.java
@@ -44,6 +44,7 @@ public boolean run(String ruleName, List ruleItemList) {
return run(ruleName);
}
+ @Override
public void addRule(String ruleName, List ruleItemList) {
int incr = 0;
for (JupiterRuleItem ruleItem : ruleItemList) {
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/jupiter/JupiterConfig.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/jupiter/JupiterConfig.java
index 54475513..e48e495b 100644
--- a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/jupiter/JupiterConfig.java
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/jupiter/JupiterConfig.java
@@ -44,6 +44,7 @@
*
* Create at 2020/05/19 16:57
*/
+@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
@EnableConfigurationProperties(JupiterProperties.class)
public class JupiterConfig implements ApplicationContextAware {
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/jupiter/JupiterOnglCompiler.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/jupiter/JupiterOnglCompiler.java
index 0e8b8caf..4e319e12 100644
--- a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/jupiter/JupiterOnglCompiler.java
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/jupiter/JupiterOnglCompiler.java
@@ -21,50 +21,28 @@
package com.github.yizzuide.milkomeda.jupiter;
-import com.github.yizzuide.milkomeda.universe.engine.ognl.OgnlClassResolver;
-import com.github.yizzuide.milkomeda.universe.engine.ognl.OgnlMemberAccess;
+import com.github.yizzuide.milkomeda.universe.engine.ognl.SimpleOgnlParser;
import lombok.extern.slf4j.Slf4j;
-import ognl.ClassResolver;
-import ognl.MemberAccess;
-import ognl.Ognl;
import ognl.OgnlException;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
/**
* JupiterOnglCompiler
*
- * @author yizzuide
* @since 3.5.0
+ * @version 3.15.0
+ * @author yizzuide
*
* Create at 2020/05/20 11:39
*/
@Slf4j
public class JupiterOnglCompiler implements JupiterExpressionCompiler {
- private final MemberAccess MEMBER_ACCESS = new OgnlMemberAccess(false);
- private final ClassResolver CLASS_RESOLVER = new OgnlClassResolver();
- private final Map expressionCache = new ConcurrentHashMap<>();
-
- @SuppressWarnings({"unchecked", "rawtypes"})
@Override
public T compile(String expression, Object root, Class resultType) {
try {
- Map ognlContext = Ognl.createDefaultContext(root, MEMBER_ACCESS, CLASS_RESOLVER, null);
- return (T) Ognl.getValue(parseExpression(expression), ognlContext, root, resultType);
+ SimpleOgnlParser.parse(expression, root, resultType);
} catch (OgnlException e) {
log.error("Jupiter ongl compiler error with msg: {}", e.getMessage(), e);
}
return null;
}
-
- // 缓存表达式解析抽象树对象
- private Object parseExpression(String expression) throws OgnlException {
- Object node = expressionCache.get(expression);
- if (node == null) {
- node = Ognl.parseExpression(expression);
- expressionCache.put(expression, node);
- }
- return node;
- }
}
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/jupiter/JupiterScopeRuleEngine.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/jupiter/JupiterScopeRuleEngine.java
index 93b95ae1..d3bbcb55 100644
--- a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/jupiter/JupiterScopeRuleEngine.java
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/jupiter/JupiterScopeRuleEngine.java
@@ -41,6 +41,7 @@
*
* Create at 2020/05/19 14:39
*/
+@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
@Slf4j
public class JupiterScopeRuleEngine extends AbstractJupiterRuleEngine {
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/light/CacheHelper.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/light/CacheHelper.java
index 88015190..57bf6792 100644
--- a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/light/CacheHelper.java
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/light/CacheHelper.java
@@ -185,7 +185,7 @@ public static E get(Cache cache, TypeReference eTypeRef, Serializable id,
Spot fastSpot = null;
if (cache instanceof LightCache) {
LightCache lightCache = (LightCache) cache;
- if (lightCache.isEnableSuperCache() && !lightCache.getOnlyCacheL2()) {
+ if (lightCache.isEnableSuperCache() && !lightCache.isOnlyCacheL2()) {
// 方案一:从超级缓存中获取,内存指针引用即可返回(耗时为O(1))
fastSpot = get(cache);
if (fastSpot != null) {
@@ -266,7 +266,7 @@ public static E put(Cache cache, Serializable id,
Spot fastSpot = null;
if (cache instanceof LightCache) {
LightCache lightCache = (LightCache) cache;
- if (lightCache.isEnableSuperCache() && !lightCache.getOnlyCacheL2()) {
+ if (lightCache.isEnableSuperCache() && !lightCache.isOnlyCacheL2()) {
fastSpot = get(cache);
if (fastSpot == null) {
// 设置超级缓存
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/light/Discard.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/light/Discard.java
index 7d163d98..b5a5563b 100644
--- a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/light/Discard.java
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/light/Discard.java
@@ -26,7 +26,6 @@
/**
* Discard
- *
* 缓存数据丢弃策略接口
*
* @since 1.8.0
@@ -42,6 +41,7 @@ public interface Discard {
*
* @return 缓存数据类
*/
+ @SuppressWarnings("rawtypes")
Class extends SortSpot> spotClazz();
/**
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/light/HotDiscard.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/light/HotDiscard.java
index b0690f03..0b6c6129 100644
--- a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/light/HotDiscard.java
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/light/HotDiscard.java
@@ -27,7 +27,6 @@
/**
* HotDiscard
- *
* 低频热点丢弃方案
*
* @since 1.8.0
@@ -38,6 +37,7 @@
*/
public class HotDiscard extends SortDiscard {
+ @SuppressWarnings("rawtypes")
@Override
public Class extends SortSpot> spotClazz() {
return HotSpot.class;
@@ -55,7 +55,10 @@ public Spot deform(String key, Spot
@Override
public boolean ascend(Spot spot) {
HotSpot hotSpot = (HotSpot) spot;
- hotSpot.setStar(hotSpot.getStar() + 1);
+ // 使用锁保证访问的真实数量
+ synchronized (this) {
+ hotSpot.setStar(hotSpot.getStar() + 1);
+ }
return false;
}
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/light/LazyExpireDiscard.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/light/LazyExpireDiscard.java
index b9f045a4..1c456a23 100644
--- a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/light/LazyExpireDiscard.java
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/light/LazyExpireDiscard.java
@@ -36,6 +36,7 @@
*/
public class LazyExpireDiscard extends SortDiscard {
+ @SuppressWarnings("rawtypes")
@Override
public Class extends SortSpot> spotClazz() {
return LazyExpireSpot.class;
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/light/LightCache.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/light/LightCache.java
index 11d5eb8e..3777a9dd 100644
--- a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/light/LightCache.java
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/light/LightCache.java
@@ -42,18 +42,17 @@
/**
* LightCache
- *
+ *
* 缓存方式:超级缓存(ThreadLocal)| 一级缓存(内存缓存池,缓存个数可控)| 二级缓存(Redis)
- *
- * V:标识数据
- * E:缓存业务数据
+ *
*
* @since 1.8.0
- * @version 3.14.0
+ * @version 3.15.0
* @author yizzuide
*
* Create at 2019/06/28 13:33
*/
+@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
@Slf4j
public class LightCache implements Cache {
/**
@@ -61,7 +60,7 @@ public class LightCache implements Cache {
*/
@Setter
@Getter
- private Integer l1MaxCount;
+ private volatile Integer l1MaxCount;
/**
* 一级缓存一次性移除百分比
@@ -81,7 +80,7 @@ public class LightCache implements Cache {
*/
@Setter
@Getter
- private Boolean onlyCacheL1;
+ private boolean onlyCacheL1;
/**
* 一级缓存丢弃策略
@@ -113,7 +112,7 @@ public class LightCache implements Cache {
*/
@Setter
@Getter
- private Boolean onlyCacheL2;
+ private boolean onlyCacheL2;
/**
* 开启超缓存
@@ -129,7 +128,7 @@ public class LightCache implements Cache {
private final LightContext superCache = new LightContext<>();
/**
- * 一级缓存容器(内存池)
+ * 一级缓存容器(内存池,跳表算法复杂度O(logN),高并发操作效率高,范围数据查找快)
*/
private final Map> cacheMap = new ConcurrentSkipListMap<>();
@@ -141,15 +140,15 @@ public class LightCache implements Cache {
/**
- * 设置超级缓存
- *
- * 如果在一级缓存池里根据缓存标识符可以取得缓存数据,则不会创建新的缓存数据对象
+ * 设置超级缓存(如果在一级缓存池里根据缓存标识符可以取得缓存数据,则不会创建新的缓存数据对象)
*
* @param id 缓存标识符
*/
@Override
public void set(Serializable id) {
- if (null == id) return;
+ if (null == id) {
+ return;
+ }
// 如果一级缓存没有数据,创建新的缓存数据对象
if (cacheMap.size() == 0) {
superCache.setId(id);
@@ -199,8 +198,8 @@ public void remove() {
@SuppressWarnings("unchecked")
@Override
public void set(String key, Spot spot) {
- // 如果是父类型,需要向下转型(会触发初始化排序状态字段)
- if (spot.getClass() == Spot.class) {
+ // 一级缓存下,如果是父类型,需要向下转型(会触发初始化排序字段)
+ if (spot.getClass() == Spot.class && !onlyCacheL2) {
spot = discardStrategy.deform(key, (Spot) spot, l1Expire);
}
@@ -253,8 +252,13 @@ private void cacheL2(String key, Spot spot) {
private boolean cacheL1(String key, Spot spot) {
// 一级缓存超出最大个数
if ((cacheMap.size() + 1) > l1MaxCount) {
- // 根据选择的策略来丢弃数据
- discardStrategy.discard(cacheMap, l1DiscardPercent);
+ // 使用双重检测确保在条件满足时只执行一次
+ synchronized (this) {
+ if ((cacheMap.size() + 1) > l1MaxCount) {
+ // 根据选择的策略来丢弃数据
+ discardStrategy.discard(cacheMap, l1DiscardPercent);
+ }
+ }
}
// 排行加分
@@ -270,7 +274,7 @@ private boolean cacheL1(String key, Spot spot) {
// 添加到一级缓存池
cacheMap.put(key, spot);
- return true;
+ return true;
}
@Override
@@ -281,7 +285,7 @@ public Spot get(String key) {
@Override
public Spot get(String key, Class vClazz, Class eClazz) {
JavaType javaType = TypeFactory.defaultInstance()
- .constructParametricType(discardStrategy.spotClazz(), vClazz, eClazz);
+ .constructParametricType(onlyCacheL2 ? Spot.class : discardStrategy.spotClazz(), vClazz, eClazz);
return get(key, javaType);
}
@@ -414,9 +418,9 @@ public void copyFrom(LightCache other) {
this.setL1Expire(other.getL1Expire());
this.setStrategy(other.getStrategy());
this.setStrategyClass(other.getStrategyClass());
- this.setOnlyCacheL1(other.getOnlyCacheL1());
+ this.setOnlyCacheL1(other.isOnlyCacheL1());
this.setL2Expire(other.getL2Expire());
- this.setOnlyCacheL2(other.getOnlyCacheL2());
+ this.setOnlyCacheL2(other.isOnlyCacheL2());
this.setEnableSuperCache(other.isEnableSuperCache());
}
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/light/LightCacheAspect.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/light/LightCacheAspect.java
index 80b5fb29..15cc4135 100644
--- a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/light/LightCacheAspect.java
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/light/LightCacheAspect.java
@@ -56,6 +56,7 @@
*
* Create at 2019/12/18 14:45
*/
+@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
@Slf4j
@Order(98)
@Aspect
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/light/LightContext.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/light/LightContext.java
index 1b664f5c..c14beb9e 100644
--- a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/light/LightContext.java
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/light/LightContext.java
@@ -23,36 +23,31 @@
import com.github.yizzuide.milkomeda.universe.context.ApplicationContextHolder;
import com.github.yizzuide.milkomeda.universe.context.SpringContext;
+import io.netty.util.concurrent.FastThreadLocal;
import lombok.Data;
import org.springframework.context.ConfigurableApplicationContext;
import java.io.Serializable;
/**
- * LightContext
- *
- * 线程缓存上下文,可以配合LightCache
当作超级缓存,也可以单独使用
- *
- * I:上下文id
- * E:上下文数据
+ * 线程缓存上下文,可以配合LightCache
当作超级缓存,也可以单独使用。
*
* @since 1.9.0
- * @version 3.13.0
+ * @version 3.15.0
* @author yizzuide
*
* Create at 2019/06/30 18:57
*/
@Data
public class LightContext {
- // 每个Thread对应ThreadLocalMap
- // 每个缓存实例都有自己的超级缓存
- private final ThreadLocal> context;
+
+ private final FastThreadLocal> context;
public LightContext() {
- context = new ThreadLocal<>();
+ context = new FastThreadLocal<>();
}
- public LightContext(ThreadLocal> threadLocal) {
+ public LightContext(FastThreadLocal> threadLocal) {
this.context = threadLocal;
}
@@ -66,6 +61,17 @@ public void setId(ID id) {
set(spot);
}
+ /**
+ * 设置上下文数据
+ * @param data 上下文数据
+ * @since 3.15.0
+ */
+ public void setData(V data) {
+ Spot spot = new Spot<>();
+ spot.setData(data);
+ set(spot);
+ }
+
/**
* 设置上下文数据
* @param spot Spot
@@ -95,14 +101,16 @@ public void remove() {
* @param value 任意对象
* @param identifier 唯一标识
* @param 对象类型
+ * @return LightContext
* @since 3.13.0
*/
@SuppressWarnings("unchecked")
- public static void setValue(V value, String identifier) {
+ public static LightContext setValue(V value, String identifier) {
LightContext lightContext = SpringContext.registerBean((ConfigurableApplicationContext) ApplicationContextHolder.get(), identifier, LightContext.class);
Spot spot = new Spot<>();
spot.setData(value);
lightContext.set(spot);
+ return lightContext;
}
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/light/LightThreadLocalScope.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/light/LightThreadLocalScope.java
index 93887b7a..7c84701d 100644
--- a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/light/LightThreadLocalScope.java
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/light/LightThreadLocalScope.java
@@ -21,9 +21,9 @@
package com.github.yizzuide.milkomeda.light;
+import io.netty.util.concurrent.FastThreadLocal;
import org.springframework.cloud.context.scope.GenericScope;
import org.springframework.cloud.context.scope.ScopeCache;
-import org.springframework.core.NamedThreadLocal;
import org.springframework.core.Ordered;
import java.util.ArrayList;
@@ -67,8 +67,7 @@ public String getConversationId() {
// 基于LightContext的Scope的缓存
static class LightScopeCache implements ScopeCache {
- private final LightContext> lightContext = new LightContext<>(new NamedThreadLocal>>("light-scope-thread-local"){
-
+ private final LightContext> lightContext = new LightContext<>(new FastThreadLocal>>(){
// 覆盖初始化,防止获了为空
@Override
protected Spot> initialValue() {
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/light/SortDiscard.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/light/SortDiscard.java
index e1f00526..0457fff6 100644
--- a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/light/SortDiscard.java
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/light/SortDiscard.java
@@ -32,7 +32,6 @@
/**
* SortDiscard
- *
* 抽象的字段排序方案
*
* @since 1.8.0
@@ -75,7 +74,7 @@ public void discard(Map> cacheMap, float l1Di
int size = list.size();
int discardCount = Math.round(size * l1DiscardPercent);
// 一级缓存百分比太小,直接返回
- if (discardCount == 0) {
+ if (discardCount < 1) {
return;
}
if (discardCount >= size) {
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/light/TimelineDiscard.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/light/TimelineDiscard.java
index e917e021..8f0dc8d5 100644
--- a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/light/TimelineDiscard.java
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/light/TimelineDiscard.java
@@ -38,6 +38,7 @@
*/
public class TimelineDiscard extends SortDiscard {
+ @SuppressWarnings("rawtypes")
@Override
public Class extends SortSpot> spotClazz() {
return TimelineSpot.class;
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/metal/MetalContainer.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/metal/MetalContainer.java
index 291d9830..06928075 100644
--- a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/metal/MetalContainer.java
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/metal/MetalContainer.java
@@ -21,6 +21,7 @@
package com.github.yizzuide.milkomeda.metal;
+import com.github.yizzuide.milkomeda.universe.extend.env.Environment;
import com.github.yizzuide.milkomeda.util.DataTypeConvertUtil;
import com.github.yizzuide.milkomeda.util.ReflectUtil;
import com.github.yizzuide.milkomeda.util.Strings;
@@ -37,7 +38,7 @@
/**
* MetalContainer
- * 配置容器,不推荐直接使用该类API,因为有线程安全问题,应该使用 {@link MetalHolder}
+ * 配置信息容器,不推荐直接使用该类API,因为有线程安全问题,应该使用 {@link MetalHolder}
*
* @author yizzuide
* @since 3.6.0
@@ -116,7 +117,9 @@ public void updateVNode(String key, String oldVal, String newVal) {
}
for (VirtualNode vNode : cacheSet) {
vNode.update(newVal);
- log.info("Metal update vnode '{}' with key '{}' from old value '{}' to '{}'", vNode.getSignature(), key, oldVal, newVal);
+ if (Environment.isShowLog()) {
+ log.info("Metal update vnode '{}' with key '{}' from old value '{}' to '{}'", vNode.getSignature(), key, oldVal, newVal);
+ }
}
}
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/metal/MetalMessageHandler.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/metal/MetalMessageHandler.java
index e7bc77d9..79f12224 100644
--- a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/metal/MetalMessageHandler.java
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/metal/MetalMessageHandler.java
@@ -37,6 +37,7 @@
*
* Create at 2020/05/22 15:59
*/
+@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
public class MetalMessageHandler {
private static String METAL_CHANGE_TOPIC;
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/metal/RedisMetalConfig.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/metal/RedisMetalConfig.java
index 5ca6bdca..8a8fb2bb 100644
--- a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/metal/RedisMetalConfig.java
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/metal/RedisMetalConfig.java
@@ -43,6 +43,7 @@
*
* Create at 2020/05/22 16:34
*/
+@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
@EnableConfigurationProperties(MetalProperties.class)
@ConditionalOnClass(RedisTemplate.class)
@AutoConfigureAfter(RedisAutoConfiguration.class)
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/moon/EnableMoon.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/moon/EnableMoon.java
index 1b4e4fb6..e1ad3acd 100644
--- a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/moon/EnableMoon.java
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/moon/EnableMoon.java
@@ -21,6 +21,7 @@
package com.github.yizzuide.milkomeda.moon;
+import com.github.yizzuide.milkomeda.universe.config.RedisGlobalConfig;
import org.springframework.context.annotation.Import;
import java.lang.annotation.*;
@@ -38,6 +39,6 @@
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
-@Import({MoonConfig.class, MixinMoonConfig.class})
+@Import({RedisGlobalConfig.class, MoonConfig.class, MixinMoonConfig.class})
public @interface EnableMoon {
}
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/moon/Moon.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/moon/Moon.java
index eaeb92f0..ecd9538b 100644
--- a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/moon/Moon.java
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/moon/Moon.java
@@ -155,7 +155,7 @@ public static T getPhase(String key, Moon prototype) {
}
/**
- * 基于分布式锁获取,需要添加 @EnableAspectJAutoProxy(exposeProxy=true)
(分布式并发安全)
+ * 基于分布式锁获取(分布式并发安全)
* @param key 缓存key,一个环对应一个key
* @return 当前环的当前阶段值
*/
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/orbit/AbstractOrbitAdvisor.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/orbit/AbstractOrbitAdvisor.java
new file mode 100644
index 00000000..96537514
--- /dev/null
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/orbit/AbstractOrbitAdvisor.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2022 yizzuide All rights Reserved.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package com.github.yizzuide.milkomeda.orbit;
+
+import com.github.yizzuide.milkomeda.util.ReflectUtil;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.springframework.aop.support.AbstractGenericPointcutAdvisor;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.support.BeanDefinitionBuilder;
+import org.springframework.beans.factory.support.BeanDefinitionRegistry;
+import org.springframework.util.CollectionUtils;
+
+import java.util.Map;
+
+/**
+ * The base abstract class that orbit advisor subclasses need to extend.
+ *
+ * @see org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator
+ * @see org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator
+ * @see org.springframework.beans.factory.config.RuntimeBeanNameReference
+ * @since 3.15.0
+ * @author yizzuide
+ *
+ * Create at 2022/02/26 00:58
+ */
+@NoArgsConstructor
+@AllArgsConstructor
+@Data
+public abstract class AbstractOrbitAdvisor implements OrbitAdvisor {
+ /**
+ * The advisor id for register in spring context.
+ */
+ private String advisorId;
+
+ /**
+ * The advice bind with this advisor.
+ */
+ private Class extends OrbitAdvice> adviceClass;
+
+ /**
+ * Advice property values.
+ */
+ private Map adviceProps;
+
+ @Override
+ public void initFrom(OrbitProperties.Item orbitItem) {
+ this.setAdvisorId(orbitItem.getKeyName());
+ this.setAdviceClass(orbitItem.getAdviceClazz());
+ this.setAdviceProps(orbitItem.getAdviceProps());
+ if (!CollectionUtils.isEmpty(orbitItem.getAdvisorProps())) {
+ ReflectUtil.setField(this, orbitItem.getAdvisorProps());
+ }
+ }
+
+ @Override
+ public BeanDefinition createAdvisorBeanDefinition(BeanDefinitionRegistry registry) {
+ String adviceBeanName = OrbitAdviceRegisterHelper.register(this, registry);
+ return this.createAdvisorBeanDefinitionBuilder()
+ // 添加Bean引用,内部创建RuntimeBeanNameReference,延迟对Advice Bean的创建
+ .addPropertyReference("advice", adviceBeanName)
+ // 将Advisor作为基础设施Bean,不添加自动代理
+ .setRole(BeanDefinition.ROLE_INFRASTRUCTURE)
+ .getBeanDefinition();
+ }
+
+ /**
+ * Extension hook that subclasses should create an advisor bean definition builder.
+ * @return the advisor must be extended of {@link AbstractGenericPointcutAdvisor}
+ */
+ protected abstract BeanDefinitionBuilder createAdvisorBeanDefinitionBuilder();
+}
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/orbit/AdvisorBeanDefinitionFactory.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/orbit/AdvisorBeanDefinitionFactory.java
new file mode 100644
index 00000000..241d0528
--- /dev/null
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/orbit/AdvisorBeanDefinitionFactory.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2023 yizzuide All rights Reserved.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package com.github.yizzuide.milkomeda.orbit;
+
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.support.BeanDefinitionRegistry;
+
+/**
+ * Abstract factory for create advisor BeanDefinition.
+ * @since 3.15.0
+ * @author yizzuide
+ *
+ * Create at 2023/01/28 20:49
+ */
+public interface AdvisorBeanDefinitionFactory {
+ /**
+ * Create advisor BeanDefinition for register and discovered with {@link org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator}.
+ * @param registry BeanDefinition registry
+ * @return advisor BeanDefinition
+ */
+ BeanDefinition createAdvisorBeanDefinition(BeanDefinitionRegistry registry);
+}
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/orbit/AnnotationOrbitAdvisor.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/orbit/AnnotationOrbitAdvisor.java
new file mode 100644
index 00000000..541b1bf5
--- /dev/null
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/orbit/AnnotationOrbitAdvisor.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2023 yizzuide All rights Reserved.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package com.github.yizzuide.milkomeda.orbit;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+import org.springframework.aop.support.DefaultPointcutAdvisor;
+import org.springframework.aop.support.annotation.AnnotationMatchingPointcut;
+import org.springframework.beans.factory.support.BeanDefinitionBuilder;
+
+import java.lang.annotation.Annotation;
+import java.util.Map;
+
+/**
+ * Simple orbit advisor metadata that looks for a specific Java 5 annotation being present on a class or method.
+ *
+ * @see org.springframework.aop.support.AbstractGenericPointcutAdvisor
+ * @see org.springframework.aop.support.annotation.AnnotationMatchingPointcut
+ * @since 3.15.0
+ * @author yizzuide
+ *
+ * Create at 2023/01/27 16:14
+ */
+@NoArgsConstructor
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class AnnotationOrbitAdvisor extends AbstractOrbitAdvisor {
+
+ /**
+ * The annotation type to look for at the class level.
+ */
+ private Class extends Annotation> classAnnotationType;
+
+ /**
+ * The annotation type to look for at the method level.
+ */
+ private Class extends Annotation> methodAnnotationType;
+
+ public AnnotationOrbitAdvisor(Class extends Annotation> classAnnotationType, Class extends Annotation> methodAnnotationType,
+ String advisorId, Class extends OrbitAdvice> adviceClass, Map props) {
+ super(advisorId, adviceClass, props);
+ this.classAnnotationType = classAnnotationType;
+ this.methodAnnotationType = methodAnnotationType;
+ }
+
+ public static AnnotationOrbitAdvisor forClass(Class extends Annotation> classAnnotationType, String advisorId, Class extends OrbitAdvice> adviceClass, Map props) {
+ return new AnnotationOrbitAdvisor(classAnnotationType, null, advisorId, adviceClass, props);
+ }
+
+ public static AnnotationOrbitAdvisor forMethod(Class extends Annotation> methodAnnotationType, String advisorId, Class extends OrbitAdvice> adviceClass, Map props) {
+ return new AnnotationOrbitAdvisor(null, methodAnnotationType, advisorId, adviceClass, props);
+ }
+
+ @Override
+ public BeanDefinitionBuilder createAdvisorBeanDefinitionBuilder() {
+ return BeanDefinitionBuilder.rootBeanDefinition(DefaultPointcutAdvisor.class)
+ .addPropertyValue("pointcut", new AnnotationMatchingPointcut(this.getClassAnnotationType(), this.getMethodAnnotationType()));
+ }
+}
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/orbit/AspectJOrbitAdvisor.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/orbit/AspectJOrbitAdvisor.java
new file mode 100644
index 00000000..6eb2f066
--- /dev/null
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/orbit/AspectJOrbitAdvisor.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2023 yizzuide All rights Reserved.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package com.github.yizzuide.milkomeda.orbit;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+import org.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor;
+import org.springframework.beans.factory.support.BeanDefinitionBuilder;
+
+import java.util.Map;
+
+/**
+ * AspectJ impl of orbit advisor metadata which support set pointcut expression.
+ *
+ * @see org.springframework.aop.aspectj.AspectJExpressionPointcut
+ * @since 3.15.0
+ * @author yizzuide
+ *
+ * Create at 2023/01/27 16:12
+ */
+@NoArgsConstructor
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class AspectJOrbitAdvisor extends AbstractOrbitAdvisor {
+
+ /**
+ * The pointcut expression value is an AspectJ expression.
+ */
+ private String pointcutExpression;
+
+ public AspectJOrbitAdvisor(String pointcutExpression, String id, Class extends OrbitAdvice> adviceClass, Map props) {
+ super(id, adviceClass, props);
+ this.pointcutExpression = pointcutExpression;
+ }
+
+ @Override
+ public void initFrom(OrbitProperties.Item orbitItem) {
+ if (orbitItem.getPointcutExpression() != null) {
+ this.setPointcutExpression(orbitItem.getPointcutExpression());
+ }
+ super.initFrom(orbitItem);
+ }
+
+ @Override
+ public BeanDefinitionBuilder createAdvisorBeanDefinitionBuilder() {
+ return BeanDefinitionBuilder.rootBeanDefinition(AspectJExpressionPointcutAdvisor.class)
+ .addPropertyValue("location", String.format("$$%s##", this.getAdvisorId())) // Set the location for debugging.
+ .addPropertyValue("expression", this.getPointcutExpression());
+ }
+}
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/orbit/Orbit.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/orbit/Orbit.java
index 6c4ff51d..b3d256bf 100644
--- a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/orbit/Orbit.java
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/orbit/Orbit.java
@@ -27,11 +27,10 @@
import java.lang.annotation.*;
/**
- * Orbit
- * 切面绑定注册
+ * Register a aspectJ advisor.
*
- * @author yizzuide
* @since 3.13.0
+ * @author yizzuide
*
* Create at 2022/02/27 01:34
*/
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/orbit/OrbitAdvice.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/orbit/OrbitAdvice.java
index 4682eb12..c3307003 100644
--- a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/orbit/OrbitAdvice.java
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/orbit/OrbitAdvice.java
@@ -5,13 +5,14 @@
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.aop.ProxyMethodInvocation;
import org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint;
+import org.springframework.aop.support.AopUtils;
import java.lang.reflect.Method;
/**
- * OrbitAdvice
- * 拦截切面
+ * Extension subclass of {@link MethodInterceptor}.
*
+ * @see org.springframework.aop.aspectj.AspectJAroundAdvice
* @author yizzuide
* @since 3.13.0
*
@@ -24,9 +25,11 @@ default Object invoke(MethodInvocation invocation) throws Throwable {
ProxyMethodInvocation pmi = (ProxyMethodInvocation) invocation;
ProceedingJoinPoint pjp = new MethodInvocationProceedingJoinPoint(pmi);
Method method = invocation.getMethod();
+ Class> targetClass = invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null;
OrbitInvocation orbitInvocation = OrbitInvocation.builder()
.pjp(pjp)
.target(pjp.getTarget())
+ .targetClass(targetClass)
.method(method)
.args(invocation.getArguments())
.build();
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/orbit/OrbitAdviceFactoryBean.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/orbit/OrbitAdviceFactoryBean.java
new file mode 100644
index 00000000..f6e73cc2
--- /dev/null
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/orbit/OrbitAdviceFactoryBean.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2023 yizzuide All rights Reserved.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package com.github.yizzuide.milkomeda.orbit;
+
+import com.github.yizzuide.milkomeda.util.ReflectUtil;
+import lombok.Data;
+import org.springframework.beans.factory.FactoryBean;
+import org.springframework.beans.factory.ObjectProvider;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+import org.springframework.util.CollectionUtils;
+
+import java.util.Map;
+
+/**
+ * Packaging the creation process of orbit advice.
+ *
+ * @since 3.15.0
+ * @author yizzuide
+ *
+ * Create at 2023/01/27 21:12
+ */
+@Data
+public class OrbitAdviceFactoryBean implements FactoryBean {
+
+ /**
+ * Orbit advice type.
+ */
+ private Class extends OrbitAdvice> adviceClass;
+
+ /**
+ * Orbit advice property values.
+ */
+ private Map adviceProps;
+
+ /**
+ * Spring context.
+ */
+ private ConfigurableListableBeanFactory beanFactory;
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public OrbitAdvice getObject() throws Exception {
+ // 使用ObjectProvider的延迟查找代替getBean()的立即查找,因为getBean()找不到会有异常
+ ObjectProvider adviceProvider = (ObjectProvider) beanFactory.getBeanProvider(this.getAdviceClass());
+ OrbitAdvice advice = adviceProvider.getIfAvailable(() -> ReflectUtil.newInstance(this.getAdviceClass()));
+ // autowire it's fields!
+ beanFactory.autowireBean(advice);
+ // reflect and set custom props
+ if (!CollectionUtils.isEmpty(this.getAdviceProps())) {
+ ReflectUtil.setField(advice, this.getAdviceProps());
+ }
+ return advice;
+ }
+
+ @Override
+ public Class> getObjectType() {
+ return OrbitAdvice.class;
+ }
+}
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/orbit/OrbitAdviceRegisterHelper.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/orbit/OrbitAdviceRegisterHelper.java
new file mode 100644
index 00000000..670ebd67
--- /dev/null
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/orbit/OrbitAdviceRegisterHelper.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2023 yizzuide All rights Reserved.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package com.github.yizzuide.milkomeda.orbit;
+
+import com.github.yizzuide.milkomeda.util.RecognizeUtil;
+import org.springframework.beans.factory.FactoryBean;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.support.AbstractBeanDefinition;
+import org.springframework.beans.factory.support.BeanDefinitionBuilder;
+import org.springframework.beans.factory.support.BeanDefinitionRegistry;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Simple helper class to register advice from advisor, it used {@link FactoryBean} for create bean.
+ *
+ * @since 3.15.0
+ * @author yizzuide
+ *
+ * Create at 2023/01/28 13:12
+ */
+public class OrbitAdviceRegisterHelper {
+ /**
+ * Bean name counter.
+ */
+ // ++操作符包含三条指令:
+ // 1.把变量count从内存加载到CPU的寄存器;2.在寄存器中执行+1操作;3.把结果写回内存(缓存)。
+ // 注:CPU只能保证指令级别的原子操作,而不是高级语言的操作符。
+ public static final Map counterMap = new HashMap<>();
+
+ /**
+ * Register advice from OrbitAdvisor with {@link BeanDefinitionRegistry}.
+ * @param orbitAdvisor OrbitAdvisor
+ * @param registry BeanDefinitionRegistry
+ * @return advice bean name
+ */
+ public static String register(OrbitAdvisor orbitAdvisor, BeanDefinitionRegistry registry) {
+ // 根据BeanDefinition生成beanName
+ //String adviceBeanName = AnnotationBeanNameGenerator.INSTANCE.generateBeanName(beanDefinition, (BeanDefinitionRegistry) beanFactory);
+ // 根据类名生成beanName
+ String adviceBeanName = RecognizeUtil.getBeanName(orbitAdvisor.getAdviceClass());
+ if (registry.containsBeanDefinition(adviceBeanName)) {
+ BeanDefinition beanDefinition = registry.getBeanDefinition(adviceBeanName);
+ Object adviceClass = beanDefinition.getPropertyValues().get("adviceClass");
+ if (adviceClass == null || orbitAdvisor.getAdviceClass() == adviceClass) {
+ return adviceBeanName;
+ }
+ adviceBeanName = adviceBeanName + "$" + counterMap.get(adviceBeanName).getAndIncrement();
+ } else {
+ counterMap.putIfAbsent(adviceBeanName, new AtomicInteger(0));
+ }
+ AbstractBeanDefinition orbitAdviceFactoryBeanDefinition = BeanDefinitionBuilder.rootBeanDefinition(OrbitAdviceFactoryBean.class)
+ .addPropertyValue("beanFactory", registry)
+ .addPropertyValue("adviceClass", orbitAdvisor.getAdviceClass())
+ .addPropertyValue("adviceProps", orbitAdvisor.getAdviceProps())
+ .getBeanDefinition();
+ registry.registerBeanDefinition(adviceBeanName, orbitAdviceFactoryBeanDefinition);
+ return adviceBeanName;
+ }
+}
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/orbit/OrbitNode.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/orbit/OrbitAdvisor.java
similarity index 59%
rename from Milkomeda/src/main/java/com/github/yizzuide/milkomeda/orbit/OrbitNode.java
rename to Milkomeda/src/main/java/com/github/yizzuide/milkomeda/orbit/OrbitAdvisor.java
index 76e7649e..d5c29fc9 100644
--- a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/orbit/OrbitNode.java
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/orbit/OrbitAdvisor.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2022 yizzuide All rights Reserved.
+ * Copyright (c) 2023 yizzuide All rights Reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
@@ -21,52 +21,38 @@
package com.github.yizzuide.milkomeda.orbit;
-import lombok.Builder;
-import lombok.Getter;
-
-import java.util.HashMap;
import java.util.Map;
/**
- * OrbitNode
- * 用于外部扩展的切面节点
+ * The top level interface with metadata to assemble advisor bean definition, all subclasses need provide a parameterless constructor.
*
+ * @since 3.15.0
* @author yizzuide
- * @since 3.13.0
*
- * Create at 2022/02/26 00:58
+ * Create at 2023/01/27 18:35
*/
-@Getter
-@Builder
-public class OrbitNode {
- /**
- * 切面节点唯一标识
- */
- private String id;
+public interface OrbitAdvisor extends AdvisorBeanDefinitionFactory {
/**
- * 切面节点表达式
+ * The advisor id for register in spring context.
+ * @return advisor id
*/
- private String pointcutExpression;
+ String getAdvisorId();
+
/**
- * 切面实现类
+ * The advice bind with this advisor.
+ * @return advice class
*/
- private Class extends OrbitAdvice> adviceClass;
+ Class extends OrbitAdvice> getAdviceClass();
+
/**
- * 切面实现属性配置
+ * Advice property values.
+ * @return property map
*/
- private Map props;
+ Map getAdviceProps();
/**
- * 添加单个KV
- * @param key 键
- * @param value 值
- * @return OrbitNode
+ * Init meta data from config item.
+ * @param orbitItem orbit config item
*/
- public OrbitNode putPropKV(String key, Object value) {
- if (props == null) {
- props = new HashMap<>();
- }
- props.put(key, value);
- return this;
- }
+ void initFrom(OrbitProperties.Item orbitItem);
}
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/orbit/OrbitInvocation.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/orbit/OrbitInvocation.java
index 1e0e77d2..ff353d5b 100644
--- a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/orbit/OrbitInvocation.java
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/orbit/OrbitInvocation.java
@@ -28,11 +28,11 @@
import java.lang.reflect.Method;
/**
- * OrbitInvocation
- * 调用对象
+ * Method invoked meta info.
*
* @author yizzuide
* @since 3.13.0
+ * @version 3.15.0
*
* Create at 2022/02/23 17:08
*/
@@ -43,14 +43,23 @@ public class OrbitInvocation {
* 连接点
*/
private ProceedingJoinPoint pjp;
+
/**
* 目标对象
*/
private Object target;
+
+ /**
+ * 被代理目标类
+ * @since 3.15.0
+ */
+ private Class> targetClass;
+
/**
* 切面方法
*/
private Method method;
+
/**
* 调用参数
*/
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/orbit/OrbitProperties.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/orbit/OrbitProperties.java
index d1fbf756..35088995 100644
--- a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/orbit/OrbitProperties.java
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/orbit/OrbitProperties.java
@@ -30,44 +30,56 @@
import java.util.Map;
/**
- * OrbitProperties
+ * Orbit properties config.
*
* @author yizzuide
* @since 3.13.0
+ * @version 3.15.0
*
* Create at 2022/02/21 01:27
*/
@Data
@ConfigurationProperties(prefix = OrbitProperties.PREFIX)
public class OrbitProperties {
- // 当前配置前缀
public static final String PREFIX = "milkomeda.orbit";
/**
- * 实例列表
+ * Orbit config item list.
*/
private List- instances = new ArrayList<>();
@Data
public static class Item {
/**
- * 唯一id名
+ * The key used for register bean name.
*/
private String keyName;
/**
- * 切点表达式,如应用给Mapper的query方法:execution(* com..mapper.*.query*(..))
+ * An advice class which impl of {@link OrbitAdvice}.
+ */
+ private Class extends OrbitAdvice> adviceClazz;
+
+ /**
+ * Advice property values.
+ */
+ private Map
adviceProps = new HashMap<>();
+
+ /**
+ * AspectJ pointcut expression,such as "execution(* com..mapper.*.query*(..))".
*/
private String pointcutExpression;
/**
- * 方法切面实现类
+ * An Advisor class which impl of {@link OrbitAdvisor}.
+ * @since 3.15.0
*/
- private Class extends OrbitAdvice> adviceClassName;
+ private Class extends OrbitAdvisor> advisorClazz = AspectJOrbitAdvisor.class;
/**
- * 切面实现属性注入
+ * Advisor config properties to interpolated dynamically.
+ * @since 3.15.0
*/
- private Map props = new HashMap<>();
+ private Map advisorProps;
}
}
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/orbit/OrbitRegistrar.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/orbit/OrbitRegistrar.java
index 1038a59a..cbc46540 100644
--- a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/orbit/OrbitRegistrar.java
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/orbit/OrbitRegistrar.java
@@ -24,12 +24,7 @@
import com.github.yizzuide.milkomeda.universe.context.SpringContext;
import com.github.yizzuide.milkomeda.util.ReflectUtil;
import lombok.extern.slf4j.Slf4j;
-import org.aopalliance.aop.Advice;
-import org.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor;
-import org.springframework.beans.factory.ObjectProvider;
-import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
-import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
@@ -39,22 +34,20 @@
import org.springframework.lang.NonNull;
import org.springframework.util.CollectionUtils;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
- * OrbitRegistrar
- * 环绕切面注册
- *
- * ImportBeanDefinitionRegistrar是Bean定义阶段注册器,适用于动态注册一个AspectJExpressionPointcutAdvisor
- * BeanPostProcessor是Bean的创建完成后置处理器,然后给它包装一个代理,但这里不适用
+ * Register orbit advisor.
*
- * @author yizzuide
- * @since 3.13.0
- * @see org.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor
* @see org.springframework.aop.support.AbstractExpressionPointcut
+ * @see org.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor
* @see org.springframework.beans.factory.config.BeanPostProcessor
+ * @since 3.13.0
+ * @version 3.15.0
+ * @author yizzuide
*
* Create at 2022/02/21 01:14
*/
@@ -63,11 +56,9 @@ public class OrbitRegistrar implements ImportBeanDefinitionRegistrar {
// 切面提供者包扫描路径
public static final String ORBIT_SOURCE_PROVIDER_SCAN_BASE_PACKAGES = "com.github.yizzuide.milkomeda.*.orbit";
- private OrbitProperties orbitProperties;
-
private final Environment environment;
- // Spring会自动传入Environment参数,调用这个构造器
+ // 注入Environment
OrbitRegistrar(Environment environment) {
this.environment = environment;
}
@@ -75,69 +66,55 @@ public class OrbitRegistrar implements ImportBeanDefinitionRegistrar {
@SuppressWarnings("unchecked")
@Override
public void registerBeanDefinitions(@NonNull AnnotationMetadata importingClassMetadata, @NonNull BeanDefinitionRegistry registry) {
- // 切面配置绑定
+ List orbitAdvisors = new ArrayList<>();
+ // 1.YAML配置方式
+ OrbitProperties orbitProperties;
try {
- // YAML配置方式
- this.orbitProperties = Binder.get(this.environment).bind(OrbitProperties.PREFIX, OrbitProperties.class).get();
- } catch (Exception e) {
+ orbitProperties = Binder.get(this.environment).bind(OrbitProperties.PREFIX, OrbitProperties.class).get();
+ } catch (Exception ignore) {
// 没用配置过Orbit,创建默认配置
- this.orbitProperties = new OrbitProperties();
+ orbitProperties = new OrbitProperties();
+ }
+ List orbitItems = orbitProperties.getInstances();
+ if (!CollectionUtils.isEmpty(orbitItems)) {
+ orbitItems.forEach(item -> {
+ OrbitAdvisor orbitAdvisor = ReflectUtil.newInstance(item.getAdvisorClazz());
+ if (orbitAdvisor != null) {
+ orbitAdvisor.initFrom(item);
+ orbitAdvisors.add(orbitAdvisor);
+ }
+ });
}
- // 框架其它模块桥接切面源提供者
+ // 2.框架其它模块桥接切面源提供者
Collection orbitSources = SpringContext.scanBeans(registry, OrbitSourceProvider.class, ORBIT_SOURCE_PROVIDER_SCAN_BASE_PACKAGES);
- orbitSources.forEach(orbitSource -> orbitSource.createNodes(this.environment).forEach(this::addNode));
+ orbitSources.forEach(orbitSource -> {
+ List advisors = orbitSource.createAdvisors(this.environment);
+ if (!CollectionUtils.isEmpty(advisors)) {
+ orbitAdvisors.addAll(advisors);
+ }
+ });
- // 注解注册方式
+ // 3.注解注册方式
ConfigurableListableBeanFactory beanFactory = (ConfigurableListableBeanFactory) registry;
Map orbits = beanFactory.getBeansWithAnnotation(Orbit.class);
orbits.forEach((id, value) -> {
Class adviceClass = (Class) value.getClass();
Orbit orbit = AnnotationUtils.findAnnotation(adviceClass, Orbit.class);
- assert orbit != null;
- String pointcutExpression = orbit.pointcutExpression();
- OrbitNode orbitNode = OrbitNode.builder().id(id).adviceClass(adviceClass).pointcutExpression(pointcutExpression).build();
- this.addNode(orbitNode);
+ if (orbit != null) {
+ orbitAdvisors.add(new AspectJOrbitAdvisor(orbit.pointcutExpression(), id, adviceClass, null));
+ }
});
- // 注册切面
- List instances = this.orbitProperties.getInstances();
- if (CollectionUtils.isEmpty(instances)) {
+ if (CollectionUtils.isEmpty(orbitAdvisors)) {
return;
}
- for (OrbitProperties.Item item : instances) {
- // 生成advice的beanName
- // AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(item.getAdviceClassName()).getBeanDefinition();
- // String adviceBeanName = AnnotationBeanNameGenerator.INSTANCE.generateBeanName(beanDefinition, registry);
-
- // 使用ObjectProvider的延迟查找代替getBean()的立即查找,因为getBean()找不到会有异常
- ObjectProvider adviceProvider = (ObjectProvider) beanFactory.getBeanProvider(item.getAdviceClassName());
- // 只有XML配置方式需要手动创建实例
- Advice advice = adviceProvider.getIfAvailable(() -> ReflectUtil.newInstance(item.getAdviceClassName()));
- // set custom props
- if (item.getProps() != null) {
- ReflectUtil.setField(advice, item.getProps());
- }
- String beanName = "mk_orbit_advisor_" + item.getKeyName();
- BeanDefinition aspectJBean = BeanDefinitionBuilder.genericBeanDefinition(AspectJExpressionPointcutAdvisor.class)
- .addPropertyValue("location", String.format("$$%s##", item.getKeyName())) // Set the location for debugging.
- .addPropertyValue("expression", item.getPointcutExpression())
- .addPropertyValue("advice", advice)
- .getBeanDefinition();
- registry.registerBeanDefinition(beanName, aspectJBean);
- }
- }
-
- /**
- * 添加切面节点
- * @param orbitNode OrbitNode
- */
- private void addNode(OrbitNode orbitNode) {
- OrbitProperties.Item item = new OrbitProperties.Item();
- item.setKeyName(orbitNode.getId());
- item.setPointcutExpression(orbitNode.getPointcutExpression());
- item.setAdviceClassName(orbitNode.getAdviceClass());
- item.setProps(orbitNode.getProps());
- this.orbitProperties.getInstances().add(item);
+ // register advisor
+ // 默认的自动代理会添加配置的Advisor Bean: AnnotationAwareAspectJAutoProxyCreator.findCandidateAdvisors() ->
+ // AbstractAdvisorAutoProxyCreator.getAdvicesAndAdvisorsForBean() -> findEligibleAdvisors() ->
+ // findCandidateAdvisors() -> BeanFactoryAdvisorRetrievalHelper.findAdvisor()
+ orbitAdvisors.forEach(orbitAdvisor ->
+ registry.registerBeanDefinition("mk_orbit_advisor_" + orbitAdvisor.getAdvisorId(),
+ orbitAdvisor.createAdvisorBeanDefinition(registry)));
}
}
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/orbit/OrbitSource.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/orbit/OrbitSource.java
index 4965d379..464eacea 100644
--- a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/orbit/OrbitSource.java
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/orbit/OrbitSource.java
@@ -26,8 +26,7 @@
import java.util.List;
/**
- * OrbitSource
- * 切面节点来源扩展接口
+ * The OrbitSource interface used for other module to register advisor.
*
* @author yizzuide
* @since 3.13.0
@@ -37,9 +36,9 @@
@FunctionalInterface
public interface OrbitSource {
/**
- * 初始化配置
- * @param environment Environment
- * @return Orbit node list
+ * Create advisor list.
+ * @param environment spring environment
+ * @return orbit advisor list
*/
- List createNodes(Environment environment);
+ List createAdvisors(Environment environment);
}
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/particle/EnableParticle.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/particle/EnableParticle.java
index 45c51c96..2fc41faf 100644
--- a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/particle/EnableParticle.java
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/particle/EnableParticle.java
@@ -21,6 +21,7 @@
package com.github.yizzuide.milkomeda.particle;
+import com.github.yizzuide.milkomeda.universe.config.RedisGlobalConfig;
import org.springframework.context.annotation.Import;
import java.lang.annotation.*;
@@ -37,6 +38,6 @@
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
-@Import(ParticleConfig.class)
+@Import({RedisGlobalConfig.class, ParticleConfig.class})
public @interface EnableParticle {
}
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/particle/LeakyBucketLimiter.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/particle/LeakyBucketLimiter.java
new file mode 100644
index 00000000..895992ec
--- /dev/null
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/particle/LeakyBucketLimiter.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2023 yizzuide All rights Reserved.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package com.github.yizzuide.milkomeda.particle;
+
+import com.github.yizzuide.milkomeda.universe.extend.env.Environment;
+import com.github.yizzuide.milkomeda.universe.extend.loader.LuaLoader;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.data.redis.core.script.DefaultRedisScript;
+import org.springframework.data.redis.core.script.RedisScript;
+
+import java.util.Collections;
+
+/**
+ * The leaky bucket limiter used fixed size bucket, fixed rate outflow, and receive requests at any rate,
+ * but reject all requests if the bucket is full.
+ *
+ * @since 3.15.0
+ * @author yizzuide
+ * Create at 2023/05/21 18:20
+ */
+@Slf4j
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class LeakyBucketLimiter extends LimitHandler implements LuaLoader {
+
+ /**
+ * Decorated postfix for a limiter key.
+ */
+ private static final String POSTFIX = ":leaky_bucket";
+
+ /**
+ * Bucket size.
+ */
+ private long bucketCapacity;
+
+ /**
+ * loss water count per second (handle request per second).
+ */
+ private long waterRate;
+
+ /**
+ * Lua script list.
+ */
+ private String[] luaScripts;
+
+ @Override
+ public R limit(String key, Process process) throws Throwable {
+ String limiterKey = key + POSTFIX;
+ RedisScript redisScript = new DefaultRedisScript<>(luaScripts[0], Long.class);
+ Long waterCount = getJsonRedisTemplate().execute(redisScript, Collections.singletonList(limiterKey), getBucketCapacity(), getWaterRate(), System.currentTimeMillis());
+ if (Environment.isShowLog()) {
+ log.info("particle drop water from bucket, leave water count: {}", waterCount);
+ }
+ // -1 is means bucket is fulled.
+ boolean isOver = waterCount == null || waterCount == -1;
+ Particle particle = new Particle(this.getClass(), isOver, null);
+ return next(particle, key, process);
+ }
+
+ @Override
+ public String[] luaFilenames() {
+ return new String[] {"particle_leakyBucket_limiter.lua"};
+ }
+}
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/particle/LimitHandler.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/particle/LimitHandler.java
index 9f29addf..4c3d3e96 100644
--- a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/particle/LimitHandler.java
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/particle/LimitHandler.java
@@ -41,10 +41,11 @@
@Data
public abstract class LimitHandler implements Limiter {
/**
- * 过期时间
+ * expire with second unit.
* @since 3.12.10
*/
protected Long expire;
+
/**
* 拦截链串,用于记录链条数据
*/
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/particle/LimiterType.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/particle/LimiterType.java
index 61e95000..4f780478 100644
--- a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/particle/LimiterType.java
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/particle/LimiterType.java
@@ -22,11 +22,11 @@
package com.github.yizzuide.milkomeda.particle;
/**
- * LimiterType
- * 限制器类型
+ * Used to specify the type of limiter.
*
- * @author yizzuide
* @since 3.1.2
+ * @version 3.15.0
+ * @author yizzuide
*
* Create at 2020/04/22 14:14
*/
@@ -41,6 +41,24 @@ public enum LimiterType {
*/
TIMES,
+ /**
+ * 滚动窗口
+ * @since 3.15.0
+ */
+ ROLL_WINDOW,
+
+ /**
+ * 令牌桶
+ * @since 3.15.0
+ */
+ TOKEN_BUCKET,
+
+ /**
+ * 漏桶
+ * @since 3.15.0
+ */
+ LEAKY_BUCKET,
+
/**
* 布隆限制器
*/
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/particle/ParticleConfig.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/particle/ParticleConfig.java
index d2713f21..77fb3916 100644
--- a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/particle/ParticleConfig.java
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/particle/ParticleConfig.java
@@ -22,7 +22,7 @@
package com.github.yizzuide.milkomeda.particle;
import com.github.yizzuide.milkomeda.universe.context.SpringContext;
-import com.github.yizzuide.milkomeda.util.IOUtils;
+import com.github.yizzuide.milkomeda.universe.extend.loader.LuaLoader;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
@@ -53,7 +53,7 @@
*
* @author yizzuide
* @since 1.14.0
- * @version 3.12.10
+ * @version 3.15.0
*
* Create at 2019/11/11 11:26
*/
@@ -120,6 +120,15 @@ public void setApplicationContext(@NonNull ApplicationContext applicationContext
case TIMES:
limiter.setHandlerClazz(TimesLimiter.class);
break;
+ case ROLL_WINDOW:
+ limiter.setHandlerClazz(RollWindowLimiter.class);
+ break;
+ case TOKEN_BUCKET:
+ limiter.setHandlerClazz(TokenBucketLimiter.class);
+ break;
+ case LEAKY_BUCKET:
+ limiter.setHandlerClazz(LeakyBucketLimiter.class);
+ break;
case BARRIER:
limiter.setHandlerClazz(BarrierLimiter.class);
break;
@@ -129,13 +138,18 @@ public void setApplicationContext(@NonNull ApplicationContext applicationContext
}
}
LimitHandler limitHandler = SpringContext.registerBean((ConfigurableApplicationContext) applicationContext, limiterName, limiter.getHandlerClazz(), limiter.getProps());
+ applicationContext.getAutowireCapableBeanFactory().autowireBean(limitHandler);
limitHandler.setExpire(limiter.getKeyExpire().getSeconds());
limiter.setLimitHandler(limitHandler);
if (limiter.getHandlerClazz() == BarrierLimiter.class) {
barrierLimiters.add(limiter);
}
// 缓存并设置属性
- cacheHandlerBeans.put(limiterName, limitHandler);
+ if(null == cacheHandlerBeans.putIfAbsent(limiterName, limitHandler) &&
+ limitHandler instanceof LuaLoader) {
+ // 仅读取配置限制器的lua脚本
+ ((LuaLoader) limitHandler).load();
+ }
}
// 创建barrierLimiter类型限制器链
@@ -157,10 +171,6 @@ public void setApplicationContext(@NonNull ApplicationContext applicationContext
List orderLimiters = limiters.stream()
.sorted(OrderComparator.INSTANCE.withSourceProvider(limiter -> limiter)).collect(Collectors.toList());
particleProperties.setLimiters(orderLimiters);
-
- // 读取lua脚本
- String luaScript = IOUtils.loadLua(IOUtils.LUA_PATH, "particle_times_limiter.lua");
- TimesLimiter.setLuaScript(luaScript);
}
static Map getCacheHandlerBeans() {
diff --git a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/particle/ParticleFilter.java b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/particle/ParticleFilter.java
index 0b454dad..a8c39b81 100644
--- a/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/particle/ParticleFilter.java
+++ b/Milkomeda/src/main/java/com/github/yizzuide/milkomeda/particle/ParticleFilter.java
@@ -58,6 +58,7 @@
*