Overview Spring中@Import注解最初主要是在配置类中使用,目的是引入其他的配置类(@Configuration)并实现自动注入。 目前Import并不只是支持引入@Configuration注解的类,也支持引入ImportSelector和ImportBeanDefinitionRegistrar接口的实现类,甚至可以引入普通的Java Bean并完成注入。 写了一个简单的应用来进行测试:spring-boot-import。 做些说明。在应用中定义了一个Worker类,应用做的事情就是结合@Import注解用不同的方式注入Worker类的多个Bean实例。 每个Worker Bean的实例通过name进行区分。 代码的一个核心是MyConfig类,代码如下: 这个类中包含@Configuration注解,说明是一个配置类,Spring会自动注入这个类的实例。此外这个类还通过@Bean注解注入了一个Worker Bean实例“tom”,又通过@Import接口引用三个其他类,目的是尝试注入其他的Worker Bean实例。最后在WorkerService中尝试获取并逐行打印注入的Worker实例: 接下来详细介绍下这个过程中是如何使用@Import接口的。 引入普通的Java Bean MyConfig类中使用@Import注解注入的MyAnotherConfig类没有继承任何超类或实现任何接口: 可以看到,如果不是内部的一个方法使用了@Bean注解,它就是一个普通的Java Bean了。也是通过这个@Bean注解,实现了另一个Worker Bean的注入。 引入ImportSelector实现类 根据Spring的文档,ImportSelector的作用是根据一些注解的属性来决定使用哪些@Configuration类。也就是配置类的选择器。通常在spring的引用包中会看到ImportSelector的实现。 因此这里定义了另一个配置类MySelectConfig,不过为了避免当前应用下Spring的自动注入,没有在这个类中添加@Configuration注解。 看起来和前面的MyAnotherConfig是一样的。不过和前例不一样的是:MySelectConfig类的注入是通过MyImportSelector来实现的。 MyImportSelector的实现如下: 这里没有基于AnnotationMetadata进行判定就直接返回了配置类的名称,在实际工作中不是一个好的实践。不过我们这里只是做一个演示,不需纠结太多。 引入ImportBeanDefinitionRegistrar实现类 ImportBeanDefinitionRegistrar与ImportSelector的作用是有着根本上的不同的:ImportSelector的作用是提供配置类;而ImportBeanDefinitionRegistrar的作用则是根据类定义完成相应Bean实例的创建。 通常ImportBeanDefinitionRegistrar多与ClassPathMapperScanner配合使用。ClassPathMapperScanner可以用来扫描指定的package,获取目标类并完成相应实例的创建。具体应用如MyBatis的@Mapper注解的解释。 看下在我们示例中的使用: 在这里通过Worker类的定义创建了一个名为“jerry”的实例。需要注意:这里虽然完成了Worker实例的创建,但是并没有配置任何属性。等在输出注入的Worker Bean的时候我们会看到这个实例的属性都是默认值。 引入Spring Component 使用@Import注解不仅可以引入普通的Java Bean,也可以引入Spring组件类,即需要使用@Component或者@Service等注解标记的类。组建类中通过@Autowired注解引用的其他组件也会被递归引用并注入。 示例应用中的WorkerService类并没有使用任何注解标记,而是在使用的时候通过@Import注解进行的引入。 这样虽然也可以使用,但并不建议这么做。 引入@Configuration注解的类 这个留到最后是因为一开始比较困惑:既然已经有@Configuration注解了,Spring就一定会自动引入这个类的,应该就没必要再使用@Import注解进行引用并注入了。 后来意识到我的想法是有漏洞的:比如一些第三方spring组件包中的配置类,既没有配置packageScan,也没有配置starter,直接使用肯定是不行的。此时使用@Import注解来导入相关的配置类及组件是一个很好地解决方案。 测试 执行WorkerControllerTest类的测试方法all(),观察测试结果,期间会输出我们创建的几个Worker Bean实例: 可以看到一个Worker实例的属性都是默认值,这个实例即是通过ImportBeanDefinitionRegistrar创建的Worker Bean “jerry”。 其他 关于@Import注解的实现原理可以参考 AbstractApplicationContext.refresh -> BeanFactoryPostProcessor -> ConfigurationClassPostProcessor -> ConfigurationClassParser.processImports()。具体就不展开了。 此外,还有另外一个注解@ImportResource主要用来引入xml或groovy配置文件。
[阅读更多...]-
SpringBoot探索01 – @Import注解
-
CaffeineCache 慎用weakKeys
前两天在一个Spring项目里使用了Caffine缓存,在application.yml中的配置如下: 为了避免缓存占用过多内存导致频繁GC,使用了weakKeys和weakValues选项。 不过测试时发现缓存不能命中,仍然会查询数据库。 通过debug发现,caffine使用WeakKeyReference将缓存的key做了封装。WeakKeyReference的结构如下: 需要注意这里的equals()和hashCode()方法。 equals()调用的referenceEquals()方法是接口InternalReference的default方法,具体为: referenceEquals()方法中调用的get()方法在WeakKeyReference类中获取的是key的原始值。在方法中对两个key是否一致的判定使用的是==,而非是equals()。也就是说需要两个key指向同一个对象才能被认为是一致的。 hashCode()的实现也与equals()方法呼应。生成hashCode使用的是System.identityHashCode()。identityHashCode方法是jre的一个native方法,这个方法的注释如下: 注释说明这个方法对于指定的对象会返回相同的hashCode。即这个方法是针对对象进行操作的,比如两个字符串对象,即使其字符序列相同,通过identityHashCode方法生成的hashCode也不会相同。 看一个示例程序: 示例程序输出了相通字符序列“zhyea”的两个字符串对象的identityHashCode执行结果,结果为: 可以看到最终结果是不同的。 到现在缓存不能命中的原因应该是找到了:因为使用了weakKeys选项,caffine使用WeakKeyReference封装了缓存key,导致相同字符序列的不同String对象的key被视为是不同的缓存主键。 果然在去掉weakKeys和weakValues配置项后,测试发现缓存能够命中了。 后来在Caffeine的文档中找到了如下说明: Caffeine.weakKeys() stores keys using weak references. This allows entries to be garbage-collected if there are no other strong references to the keys. Since garbage collection depends only on identity equality, this causes the whole cache to use identity (==) equality to compare keys, instead of equals(). 文档中提到因为GC的限制,需要对weakKey使用“==”替换equals()。 原因算是找到了,不过回过头来想想,在Spring中Caffeine的weakKeys选项确实有些鸡肋:Spring的CacheKey生成方式导致weakKey必然指向不同的对象,结果就是缓存注定不能命中,并且每次调用都会在缓存中插入一条新的记录。这样尽管使用weakKey不会造成内存泄漏,可是也会增加GC负担。因此在SpringBoot中使用Caffeine时需要慎用weakKeys。
[阅读更多...] -
Spring Controller层测试 – 05 SpringBootTest & WebServer
在使用@SpringBootTest测试时可以指定一个端口,如@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) 或 @SpringBootTest(webEnvironment = WebEnvironment.DEFINED_PORT),这样在测试时会启动Spring内嵌的Http Server。 这时就可以使用一个RestTemplate 或者 TestRestTemplate。 使用RANDOM_PORT和DEFINED_PORT的区别在于前着使用的是配置文件中的端口号(server.port,默认值为8080),后者使用的是一个随机端口号。在进行并行测试的时候可以使用随机端口号以避免端口冲突。 看下测试代码: Web Server测试 这里依然使用SpringRunner执行测试。同时使用@SpringBootTest注解的RANDOM_PORT 模式来得到一个内嵌的WebServer运行当前应用。 测试代码中使用RestTemplate来触发请求,这个过程和使用外部服务器很像。 测试中的assertion现在有了一点儿变化,因为要验证的返回值由MockHttpServletResponse变成了ResponseEntity。 TestRestTemplate 因为使用了@SpringBootTest注解,就可以使用@Autowired注解来获取TestRestTemplate的实例。这个TestRestTemplate实例的作用和常见的RestTemplate实例几乎没有任何区别,只是添加了一些额外的能力。实际上,可以将TestRestTemplate视为RestTemplate在测试环境的装饰类。 参与测试的角色关系如下图: 关于性能 也许我们会觉得第一个方案会更加有性能优势,因为它不需要加载Spring Context。事实上确实如此。但是即使加载了SpringContext,也不会造成特别恐怖的影响,因为在同一个Test Suite中,已经加载的Spring Context是可以重用的。 不过Spring Context重用也会导致一些问题,比如一些测试方法对公共的Bean做了修改就可能会影响到其他测试方法。此时可以使用@DirtiesContext注解来要求重新加载Context。 总结 到现在为止我们从轻到重共介绍了四种SpringBoot Controller测试方案。 虽然我们的目标一直都是对Controller层进行测试,但是从第一种测试方案(Standalone MockMVC)到现在,测试的角度还是有些变化的。一开始我们只是会加载测试的Controller类,却不会加载其周边的一些角色如Filter或Advice。到现在这个方案里我们启动了内嵌的WebServer,加载了整个SpringBoot Context。 目前这个方案是提到的四个测试方案里最重的一个,也是离单元测试的概念最远的一个。 下面说几个使用建议: 如果在单元测试中关注Controller的逻辑,优先选择第一种方案:Standalone MockMVC方案; 如果要测试Web层的其它角色(Filter或Advice)的行为,优先选择第四种测试方案执行集成测试; 避免将单元测试和集成测试混在一起,最好分开来写。 其他 Spring Controller测试 – 01 概述 Spring Controller测试 – 02 Standalone MockMVC Spring Controller测试 – 03 WebContext & MockMVC Spring Controller测试 – 04 SpringBootTest & MockMVC Spring Controller测试 – 05 SpringBootTest & WebServer 示例代码可在CSDN下载,地址:https://download.csdn.net/download/tianxiexingyun/11065824 参考文档:https://thepracticaldeveloper.com/2017/07/31/guide-spring-boot-controller-tests/
[阅读更多...] -
Spring Controller层测试 – 04 SpringBootTest & MockMVC
这种测试方案会加载完整的SpringContext,但我们仍然不需要Web Server,需要继续通过MockMVC来模拟请求。 在测试的时候主要用到了@SpringBootTest注解。看下代码: @SpringBootTest和@AutoConfigureMockMvc 使用@SpringBootTest注解会加载整个Context。这样我们可以自动获得所有在Context中注入的Bean,以及从application.properties中加载的配置信息。 在@SpringBootTest中声明webEnvironment为WebEnvironment.MOCK(默认值就是WebEnvironment.MOCK)后,结合@AutoConfigureMockMvc注解,在测试的时候会得到一个模拟的Web/Servlet环境。 因为没有Web Server,所以就无法使用RestTemplate,也就只能继续使用MockMVC了。这次MockMVC的实例是由@AutoConfigureMockMvc注解来完成的。这归功于SpringBoot的自动化配置。 所有参与测试的对象的关系如下图: 总结 这种测试方案更倾向于集成测试。它的关注点主要在于SpringBoot不同类之间的交互。 在这个测试方案中,请求仍然是通过MockMVC模拟的。不过因为有一个完整的Context,请求处理过程中的所有逻辑都是真实的,请求返回结果也是真实的。因此测试效果和使用Web Server几乎是差不多的了。 如果还是要只测试Controller中的逻辑,也可以继续使用 @MockBean注解来mock一个IWorkerService实例来覆盖Context中已有的实例。即使使用了真正的WebServer,也可以继续使用MockBean。同样在测试中也需要继续mock数据。 概括来说,这种测试的位置稍显尴尬:如果要执行单元测试来测试WEB层的逻辑,建议优先第二种方案;如果要执行集成测试,启动一个真正的Web Server,使用RestTemplate进行测试会更彻底也更加方便。 Spring Controller测试 – 01 概述 Spring Controller测试 – 02 Standalone MockMVC Spring Controller测试 – 03 WebContext & MockMVC Spring Controller测试 – 04 SpringBootTest & MockMVC Spring Controller测试 – 05 SpringBootTest & WebServer 其他:示例代码可在CSDN下载,地址:https://download.csdn.net/download/tianxiexingyun/11065824
[阅读更多...] -
Spring Controller层测试 – 03 WebContext & MockMVC
这种Controller层测试方案会(部分)加载Spring Application Context。不过仍然还是主要是用MockMVC来进行测试,也不需要部署WebServer。 示例代码如下: 和standalone MockMVC模式相比,这种测试方案主要有如下几点不同。 SpringRunner 这种测试是由SpringRunner来执行的。SpringRunner(部分)完成了Spring Context的初始化工作,在执行测试的时候,在日志开始的部分可以看到加载Context的内容。 MockMVC 自动化配置 使用@WebMVCTest注解可以完成MockMVC实例的自动化配置,以便于使用@Autowire注解来引用这个实例。同时,在@WebMVCTest注解中还指明了要测试的Controller类,这样Spring就会在Context中加载该Controller类及其相关的配置项。 此外,@WebMVCTest注解还会主动发现Controller类相关的Filter类和Controller Advice类并完成注入。这样,我们就不需要在setup()方法中再对其进行配置了。 使用MockBean 前面提到过这种测试方案是部分加载了Spring Context。原因就是@WebMVCTest在加载类的时候只会扫描含有@Controller,@ControllerAdvice, @JsonComponent,Filter,WebMvcConfigurer和HandlerMethodArgumentResolver这些注解或接口的类,也就是在三层模型中WEB层相关的类,而@Component注解的类则会被忽略掉。因此我们无法直接从Context获取IWorkerService实例。 因此测试中使用的WorkerService实例是通过@MockBean注解生成并注入到了Spring Context中的,并不是真正的WorkerService实例。 没有真正的服务调用 需要注意这里的返回值仍然是伪造的,在测试中也没有用到任何Web Server。 这次测试的主要关注点仍然是Controller类的内部逻辑,以及一些相关的角色如Filter和Controller Advice是如何影响Controller的返回值的。 总结 这次测试和使用MockMVC Standalone模式的主要区别在于不需要显式加载Controller相关的角色,因为这里使用到了Spring Context。参与测试的角色如下图: 如果我们创建了新的Filter、新的Controller Advice或者其他参与到WEB请求响应过程中的角色,也都会在测试中完成自动注入,不需要任何其它的配置。这和我们实际使用的场景非常类似。 这次测试可以算是向集成测试的一个小小过渡。这里我们没有做任何配置就实现了对Filter和Controller Advice的测试,如果有更多的其它角色,也可以直接集成到测试中。 Spring Controller测试 – 01 概述 Spring Controller测试 – 02 Standalone MockMVC Spring Controller测试 – 03 WebContext & MockMVC Spring Controller测试 – 04 SpringBootTest & MockMVC Spring Controller测试 – 05 SpringBootTest & WebServer 其他:示例代码可在CSDN下载,地址:https://download.csdn.net/download/tianxiexingyun/11065824
[阅读更多...] -
Spring Controller层测试 – 02 Standalone MockMVC
在Spring中,可以在Standalone模式下使用MockMVC来进行服务内测试,此时我们不会加载任何Context。来看个例子: MockitoJUnitRunner和MockMVC 代码中使用了MockitoJUnitRunner来运行单元测试。这个类是由Mockito提供的,并在内置的JUnit Runner上添加了一些功能:比如检测框架运行,初始化所有用@Mock注解声明的变量,这样就不需要再调用Mockito.initMocks()方法来显式mock对象了了。 代码中使用@Mock注解mock了一个IWorkerService的实例。这个IWorkerService的实例需要注入到WorkerController类的实例中。所以我们在声明WorkerController实例的时候使用了@InjectMocks注解。这样,通过这个注解我们使用mock出的IWorkerService实例替换了真正的WorkerServiceImpl实例。 在每个测试中,我们使用MockMvc对象来执行各种各样的模拟请求(如GET、POST等请求),之后我们也会收到一个MockHttpServletResponse对象作为返回值。注意这个返回值也不是一个真正的返回值,也是模拟的。 JacksonTester初始化 代码中使用JacksonTester.initFields()方法创建了一个JacksonTester对象。JacksonTester是Spring提供的一个工具类,可以使用这个JacksonTester对象来生成Worker实例的json字符串。 配置standalone MockMVC实例 每个测试在执行前都会调用setup方法。在setup方法中我们采用Standalone模式创建了MockMVC实例,并在配置中添加了要进行测试的Controller实例,以及新建的Controller Advice实例和HTTP Filter实例。当然也可以用类成员的形式创建Controller Advice和HTTPFilter实例。现在我们可以看到这种测试方法的不足了:所有Controller Advice和Filter中的逻辑都需要在setup()方法中进行配置。这是因为我们这里没有任何Spring Context可以注入Advice和Filter。 使用MockMVC测试ControllerAdvice和Filter 在测试方法getWhenNotExists()中,我们测试了id不存在的情况下,请求返回的状态码是否是HttpStatus.NOT_FOUND。如果返回值与预期一致的话,说明ControllerAdvice是能够正常工作的。 在最后的测试方法workerFilter()中,我们测试了返回结果中有没有WorkerFilter添加的请求头信息。如果返回结果的header中有添加的请求头信息,说明Filter是能够正常工作的。 也可以做个验证,注释掉setup()方法中关于ControllerAdvice和Filter的配置,然后再运行getWhenNotExists()和workerFilter()方法。可以看到测试会执行失败,因为相关的ControllerAdvice和Filter无法注入到运行环境中。 另外,在这里进行ControllerAdvice和Filter的测试多少有些不妥,因为我们的目标是测试Controller层的执行逻辑。对这二者的测试可以留到集成测试中进行。 总结 使用MockMVC Standalone模式测试对Controller层进行测试不会使用任何Spring容器,Controller层所依赖的其他服务均是mock出来的,也就是说除了Controller层的逻辑,其他相关数据都是模拟的。描述如下图: 这种测试方法的优势是: 不依赖Spring容器,启动和测试耗时较少 集中于Controller层逻辑,不受其他服务接口的影响 当然其优势也是其不足所在,我们通常不会在Controller层写入太多的业务逻辑,而是将业务逻辑写在Service层。但是因为这种测试方案中没有加载Spring容器,所以无法注入依赖的Service类,因此就无法测试到Service中实现的业务逻辑。 Spring Controller测试 – 01 概述 Spring Controller测试 – 02 Standalone MockMVC Spring Controller测试 – 03 WebContext & MockMVC Spring Controller测试 – 04 SpringBootTest & MockMVC Spring Controller测试 – 05 SpringBootTest & WebServer 其他:示例代码可在CSDN下载,地址:https://download.csdn.net/download/tianxiexingyun/11065824
[阅读更多...] -
Spring Controller层测试 – 01 概述
Spring Controller层(Web层或API层)的测试有多种方案。有人倾向于使用纯单元测试,有人则倾向于使用集成测试。 单元测试和集成测试 先来看一下单元测试和集成测试的概念。 单元测试 单元测试是对软件中的最小可验证单元进行检查和验证。比如对Java中的类和方法的测试。 测试原则: 尽可能保证测试用例相互独立(测试用例中不能直接调用其他类的方法,而应在测试用例中重写模拟方法); 此阶段一般由软件的开发人员来实施,用以检验所开发的代码功能是否符合自己的设计要求 单元测试的作用: 可以尽早的发现缺陷; 利于重构; 简化集成; 辅助文档; 辅助设计。 单元测试的不足: 每个测试单元至少需要3~5行代码,存在投入与产出的平衡。 集成测试 集成测试是根据集成测试计划,一边将模块或其他软件单位组合成越来越大的系统,一边运行该系统,以分析所组成的系统是否正确,各组成部分是否合拍。集成测试的策略主要有自顶向下和自底向上两种。 集成测试的作用: 目的是覆盖更多的执行路径; 发现单元之间接口的错误; 发现集成后的软件同软件需求不一致的地方。 不足在于: 关注的粒度较大,控制起来比较困难 比较来说单元测试更加重视过程,而集成测试更加重视结果。 Spring提供的Controller层测试方案 我们可以在不运行web server的情况下直接对Controller的逻辑进行测试。要执行这种测试,就需要mock服务器的行为,因此也有可能在测试中漏掉一些内容。不过无需担心,我们可以在集成测试中覆盖可能会漏掉的那部分内容。 当然也可以启动一个WebServer来执行Controller层的测试。这样就可以加载整个应用的Context,因此这种方案也可以被视为一种较重的方案。 在这里介绍三种Spring Controller层的测试方案: 在standalone模式下使用MockMVC方式; 结合SpringRunner使用MockMVC方式; 结合SpringBootTest和RestTemplate进行测试。 通常在执行单元测试的时候使用MockMVC方案,在进行集成测试的时候则建议使用RestTemplate方案。原因是使用MockMVC方式可以对Controller进行细粒度的断言,而RestTemplate则是能够使用Spring的WebApplicationContext(部分或全部)。 示例程序 为了便于说明,写了一个简单的SpringBoot Application来进行演示。 在应用中有如下几个类: WorkerController:WEB接口类; WorkerControllerAdvisor:Controller异常统一处理类,这里捕获了NonExistException并修改返回的status为404; WorkerFilter:过滤器类,在response中添加了header值X-CHOBIT-APP=chobit-header IWorkerService:服务接口类; WorkerServiceImpl:服务实现类,模拟了数据库操作。 示例代码可在CSDN下载,地址:https://download.csdn.net/download/tianxiexingyun/11065824 接下来分几篇文章来介绍几种测试方案。 Spring Controller测试 – 01 概述 Spring Controller测试 – 02 Standalone MockMVC Spring Controller测试 – 03 WebContext & MockMVC Spring Controller测试 – 04 SpringBootTest & MockMVC Spring Controller测试 – 05 SpringBootTest & WebServer
[阅读更多...] -
WordPress java操作库
前段时间有些需求就撸了个wordpress java操作库,主要是依赖wordpress XMLRPC实现。简单介绍下使用方式。 引入dependency: WordPress实例是所有操作的基础。简单的WordPress实例创建方式如下: xmlRpcUrl:xmlRpc服务端地址,WordPress博客的地址通常为博客地址 + xmlrpc.php,如:https://www.zhyea.com/xmlrpc.php username和password:登录WordPress博客后台使用的用户名和密码 也可以通过WPConfig(即WordPress配置对象)来更精细化地创建WordPress实例。WPConfig实例构建方式如下: 构建中的几个参数如下: trustAll:如博客未启用https,可忽略;如已启用https,建议将之设置为true,否则需要导入证书文件后再进行操作; connectTimeout:连接超时时间,单位ms; readTimeout:响应超时时间,单位ms。 使用WPConfig实例来创建WordPress实例: 新增文章可以使用WordPress实例的newPost()方法,示例代码如下: 该方法的返回结果为postId,即文章ID。 关于postName和postTitle:postTitle指的是文章标题;postName指的则是文章别名,主要在文章的url路径中使用;通常建议将postName设置为英文字符。 setCategories设置的是文章分类,如设置的分类在博客中不存在,将会按提交的分类名称创建新的分类。 setTags设置的是文章标签,同样的,如标签在博客中不存在将会创建新的标签。 我只用到了写入文章的能力,所以就写这些好了。更多用法参考wp-client的文档好了。 #########
[阅读更多...] -
Java 中文字符按Unicode排序
遇到了一个对包含中文的字符串进行排序的问题。要求按unicode编码对字符串进行排序。 测试字符串数组如下: 按unicode排序的期望结果应该是这样的: 先按java.lang.String类提供的默认比较方案进行实现,大致如下: 结果如下: 可以看到中文字符不能按照拼音进行排序。这时最直接的思路就是将中文字符转为拼音后再进行排序。但是要注意下,在这里面有个字符串不包含中文字符,这就容易导致顺序混乱。 如下面这几个字符串按拼音进行排序顺序如下: 可以看到字符串“1-qt”的位置出错了。 但是按拼音来说它的位置又是对的。这不能不说是一个让人有些头疼的地方。 不过不用担心,java提供了java.text.Collator类来支持规范化的字符串比较。 使用Collator来改造之前的代码: 改造后的程序执行排序的结果如下: 结果看着好像还OK。但是停停、注意下、字符串“1结束”的位置好像比较奇妙,理想情况下它应该在“1-营销”的后面。 这里出问题的原因我没有弄清楚。猜测着应该是java在Chinese语法中将中划线处理为空字符了。不过最根本的问题还是java对Unicode Collation Algorithm(UCA,Unicode整理算法)的支持并不好。 此时可以考虑使用IBM ICU提供的Collator来替换jdk默认的Collator。代码如下: 相关的依赖为: 执行结果为: 可以看到是和预期一致的。 参考文档 Java Unicode strings sorting ICU User Guide – JNI ##########
[阅读更多...] -
解决PKIX:unable to find valid certification path to requested target
遇到了“unable to find valid certification path to requested target”这样的问题。 错误详情如下: 从异常信息中可以看到错误是因为找不到证书导致的。 解决方案有两个。 正常的方案当然是找到证书并安装证书。这种方案能找到一大把,在这里不做展开。 说个暴力的方案:取消证书认证,信任所有请求链接。 使用的工具是fluent-http-client,maven地址是: 首先创建一个自定义的HttpClient实例。这个实例将信任所有的请求链接。实例详情如下: 注意代码中的两处注释,是关键。 然后绑定新创建的实例,执行一个Get请求:就这样。 #######
[阅读更多...]