文档章节

Spring Cloud版——电影售票系统<三>使用Feign实现声明式REST调用

Mr_ET
 Mr_ET
发布于 2017/09/11 23:20
字数 1407
阅读 195
收藏 1

    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的配置类:

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构造多参数请求

© 著作权归作者所有

共有 人打赏支持
Mr_ET
粉丝 29
博文 91
码字总数 125495
作品 0
朝阳
高级程序员
疯狂Spring Cloud连载(11)——Feign的编码器与解码器

本文节选自《疯狂Spring Cloud微服务架构实战》 京东购买地址:https://item.jd.com/12256011.html 当当网购买地址:http://product.dangdang.com/25201393.html Spring Cloud教学视频:htt...

杨大仙的程序空间
2017/10/23
0
4
疯狂Spring Cloud连载(10)——Rest客户端Feign介绍

本文节选自《疯狂Spring Cloud微服务架构实战》 京东购买地址:https://item.jd.com/12256011.html 当当网购买地址:http://product.dangdang.com/25201393.html Spring Cloud教学视频:htt...

杨大仙的程序空间
2017/10/19
0
3
【Spring Cloud】分布式必学springcloud(七)——声明式服务调用Feign

一、前言 在上篇博客中,小编带大家接触了断路器Hystrix,是不是很好玩。分布式服务之间有了Hystrix,可以很好的提高容错性能。 但是在实际开发中,项目中会有很多的服务间的调用,对于服务的...

kisscatforever
04/23
0
0
springCloud(9):使用Feign实现声明式REST调用-为消费者整合Feign与自定义Feign配置

一、简介 前面我们是使用RestTemplate实现rest api调用的,代码如下: @GetMapping("/user/{id}")public User findById(@PathVariable Long id) throws Exception { return this.restTempla......

我爱大金子
2017/07/17
0
0
Spring Cloud学习:02服务消费者(Ribbon&Feign)

在微服务架构中,业务会拆分成一个独立的服务,服务与服务之间基于http restful进行通信。Spring Cloud有两种服务调用方式,一种是Ribbon+restTemplate,另一种是Feign。 1 Ribbon+restTempl...

寒武没有纪
2017/10/21
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

设计模式:简单工厂模式(静态共产方法模式)

简单工厂的构成包括三个角色: 1)抽象产品类 2)具体产品类(继承抽闲产品类) 3)工厂类(生产具体产品) 具体代码实现 1、抽象产品类 /** * 抽象类 */public abstract class Car ...

京一
6分钟前
0
0
Lombok使用

1 介绍 Lombok是一个简单的Java库,改工具可以通过注解自动地提供一些方法,简化开发。比如编写POJO时需要为每个属性提供getter()方法和setter()方法,这就可以使用Lombok实现。 2 配置 Lomb...

SuperHeroes
14分钟前
0
0
Confluence 6 删除垃圾内容

属性(profile)垃圾 属性垃圾的定义为,一个垃圾用户在 Confluence 创建了用户,但是这个用户在自己的属性页面中添加了垃圾 URL。 如果你有很多垃圾用户在你的系统中创建了属性,你可以使用...

honeymose
16分钟前
0
0
java虚拟机之内存模型

1. 概述 对于从事 C、C++ 程序开发的人员来说,在内存管理领域,他们既是拥有最高权力的「皇帝」又是从事基础工作的「劳动人民」 --- 既拥有每个对象的「所有权」,又担负着每一个对象生命开...

firepation
19分钟前
0
0
解决linux的-bash: ./xx: Permission denied

解决linux的-bash: ./xx: Permission denied 在linux下执行sh文件时提示下面信息: -bash: ./xx.sh: Permission denied解决:chmod 777 xx.sh 1 2 3 4 Linux chmod +755和chmod +777 ......

DemonsI
30分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部