Spring-cloud(五) 使用Ribbon进行Restful请求
写在前面
本文由markdown格式写成,为本人第一次这么写,排版可能会有点乱,还望各位海涵。
主要写的是使用Ribbon进行Restful请求,测试各个方法的使用,代码冗余较高,比较适合初学者,介意轻喷谢谢。
前提
- 一个可用的Eureka注册中心(文中以之前博客中双节点注册中心,不重要)
- 一个连接到这个注册中心的服务提供者
- 一个ribbon的消费者
注意:文中使用@GetMapping、@PostMapping、@PutMapping、@DeleteMapping等注解需要升级 spring-boot-starter-parent版本到1.5.9.REALEASE以上(1.3.7.RELEASE版本没有这些注解)
建议:每个微服务应用都有自己的spring-boot-maven-plugin和maven-compiler-plugin并指定jdk编译版本为1.8 ,指定方式如下,pom.xml中添加
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
测试项目构建
Eureka注册中心:参考注册中心的搭建
服务提供者:参考注册服务提供者
ribbon消费者:参考服务发现与消费
项目搭建完后,记得按照这几个教程中提到的配置hosts文件
为了防止项目中的RequestMapping相同,这里就删除所有的controller类(服务提供者和消费者),接下来我会将每个restful方法都封装成一个类,方便大家查看
Get请求
getForEntity:此方法有三种重载形式,分别为:
getForEntity(String url, Class<T> responseType)getForEntity(String url, Class<T> responseType, Object... uriVariables)getForEntity(String url, Class<T> responseType, Map<String, ?> uriVariables)getForEntity(URI url, Class<T> responseType)
注意:此方法返回的是一个包装对象ResponseEntity<T>其中T为responseType传入类型,想拿到返回类型需要使用这个包装类对象的getBody()方法
getForObject:此方法也有三种重载形式,这点与getForEntity方法相同:
getForObject(String url, Class<T> responseType)getForObject(String url, Class<T> responseType, Object... uriVariables)getForObject(String url, Class<T> responseType, Map<String, ?> uriVariables)getForObject(URI url, Class<T> responseType)
注意:此方法返回的对象类型为responseType传入类型
为了方便测试,这里分别在服务提供者和服务消费者中提供相同的User类,用于方便测试
package com.cnblogs.hellxz;
/**
* 用于测试的pojo
*/
public class User {
private String name;
private String sex;
private String phone;
public User(){}
public User(String name, String sex, String phone) {
this.name = name;
this.sex = sex;
this.phone = phone;
}
public String toString(){
return "user:{"
+"name: " + name + ", "
+"sex: " + sex + ", "
+"phone: " + phone
+" }";
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
}
下边我们在服务提供者处创建一个GetRequestController
package com.cnblogs.hellxz;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.*;
/**
* @Author : Hellxz
* @Description: 服务提供者
* @Date : 2018/4/18 11:36
*/
@RestController
public class GetRequestController {
@Autowired
private DiscoveryClient client; //注入发现客户端
private final Logger logger = Logger.getLogger(GetRequestController.class);
/**
* go straight test
*/
@GetMapping(value = "/hello")
public String hello(){
//获取服务实例,作用为之后console显示效果
ServiceInstance serviceInstance = client.getLocalServiceInstance();
logger.info("/hello host:"+serviceInstance.getHost()+" service_id:" +serviceInstance.getServiceId());
return "hello";
}
/**
* parameter test
*/
@GetMapping(value = "/greet/{dd}")
public String greet(@PathVariable String dd){
ServiceInstance serviceInstance = client.getLocalServiceInstance();
logger.info("/hello host:"+serviceInstance.getHost()+" service_id:" +serviceInstance.getServiceId());
return "hello "+dd;
}
/**
* 返回测试对象
*/
@GetMapping("/user")
public User getUser(){
ServiceInstance serviceInstance = client.getLocalServiceInstance();
logger.info("/user "+serviceInstance.getHost()+" port:"+serviceInstance.getPort()+" serviceInstanceid:"+serviceInstance.getServiceId());
return new User("hellxz","male", "123456789");
}
/**
* 根据名称返回对象,这里模拟查数据库操作
*/
@GetMapping("/user/{name}")
public User getUserSelect(@PathVariable String name){
ServiceInstance serviceInstance = client.getLocalServiceInstance();
logger.info("/user "+serviceInstance.getHost()+" port:"+serviceInstance.getPort()+" serviceInstanceid:"+serviceInstance.getServiceId());
if(name.isEmpty()){
return new User();
}else if(name.equals("hellxz")){
return new User("hellxz","male", "123456789");
}else{
return new User("随机用户","male", "987654321");
}
}
}
接下来我们在服务消费者项目中创建GetRequestController
package com.cnblogs.hellxz;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;
/**
* @Author : Hellxz
* @Description: ribbon消费者应用Controller,get请求
* @Date : 2018/4/16 15:54
*/
@RestController
public class GetRequestController {
private Logger logger = Logger.getLogger(GetRequestController.class);
@Autowired
//注入restTemplate
private RestTemplate restTemplate;
/**
* ResponseEntity<T> getForEntity(String url, Class<T> responseType)
* T getBody() 以下此方法相同
*/
@GetMapping(value="/entity/noparam")
public String noParamGetForEntity(){
//这里注释掉,因为之前想当然使用了直链访问服务提供者的接口,这样是不会返回结果的,而且会报错
//return restTemplate.getForEntity("http://localhost:8080/hello",String.class).getBody();
//使用restTemplate调用微服务接口
return restTemplate.getForEntity("http://hello-service/hello", String.class).getBody();
}
/**
* ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables)
*/
@GetMapping("/entity/type")
public User getForEntityIdentifyByType(){
//不传参返回指定类型结果
ResponseEntity<User> entity = restTemplate.getForEntity("http://hello-service/user", User.class);
User body = entity.getBody();
logger.info("user:"+body);
return body;
//以上可简写为
// return restTemplate.getForEntity("http://hello-service/user", User.class).getBody();
}
/**
* ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables)
* 使用占位符对参数进行替换,内部使用String.format方法实现
*/
@GetMapping(value="/entity")
//如果接收的参数是使用参数没有使用?有则使用@PathVariable,否则用@RequestParam
public String getForEntityByQuestionMarkParam(@RequestParam("name") String name){
//主要测试getEntity方法,这里测试直接传参
return restTemplate.getForEntity("http://hello-service/greet/{1}", String.class, name).getBody();
}
/**
* getForEntity方法内部会提取map中,以占位符为key的值作为参数回填入url中
* ResponseEntity<T> getForEntity(String url, Class<T> responseType, Map<String, ?> uriVariables)
*/
@GetMapping(value="/entity/map/{name}")
//如果接收的参数是使用参数没有使用?有则使用@PathVariable,否则用@RequestParam
public String getForEntityByMap(@PathVariable("name") String name){
//主要测试getEntity方法,这里测试map传参
Map<String, String> reqMap = new HashMap();
reqMap.put("name",name);
return restTemplate.getForEntity("http://hello-service/greet/{name}", String.class,reqMap).getBody();
}
/**
* ResponseEntity<T> getForObject(URI url, Class<T> responseType)
*/
@GetMapping("/entity/uri")
public String getForEntityByURI(){
//使用uri进行传参并访问
UriComponents uriComponents = UriComponentsBuilder.fromUriString("http://hello-service/greet/{name}").build().expand("laozhang").encode();
URI uri = uriComponents.toUri();
return restTemplate.getForEntity(uri, String.class).getBody();
}
/**
* T getForObject(String url, Class<T> responseType)
*/
@GetMapping("/object")
public User getForObjectWithNoParam(){
//相比getForEntity方法,获取对象可以省去调用getBody
return restTemplate.getForObject("http://hello-service/user", User.class);
}
/**
* T getForObject(String url, Class<T> responseType, Map<String, ?> uriVariables)
*/
@GetMapping("/object/map")
public User getForObjectByMap(){
//使用map传参
Map<String, String> paramMap = new HashMap<>();
paramMap.put("name","hellxz");
return restTemplate.getForObject("http://hello-service/user", User.class, paramMap);
}
/**
* T getForObject(String url, Class<T> responseType, Object... uriVariables)
*/
@GetMapping("/object/param/{name}")
public User getForObjectByParam(@PathVariable String name){
return restTemplate.getForObject("http://hello-service/user/{name}",User.class, name);
}
/**
* T getForObject(URI url, Class<T> responseType)
*/
@GetMapping("/object/uri/{name}")
public User getForObjectByURI(@PathVariable String name){
UriComponents uriComponents = UriComponentsBuilder.fromUriString("http://hello-service/user/{name}")
.build().expand(name).encode();
URI uri = uriComponents.toUri();
return restTemplate.getForObject(uri,User.class);
}
}
先启动注册中心,然后通过访问消费者对外提供的接口进行测试,这些都是本人实际操作过的了,这里就不写测试了
Post请求
post请求和get请求都有*ForEntity和*ForObject方法,其中参数列表有些不同,除了这两个方法外,还有一个postForLocation方法,其中postForLocation以post请求提交资源,并返回新资源的URI
postForEntity:此方法有三种重载形式,分别为:
postForEntity(String url, Object request, Class<T> responseType, Object... uriVariables)postForEntity(String url, Object request, Class<T> responseType, Map<String, ?> uriVariables)postForEntity(URI url, Object request, Class<T> responseType)
注意:此方法返回的是一个包装对象ResponseEntity<T>其中T为responseType传入类型,想拿到返回类型需要使用这个包装类对象的getBody()方法
postForObject:此方法也有三种重载形式,这点与postForEntity方法相同:
postForObject(String url, Object request, Class<T> responseType, Object... uriVariables)postForObject(String url, Object request, Class<T> responseType, Map<String, ?> uriVariables)postForObject(URI url, Object request, Class<T> responseType)
注意:此方法返回的对象类型为responseType传入类型
postForLocation:此方法中同样有三种重载形式,分别为:
postForLocation(String url, Object request, Object... uriVariables)postForLocation(String url, Object request, Map<String, ?> uriVariables)postForLocation(URI url, Object request)
注意:此方法返回的是新资源的URI,相比getForEntity、getForObject、postForEntity、postForObject方法不同的是这个方法中无需指定返回类型,因为返回类型就是URI,通过Object... uriVariables、Map<String, ?> uriVariables进行传参依旧需要占位符,参看postForEntity部分代码
按照之前的方式,我们分别在提供服务者和消费者的项目中分别创建PostRequestController
如下服务者PostRequestController代码如下:
package com.shunneng.springcloudhelloworld;
import org.apache.log4j.Logger;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;
import java.net.URI;
/**
* @Author : Hellxz
* @Description:
* @Date : 2018/4/18 10:21
*/
@RestController
public class PostRequestController {
private Logger logger = Logger.getLogger(PostRequestController.class);
/**
* 接收一个对象再返回回去,postForEntity/postForObject方法通用
*/
@PostMapping("/user")
public User returnUserByPost(@RequestBody User user){
logger.info("/use接口 "+user);
if(user == null) return new User("这是一个空对象","","");
return user;
}
/**
* 测试PostForEntity方法的参数,可以直接看输出判断结果了
*/
@PostMapping("/user/{str}")
public User returnUserByPost(@PathVariable String str, @RequestBody User user){
logger.info("/user/someparam 接口传参 name:"+str +" "+user);
if(user == null) return new User("这是一个空对象","","");
return user;
}
/**
* 为postForLocation方法返回URI
*/
@PostMapping("/location")
public URI returnURI(@RequestBody User user){
//这里模拟一个url,真实资源位置不一定是这里
UriComponents uriComponents = UriComponentsBuilder.fromUriString("http://hello-service/location")
.build().expand(user).encode();
URI toUri = uriComponents.toUri();
//这里不知道是什么问题,明明生成uri了,返回之后好像并没有被获取到
logger.info("/location uri:"+toUri);
return toUri;
}
}
消费端PostRequestController代码:
package com.cnblogs.hellxz;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;
import java.net.URI;
/**
* @Author : Hellxz
* @Description: Ribbon消费者post请求controller
* @Date : 2018/4/18 9:47
*/
@RestController
public class PostRequestController {
private Logger logger = Logger.getLogger(PostRequestController.class);
@Autowired
private RestTemplate restTemplate;
/**
* ResponseEntity<T> postForEntity(String url, Object request, Class<T> responseType)
* 其中参数url不多说,Object request如果是不是一个HttpEntity对象,会自动转换为HttpEntity对象,视作完整的body来处理;
* 如果是HttpEntity对象,那么会被直接当做body处理并且包含header内容。
* 以下对于重写的方法就不多说了,使用方法大体同getForEntity,如果仅是简单post对象,那么使用不带Object...variables或Map variables的方法即可。
* postForEntity(String url, Object request, Class<T> responseType, Object... uriVariables)
* postForEntity(String url, Object request, Class<T> responseType, Map<String, ?> uriVariables)
*
* 这里详细说下我遇到的坑:
* 1、其他几个重载方法的最后边的Object...variables和Map variables都是对之前的url进行操作的,
* 也就是说,在post请求的url中使用占位符进行传参,而如果在url中没有使用占位符,那么这些最后传的参数是无效的!
* 2、方法中Object request这个对象如果和服务提供者的接收参数类型相同,那么服务提供者仅需使用@RequestBody接收参数即可。
* 3、如果二者都使用了,这就比较有趣了,需要一边通过@PathVariable注解接收uri中的参数,一边还需要@RequestBody接收对象或RequestParam按字段接收参数!
* 4、如果报错了,请仔细看看我上边写的三条,并注意服务提供者的参数接收注解的使用等。
*/
@PostMapping("/entity")
public User postForEntity(){
User user = new User("hellxz1","1","678912345");
ResponseEntity<User> entity = restTemplate.postForEntity("http://hello-service/user/{str}", user, User.class, "测试参数");
User body = entity.getBody(); //所有restTemplate.*ForEntity方法都是包装类,body为返回类型对象
return body;
}
/**
* 使用URI传参,测试结果会显示在服务提供者的终端中
* ResponseEntity<T> postForEntity(URI url, Object request, Class<T> responseType)
*/
@PostMapping("/entity/uri")
public User postForEntityByURI(){
User user = new User("老张","1","678912345");
//这里只是将url转成URI,并没有添加参数
UriComponents uriComponents = UriComponentsBuilder.fromUriString("http://hello-service/user")
.build().encode();
URI toUri = uriComponents.toUri();
//使用user传参
User object = restTemplate.postForObject(toUri, user, User.class);
return object;
}
/**
* 这里测试postForObject方法,需要注意的参数如上述方法的描述,区别只是不需要getBody了,这里就不再累述了
* postForObject(String url, Object request, Class<T> responseType, Object... uriVariables)
* postForObject(String url, Object request, Class<T> responseType, Map<String, ?> uriVariables)
*/
@PostMapping("/object")
public User postForObject(){
User user = new User("hellxz2","1","123654987");
//这里url传1是为了调用服务者项目中的一个接口
User responseBody = restTemplate.postForObject("http://hello-service/user/1", user, User.class);
return responseBody;
}
/**
* post请求还有一种:postForLocation,这里也同样有三种重载,除了无需指定返回类型外,用法相同,返回类型均为URI,也就不累述了
* postForLocation(String url, Object request, Object... uriVariables)
* postForLocation(String url, Object request, Map<String, ?> uriVariables)
* postForLocation(URI url, Object request)
*/
@PostMapping("/location")
public URI postForLocation(){
User user = new User("hellxz3","1","987654321");
URI uri = restTemplate.postForLocation("http://hello-service/location", user);
//不知道为什么返回来是空,这个方法仅供参考吧,如果知道是什么情况,我会回来改的
logger.info("/location uri:"+uri);
return uri;
}
}
Put请求&&Delete请求
put请求相对于get和post请求方法来的更为简单,其中无需指定put请求的返回类型,当然也没有返回值,也是三种重载,和之前写的基本一致,这里就不想多说了,delete请求和put请求都是没有返回值的,这里再特地重复写也没什么意思,这里先分别列出这两个请求的方法,代码写在一个类中了
put请求方法如下:
put(String url, Object request, Object... uriVariables)put(String url, Object request, Map<String, ?> uriVariables)put(URI url, Object request)
delete请求方法如下:
delete(String url, Object... uriVariables)delete(String url, Map<String, ?> uriVariables)delete(URI url)
在提供服务者项目中添加PutAndDeleteRequestController,代码如下
package com.cnblogs.hellxz;
import org.apache.log4j.Logger;
import org.springframework.web.bind.annotation.*;
/**
* @Author : Hellxz
* @Description: 服务提供者 put&delete请求controller
* @Date : 2018/4/19 14:11
*/
@RestController
public class PutAndDeleteRequestController {
private Logger logger = Logger.getLogger(PutAndDeleteRequestController.class);
@PutMapping("/put")
public void put(@RequestBody User user){
logger.info("/put "+user);
}
@DeleteMapping("/delete/{id}")
public void delete(@PathVariable Long id){
logger.info("/delete id:"+id);
}
}
在提供服务者项目中添加PutAndDeleteRequestController,代码如下
package com.cnblogs.hellxz;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
/**
* @Author : Hellxz
* @Description: put请求、delete请求,重载的参数与上述demo基本相同,不予列出
* @Date : 2018/4/19 13:43
*/
@RestController
public class PutRequestController {
private Logger logger = Logger.getLogger(PostRequestController.class);
@Autowired
private RestTemplate restTemplate;
/**
* put请求示例,一般put请求多用作修改
*/
@PutMapping("/put")
public void put(@RequestBody User user){
restTemplate.put("http://hello-service/put",user);
}
/**
* delete请求示例
*/
@DeleteMapping("/del/{id}")
public void delete(@PathVariable Long id){
restTemplate.delete("http://hello-service/delete/{1}", id);
}
}
结语
这篇博文使用markdown写成,第一次写不知道如何将代码块中加入序号以及折叠代码功能,这可能不是一篇好文章,但是写这篇博文写了快两天,有什么好的建议欢迎评论交流,啥也不说了,如果本文对你有帮助,请帮忙点个推荐,加个关注吧!
声明:本博客无需许可也可以转载,但烦请注明出处
Spring-cloud(五) 使用Ribbon进行Restful请求的更多相关文章
- Spring Cloud 入门 之 Ribbon 篇(二)
原文地址:Spring Cloud 入门 之 Ribbon 篇(二) 博客地址:http://www.extlight.com 一.前言 上一篇<Spring Cloud 入门 之 Eureka ...
- Spring Cloud(Dalston.SR5)--Ribbon 中间层负载均衡
Spring Cloud 集成了 Ribbon 并结合 Eureka 可以实现客户端的负载均衡,使用 @LoadBalanced 修饰的 RestTemplate 类拥有了负载均衡功能,在 Sprin ...
- Spring Cloud:使用Ribbon实现负载均衡详解(下)
在上一篇文章(Spring Cloud:使用Ribbon实现负载均衡详解(上))中,我对 Ribbon 做了一个介绍,Ribbon 可以实现直接通过服务名称对服务进行访问.这一篇文章我详细分析一下如何 ...
- Spring Cloud Zuul API服务网关之请求路由
目录 一.Zuul 介绍 二.构建Spring Cloud Zuul网关 构建网关 请求路由 请求过滤 三.路由详解 一.Zuul 介绍 通过前几篇文章的介绍,我们了解了Spring Cloud ...
- Spring Cloud实战 | 第十一篇:Spring Cloud Gateway 网关实现对RESTful接口权限控制和按钮权限控制
一. 前言 hi,大家好,这应该是农历年前的关于开源项目 的最后一篇文章了. 有来商城 是基于 Spring Cloud OAuth2 + Spring Cloud Gateway + JWT实现的统 ...
- Spring Cloud入门教程-Ribbon实现客户端负载均衡
简介 我们继续以之前博客的代码为基础,增加Ribbon组件来提供客户端负载均衡.负载均衡是实现高并发.高性能.可伸缩服务的重要组成部分,它可以把请求分散到一个集群中不同的服务器中,以减轻每个服务器的负 ...
- Spring Cloud微服务Ribbon负载均衡/Zuul网关使用
客户端负载均衡,当服务节点出现问题时进行调节或是在正常情况下进行 服务调度.所谓的负载均衡,就是当服务提供的数量和调用方对服务进行 取舍的调节问题,在spring cloud中是通过Ribbon来解决 ...
- Spring Cloud(五) --- zuul
微服务网关 在微服务架构中,后端服务往往不直接开放给调用端,而是通过一个API网关根据请求的url,路由到相应的服务.当添加API网关后,在第三方调用端和服务提供方之间就创建了一面墙,这面墙直接与调用 ...
- Spring Cloud(Dalston.SR5)--Hystrix 断路器-合并请求
在 Spring Cloud 中可以使用注解的方式来支持 Hystrix 的合并请求,缓存与合并请求功能需要先初始化请求上下文才能实现,因此,必须实现 javax.servlet.Filter 用于创 ...
随机推荐
- js面向对象的理解
ECMAScript 有两种开发模式:1.函数式(过程化),2.面向对象(OOP).面向对象的语言有一个标志,那就是类的概念,而通过类可以创建任意多个具有相同属性和方法的对象.但是,ECMAScrip ...
- bootstrap 模态框(modal)插件使用
今天用户登陆时,在原网页上弹出新登陆窗口,发现使用的是modal插件,记录下该插件的使用方法,手写强化下. 首先,模态框(modal)是覆盖在父窗体上的子窗体,目的是显示来自一个单独的源的内容,可以在 ...
- 网络1711班 C语言第四次作业批改总结
网络1711班 C语言第四次作业批改总结 助教有话说(写在前面) 近来,有同学跟老师和助教们反映:博客作业太多太麻烦,而且对编程能力提高似乎没什么帮助?在这里我要谈一谈我的感想. 博客作业的意义? 首 ...
- PTA題目的處理(二)
題目7-1 計算分段函數[1] 1.實驗代碼 #include <stdio.h> int main() { float x,y; scanf("%f",&x) ...
- JAVAGUI设计步骤
①创建容器 首先要创建一个GUI应用程序,需要创建一个用于容纳所有其它GUI组件元素的载体,Java中称为容器.典型的包括窗口(Window).框架(Frame/JFrame).对话框(Dialog/ ...
- RxSwift 函数响应式编程
Max 在 Boston 上学,在 San Francisco 工作,是一名软件工程师及创业者.当他还在高中的时候就在一家创业公司工作了,他非常喜欢使用 iOS.Android 以及 JavaScri ...
- 关于mule中使用jdbc时报No Suitable Driver found错误的问题
错误大概信息: Exception in thread "main" org.mule.module.launcher.DeploymentStartException: SQLE ...
- 关于java中的数组
前言:最近刚刚看完了<Java编程思想>中关于数组的一章,所有关于Java数组的知识,应该算是了解的差不多了.在此再梳理一遍,以便以后遇到模糊的知识,方便查阅. Java中持有对象的方式, ...
- img加载卡顿,解决办法
我觉得我在这个项目里遇到了太多的第一次.比如上一篇博文:在在360.UC等浏览器,img不加载原因. 当前情况是:图片加载缓慢,图片加载时出现卡顿. 上图:我缩放了图片,估计有点变形.能说明情况就行, ...
- New UWP Community Toolkit - ImageEx
概述 UWP Community Toolkit 中有一个图片的扩展控件 - ImageEx,本篇我们结合代码详细讲解 ImageEx 的实现. ImageEx 是一个图片的扩展控件,包括 Ima ...