目录
一、SpringCloud核心组件——Feign
二、SpringCloud远程消费接口定义以及组件Feign使用
1、在provider新建实体类User与controller层
2、在消费者consumer中建立实体类和实现生产者方法的接口类
3、实现接口中的方法
4、在消费者启动类中加上启动远程通信注解
5、进行测试
三、优化远程消费(DTO封装)
1、介绍DTO封装:
2、步骤:
1、创建一个commons类,将相同部分取出来
2、进行打包,在父类进行引用此依赖
3、将之前的user类进行删除,改成dto进行测试
一、SpringCloud核心组件——Feign
如何从订单服务跟其他服务建立网络连接,接着发送请求过去。
Feign组件就是进行个服务之间的通信
二、SpringCloud远程消费接口定义以及组件Feign使用
1、在provider新建实体类User与controller层
package com.zj.provider.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
@NoArgsConstructor
@AllArgsConstructor
@Data
@Accessors(chain = true)
public class User {
private String account;
private String password;
}
controller层:
package com.zj.provider.controller;
import ch.qos.logback.core.net.SyslogOutputStream;
import com.zj.provider.pojo.User;
import org.springframework.scheduling.support.SimpleTriggerContext;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.websocket.server.PathParam;
import java.util.Map;
@RestController
@RequestMapping("/user")
public class UserController {
//以路径的方式访问
@RequestMapping("/{account}")
public String getByPath(@PathVariable String account){
System.out.println("account:"+account);
return "provider say : yes";
}
//以请求的方式访问
@RequestMapping("/param")
public String getByParam(@PathParam("account") String account, @PathParam("account") String password){
System.out.println("account:"+account);
return "provider say : yes";
}
@RequestMapping("/pojo")
public String getByPojo(@RequestBody User user){
System.out.println("pojo"+user);
return "provider say : yes";
}
@RequestMapping("/more")
public String getByMore(@RequestBody Map<String,Object> map){
System.out.println("more"+map);
return "provider say : yes";
}
}
2、在消费者consumer中建立实体类和实现生产者方法的接口类
实体类中的内容和生产者中内容一样
接口FeignService:
package com.zj.consumer.service;
import com.zj.consumer.pojo.User;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import javax.websocket.server.PathParam;
import java.util.Map;
@FeignClient("provider")
@SuppressWarnings("all")
public interface FeignService {
//以路径的方式访问
@RequestMapping("/user/{account}")
public String getByPath(@PathVariable(value = "account") String account);
//以请求的方式访问
@RequestMapping("/user/param")
public String getByParam(@RequestParam("account") String account, @RequestParam ("password") String password);
@RequestMapping("/user/pojo")
public String getByPojo(@RequestBody User user);
@RequestMapping("/user/more")
public String getByMore(@RequestBody Map<String, Object> map);
}
注意:
@PathVariable中必须含有value()
@FeignClient("provider")消费者要与生产者进行通信,provider应为服务名
三种传参方式:
①.@PathVariable 路径传参
②.@RequestParam 请求参数传参
③.@RequestBody json传参
3、实现接口中的方法
package com.zj.consumer.controller;
import com.zj.consumer.pojo.User;
import com.zj.consumer.service.FeignService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/user")
public class UserController {
private FeignService service;
@Autowired
public UserController(FeignService service) {
this.service = service;
}
@RequestMapping("/test01")
public String test01(String account) {
service.getByPath(account);
return "yes";
}
@RequestMapping("/{account}")
public String test02(@PathVariable String account) {
service.getByPath(account);
return "yes";
}
@RequestMapping("/test03")
public String test03(String account, String password) {
service.getByParam(account, password);
return "yes";
}
@RequestMapping("/test04")
public String test04(String account, String password) {
service.getByPojo(new User().setAccount(account).setPassword(password));
return "yes";
}
@RequestMapping("/test05")
public String test05(String account, String password) {
Map<String,Object> map=new HashMap<String,Object>();
map.put("account",account);
map.put("password",password);
service.getByMore(map);
return "yes";
}
}
4、在消费者启动类中加上启动远程通信注解
package com.zj.consumer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
5、进行测试
在消费者实现方法进行测试,当运行方法后,生产者会出现对应的结果:
测试成功
三、优化远程消费(DTO封装)
由于两者之间可能存在许多冗余的代码,那么要将相同的部分提取出来,成为公共的部分,
1、介绍DTO封装:
VO(View Object):视图对象,用于展示层,它的作用是把某个指定页面(或组件)的所有数据封装起来。
DTO(Data Transfer Object):数据传输对象,这个概念来源于J2EE的设计模式,原来的目的是为了EJB的分布式应用提供粗粒度的数据实体,以减少分布式调用的次数,从而提高分布式调用的
性能和降低网络负载,但在这里,我泛指用于展示层与服务层之间的数据传输对象。
DO(Domain Object):领域对象,就是从现实世界中抽象出来的有形或无形的业务实体。
PO(Persistent Object):持久化对象,它跟持久层(通常是关系型数据库)的数据结构形成一一对应的映射关系,如果持久层是关系型数据库,那么,数据表中的每个字段(或若干个)就对应PO的一个(或若干个)属性。消费者 远程调用 生产者 : 需要网络传输,使用DTO同一封装对象
原理与SpringBoot启动类相同
1.将DTO对象封装到公共DTO模块
2.为需要的项目引入公共DTO模块
注意点:
1.不需要继承父模块(重复引用问题)
2.打包方式为jar
3.不需要添加启动类的编译
2、步骤:
1、创建一个commons类,将相同部分取出来
package com.zj.commons.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
@NoArgsConstructor
@AllArgsConstructor
@Data
@Accessors(chain = true)
public class UserDto {
private String account;
private String password;
}
2、进行打包,在父类进行引用此依赖
<dependency>
<groupId>com.ZJ</groupId>
<artifactId>commons</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<?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>org.zj</groupId>
<artifactId>cloud02</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>cloud02 Maven Webapp</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<modules>
<module>provider</module>
<module>consumer</module>
<module>commons</module>
</modules>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
<spring-boot.version>2.4.1</spring-boot.version>
<spring-cloud.version>2020.0.0</spring-cloud.version>
<spring-cloud-alibaba.version>2021.1</spring-cloud-alibaba.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.ZJ</groupId>
<artifactId>commons</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<finalName>cloud02</finalName>
</build>
</project>
并且在父类将commons加入到modules中
注意:但是在commos不要继承父类依赖
原因:在父类实现了commons的依赖供给给消费者和生产者使用,以及认了commons作为儿子,如果还在commons中加入父类依赖,就会继承自己的依赖,导致重复依赖,就会你报错
3、将之前的user类进行删除,改成dto进行测试
但是这里就会产生一个问题,如果实体类和我数据库中的字段数量或者字段名不一样,那该怎么解决?
这里将会用orika框架
Orika是java Bean映射框架,可以实现从一个对象递归拷贝数据至另一个对象。
在开发多层应用程序中非常有用。在这些层之间交换数据时,通常为了适应不同API需要转换一个实例至另一个实例。
在生产者中导入以下依赖:
<dependency>
<groupId>ma.glasnost.orika</groupId>
<artifactId>orika-core</artifactId>
<version>1.4.6</version>
</dependency>
在生产者启动类中实例出mapper工厂
@Bean
@Scope("prototype")
public MapperFactory mapperFactory(){
return new DefaultMapperFactory.Builder().build();
}
对于以上问题的两种情况:
1、如果字段属性一致:
User u = mapperFactory.getMapperFacade().map(dto, User.class);
2、如果字段属性不一致:
@RequestMapping("/pojo")
public String getByPojo(@RequestBody UserDto dto) {
// User u = mapperFactory.getMapperFacade().map(dto, User.class);
mapperFactory.classMap(UserDto.class, User.class)
.field("name", "account")
.byDefault().register();
User map = mapperFactory.getMapperFacade().map(dto, User.class);
System.out.println("pojo" + dto);
return "provider say : yes";
}
mapperFactory.classMap(User.class, UserVo.class)
.field("name", "userName")
.field("age", "ageOne")
.byDefault().register();
List<UserVo> userVos = mapperFactory.getMapperFacade().mapAsList(A, UserVo.class);
今天的知识就分享到这了,希望能够帮助到你!