每天在刷机票的时候都会看到这价格是每天一个价。 https://www.isharkfly.com/t/topic/324/1
每天在刷机票的时候都会看到这价格是每天一个价。 https://www.isharkfly.com/t/topic/324/1
第一次去 Boston 办日本签证的时候,因为资料不全没有办成。 但是问了是否可以代办,领事馆的工作人员说是可以的。 需要代办人填一个下面的表 领事馆工作人员给了一个下面的表。 如果你需要代办的话,你需要填好上面的表格,然后交给你的朋友或者家人就好了。 日本领事馆是可以代办的。 https://www.isharkfly.com/t/topic/323
如果你是积极的开源贡献者,并且 GitHub 上有你自己维护的项目。 并且你的项目还有点流量的话,你是可以申请 JetBrains 的开源许可证的。 JetBrains 对开源社区还是比较包容的,只要项目是在正常维护,通常他们都会签发一年的许可证给你。 https://www.ossez.com/t/jetbrains/14402
微信 API 中,针对用户数据统计可以获得用户增减数据,同时还可以获得用户累计数据。 分别是 2 个 API ,但是返回是下面 2 个对象。 2023-04-24_11-58-061024×758 35.9 KB 用户分析微信官方的 API 文档链接地址为:微信开放文档 其中一个 API 的返回的字符串。 { "list": [ { "ref_date": "2023-04-20", "user_source": 0, "new_user": 0, "cancel_user": 0 }, { "ref_date": "2023-04-21", "user_source": 0, "new_user": 0, "cancel_user": 0 }, { "ref_date": "2023-04-22", "user_source": 0, "new_user": 0, "cancel_user": 0 }, { "ref_date": "2023-04-23", "user_source": 0, "new_user": 0, "cancel_user": 0 } ] } 微信数据统计的问题 在微信数据统计的时候,如果你的请求日期是当天的话,API 会提示数据错误。 这是因为微信的数据统计不能提供当前的数据。 你将看到下面的错误提示: {"errcode":61501,"errmsg":"date range error rid: 6446a7e2-6382ec81-1f9d72d2"} 出现这个提示的原因是你的查询数据日期不正确,微信没有办法正确的返回数据。 https://www.ossez.com/t/topic/14398
提示的异常信息如下: java.lang.RuntimeException: com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "new_user" (class com.ossez.wechat.common.model.res.UserSummaryResponse$UserData), not marked as ignorable (5 known properties: "cancel_user", "new_users", "ref_date", "user_source", "cumulate_user"]) at [Source: (okhttp3.ResponseBody$BomAwareReader); line: 1, column: 63] (through reference chain: com.ossez.wechat.common.model.res.UserSummaryResponse["list"]->java.util.ArrayList[0]->com.ossez.wechat.common.model.res.UserSummaryResponse$UserData["new_user"]) at io.reactivex.internal.util.ExceptionHelper.wrapOrThrow(ExceptionHelper.java:46) at io.reactivex.internal.observers.BlockingMultiObserver.blockingGet(BlockingMultiObserver.java:93) 问题和解决 这是因为 retrofit 在反序列化的时候,如果没有找到对应的对象名,将会报错。 关键是在下面这句话: .addConverterFactory(JacksonConverterFactory.create()) 需要做的也非常简单,只需要将上面的 Jackson 的 mapper 对象映射过来就可以了。 在 ObjectMapper 对象中,我们可以定义是不是忽略没有找到的字段。 就是下面这句话: mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); 这样我们就不会因为没有找到字段而出现异常的问题了。 https://www.ossez.com/t/retrofit-json-unrecognizedpropertyexception/14399
Guice是Google开发的一个轻量级,基于Java5(主要运用泛型与注释特性)的依赖注入框架(IOC)。 Guice非常小而且快。Guice是类型安全的,它能够对构造函数,属性,方法(包含任意个参数的任意方法,而不仅仅是setter方法)进行注入。 Guice采用Java加注解的方式进行托管对象的配置,充分利用IDE编译器的类型安全检查功能和自动重构功能,使得配置的更改也是类型安全的。 Guice提供模块对应的抽象module,使得架构和设计的模块概念产物与代码中的module类一一对应,更加便利的组织和梳理模块依赖关系,利于整体应用内部的依赖关系维护,而其他IOC框架是没有对应物的。 此外,借助privateModule的功能,可以实现模块接口的明确导出和实现封装,使得支持多数据源这类需求实现起来异常简单。 定义一个 Guice Module 这个 Guice Model 会实现 Module 接口。 2023-04-23_15-32-551123×764 92.8 KB 然后对需要注入的类进行绑定。 绑定的语句在这里: binder.bind(TestConfigStorage.class).toInstance(config); binder.bind(WeChatOfficialAccountService.class).toInstance(weChatOfficialAccountService); binder.bind(WeChatMsgService.class).toInstance(weChatMsgService); 测试中使用 因为我们需要在测试中使用,所以我们会使用 testNG 的 Guice 注解。 2023-04-23_15-34-45825×442 33 KB 使用下面的语句直接注入到模块中。 然后把需要的服务,注入进来就可以了。 @Inject protected WeChatOfficialAccountService wxService; 是不是非常简单。 2023-04-23_15-34-45825×442 33 KB 相对 Junit 测试框架来说,TestNG 使用 Guice 更加方便。 如何在 Junit 中使用 Guice ,请参考文章:Junit 5 如何使用 Guice DI 中的内容。 https://www.ossez.com/t/testng-guice/14396
Guice 是一个依赖注入的小清新工具。 相比 Spring 的依赖管理来说,这个工具更加小巧,我们可以在测试中直接使用。 Junit 5 在 Junit 中使用就没有那么方便了,因为 Junit 没有 Guice 的注解。 你需要手动写一个类,在这个类中,对 Injector 的模块进行配置。 例如我们下面的代码: package com.ossez.wechat.oa.api.test; import com.google.inject.AbstractModule; import com.google.inject.Guice; import com.google.inject.Injector; import com.ossez.wechat.common.exception.WxRuntimeException; import com.ossez.wechat.oa.api.WeChatOfficialAccountService; import com.ossez.wechat.oa.api.impl.okhttp.WeChatMsgService; import com.ossez.wechat.oa.api.impl.okhttp.WeChatOfficialAccountServiceOkHttp; import org.apache.commons.lang3.ObjectUtils; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.io.SAXReader; import org.junit.jupiter.api.BeforeAll; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.io.InputStream; import java.util.concurrent.locks.ReentrantLock; /** * Init Guice DI * * @author YuCheng */ public class TestBase { private static final Logger log = LoggerFactory.getLogger(TestBase.class); private static final String TEST_CONFIG_XML = "test-config.xml"; private static final Injector injector = Guice.createInjector(new AbstractModule() { @Override public void configure() { try (InputStream inputStream = ClassLoader.getSystemResourceAsStream(TEST_CONFIG_XML)) { if (ObjectUtils.isEmpty(inputStream)) { throw new WxRuntimeException("测试配置文件【" + TEST_CONFIG_XML + "】未找到,请参照test-config-sample.xml文件生成"); } // Init WeChat config for testing Document document = new SAXReader().read(inputStream); TestConfigStorage config = new TestConfigStorage(); config.setAppId(document.getRootElement().element("appId").getText()); config.setSecret(document.getRootElement().element("secret").getText()); config.setToken(document.getRootElement().element("token").getText()); config.setOpenid(document.getRootElement().element("openid").getText()); config.setAccessTokenLock(new ReentrantLock()); // Init WeChat Service WeChatOfficialAccountService weChatOfficialAccountService = new WeChatOfficialAccountServiceOkHttp(); weChatOfficialAccountService.setWxMpConfigStorage(config); weChatOfficialAccountService.addConfigStorage("another", config); // Init WeChatMsgService WeChatMsgService weChatMsgService = new WeChatMsgService(weChatOfficialAccountService); bind(TestConfigStorage.class).toInstance(config); bind(WeChatOfficialAccountService.class).toInstance(weChatOfficialAccountService); bind(WeChatMsgService.class).toInstance(weChatMsgService); } catch (IOException e) { log.error(e.getMessage(), e); } catch (DocumentException e) { throw new RuntimeException(e); } } }); @BeforeAll public void setup() { injector.injectMembers(this); } } 在这个代码中,我们定义了一个 TestBase 的类,然后在测试启动的时候对齐进行了初始化和配置。 最主要的就是这个方法:`private static final Injector injector = Guice.createInjector(new AbstractModule() {} 在测试中使用 在测试中使用就比较简单了。 首先需要继承这个 TestBase,然后对需要的类进行注入后就可以直接使用了。 如上图,注入后直接使用。 https://www.ossez.com/t/junit-5-guice-di/14395
IoC 是 Inversion of Control 的简写,译为“控制反转”,它不是一门技术,而是一种设计思想,也是一个概念,同时是一个重要的面向对象编程法则,能够指导我们如何设计出松耦合、更优良的程序。 在这里说 IoC 之前,你需要完全理解一个概念 DI(依赖注入)。 当你理解依赖注入以后,对控制反转就非常简单了,只是一个概念而已。 什么是依赖注入,请参考文章:Java 依赖注入(DI) 控制反转 用土话来说就是本来应该是用你自己程序解决的依赖注入,我们现在把这个权力交给 Spring 来进行管理。 由 Spring 来管理所有的对象,因为你的权力下放给 Spring 了,Spring 就来进行控制了。 这个现象就叫做控制反转。 所以控制反转只是对一个现象的定义,Spring 是这个现象的具体实现罢了。 Spring 通过 IoC 容器来管理所有 Java 对象的实例化和初始化,控制对象与对象之间的依赖关系。我们将由 IoC 容器管理的 Java 对象称为 Spring Bean,它与使用关键字 new 创建的 Java 对象没有任何区别。 IoC 容器是 Spring 框架中最重要的核心组件之一,它贯穿了 Spring 从诞生到成长的整个过程。 如果你特别牛逼,你也弄个框架,让大家把对象的控制权都给你,那么你做的事情也就是在实现控制反转。 https://www.ossez.com/t/spring-ioc/14391
根据 Google 官方的消息: Google Analytics(分析)4 是我们的新一代效果衡量解决方案,即将取代 Universal Analytics。自 2023 年 7 月 1 日起,标准 Universal Analytics 媒体资源将停止处理新的命中数据。如果您仍在使用 Universal Analytics,我们建议您为以后使用 Google Analytics(分析)4 做好准备 Discourse 也顺其自然的有了下面的提示: Your Discourse is currently using Google Analytics 3, which will no longer be supported after July 2023. Upgrade to Google Analytics 4 now to continue receiving valuable insights and analytics for your website's performance. 如果通过管理员登录到后台就可以看见了。 解决办法 解决办法也非常简单,只需要升级到 4 就可以了。 进入Admin > Settings 然后找到 ga version 的站点设置 如果你现在还使用的是 v3_analytics 的话,切换到 v4_gtag 然后再刷新管理员控制台界面就会发现没有这个提示了。 https://www.ossez.com/t/discourse-google-analytics-3/14389
概述 在这篇短文中,我们将会展示如何把 Map 中的值取出来,转换为一个 Array,、List 或者一个 Set。 当然,你可以使用 Java JDK 来进行转换,你也可以使用 Guava 来进行转换。 首先,让我们来看看,如何使用原生的 Java JDK把一个 Map 的值换行为 Array。 @Test public final void givenUsingCoreJava_whenMapValuesConvertedToArray_thenCorrect() { final Map<Integer, String> sourceMap = createMap(); final Collection<String> values = sourceMap.values(); final String[] targetArray = values.toArray(new String[0]); } 在上面的代码中,我们使用了 values.toArray(new String[0]); 来对数组变量进行初始化。 根据: Arrays of Wisdom of the Ancients 文章中的内容,使用 toArray(new T[0]) 来对数组对象进行初始化更加高效和干净。 Map 的值转换为 List 下面,让我们看看如何使用原生 Java 来把一个 Map 中的值转换为 List。 @Test public final void givenUsingCoreJava_whenMapValuesConvertedToList_thenCorrect() { final Map<Integer, String> sourceMap = createMap(); final List<String> targetList = new ArrayList<>(sourceMap.values()); } 可以看到,我们还是使用了 Map 中提供的 values 方法。 使用 Guava @Test public final void givenUsingGuava_whenMapValuesConvertedToList_thenCorrect() { final Map<Integer, String> sourceMap = createMap(); final List<String> targetList = Lists.newArrayList(sourceMap.values()); } 简单来说,我们只是使用 Guava 的 Lists 方法进行了下包装,核心还是使用 Map 自带的 values 方法。 Map 的值转换为 Set 最后,让我们来看看如何使用原生 Java 来把 Map 中的值转换为 Set。 @Test public void givenUsingCoreJava_whenMapValuesConvertedToS_thenCorrect() { Map<Integer, String> sourceMap = createMap(); Set<String> targetSet = new HashSet<>(sourceMap.values()); } 结论 通过上面的代码,我们可以看到 Java 是非常容易把 Map 中的值取出来转换为其他集合的。 其中核心的方法就是其自带的 values() 方法。 然后使用其他的类包装下。 https://www.ossez.com/t/java-map-value-array-list-set/14388#h-1