Spring Cloud版——电影售票系统<三>使用Feign实现声明式REST调用
2017-09-11 00:00
1106 查看
GitHub地址:https://github.com/leebingbin/SpringCloud.MovieTicketing
Spring Cloud版——电影售票系统<三>使用Feign实现声明式REST调用
一、Feign简介
Feign是Netflix开发的声明式、模块化的HTTP客户端,其灵感来自Retrofit, JAXRS-2.0以及WebSocket。Feign可帮助我们更好更快的便捷、优雅地调用HTTP API。
在Spring Cloud中,使用Feign非常简单——创建一个接口,并在接口上添加一些注释,代码就OK了。Feign 支持多种注释,例如Feign自带的注解或者JAX-RS注解等。
Spring Cloud对Feign进行了增强,使Feign支持了Spring MVC注解,并整合了Ribbon和 Eureka,从而让Feign 的使用更加方便。
二、为服务消费者整合Feign
之前的电影微服务是使用RestTemplate(负载均衡通过整合Ribbon实现)调用RESTful API的。现在进一步完善优化项目使用Feign,实现声明式的RESTful API调用。
添加Feign的依赖:
创建一个Feign接口,并添加@FeignClient注解:
Tips: @FeignClient注解中的"movieticketing-provider-user"是一个任意的客户端名称,用于创建Ribbon负载均衡器。由于使用了Eureka ,所以Ribbon会把"movieticketing-provider-user"解析成Eureka Server服务注册表中的服务。当然也可以,使用service.ribbon.listOfServer属性配置;还可以使用,url属性指定请求的URL (URL可以是完整的URL或者主机名),例如@FeignClient(name = "movieticketing-provider-user", url = "http://localhost:8000/")
修改Controller,让其调用Feign接口:
修改启动类,为其添加@EnableFeignClients注解:
三、自定义Feign配置
在Spring Cloud中,Feign的默认配置类是FeignClientsConfiguration, 该类定义了Feign默认使用的编码器、解码器、所使用的契约等。
Spring Cloud 允许通过注解@FeignClient的configuration属性自定义Feign的配置,自定义配置的优先级比FeignClientsConfiguration更高。在Spring Cloud中,Feign默认使用的契约是SpringMvcContract,因此它可以使用Spring MVC的注解。
四、手动创建Feign
为项目添加以下依赖:
创建Spring Security的配置类:
修改Controller,测试打印当前登录的用户信息:
五、Feign支持继承。使用继承,可将一些公共操作分组到一些父接口中,从而简化Feign的开发。尽管Feign的继承可帮助我们进一步简化Feign的开发,但Spring Cloud官方也指出——通常情况下,不建议在服务器端与客户端之间共享接口,因为这种方式会造成服务器端和客户端代码的紧耦合。并且,Feign本身并不使用Spring MVC的工作机制(方法参数映射不被继承)。但我个人认为,放弃开发的方便性或者接受代码的紧耦合,应该在具体问题下权衡利弊,取其利。
六、Feign对日志的处理非常灵活,可为每个Feign客户端指定日志记录策略,每个Feign客户端都会创建一个logger。默认下,logger的名称是Feign接口的完整类名。但是,Feign的日志打印只会对DEBUG级别做出响应。我们可为每个Feign客户端配置各自的Logger.Level对象,Logger.Level的值有以下选择:
编写Feign配置类:
修改Feign接口,指定配置类:
在application.yml中添加以下内容:
七、也可以使用Feign构造多参数请求
Spring Cloud版——电影售票系统<三>使用Feign实现声明式REST调用
一、Feign简介
Feign是Netflix开发的声明式、模块化的HTTP客户端,其灵感来自Retrofit, JAXRS-2.0以及WebSocket。Feign可帮助我们更好更快的便捷、优雅地调用HTTP API。
在Spring Cloud中,使用Feign非常简单——创建一个接口,并在接口上添加一些注释,代码就OK了。Feign 支持多种注释,例如Feign自带的注解或者JAX-RS注解等。
Spring Cloud对Feign进行了增强,使Feign支持了Spring MVC注解,并整合了Ribbon和 Eureka,从而让Feign 的使用更加方便。
二、为服务消费者整合Feign
之前的电影微服务是使用RestTemplate(负载均衡通过整合Ribbon实现)调用RESTful API的。现在进一步完善优化项目使用Feign,实现声明式的RESTful API调用。
添加Feign的依赖:
创建一个Feign接口,并添加@FeignClient注解:
Tips: @FeignClient注解中的"movieticketing-provider-user"是一个任意的客户端名称,用于创建Ribbon负载均衡器。由于使用了Eureka ,所以Ribbon会把"movieticketing-provider-user"解析成Eureka Server服务注册表中的服务。当然也可以,使用service.ribbon.listOfServer属性配置;还可以使用,url属性指定请求的URL (URL可以是完整的URL或者主机名),例如@FeignClient(name = "movieticketing-provider-user", url = "http://localhost:8000/")
修改Controller,让其调用Feign接口:
修改启动类,为其添加@EnableFeignClients注解:
三、自定义Feign配置
在Spring Cloud中,Feign的默认配置类是FeignClientsConfiguration, 该类定义了Feign默认使用的编码器、解码器、所使用的契约等。
Spring Cloud 允许通过注解@FeignClient的configuration属性自定义Feign的配置,自定义配置的优先级比FeignClientsConfiguration更高。在Spring Cloud中,Feign默认使用的契约是SpringMvcContract,因此它可以使用Spring MVC的注解。
四、手动创建Feign
为项目添加以下依赖:
创建Spring Security的配置类:
package com.bingbinlee.springcloud.micro.security; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.crypto.password.NoOpPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Component; import java.util.ArrayList; import java.util.Collection; /** * Spring Security 的配置类 * @author libingbin2015@aliyun.com */ @Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { // 所有的请求,都需要经过HTTP basic认证 http.authorizeRequests().anyRequest().authenticated().and().httpBasic(); } @Bean public PasswordEncoder passwordEncoder() { // 明文编码器。这是一个不做任何操作的密码编码器,是Spring提供给我们做明文测试的。A password encoder that does nothing. Useful for testing where working with plain text return NoOpPasswordEncoder.getInstance(); } @Autowired private CustomUserDetailsService userDetailsService; @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(this.userDetailsService).passwordEncoder(this.passwordEncoder()); } @Component class CustomUserDetailsService implements UserDetailsService { /** * 模拟两个账户: * ① 账号是user,密码是user,角色是user-role * ② 账号是admin,密码是admin,角色是admin-role */ @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { if ("user".equals(username)) { return new SecurityUser("user", "user", "user-role"); } else if ("admin".equals(username)) { return new SecurityUser("admin", "admin", "admin-role"); } else { return null; } } } class SecurityUser implements UserDetails { private static final long serialVersionUID = 1L; public SecurityUser(String username, String password, String role) { super(); this.username = username; this.password = password; this.role = role; } public SecurityUser() { } private Long id; private String username; private String password; private String role; @Override public Collection<? extends GrantedAuthority> getAuthorities() { Collection<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>(); SimpleGrantedAuthority authority = new SimpleGrantedAuthority(this.role); authorities.add(authority); return authorities; } @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return true; } @Override public String getPassword() { return this.password; } @Override public String getUsername() { return this.username; } public Long getId() { return this.id; } public void setId(Long id) { this.id = id; } public void setUsername(String username) { this.username = username; } public void setPassword(String password) { this.password = password; } public String getRole() { return this.role; } public void setRole(String role) { this.role = role; } } }
修改Controller,测试打印当前登录的用户信息:
package com.bingbinlee.springcloud.micro.controller; import com.bingbinlee.springcloud.micro.entity.User; import com.bingbinlee.springcloud.micro.repository.UserRepository; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; import java.util.Collection; /** * UserController * @author libingbin2015@aliyun.com */ @RestController public class UserController { @Autowired private UserRepository userRepository; private static final Logger LOGGER = LoggerFactory.getLogger(UserController.class); /* @GetMapping("/{id}") public User findById(@PathVariable Long id){ User findOne = this.userRepository.findOne(id); return findOne; }*/ /** * 打印当前登录的用户信息 * @author libingbin2015@aliyun.com */ @GetMapping("/{id}") public User findById(@PathVariable Long id) { Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal(); if (principal instanceof UserDetails) { UserDetails user = (UserDetails) principal; Collection<? extends GrantedAuthority> collection = user.getAuthorities(); for (GrantedAuthority c : collection) { // 打印当前登录用户的信息 UserController.LOGGER.info("当前用户是{},角色是{}", user.getUsername(), c.getAuthority()); } } else { // do other things } User findOne = this.userRepository.findOne(id); return findOne; } }
五、Feign支持继承。使用继承,可将一些公共操作分组到一些父接口中,从而简化Feign的开发。尽管Feign的继承可帮助我们进一步简化Feign的开发,但Spring Cloud官方也指出——通常情况下,不建议在服务器端与客户端之间共享接口,因为这种方式会造成服务器端和客户端代码的紧耦合。并且,Feign本身并不使用Spring MVC的工作机制(方法参数映射不被继承)。但我个人认为,放弃开发的方便性或者接受代码的紧耦合,应该在具体问题下权衡利弊,取其利。
六、Feign对日志的处理非常灵活,可为每个Feign客户端指定日志记录策略,每个Feign客户端都会创建一个logger。默认下,logger的名称是Feign接口的完整类名。但是,Feign的日志打印只会对DEBUG级别做出响应。我们可为每个Feign客户端配置各自的Logger.Level对象,Logger.Level的值有以下选择:
NONE:不记录任何日志(默认值) BASIC:仅记录请求方法、URL、响应状态代码以及执行时间 HEADERS:记录BASIC级别基础上,记录请求和响应的header FULL:记录请求和响应的header、body和元数据
编写Feign配置类:
修改Feign接口,指定配置类:
在application.yml中添加以下内容:
七、也可以使用Feign构造多参数请求
相关文章推荐
- Spring Cloud版——电影售票系统<五>Hystrix使用消息中间件RabbitMQ收集数据
- Spring Cloud版——电影售票系统<六>使用 Spring Cloud Config 统一管理微服务配置
- Spring Cloud版——电影售票系统<八>使用 Spring Sleuth 实现微服务追踪
- Spring Cloud版——电影售票系统<二>使用Ribbon实现客户端负载均衡
- spring cloud使用Feign实现远程接口的调用
- SpringCloud 查找调用REST服务使用RestTemplate(ribbon负载)或feign模式 教程源码 火推
- Spring Cloud 入门教程(六): 用声明式REST客户端Feign调用远端HTTP服务
- 微服务框架Spring Cloud介绍 Part4: 使用Eureka, Ribbon, Feign实现REST服务客户端
- springcloud使用feign实现服务间条用,参数数据太大,无法调用成功解决方法
- Spring Cloud版——电影售票系统<四>使用Hystrix实现微服务的容错处理
- Spring Cloud版——电影售票系统<—>Eureka微服务注册与发现
- spring cloud Feign(声明式服务调用)
- 【Spring Cloud】--声明式服务调用Feign
- 【图文经典版】声明式调用服务SpringCloud之Feign实例讲解
- Spring Cloud中如何优雅的使用Feign调用接口
- 使用Spring Cloud Feign作为HTTP客户端调用远程HTTP服务
- spring cloud之服务调用及使用ribbon实现负载均衡(三)
- SpringCloud零基础上手(四)——服务发现以及Feign(声明式RESTful服务调用)
- spring boot / cloud (八) 使用RestTemplate来构建远程调用服务
- SpringCloud使用Feign进行服务调用