From 86a5952ca572f879fefafadb2363b2c6ac4154c2 Mon Sep 17 00:00:00 2001 From: patrick Date: Wed, 17 Jul 2024 14:56:45 +0800 Subject: [PATCH] update parameters --- .../io/fluent/builtin/BigDecimalUtil.java | 17 ++ components/fluent-spring/pom.xml | 50 ++++ .../io/fluent/spring/config/SpringConfig.java | 12 + .../java/io/fluent/spring/util/IpUtils.java | 177 +++++++++++++ .../spring/util/SpringRegisterUtil.java | 123 +++++++++ .../io/fluent/spring/util/SpringUtils.java | 234 ++++++++++++++++++ components/pom.xml | 1 + docs/references.yaml | 5 +- .../jobs/GithubStarredCollectorJob.java | 6 +- 9 files changed, 621 insertions(+), 4 deletions(-) create mode 100644 components/fluent-builtin/src/main/java/io/fluent/builtin/BigDecimalUtil.java create mode 100644 components/fluent-spring/pom.xml create mode 100644 components/fluent-spring/src/main/java/io/fluent/spring/config/SpringConfig.java create mode 100644 components/fluent-spring/src/main/java/io/fluent/spring/util/IpUtils.java create mode 100644 components/fluent-spring/src/main/java/io/fluent/spring/util/SpringRegisterUtil.java create mode 100644 components/fluent-spring/src/main/java/io/fluent/spring/util/SpringUtils.java diff --git a/components/fluent-builtin/src/main/java/io/fluent/builtin/BigDecimalUtil.java b/components/fluent-builtin/src/main/java/io/fluent/builtin/BigDecimalUtil.java new file mode 100644 index 0000000..12a7759 --- /dev/null +++ b/components/fluent-builtin/src/main/java/io/fluent/builtin/BigDecimalUtil.java @@ -0,0 +1,17 @@ +package io.fluent.builtin; + +import java.math.BigDecimal; + +public class BigDecimalUtil { + + //-1 + public static final BigDecimal NEGATIVE_ONE = BigDecimal.valueOf(-1); + public static final BigDecimal ONE_HUNDRED = BigDecimal.valueOf(100); + public static final BigDecimal ONE_HUNDREDTH = new BigDecimal("0.01"); + public static final BigDecimal HOUR_8 = BigDecimal.valueOf(8); + + public static boolean isEmpty(BigDecimal bigDecimal) { + + return bigDecimal == null || BigDecimal.ZERO.compareTo(bigDecimal) == 0; + } +} diff --git a/components/fluent-spring/pom.xml b/components/fluent-spring/pom.xml new file mode 100644 index 0000000..c0702bd --- /dev/null +++ b/components/fluent-spring/pom.xml @@ -0,0 +1,50 @@ + + + 4.0.0 + + io.fluent + components + 1.0-SNAPSHOT + + + fluent-spring + + + UTF-8 + + + + + org.springframework + spring-core + 6.1.11 + + + org.springframework + spring-context + 6.1.11 + + + + org.springframework + spring-webmvc + 6.1.11 + + + + org.springframework + spring-context-support + 6.1.11 + + + + jakarta.servlet + jakarta.servlet-api + 6.1.0 + provided + + + + \ No newline at end of file diff --git a/components/fluent-spring/src/main/java/io/fluent/spring/config/SpringConfig.java b/components/fluent-spring/src/main/java/io/fluent/spring/config/SpringConfig.java new file mode 100644 index 0000000..a501263 --- /dev/null +++ b/components/fluent-spring/src/main/java/io/fluent/spring/config/SpringConfig.java @@ -0,0 +1,12 @@ +package io.fluent.spring.config; + +import io.fluent.spring.util.SpringUtils; +import org.springframework.context.annotation.Bean; + + +public class SpringConfig { + @Bean("fluentSpringUtils") + public SpringUtils springUtils() { + return new SpringUtils(); + } +} diff --git a/components/fluent-spring/src/main/java/io/fluent/spring/util/IpUtils.java b/components/fluent-spring/src/main/java/io/fluent/spring/util/IpUtils.java new file mode 100644 index 0000000..95772e8 --- /dev/null +++ b/components/fluent-spring/src/main/java/io/fluent/spring/util/IpUtils.java @@ -0,0 +1,177 @@ +package io.fluent.spring.util; + + +import jakarta.servlet.http.HttpServletRequest; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.Objects; + +/** + * 获取IP方法 + */ +public class IpUtils { + public static String getIpAddr(HttpServletRequest request) { + if (Objects.isNull(request)) { + return "127.0.0.1"; + } + String ip = null; + + // X-Forwarded-For:Squid 服务代理 + String ipAddresses = request.getHeader("X-Forwarded-For"); + if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) { + // Proxy-Client-IP:apache 服务代理 + ipAddresses = request.getHeader("Proxy-Client-IP"); + } + if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) { + // WL-Proxy-Client-IP:weblogic 服务代理 + ipAddresses = request.getHeader("WL-Proxy-Client-IP"); + } + if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) { + // HTTP_CLIENT_IP:有些代理服务器 + ipAddresses = request.getHeader("HTTP_CLIENT_IP"); + } + if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) { + // X-Real-IP:nginx服务代理 + ipAddresses = request.getHeader("X-Real-IP"); + } + + // 有些网络通过多层代理,那么获取到的ip就会有多个,一般都是通过逗号(,)分割开来,并且第一个ip为客户端的真实IP + if (ipAddresses != null && ipAddresses.length() != 0) { + ip = ipAddresses.split(",")[0]; + } + + // 还是不能获取到,最后再通过request.getRemoteAddr();获取 + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) { + ip = request.getRemoteAddr(); + } + return ip.equals("0:0:0:0:0:0:0:1") ? "127.0.0.1" : ip; + } + + public static boolean internalIp(String ip) { + byte[] addr = textToNumericFormatV4(ip); + return internalIp(addr) || "127.0.0.1".equals(ip); + } + + private static boolean internalIp(byte[] addr) { + if (addr == null || addr.length < 2) { + return true; + } + final byte b0 = addr[0]; + final byte b1 = addr[1]; + // 10.x.x.x/8 + final byte SECTION_1 = 0x0A; + // 172.16.x.x/12 + final byte SECTION_2 = (byte) 0xAC; + final byte SECTION_3 = (byte) 0x10; + final byte SECTION_4 = (byte) 0x1F; + // 192.168.x.x/16 + final byte SECTION_5 = (byte) 0xC0; + final byte SECTION_6 = (byte) 0xA8; + switch (b0) { + case SECTION_1: + return true; + case SECTION_2: + if (b1 >= SECTION_3 && b1 <= SECTION_4) { + return true; + } + case SECTION_5: + switch (b1) { + case SECTION_6: + return true; + } + default: + return false; + } + } + + /** + * 将IPv4地址转换成字节 + * + * @param text IPv4地址 + * @return byte 字节 + */ + public static byte[] textToNumericFormatV4(String text) { + if (text.length() == 0) { + return null; + } + + byte[] bytes = new byte[4]; + String[] elements = text.split("\\.", -1); + try { + long l; + int i; + switch (elements.length) { + case 1: + l = Long.parseLong(elements[0]); + if ((l < 0L) || (l > 4294967295L)) { + return null; + } + bytes[0] = (byte) (int) (l >> 24 & 0xFF); + bytes[1] = (byte) (int) ((l & 0xFFFFFF) >> 16 & 0xFF); + bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF); + bytes[3] = (byte) (int) (l & 0xFF); + break; + case 2: + l = Integer.parseInt(elements[0]); + if ((l < 0L) || (l > 255L)) { + return null; + } + bytes[0] = (byte) (int) (l & 0xFF); + l = Integer.parseInt(elements[1]); + if ((l < 0L) || (l > 16777215L)) { + return null; + } + bytes[1] = (byte) (int) (l >> 16 & 0xFF); + bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF); + bytes[3] = (byte) (int) (l & 0xFF); + break; + case 3: + for (i = 0; i < 2; ++i) { + l = Integer.parseInt(elements[i]); + if ((l < 0L) || (l > 255L)) { + return null; + } + bytes[i] = (byte) (int) (l & 0xFF); + } + l = Integer.parseInt(elements[2]); + if ((l < 0L) || (l > 65535L)) { + return null; + } + bytes[2] = (byte) (int) (l >> 8 & 0xFF); + bytes[3] = (byte) (int) (l & 0xFF); + break; + case 4: + for (i = 0; i < 4; ++i) { + l = Integer.parseInt(elements[i]); + if ((l < 0L) || (l > 255L)) { + return null; + } + bytes[i] = (byte) (int) (l & 0xFF); + } + break; + default: + return null; + } + } catch (NumberFormatException e) { + return null; + } + return bytes; + } + + public static String getHostIp() { + try { + return InetAddress.getLocalHost().getHostAddress(); + } catch (UnknownHostException e) { + } + return "127.0.0.1"; + } + + public static String getHostName() { + try { + return InetAddress.getLocalHost().getHostName(); + } catch (UnknownHostException e) { + } + return "未知"; + } +} \ No newline at end of file diff --git a/components/fluent-spring/src/main/java/io/fluent/spring/util/SpringRegisterUtil.java b/components/fluent-spring/src/main/java/io/fluent/spring/util/SpringRegisterUtil.java new file mode 100644 index 0000000..1e5f3bb --- /dev/null +++ b/components/fluent-spring/src/main/java/io/fluent/spring/util/SpringRegisterUtil.java @@ -0,0 +1,123 @@ +package io.fluent.spring.util; + +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.util.ClassUtils; +import org.springframework.util.ReflectionUtils; +import org.springframework.util.StringUtils; +import org.springframework.web.servlet.mvc.method.RequestMappingInfo; +import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +/** + * 手动注入 + + */ +public class SpringRegisterUtil { + + /** + * 向IOC 容器中注入 bean + * + * @param clazz bean class + * @param beanName bean beanName + */ + public static void registerBean(Class clazz, String beanName) { + BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(clazz); + BeanDefinition beanDefinition = beanDefinitionBuilder.getRawBeanDefinition(); + //设置当前bean定义对象是单利的 + beanDefinition.setScope("singleton"); + if (!StringUtils.hasText(beanName)) { + //将变量首字母置小写 + beanName = StringUtils.uncapitalize(clazz.getSimpleName()); + beanName = beanName.substring(beanName.lastIndexOf(".") + 1); + beanName = StringUtils.uncapitalize(beanName); + } + + //将applicationContext转换为ConfigurableApplicationContext + ConfigurableApplicationContext configurableApplicationContext = (ConfigurableApplicationContext) SpringUtils.getApplicationContext(); + // 获取bean工厂并转换为DefaultListableBeanFactory + DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) configurableApplicationContext.getBeanFactory(); + defaultListableBeanFactory.registerBeanDefinition(beanName, beanDefinition); + } + + /** + * 卸载bean + * + * @param className + * @throws Exception + */ + public void unregisterBean(String className) throws Exception { + + ConfigurableApplicationContext configurableApplicationContext = (ConfigurableApplicationContext) SpringUtils.getApplicationContext(); + // 获取bean工厂并转换为DefaultListableBeanFactory + DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) configurableApplicationContext.getBeanFactory(); + defaultListableBeanFactory.removeBeanDefinition(className); + } + + /** + * 注册Controller + * + * @param controllerBeanName + * @throws NoSuchMethodException + * @throws InvocationTargetException + * @throws IllegalAccessException + */ + public static void registerController(String controllerBeanName) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + + //先注册bean + //registerBean(controllerBeanName); + + final RequestMappingHandlerMapping requestMappingHandlerMapping = SpringUtils.getBean(RequestMappingHandlerMapping.class); + String handler = controllerBeanName; + Object controller = SpringUtils.getBean(handler); + // unregisterController(controllerBeanName); + if (controller == null) { + return; + } + //注册Controller + Method method = requestMappingHandlerMapping.getClass().getSuperclass().getSuperclass(). + getDeclaredMethod("detectHandlerMethods", Object.class); + //将private改为可使用 + method.setAccessible(true); + method.invoke(requestMappingHandlerMapping, controllerBeanName); + } + + /** + * 去掉Controller的Mapping + * + * @param controllerBeanName + */ + public static void unregisterController(String controllerBeanName) { + final RequestMappingHandlerMapping requestMappingHandlerMapping = (RequestMappingHandlerMapping) + SpringUtils.getBean("requestMappingHandlerMapping"); + if (requestMappingHandlerMapping != null) { + String handler = controllerBeanName; + Object controller = SpringUtils.getBean(handler); + if (controller == null) { + return; + } + final Class targetClass = controller.getClass(); + ReflectionUtils.doWithMethods(targetClass, new ReflectionUtils.MethodCallback() { + public void doWith(Method method) { + Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass); + try { + Method createMappingMethod = RequestMappingHandlerMapping.class. + getDeclaredMethod("getMappingForMethod", Method.class, Class.class); + createMappingMethod.setAccessible(true); + RequestMappingInfo requestMappingInfo = (RequestMappingInfo) + createMappingMethod.invoke(requestMappingHandlerMapping, specificMethod, targetClass); + if (requestMappingInfo != null) { + requestMappingHandlerMapping.unregisterMapping(requestMappingInfo); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + }, ReflectionUtils.USER_DECLARED_METHODS); + } + } +} diff --git a/components/fluent-spring/src/main/java/io/fluent/spring/util/SpringUtils.java b/components/fluent-spring/src/main/java/io/fluent/spring/util/SpringUtils.java new file mode 100644 index 0000000..5ceb838 --- /dev/null +++ b/components/fluent-spring/src/main/java/io/fluent/spring/util/SpringUtils.java @@ -0,0 +1,234 @@ +package io.fluent.spring.util; + + +import io.micrometer.common.util.StringUtils; +import lombok.Getter; +import org.springframework.aop.framework.AopContext; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.beans.factory.config.BeanFactoryPostProcessor; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.support.DefaultSingletonBeanRegistry; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.util.ObjectUtils; + +import java.util.Objects; + +/** + * spring工具类 方便在非spring管理环境中获取bean + * + * @author andanyang + */ +public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationContextAware { + + /** + * Spring应用上下文环境 + */ + @Getter + private static ApplicationContext applicationContext; + + /** + * "@PostConstruct"注解标记的类中,由于ApplicationContext还未加载,导致空指针
+ * 因此实现BeanFactoryPostProcessor注入ConfigurableListableBeanFactory实现bean的操作 + */ + private static ConfigurableListableBeanFactory beanFactory; + + @Override + public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException { + SpringUtils.beanFactory = configurableListableBeanFactory; + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + SpringUtils.applicationContext = applicationContext; + } + + /** + * 发布事件 + * + * @param event + */ + public static void publishEvent(Object event) { + applicationContext.publishEvent(event); + } + + /** + * 获取对象 + * + * @param name + * @return Object 一个以所给名字注册的bean的实例 + * @throws BeansException + */ + @SuppressWarnings("unchecked") + public static T getBean(String name) throws BeansException { + return (T) applicationContext.getBean(name); + } + + /** + * 获取类型为requiredType的对象 + * + * @param clz + * @return + * @throws BeansException + */ + public static T getBean(Class clz) throws BeansException { + return applicationContext.getBean(clz); + } + + /** + * 如果beanFactory包含一个与所给名称匹配的bean定义,则返回true + * + * @param name + * @return boolean + */ + public static boolean containsBean(String name) { + return applicationContext.containsBean(name); + } + + /** + * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException) + * + * @param name + * @return boolean + * @throws NoSuchBeanDefinitionException + */ + public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException { + return applicationContext.isSingleton(name); + } + + /** + * @param name + * @return Class 注册对象的类型 + * @throws NoSuchBeanDefinitionException + */ + public static Class getType(String name) throws NoSuchBeanDefinitionException { + return applicationContext.getType(name); + } + + /** + * 如果给定的bean名字在bean定义中有别名,则返回这些别名 + * + * @param name + * @return + * @throws NoSuchBeanDefinitionException + */ + public static String[] getAliases(String name) throws NoSuchBeanDefinitionException { + return applicationContext.getAliases(name); + } + + /** + * 获取aop代理对象 + * + * @param invoker + * @return + */ + @SuppressWarnings("unchecked") + public static T getAopProxy(T invoker) { + return (T) AopContext.currentProxy(); + } + + /** + * 获取{@link ConfigurableListableBeanFactory} + * + * @return {@link ConfigurableListableBeanFactory} + * @throws Admin4jException 当上下文非ConfigurableListableBeanFactory抛出异常 + * @since 5.7.7 + */ + public static ConfigurableListableBeanFactory getConfigurableBeanFactory() { + final ConfigurableListableBeanFactory factory; + if (null != beanFactory) { + factory = beanFactory; + } else if (applicationContext instanceof ConfigurableApplicationContext) { + factory = ((ConfigurableApplicationContext) applicationContext).getBeanFactory(); + } else { + throw new RuntimeException("No ConfigurableListableBeanFactory from context!"); + } + return factory; + } + + /** + * 获取配置文件配置项的值 + * + * @param key 配置项key + * @return 属性值 + */ + public static String getProperty(String key) { + if (null == applicationContext) { + return null; + } + return applicationContext.getEnvironment().getProperty(key); + } + + public static String getProperty(String key, String defaultValue) { + if (null == applicationContext) { + return null; + } + return applicationContext.getEnvironment().getProperty(key, defaultValue); + } + + /** + * 获取应用程序名称 + * + * @return 应用程序名称 + */ + public static String getApplicationName() { + return getProperty("spring.application.name"); + } + + /** + * 获取当前的环境配置,无配置返回null + * + * @return 当前的环境配置 + */ + public static String[] getActiveProfiles() { + if (null == applicationContext) { + return null; + } + return applicationContext.getEnvironment().getActiveProfiles(); + } + + /** + * 获取当前的环境配置,当有多个环境配置时,只获取第一个 + * + * @return 当前的环境配置 + */ + public static String getActiveProfile() { + final String[] activeProfiles = getActiveProfiles(); + return ObjectUtils.isEmpty(activeProfiles) ? Objects.requireNonNull(activeProfiles)[0] : null; + } + + /** + * 动态向Spring注册Bean + *

+ * 由{@link org.springframework.beans.factory.BeanFactory} 实现,通过工具开放API + *

+ * + * @param Bean类型 + * @param beanName 名称 + * @param bean bean + */ + public static void registerBean(String beanName, T bean) { + final ConfigurableListableBeanFactory factory = getConfigurableBeanFactory(); + factory.autowireBean(bean); + factory.registerSingleton(beanName, bean); + } + + /** + * 注销bean + *

+ * 将Spring中的bean注销,请谨慎使用 + * + * @param beanName bean名称 + */ + public static void unregisterBean(String beanName) { + final ConfigurableListableBeanFactory factory = getConfigurableBeanFactory(); + if (factory instanceof DefaultSingletonBeanRegistry) { + DefaultSingletonBeanRegistry registry = (DefaultSingletonBeanRegistry) factory; + registry.destroySingleton(beanName); + } else { + throw new RuntimeException("Can not unregister bean, the factory is not a DefaultSingletonBeanRegistry!"); + } + } +} diff --git a/components/pom.xml b/components/pom.xml index 12fb1c0..a52011b 100644 --- a/components/pom.xml +++ b/components/pom.xml @@ -26,6 +26,7 @@ fluent-datafactory fluent-testlibs fleunt-clients + fluent-spring diff --git a/docs/references.yaml b/docs/references.yaml index 79adeaf..d922654 100644 --- a/docs/references.yaml +++ b/docs/references.yaml @@ -4,4 +4,7 @@ blogs: - https://github.com/camunda/camunda-bpm-platform.git resources: - - https://itnext.io/ \ No newline at end of file + - https://itnext.io/ + +build-in: + - https://github.com/admin4j/admin4j-framework.git \ No newline at end of file diff --git a/fluent-apps/qaserver/src/main/java/io/fluentqa/jobs/GithubStarredCollectorJob.java b/fluent-apps/qaserver/src/main/java/io/fluentqa/jobs/GithubStarredCollectorJob.java index 7407de7..12d132b 100644 --- a/fluent-apps/qaserver/src/main/java/io/fluentqa/jobs/GithubStarredCollectorJob.java +++ b/fluent-apps/qaserver/src/main/java/io/fluentqa/jobs/GithubStarredCollectorJob.java @@ -5,6 +5,7 @@ import io.fluentqa.github.service.GithubService; import io.fluentqa.jobs.github.GithubJobFetchParameters; import lombok.extern.slf4j.Slf4j; +import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import xyz.erupt.core.annotation.EruptHandlerNaming; import xyz.erupt.job.handler.EruptJobHandler; @@ -23,18 +24,17 @@ public class GithubStarredCollectorJob implements EruptJobHandler { * @param code 任务编码 * @param param 任务参数 */ + @Override public String exec(String code, String param) { log.info("start job %s with parameter %s".formatted(code,param)); GithubJobFetchParameters parameters ; - List userNames = new ArrayList<>(); if(StringUtils.isAllEmpty(param)){ parameters = new GithubJobFetchParameters(); }else { parameters = JSONUtil.toBean(param, GithubJobFetchParameters.class); } - //TODO: make it async - userNames=StringUtils.split(parameters.getUserNames(),","); + List userNames=StringUtils.split(parameters.getUserNames(),","); for (String userName : userNames) { githubService.saveUserStarredRepo(userName,parameters.getFromPage()); }