首页
归档
留言
友链
广告合作
壁纸
更多
美女主播
Search
1
博瑞GE车机升级/降级
5,608 阅读
2
Mac打印机设置黑白打印
4,936 阅读
3
修改elementUI中el-table树形结构图标
4,894 阅读
4
Mac客户端添加腾讯企业邮箱方法
4,672 阅读
5
intelliJ Idea 2022.2.X破解
4,354 阅读
后端开发
HarmonyOS Next
Web前端
微信开发
开发辅助
App开发
数据库
随笔日记
登录
/
注册
Search
标签搜索
Spring Boot
Java
Vue
Spring Cloud
Mac
MyBatis
WordPress
MacOS
asp.net
Element UI
Nacos
.Net
Spring Cloud Alibaba
MySQL
Mybatis-Plus
Typecho
jQuery
Java Script
IntelliJ IDEA
微信小程序
Laughing
累计撰写
627
篇文章
累计收到
1,421
条评论
首页
栏目
后端开发
HarmonyOS Next
Web前端
微信开发
开发辅助
App开发
数据库
随笔日记
页面
归档
留言
友链
广告合作
壁纸
美女主播
搜索到
627
篇与
的结果
2021-07-20
docker部署ActiveMQ并整合到Spring Boot
ActiveMQ是由Apache基金会采用Java语言开发的一个开源的消息中间件,完美的遵循JMS规范。ActiveMQ易于实现高级场景,而且只需要付出低消耗。被誉为消息中间件的“瑞士军刀”。常用来处理高并发请求的流量削峰、事务处理。本文,我们通过docker部署ActiveMQ,然后结合ActiveMQ的queue及topic模式,分别介绍在spring boot中的应用。通过docker安装ActiveMQ搜索可用的ActiveMQdocker search activemq我这里下载评星最高的webcenter/activemq下载docker镜像docker pull webcenter/activemq运行dockerdocker run -d --name activemq -p 61616:61616 -p 8161:8161 webcenter/activemq检查是否运行成功打开http://139.198.172.114:8161/,出现以下页面可以点击Manage ActiveMQ broker,默认用户名、密码都是adminSpring Boot整合ActiveMQActiveMQ在实际企业开发中主要有两种模式:点对点模式(Queue)和发布订阅模式(Topic)Queue模式Queue模式即队列(先进先出),消息提供者生产消息发布到Queue中,然后消费者从Queue中取出,并消费消息。这里需要注意的是,消息被消费者消费之后,Queue不再存储,所以消息只能被消费一次。Queue支持存在多个消息消费者,但是对一个消息而言,只会有一个消费者可以消费。创建Spring Boot项目并添加依赖<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-activemq</artifactId> </dependency> <dependency> <groupId>org.apache.activemq</groupId> <artifactId>activemq-pool</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>定义常量Constants.javapublic class Constants { public final static String QUEUEMESSAGE = "queue"; }添加配置类ActiveMQConfig.java@Configuration public class ActiveMQConfig { @Bean public Queue queue(){ return new ActiveMQQueue(Constants.QUEUEMESSAGE); } }创建消息提供者@RestController public class ProducerController { @Resource private JmsMessagingTemplate jmsMessagingTemplate; @Resource private Queue queue; @RequestMapping("/sendQueueMessage/{msg}") public String sendQueueMessage(@PathVariable("msg") String msg) { this.jmsMessagingTemplate.convertAndSend(queue, msg); return msg; } }配置ActiveMQ修改项目配置文件,配置ActiveMQspring.activemq.broker-url=tcp://139.198.172.114:61616 #接收所有消息 spring.activemq.packages.trust-all=true创建消息消费者Consumer.java@Component @Slf4j public class Consumer { @JmsListener(destination = Constants.QUEUEMESSAGE) public void receiveQueueMessage1(String message) { if (null != message) { log.info("收到queue报文1:" + message); } } @JmsListener(destination = Constants.QUEUEMESSAGE) public void receiveQueueMessage2(String message) { if (null != message) { log.info("收到queue报文2:" + message); } } }测试我们访问http://localhost:8083/sendQueueMessage/123,可以查看控制台输出信息可以看到,虽然我们提供了两个消费者,但是实际上只有一个消费者消费成功。Topic模式消息提供者将消息发布到Topic中,同时有多个消费者(订阅者)消费该消息,与Queue模式不同,发布到Topic消息会被所有的订阅者消费。Topic模式与Queue模式类似,我们这里基于Queue模式的代码进行修改。修改配置类ActiveMQConfig.java增加Topic的Bean,修改后代码如下@Configuration public class ActiveMQConfig { @Bean public Queue queue(){ return new ActiveMQQueue(Constants.QUEUEMESSAGE); } @Bean public Topic topic(){ return new ActiveMQTopic(Constants.TOPICMESSAGE); } }修改消息消费者@RestController public class ProducerController { @Resource private JmsMessagingTemplate jmsMessagingTemplate; @Resource private Queue queue; @Resource private Topic topic; @RequestMapping("/sendQueueMessage/{msg}") public String sendQueueMessage(@PathVariable("msg") String msg) { this.jmsMessagingTemplate.convertAndSend(queue, msg); return msg; } @RequestMapping("/sendTopicMessage/{msg}") public String sendTopicMessage(@PathVariable("msg") String msg) { this.jmsMessagingTemplate.convertAndSend(topic, msg); return msg; } }修改消费者@Component @Slf4j public class Consumer { @JmsListener(destination = Constants.QUEUEMESSAGE) public void receiveQueueMessage1(String message) { if (null != message) { log.info("收到queue报文1:" + message); } } @JmsListener(destination = Constants.QUEUEMESSAGE) public void receiveQueueMessage2(String message) { if (null != message) { log.info("收到queue报文2:" + message); } } @JmsListener(destination = Constants.TOPICMESSAGE) public void receiveTopicMessage1(String message) { if (null != message) { log.info("收到topic报文1:" + message); } } @JmsListener(destination = Constants.TOPICMESSAGE) public void receiveTopicMessage2(String message) { if (null != message) { log.info("收到topic报文2:" + message); } } }修改项目配置文件通过spring.jms.pub-sub-domain=true配置启用Topic。server.port=8083 spring.activemq.broker-url=tcp://139.198.172.114:61616 #接收所有消息 spring.activemq.packages.trust-all=true #配置使用topic spring.jms.pub-sub-domain=true测试访问http://localhost:8083/sendTopicMessage/123,查看控制台。可以看到两个订阅者都输出了消息。ActiveMQ监控我们通过http://139.198.172.114:8161/admin/topics.jsp,也可以查看服务器的信息
2021年07月20日
869 阅读
0 评论
0 点赞
2021-07-20
使用docker部署dubbo/dubbox并实现服务提供者、消费者
实现目标基于centos 7,通过docker打包部署dubbox(2.8.4),并实现简单的服务提供者与消费者。前置条件服务器安装JDK1.8并正确配置环境变量开发环境安装JDK1.8并正确配置环境变量开发环境安装maven并正确配置环境变量(建议使用阿里云仓库)安装zookeeperZooKeeper 是 Apache 软件基金会的一个软件项目,它为大型分布式计算提供开源的分布式配置服务、同步服务和命名注册。下载压缩包下载压缩包curl -O https://www.apache.org/dyn/closer.lua/zookeeper/zookeeper-3.7.0/apache-zookeeper-3.7.0-bin.tar.gz解压压缩包tar -zxvf apache-zookeeper-3.7.0-bin.tar.gz修改zookeeper配置文件进入zookeeper配置文件所在目录cd /home/apache-zookeeper-3.7.0-bin/conf修改配置文件cp zoo_sample.cfg zoo.cfg运行zookeepercd /home/apache-zookeeper-3.7.0-bin/bin ./zkServer.sh start检查zookeeper运行状态cd /home/apache-zookeeper-3.7.0-bin/bin ./zkServer.sh statusdubbox打包Dubbox 是一个分布式服务框架,其前身是阿里巴巴开源项目Dubbo ,被国内电商及互联网项目中使用,后期阿里巴巴停止了该项目的维护,当当网便在Dubbo基础上进行优化,并继续维护,为了与原有的Dubbo区分,故将其命名为Dubbox。Dubbox 致力于提供高性能和透明化的RPC远程服务调用方案,以及SOA服务治理方案。简单的说,dubbox就是个服务框架,如果没有分布式的需求,其实是不需要用的,只有在分布式的时候,才有dubbox这样的分布式服务框架的需求,并且本质上是个服务调用的东东,说白了就是个远程服务调用的分布式框架。dubbox没有提供打包好的jar包或者war包,我们需要自己在github下载源码并编译。下载dubbox源码dubbox代码下载地址:https://github.com/dangdangdotcom/dubbox编译代码进入dubbox代码文件见,执行打包命令mvn install -D maven.test.skip=true打包完成后,在dubbo-admin\target中能看到一个dubbo-admin-2.8.4.war修改dubbox配置文件我们用压缩软件打开dubbo-admin-2.8.4.war,打开WEB-INF\dubbo.properties,修改zookeeper地址及对应用户密码,如下:dubbo.registry.address=zookeeper://IP地址:2181 dubbo.admin.root.password=root dubbo.admin.guest.password=guest注意修改成自己zookeeper对应的ip地址 war包修改完成后备用。安装docker我们通过docker部署dubbox。更新yumyum -y update安装依赖 yum install -y yum-utils device-mapper-persistent-data lvm2安装dockeryum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo yum install docker-ce-18.06.0.ce-3.el7启动docker并配置开机启动systemctl start docker systemctl enable docker配置docker为国内源vi /etc/docker/daemon.json输入以下内容{ "registry-mirrors": ["http://hub-mirror.c.163.com", "https://docker.mirrors.ustc.edu.cn"] }通过docker部署dubbox我们这里自己打包一个docker,并部署dubbox创建文件夹创建一个文件夹/home/makedocker,用于存储Dockerfile以及war包。mkdir /home/makedocker cd /home/makedocker将war包上传到/home/makedocker制作Dockerfilevi Dockerfile并输入以下内容FROM registry.cn-hangzhou.aliyuncs.com/shuodao/tomcat-8.5.27 MAINTAINER Laughing COPY dubbo.war /usr/tomcat/webapps/ EXPOSE 8080/tcp打包dockerdocker build -t tomcat-dubbo .启动dockerdocker run -d -p 8888:8080 --name tomcat-dubbo tomcat-dubbo查看docker运行状态docker ps显示以下内容,代表docker运行成功查看dubbox运行状态打开http://139.198.172.114:8888/dubbo,系统要求输入密码,密码为dubbo.properties中配置的密码,输入完成后,正常打开界面如下实现dubbox服务我们创建一个用户服务,作为服务提供者,判断用户是否登录。一个商品服务,作为服务消费者,调用用户是否登录的接口。实现通用服务接口创建一个maven项目,提供基本的接口定义。pom.xml内容如下<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>cc.lisen</groupId> <artifactId>dubbo-common</artifactId> <version>1.0-SNAPSHOT</version> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> </properties> </project>创建接口package cc.lisen.dubbo.common.service; /** * 博客:https://lisen.cc * Description: * * @Author: 李森的博客 * DateTime: 2021-07-19 20:20 */ public interface RpcUserService { boolean checkUserLogin(String userName); }将代码部署到本地maven仓库由于我们没有远程maven仓库,所以我们将jar包install到本地仓库。在代码根目录,执行以下命令,打包到本地仓库。mvn install -D maven.test.skip=true实现服务提供者创建服务提供者。pom.xml如下<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.5.2</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>cc.lisen</groupId> <artifactId>dubbo-provider</artifactId> <version>0.0.1-SNAPSHOT</version> <name>dubbo-provider</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>dubbo</artifactId> <version>2.8.4</version> </dependency> <dependency> <groupId>io.dubbo.springboot</groupId> <artifactId>spring-boot-starter-dubbo</artifactId> <version>1.0.0</version> </dependency> <dependency> <groupId>cc.lisen</groupId> <artifactId>dubbo-common</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <includeSystemScope>true</includeSystemScope> <excludes> <exclude> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </exclude> </excludes> </configuration> </plugin> </plugins> </build> </project>实现接口package cc.lisen.dubboprovider.service; import cc.lisen.dubbo.common.service.RpcUserService; import org.springframework.stereotype.Service; /** * 博客:https://lisen.cc * Description: * * @Author: 李森的博客 * DateTime: 2021-07-19 19:56 */ @Service @com.alibaba.dubbo.config.annotation.Service(interfaceClass = RpcUserService.class) public class RpcUserServiceImpl implements RpcUserService{ @Override public boolean checkUserLogin(String userName){ return "admin".equals(userName); } } 修改配置文件配置dubboserver.port=8081 #dubbo提供者的别名,只是个标识 spring.dubbo.application.name=dubbo-provider #zookeeper地址 spring.dubbo.registry.address=zookeeper://139.198.172.114:2181 #dubbo协议 spring.dubbo.protocol.name=dubbo #dubbo端口号 spring.dubbo.protocol.port=20880 #这是你要发布到dubbo的接口所在包位置 spring.dubbo.scan=cc.lisen.dubboprovider.service测试运行项目,然后我们打开dubbo服务端,查看是否注册成功。实现服务消费者创建服务消费者。pom.xml如下<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.5.2</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>cc.lisen</groupId> <artifactId>dubbo-provider</artifactId> <version>0.0.1-SNAPSHOT</version> <name>dubbo-provider</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>dubbo</artifactId> <version>2.8.4</version> </dependency> <dependency> <groupId>io.dubbo.springboot</groupId> <artifactId>spring-boot-starter-dubbo</artifactId> <version>1.0.0</version> </dependency> <dependency> <groupId>cc.lisen</groupId> <artifactId>dubbo-common</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <includeSystemScope>true</includeSystemScope> <excludes> <exclude> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </exclude> </excludes> </configuration> </plugin> </plugins> </build> </project> 创建商品服务package cc.lisen.dubboprovider.service; /** * 博客:https://lisen.cc * Description: * * @Author: 李森的博客 * DateTime: 2021-07-19 20:36 */ public interface GoodService { boolean checkUserLogin(String userName); }创建商品服务类,调用用户登录判断接口package cc.lisen.dubboprovider.service.impl; import com.alibaba.dubbo.config.annotation.Reference; import cc.lisen.dubbo.common.service.RpcUserService; import cc.lisen.dubboprovider.service.GoodService; import org.springframework.stereotype.Service; /** * 博客:https://lisen.cc * Description: * * @Author: 李森的博客 * DateTime: 2021-07-19 19:56 */ @Service public class GoodServiceImpl implements GoodService { @Reference private RpcUserService rpcUserService; @Override public boolean checkUserLogin(String userName) { return rpcUserService.checkUserLogin(userName); } } 测试消费者服务是否正常注册测试创建商品销售接口package cc.lisen.dubboprovider.controller; import cc.lisen.dubboprovider.service.GoodService; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; /** * 博客:https://lisen.cc * Description: * * @Author: 李森的博客 * DateTime: 2021-07-19 20:40 */ @RestController public class GoodController { @Resource private GoodService goodService; @GetMapping("sell/good") public boolean sellGood(){ return goodService.checkUserLogin("admin"); } }
2021年07月20日
956 阅读
0 评论
1 点赞
2021-07-18
使用Hystrix实现微服务的熔断处理
Hystrix是Netflix开源的一个延迟和容错库,用于隔离访问远程系统、服务或者第三方库,防止级联失效,从而提升系统的可用性与容错性。Hystrix基本使用添加依赖<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> <version>2.2.8.RELEASE</version> </dependency>为启动类增加@EnableHystrix注解,从而为项目启用断路器支持@SpringBootApplication @EnableFeignClients @EnableHystrix public class MicroserviceConsumerMovieApplication { public static void main(String[] args) { SpringApplication.run(MicroserviceConsumerMovieApplication.class, args); } }修改方法,增加容错能力 @GetMapping("/user/{id}") @HystrixCommand(fallbackMethod = "findByIdFallback", commandProperties = { @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "5000"), @HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds", value = "10000") }, threadPoolProperties = { @HystrixProperty(name = "coreSize", value = "1"), @HystrixProperty(name = "maxQueueSize", value = "10") }) public User findById(@PathVariable Long id) { return userFeignClient.findById(id); } public User findByIdFallback(Long id, Throwable throwable) { log.info(throwable.getMessage()); User user = new User(); user.setName("默认用户"); return user; }测试我们断开此时的服务提供者,访问方法,可以看到进入了容错方法,并获取到了异常信息。feign集成hystrix{message type="info" content="关于soring Cloud版本提示"/}一定要注意Spring Cloud版本问题,Spring Cloud 2020 后,开启feign的hystrix支持需要在配置文件配置如下feign: circuitbreaker: enabled: true早期版本的Spring Cloud需要配置如下feign: hystrix: enabled: true为feign添加回退通过fallback添加回退通过fallback添加回退,不能获取回退原因。@FeignClient(name = "microservice-provider-user",fallback = UserFeignClientCallback.class) public interface UserFeignClient { /** * 使用Feign自带的注解 @RequestLine * @param id * @return */ @RequestLine("GET /{id}") User findById(@Param("id") Long id); } @Slf4j @Component class UserFeignClientCallback implements UserFeignClient{ /** * 使用Feign自带的注解 @RequestLine * * @param id * @return */ @Override public User findById(Long id) { User user = new User(); user.setName("默认用户"); return user; } }通过fallbackFactory添加回退fallbackFactory是fallback的升级版本,能够获取到回退原因。@FeignClient(name = "microservice-provider-user",fallbackFactory = UserFeignClientCallbackFactory.class) public interface UserFeignClient { /** * 使用Feign自带的注解 @RequestLine * @param id * @return */ @RequestLine("GET /{id}") User findById(@Param("id") Long id); } @Slf4j @Component class UserFeignClientCallbackFactory implements FallbackFactory<UserFeignClient> { @Override public UserFeignClient create(Throwable cause) { return new UserFeignClient() { @Override public User findById(Long id) { log.info(cause.getMessage()); User user = new User(); user.setName("默认用户"); return user; } }; } }温馨提示 日志最好放到各个fallback方法中,而不要放到create方法中,否则在引用启动时,会打印日志。feign项目中Hystrix的监控首先需要添加依赖<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> <version>2.2.8.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>启动类添加@EnableHystrix@SpringBootApplication @EnableFeignClients @EnableHystrix public class MicroserviceConsumerMovieApplication { public static void main(String[] args) { SpringApplication.run(MicroserviceConsumerMovieApplication.class, args); } }修改配置文件,暴漏地址management: endpoints: web: exposure: include: hystrix.stream测试打开地址http://localhost:8081/actuator/hystrix.stream使用hystrix-dashboard可视化监控项目我们刚才项目监控时,返回的都是Json数据,通过hystrix-dashboard我们可以实现可视化的监控。添加hystrix-dashboard依赖<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId> <version>2.2.8.RELEASE</version> </dependency>在启动类添加@EnableHystrixDashboard注解@SpringBootApplication @EnableFeignClients @EnableHystrix @EnableHystrixDashboard public class MicroserviceConsumerMovieApplication { public static void main(String[] args) { SpringApplication.run(MicroserviceConsumerMovieApplication.class, args); } }修改配置文件配置文件添加hystrix: dashboard: proxy-stream-allow-list: "*"如果不添加,监控会提示Unable to connect to Command Metric Stream.测试打开http://localhost:8081/hystrix,监控地址输入http://localhost:8081/actuator/hystrix.stream,title随便输入即可。点击Monitor Stream,然后访问任意的接口,系统输出界面如下
2021年07月18日
1,028 阅读
0 评论
1 点赞
2021-07-18
Spring Cloud 使用Feign实现声明式Rest调用
Feign是Netflix开发的声明式、模块化的HTTP客户端。Feign可以帮我们更加便捷、优雅的调用HTTP API。在Spring Cloud中,使用Feign非常简单,创建一个接口,并在接口上添加一些注解,代码就完成了。基本使用添加依赖<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> 增加配置文件配置文件与之前类似,不再赘诉。server: port: 8081 eureka: instance: prefer-ip-address: false client: service-url: defaultZone: http://peer1:8761/eureka,http://peer2:8762/eureka spring: application: name: microservice-consumer-movie修改启动类,增加@EnableFeignClients@SpringBootApplication @EnableFeignClients public class MicroserviceConsumerMovieApplication { public static void main(String[] args) { SpringApplication.run(MicroserviceConsumerMovieApplication.class, args); } }增加Feign接口/** * 博客:https://lisen.cc * Description: * * @Author: 香草物语 * DateTime: 2021-07-18 12:52 */ @FeignClient(name = "microservice-provider-user") public interface UserFeignClient { @RequestMapping(value = "{id}",method = RequestMethod.GET) User findById(@PathVariable Long id); } 调用@RestController public class MovieController { @Resource private DiscoveryClient discoveryClient; // @Resource // private RestTemplate restTemplate; @Resource private UserFeignClient userFeignClient; // @Resource // private LoadBalancerClient loadBalancerClient; @GetMapping("/user-instance") public List<ServiceInstance> showInfo() { return discoveryClient.getInstances("microservice-provider-user"); } @GetMapping("/user/{id}") public User findById(@PathVariable Long id) { // return restTemplate.getForObject("http://microservice-provider-user/"+id, User.class); return userFeignClient.findById(id); } // @GetMapping("log/microservice-provider-user") // public ServiceInstance userLog() { // return loadBalancerClient.choose("microservice-provider-user"); // } }基于代码Feign自定义配置创建配置类创建Feign的配置类UserFeignConfig,需要注意的是,该类不能添加@Configuration注解,如果添加了@Configuration注解,那么该类不能在主应用程序的@ComponentScan注解扫描包内。/** * 博客:https://lisen.cc * Description:feign配置文件 * * @Author: 香草物语 * DateTime: 2021-07-18 15:39 */ public class UserFeignConfig { /** * 将契约改成Feign原生的契约,这样就可以使用Feign自带的注解了 * @return */ @Bean public Contract feignContract(){ return new Contract.Default(); } }修改Feign接口/** * 博客:https://lisen.cc * Description: * * @Author: 香草物语 * DateTime: 2021-07-18 12:52 */ @FeignClient(name = "microservice-provider-user",configuration = UserFeignConfig.class) public interface UserFeignClient { /** * 使用Feign自带的注解 @RequestLine * @param id * @return */ @RequestLine("GET /{id}") User findById(@Param("id") Long id); }测试@RestController public class MovieController { @Resource private DiscoveryClient discoveryClient; // @Resource // private RestTemplate restTemplate; @Resource private UserFeignClient userFeignClient; // @Resource // private LoadBalancerClient loadBalancerClient; @GetMapping("/user-instance") public List<ServiceInstance> showInfo() { return discoveryClient.getInstances("microservice-provider-user"); } @GetMapping("/user/{id}") public User findById(@PathVariable Long id) { // return restTemplate.getForObject("http://microservice-provider-user/"+id, User.class); return userFeignClient.findById(id); } // @GetMapping("log/microservice-provider-user") // public ServiceInstance userLog() { // return loadBalancerClient.choose("microservice-provider-user"); // } }再次访问http://localhost:8081/user/1,查看输出全局配置以上我们是根据服务进行配置的,我们也可以进行全局配置。在启动类中添加@EnableFeignClients注解@SpringBootApplication @EnableFeignClients(defaultConfiguration = UserFeignConfig.class) public class MicroserviceConsumerMovieApplication { public static void main(String[] args) { SpringApplication.run(MicroserviceConsumerMovieApplication.class, args); } } 使用属性自定义Feign配置配置指定名称的Feign Client修改配置文件server: port: 8081 eureka: instance: prefer-ip-address: false client: service-url: defaultZone: http://peer1:8761/eureka,http://peer2:8762/eureka spring: application: name: microservice-consumer-movie feign: client: config: microservice-provider-user: contract: feign.Contract.Default修改Feign接口@FeignClient(name = "microservice-provider-user") public interface UserFeignClient { /** * 使用Feign自带的注解 @RequestLine * @param id * @return */ @RequestLine("GET /{id}") User findById(@Param("id") Long id); }配置全局Feign Client修改配置文件,将指定名称的Feign Client改成default即可。server: port: 8081 eureka: instance: prefer-ip-address: false client: service-url: defaultZone: http://peer1:8761/eureka,http://peer2:8762/eureka spring: application: name: microservice-consumer-movie feign: client: config: default: contract: feign.Contract.DefaultFeign对压缩的支持可以通过以下配置对请求或相应进行压缩。feign: compression: request: enabled: true mime-types: text/xml,application/xml,application/json min-request-size: 2048 response: enabled: true配置Feign日志feign: client: config: default: contract: feign.Contract.Default logging-level: full compression: request: enabled: true mime-types: text/xml,application/xml,application/json min-request-size: 2048 response: enabled: true logging: level: cc.lisen.microserviceconsumermovie.service.UserFeignClient: debug
2021年07月18日
977 阅读
0 评论
0 点赞
2021-07-17
Spring Cloud 服务消费者通过ribbon调用服务提供者
在Spring Cloud Eureka配置集群中,我们介绍了Eureka集群部署的方式。在[pring Cloud Eureka消费者获取服务者信息](https://lisen.cc/back/spring-cloud-eureka-consumer-access-to-service-provider-information.html)中,我们介绍了Eureka集群部署的方式,)中,我们介绍了Eureka服务消费者获取服务提供者信息的方式。这篇文章,我通过ribbon的方式,介绍服务消费者调用服务提供者的方式。服务提供者代码无需改动,我们只基于microservice-consumer-movie增加调用服务提供者的相关代码。基本调用方式修改启动类主要是提供RestTemplate相关Bean,增加@LoadBalanced注解使其具备负载均衡能力。@SpringBootApplication public class MicroserviceConsumerMovieApplication { public static void main(String[] args) { SpringApplication.run(MicroserviceConsumerMovieApplication.class, args); } @Bean @LoadBalanced //基于rest+ribbon的调用方式需要加此注解 public RestTemplate restTemplate(){ return new RestTemplate(); } }调用@RestController public class MovieController { @Resource private DiscoveryClient discoveryClient; @Resource private RestTemplate restTemplate; @GetMapping("/user-instance") public List<ServiceInstance> showInfo(){ return discoveryClient.getInstances("microservice-provider-user"); } @GetMapping("/user/{id}") public User findById(@PathVariable Long id){ return restTemplate.getForObject("http://microservice-provider-user/"+id, User.class); } }其他配置其他配置比如依赖、配置文件,请参考之前的文章。测试访问http://localhost:8081/user/1,正确返回用户信息负载均衡算法Ribbon是Netflix发布的负载均衡器,他有助于控制HTTP和TCP客户端的行为。Ribbon默认为我们提供了很多负载均衡算法,比如轮询、随机等,我们也可为Ribbon实现自定义的轮询算法。使用默认轮询算法测试启动两个客户端还是使用前面的用户服务,我们启动两个服务提供者。java -jar microservice-simple-provider-user-0.0.1-SNAPSHOT.jar --server.port=9001 java -jar microservice-simple-provider-user-0.0.1-SNAPSHOT.jar --server.port=9002此时查看Eureka Server可以查看注册的服务,如下编写测试代码,查看服务信息/** * 博客:https://lisen.cc * Description: * * @Author: 香草物语 * DateTime: 2021-07-17 22:11 */ @RestController public class MovieController { @Resource private DiscoveryClient discoveryClient; @Resource private RestTemplate restTemplate; @Resource private LoadBalancerClient loadBalancerClient; @GetMapping("/user-instance") public List<ServiceInstance> showInfo(){ return discoveryClient.getInstances("microservice-provider-user"); } @GetMapping("/user/{id}") public User findById(@PathVariable Long id){ return restTemplate.getForObject("http://microservice-provider-user/"+id, User.class); } @GetMapping("log/microservice-provider-user") public ServiceInstance userLog(){ return loadBalancerClient.choose("microservice-provider-user"); } }多次访问http://localhost:8081/log/microservice-provider-user,查看输出信息可以看到,服务会依次调用9001、9002端口,也就是通过轮询的方式,依次访问两个服务提供者。Ribbon自定义配置很多场景下,可以根据需要自定义Ribbon的配置,例如修改Ribbon负载均衡器规则等。Spring Cloud允许通过Java代码或属性自定义Ribbon的配置。使用代码自定义Ribbon配置使用Ribbon时一定要注意版本的问题,不然会各种报错。springcloud 2020.0.1 版本之后 删除了eureka中的ribbon,替代ribbon的是spring cloud自带的LoadBalancer,默认使用的是轮询的方式。先说一下我这边使用的组件的版本信息:Spring Boot 2.5.2Spring Cloud 2020.0.3添加依赖<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>{message type="success" content="无需添加ribbon依赖,eureka-client中已包含"/}配置文件配置文件与上面配置一致,不再单独说明。server: port: 8081 eureka: instance: prefer-ip-address: false client: service-url: defaultZone: http://peer1:8761/eureka,http://peer2:8762/eureka spring: application: name: microservice-consumer-movie自定义轮询规则PeachLoadBalancer.java/** * 博客:https://lisen.cc * Description: * * @Author: 香草物语 * DateTime: 2021-07-18 11:54 */ public class PeachLoadBalancer implements ReactorServiceInstanceLoadBalancer { private static final Log log = LogFactory.getLog(PeachLoadBalancer.class); final AtomicInteger position;//请求的次数 final String serviceId; //服务名称 用于提示报错信息的 private int flag = 0; //自己定义的计数器 //两个参数的构造方法 需要服务名称和实例提供者 这个在方法中传递进来 public PeachLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider, String serviceId) { //如果不传人请求次数就自己初始化 反正每次都+1 this(new Random().nextInt(1000), serviceId,serviceInstanceListSupplierProvider); } public PeachLoadBalancer(int seedPosition, String serviceId, ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider) { this.position = new AtomicInteger(seedPosition); this.serviceId = serviceId; this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider; } ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider; @Override public Mono<Response<ServiceInstance>> choose(Request request) { //从服务提供者中获取到当前request请求中的serviceInstances并且遍历 ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider .getIfAvailable(NoopServiceInstanceListSupplier::new); return supplier.get(request).next() .map(serviceInstances -> processInstanceResponse(supplier, serviceInstances)); } private Response<ServiceInstance> processInstanceResponse(ServiceInstanceListSupplier supplier, List<ServiceInstance> serviceInstances) { Response<ServiceInstance> serviceInstanceResponse = getInstanceResponse(serviceInstances); if (supplier instanceof SelectedInstanceCallback && serviceInstanceResponse.hasServer()) { ((SelectedInstanceCallback) supplier).selectedServiceInstance(serviceInstanceResponse.getServer()); } return serviceInstanceResponse; } private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances) { if (instances.isEmpty()) { if (log.isWarnEnabled()) { log.warn("No servers available for service: " + serviceId); } return new EmptyResponse(); } //pos是当前请求的次数 这样可以自定义负载均衡的切换 这个每次+1的操作是复制的 最好是不删 int pos = Math.abs(this.position.incrementAndGet()); if (pos%4==0){ //是4的倍数就切换 flag += 1; } if (flag >= instances.size()){ flag = 0; } //主要的就是这句代码设置负载均衡切换 ServiceInstance instance = instances.get(flag); return new DefaultResponse(instance); } }增加轮询配置/** * 博客:https://lisen.cc * Description: * * @Author: 香草物语 * DateTime: 2021-07-18 11:21 */ public class CustomLoadBalancerConfiguration { @Bean ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment, LoadBalancerClientFactory loadBalancerClientFactory) { String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME); return new PeachLoadBalancer(loadBalancerClientFactory .getLazyProvider(name, ServiceInstanceListSupplier.class), name); } }配置轮询对应的服务/** * 博客:https://lisen.cc * Description:使用RibbonClient为特定name的Ribbon Client自定义配置 * 使用@RibbonClient的configuration,指定Ribbon的配置类 * * @Author: 香草物语 * DateTime: 2021-07-18 10:16 */ @Configuration @LoadBalancerClient(name = "microservice-provider-user",configuration = CustomLoadBalancerConfiguration.class) public class MyRibbonConfig { @Bean @LoadBalanced //基于rest+ribbon的调用方式需要加此注解 public RestTemplate restTemplate() { return new RestTemplate(); } }测试/** * 博客:https://lisen.cc * Description: * * @Author: 香草物语 * DateTime: 2021-07-17 22:11 */ @RestController public class MovieController { @Resource private DiscoveryClient discoveryClient; @Resource private RestTemplate restTemplate; @Resource private LoadBalancerClient loadBalancerClient; @GetMapping("/user-instance") public List<ServiceInstance> showInfo(){ return discoveryClient.getInstances("microservice-provider-user"); } @GetMapping("/user/{id}") public User findById(@PathVariable Long id){ return restTemplate.getForObject("http://microservice-provider-user/"+id, User.class); } @GetMapping("log/microservice-provider-user") public ServiceInstance userLog(){ return loadBalancerClient.choose("microservice-provider-user"); } }多次访问http://localhost:8081/log/microservice-provider-user,可以查看端口号,判断是否根据配置的轮询规则分别访问9001和9002端口。
2021年07月17日
1,278 阅读
0 评论
0 点赞
2021-07-17
Spring Cloud Eureka消费者获取服务者信息
在Spring Cloud Eureka配置集群中,我们介绍了Eureka集群部署的方式,这篇文章,我们继续编写服务的提供者及服务的消费者。服务提供者我们创建microservice-simple-provider-user工程,作为服务的提供者。添加依赖<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>添加配置文件spring: application: name: microservice-provider-user eureka: client: service-url: default-zone: http://peer1:8761/eureka,http://peer2:8762/eureka instance: prefer-ip-address: true metadata-map: my-metadata: 自定义元数据创建用户实体User.java@Data public class User { private Long id; private String userName; private String name; private Integer age; private BigDecimal balance; }创建服务提供者@RestController public class UserController { @GetMapping("{id}") public User findById(@PathVariable Long id){ User user = new User(); user.setId(id); user.setName("zhangsan"); user.setName("张三"); user.setAge(31); user.setBalance(new BigDecimal(300)); return user; } }服务消费者我们创建microservice-consumer-movie工程,作为服务的消费者。添加依赖 <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>添加配置文件spring: application: name: microservice-provider-user eureka: client: service-url: default-zone: http://peer1:8761/eureka,http://peer2:8762/eureka instance: prefer-ip-address: true metadata-map: my-metadata: 自定义元数据我们通过metadata-map可以自定义元数据。测试获取服务提供者信息import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; import java.util.List; /** * 博客:https://lisen.cc * Description: * * @Author: 香草物语 * DateTime: 2021-07-17 22:11 */ @RestController public class MovieController { @Resource private DiscoveryClient discoveryClient; @GetMapping("/user-instance") public List<ServiceInstance> showInfo(){ return discoveryClient.getInstances("microservice-provider-user"); } }我们访问http://localhost:8081/user-instance,可以获取到服务提供者的详细信息[{ "scheme": "http", "host": "192.168.3.48", "port": 8080, "metadata": { "management.port": "8080", "my-metadata": "自定义元数据" }, "secure": false, "uri": "http://192.168.3.48:8080", "serviceId": "MICROSERVICE-PROVIDER-USER", "instanceId": "DESKTOP-PPDVS3E:microservice-provider-user", "instanceInfo": { "instanceId": "DESKTOP-PPDVS3E:microservice-provider-user", "app": "MICROSERVICE-PROVIDER-USER", "appGroupName": null, "ipAddr": "192.168.3.48", "sid": "na", "homePageUrl": "http://192.168.3.48:8080/", "statusPageUrl": "http://192.168.3.48:8080/actuator/info", "healthCheckUrl": "http://192.168.3.48:8080/actuator/health", "secureHealthCheckUrl": null, "vipAddress": "microservice-provider-user", "secureVipAddress": "microservice-provider-user", "countryId": 1, "dataCenterInfo": { "@class": "com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo", "name": "MyOwn" }, "hostName": "192.168.3.48", "status": "UP", "overriddenStatus": "UNKNOWN", "leaseInfo": { "renewalIntervalInSecs": 30, "durationInSecs": 90, "registrationTimestamp": 1626531693579, "lastRenewalTimestamp": 1626531693579, "evictionTimestamp": 0, "serviceUpTimestamp": 1626531693579 }, "isCoordinatingDiscoveryServer": false, "metadata": { "management.port": "8080", "my-metadata": "自定义元数据" }, "lastUpdatedTimestamp": 1626531693579, "lastDirtyTimestamp": 1626531693019, "actionType": "ADDED", "asgName": null } }]
2021年07月17日
1,244 阅读
0 评论
0 点赞
2021-07-17
Spring Cloud Eureka配置集群
Eureka Server可以通过运行多个实例并通过相互注册的方式实现高可用性的部署。Eureka Server实例会彼此通过增量同步的方式同步信息,确保节点数据之间的一致性。配置hosts由于我这里部署到同一个电脑上,因此需要配置hosts,以便进行服务之间的区分。win电脑hosts文件位于:C:\Windows\System32\drivers\etc\hostsMac及Linux电脑hosts文件位于:/etc/hosts在hosts文件末尾增加以下内容:C:\Windows\System32\drivers\etc配置服务提供者分别创建两个Spring Boot应用,分别为microservice-discovery-eureka、microservice-discovery-eureka-ha,代表两个服务提供者工程。添加依赖两个工程,同时添加以下依赖。<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency>修改启动类两个工程的启动类,分别添加@EnableEurekaServer注解。@SpringBootApplication @EnableEurekaServer public class MicroserviceDiscoveryEurekaApplication { public static void main(String[] args) { SpringApplication.run(MicroserviceDiscoveryEurekaApplication.class, args); } }microservice-discovery-eureka配置文件如下spring: application: name: microservice-discovery-eureka server: port: 8761 eureka: instance: hostname: peer1 appname: microservice-discovery-eureka prefer-ip-address: false client: serviceUrl: defaultZone: http://peer2:8762/eureka # false表示不向注册中心注册自己 register-with-eureka: true # false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务 fetch-registry: truemicroservice-discovery-eureka-ha配置文件如下spring: application: name: microservice-discovery-eureka server: port: 8762 eureka: instance: hostname: peer2 appname: microservice-discovery-eureka client: serviceUrl: defaultZone: http://peer1:8761/eureka # false表示不向注册中心注册自己 register-with-eureka: true # false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务 fetch-registry: true可以看到,两个工程通过相互注册的方式,实现集群。配置文件,需要注意以下内容:hostname需要不同appname必须保持一致defaultZone需要使用我们配置的IP映射,而不是使用IP
2021年07月17日
1,008 阅读
0 评论
1 点赞
2021-07-17
spring boot 通过actuator监控应用程序
Spring Boot Actuator提供了很多的监控端点,可以帮助实现对程序内部运行情况监控,比如监控状况、Bean加载情况、环境变量、日志信息、线程信息等。我们可以使用http://{ip}/{port}/actuator/{endpoint}的形式访问这些端点。这些端点大部分是GET方法,当然也有POST方法,比如shutdown。actuator提供的部分端点如下表所示序号端点描述HTTP方法1conditions(老版本为autoconfig)显示自动配置的信息GET2beans显示应用程序所有的Spring beanGET3configprops显示所有@ConfigurationPropertiesGET4threaddump(老版本为dump)显示线程活动快照GET5env显示所有的环境变量GET6health显示应用程序的健康指标,如需显示详情,需要在配置文件配置management.endpoint.health.show-details=alwaysGET7info显示应用信息。GET8mappings显示所有@RequestMapping的路径列表GET9metrics显示应用的度量标准信息GET10shutdown关闭应用,默认关闭,需要在配置文件配置management.endpoint.shutdown=true开启POST11loggers显示日志信息GET12heapdump下载应用程序堆栈信息GET部分配置文件如下,具体内容看注释management: endpoint: beans: enabled: true health: # 显示详细信息 show-details: always shutdown: enabled: true endpoints: web: exposure: # 配置暴漏的端点 include: '*'
2021年07月17日
833 阅读
0 评论
0 点赞
2021-07-17
Spring Boot Yaml文件配置特殊字符
Spring Boot中我们一般使用yaml作为配置文件,yaml都是通过键值对的形式,一般情况下,我们的键值都是字母、数字之类的,但是偶尔的,我们可能使用到特殊字符。比如,有时候我们设置actuator时,可以设置暴漏的网址,我们可能为了方便,就直接设置所有的网址,也就是设置*,但是如果直接设置,启动时会报错。对于这种特殊字符,我们可以通过'将特殊字符包裹起来,如下management: endpoint: beans: enabled: true health: # 显示详细信息 show-details: always endpoints: web: exposure: include: '*'
2021年07月17日
2,142 阅读
0 评论
0 点赞
2021-07-10
华为matebook关于win11升级后蓝牙闪断解决办法
蓝牙问题解决办法 解决方法:降级蓝牙驱动,目前测试Intel 22.40版本没问题,一切正常,22.50以及22.60版本均无法正常使用。操作步骤:1、进入设备管理器-蓝牙-英特尔无线Bluetooth-属性-驱动程序2、选择回滚,如果无法选择,并且电脑已安装22.40版本,就点击更新驱动程序-浏览我的电脑查找-从可用驱动列表选择3、选择22.40,就可以啦。
2021年07月10日
1,707 阅读
0 评论
0 点赞
2021-06-30
el-table隐藏顶部复选框
有时候我们使用el-table的选择框时,如果涉及修改、删除时,可能一次只允许用户选择一条,这样的话,如果使用顶部的全选复选框就不合适了。我们可以提供两种方式,一种是隐藏顶部的复选框,另一种是将顶部复选框改成文字,禁止点击。多选框那一列加label-class-name <el-table v-loading="loading" :data="assetClass.assetClassPropertyList" row-key="propertyId" :row-class-name="rowClassName" @selection-change="handleSelectionChange" ref="table"> <el-table-column type="index" width="50" align="center" label="序号"/> <el-table-column type="selection" width="50" align="center" label-class-name="DisabledSelection"/> <el-table-column prop="propertyName" label="属性名称" show-overflow-tooltip align="center"> <template slot-scope="scope"> <el-input v-model="scope.row.propertyName" size="mini" maxlength="64" :show-word-limit="true"></el-input> </template> </el-table-column> <el-table-column prop="isRequired" label="是否必填" :formatter="isRequiredFormatter" width="100"> <template slot-scope="scope"> <el-switch v-model="scope.row.isRequired" active-value="Y" inactive-value="N" ></el-switch> </template> </el-table-column> </el-table>style加样式隐藏顶部复选框<style scoped> /*表格全选框去除空框*/ .el-table >>> .DisabledSelection .cell .el-checkbox__inner { display: none; position: relative; } </style>将顶部复选框改成汉字<style scoped> /*表格全选框去除空框*/ .el-table >>> .DisabledSelection .cell .el-checkbox__inner { display: none; position: relative; } /*表格全选框改为:选择*/ .el-table >>> .DisabledSelection .cell:before { content: "选择"; position: absolute; left: 7px; } </style>
2021年06月30日
1,542 阅读
0 评论
0 点赞
2021-06-26
安卓ListView的item中含有EditText,动态添加item时EditText值的保存
EditText是ListView的item,ListView的item可以动态添加,从而让用户动态输入一些内容。ListView是依靠Adapter将View和数据联系起来的,实现动态添加item的效果,比较简单,只需数据源再加一个条目,比如list.add(…),然后Adapter调用notifyDataSetChanged方法刷新View即可。到这里,我们的关注点来了,我们最终是需要获取ListView中所有EditText的值,保存到本地或者提交至服务器的,那么如何做到呢?我们知道,ListView的每个item都对应着一个实体类对象,显然,这个实体类有一个属性,用于保存EditText的值,在Adapter的getView()中,又用于EditText值的显示。所以我们需要实刻保存EditText的值到这个实体类对象的属性里,不然当我们调用notifyDataSetChanged刷新视图后会发现,EditText的值为空或者错乱。正如下图的效果:要时刻保存EditText的值,我们需要给EditText设置一个文字改变时的监听器(addTextChangedListener),当文字发生改变后,我们获取EditText的值并存于itemObject的某个属性中。这里千万要脚下留心,在EditText调用setText之前,一定要把textChangedListener移除掉,否则setText后又会多次调用监听器里面的方法,造成值的清空。解决方法是每次getView时先remove掉监听器,再setText,最后再add监听器。适配器的代码如下:import android.content.Context; import android.text.Editable; import android.text.TextUtils; import android.text.TextWatcher; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.EditText; import java.util.List; /** * Created by zengd on 2016/8/17 0017. */ public class ListViewAdapter extends BaseAdapter { private List<ItemBean> mData; private Context mContext; public ListViewAdapter(Context mContext, List<ItemBean> mData) { this.mContext = mContext; this.mData = mData; } @Override public int getCount() { return mData.size(); } @Override public Object getItem(int position) { return mData.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder = null; if (convertView == null) { convertView = LayoutInflater.from(mContext).inflate(R.layout.item_edittext, null); holder = new ViewHolder(convertView); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } final ItemBean itemObj = mData.get(position); if (holder.editText.getTag() instanceof TextWatcher) { holder.editText.removeTextChangedListener((TextWatcher) holder.editText.getTag()); } holder.editText.setText(itemObj.getText()); TextWatcher watcher = new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { } @Override public void afterTextChanged(Editable s) { if (TextUtils.isEmpty(s)) { itemObj.setText(""); } else { itemObj.setText(s.toString()); } } }; holder.editText.addTextChangedListener(watcher); holder.editText.setTag(watcher); return convertView; } private class ViewHolder { private EditText editText; public ViewHolder(View convertView) { editText = (EditText) convertView.findViewById(R.id.edit_text); } } }由于我的实际项目中item里有三个EditText,在这里我简化成只有一个EditText。再多个EditText也是同理实现的。
2021年06月26日
1,149 阅读
0 评论
2 点赞
1
...
22
23
24
...
53