CSE REST source code analysis

1 How to use

1.1 Provider

An example of use is as follows:

@RestSchema(schemaId = "springmvcHello")
@RequestMapping(path = "/springmvchello", produces = MediaType.APPLICATION_JSON)
public class SpringmvcHelloEndpoint implements Hello {
    
    
  @Override
  @RequestMapping(path = "/sayhi", method = RequestMethod.POST)
  public String sayHi(@RequestParam(name = "name", defaultValue = "test") String name) {
    
    
    return "Hello " + name;
  }

  @Override
  @RequestMapping(path = "/sayhello", method = RequestMethod.POST)
  public String sayHello(@RequestBody Person person) {
    
    
    return "Hello person " + person.getName();
  }
}

public interface Hello {
    
    
  String sayHi(String name);
  String sayHello(Person person);
}

The configuration of microservice.yml is as follows:

servicecomb:
  service:
    application: springmvc-sample
    name: springmvc
    version: 0.0.1

    registry:
      # Default using local service center
      address: http://localhost:30100
      # address: https://cse.cn-south-1.myhuaweicloud.com
      instance:
        watch: false
  config:
    client:
      # Default using local config center
      # serverUri: https://cse.cn-south-1.myhuaweicloud.com
      serverUri: http://localhost:30113
      refreshMode: 1

1.2 Consumer

An example of use is as follows:

@Component("SpringmvcHelloClient")
public class SpringmvcHelloClient {
    
    
  private static final Logger LOG = LoggerFactory.getLogger(SpringmvcHelloClient.class);

  private static RestTemplate restTemplate = RestTemplateBuilder.create();

  @RpcReference(microserviceName = "springmvc", schemaId = "springmvcHello")
  private static Hello hello;

  public void run() throws Exception {
    
    
    Person person = new Person();
    person.setName("ServiceComb/Java Chassis");

    // RestTemplate Consumer or POJO Consumer. You can choose whatever you like
    // 1 RestTemplate Consumer
    String sayHiResult =
        restTemplate.postForObject("cse://springmvc/springmvchello/sayhi?name={name}", null, String.class, "Java Chassis");
    Assertion.assertEquals("Hello Java Chassis", sayHiResult);

    String sayHiDefaultResult =
        restTemplate.postForObject("cse://springmvc/springmvchello/sayhi", null, String.class);
    Assertion.assertEquals("Hello test", sayHiDefaultResult);

    String sayHelloResult = restTemplate.postForObject("cse://springmvc/springmvchello/sayhello", person, String.class);
    Assertion.assertEquals("Hello person ServiceComb/Java Chassis", sayHelloResult);

    System.out.println("RestTemplate Consumer or POJO Consumer.  You can choose whatever you like.");
    System.out.println("RestTemplate consumer sayhi services: " + sayHiResult);
    System.out.println("RestTemplate consumer sayHiDefault services: " + sayHiDefaultResult);
    System.out.println("RestTemplate consumer sayhello services: " + sayHelloResult);

    // 2 POJO Consumer
    String pojoSayHi = hello.sayHi("Java Chassis");
    Assertion.assertEquals("Hello Java Chassis", pojoSayHi);
    String pojoSayHello = hello.sayHello(person);
    Assertion.assertEquals("Hello person ServiceComb/Java Chassis", pojoSayHello);

    System.out.println("POJO consumer sayhi services: " + pojoSayHi);
    System.out.println("POJO consumer sayhello services: " + pojoSayHello);

    // 3 AsyncRestTemplate Consumer
    // NOTICE: since 2.0.0, spring deprecated AsyncRestTemplate, user's can use CompletableFuture of RPC instead
    CseAsyncRestTemplate cseAsyncRestTemplate = new CseAsyncRestTemplate();
    ListenableFuture<ResponseEntity<String>> responseEntityListenableFuture = cseAsyncRestTemplate
        .postForEntity("cse://springmvc/springmvchello/sayhi?name={name}", null, String.class, "Java Chassis");
    ResponseEntity<String> responseEntity = responseEntityListenableFuture.get();
    Assertion.assertEquals("Hello Java Chassis", responseEntity.getBody());
    System.out.println("AsyncRestTemplate Consumer sayHi services: " + responseEntity.getBody());

    HttpEntity<Person> entity = new HttpEntity<>(person);
    ListenableFuture<ResponseEntity<String>> listenableFuture = cseAsyncRestTemplate
        .exchange("cse://springmvc/springmvchello/sayhello", HttpMethod.POST, entity, String.class);

    listenableFuture.addCallback(
        new ListenableFutureCallback<ResponseEntity<String>>() {
    
    
          @Override
          public void onFailure(Throwable ex) {
    
    
            LOG.error("AsyncResTemplate Consumer catched exception when sayHello, ", ex);
          }

          @Override
          public void onSuccess(ResponseEntity<String> result) {
    
    
            System.out.println("AsyncRestTemplate Consumer sayHello services: " + result.getBody());
          }
        });
  }
}

cse shows three ways to call the provider. The second way is to use RPC to call the provider. The essence is to send an http request, and the implementation logic of the consumer will not be further discussed.

2 initialization

Provider

The important annotation in the provider is RestSchema, and the RestSchema annotation scan is in the RestProducers class, which is implemented as follows:

@Component
public class RestProducers implements BeanPostProcessor {
    
    
  private List<ProducerMeta> producerMetaList = new ArrayList<>();

  // 1 
  @SuppressWarnings("unchecked")
  private Class<? extends Annotation> restControllerCls = (Class<? extends Annotation>) ReflectUtils
      .getClassByName("org.springframework.web.bind.annotation.RestController");

  private boolean scanRestController = restControllerCls != null &&
      DynamicPropertyFactory.getInstance().getBooleanProperty(RestConst.PROVIDER_SCAN_REST_CONTROLLER, true).get();

  public List<ProducerMeta> getProducerMetaList() {
    
    
    return producerMetaList;
  }

  @Override
  public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    
    
    return bean;
  }

  @Override
  public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    
    
    processProvider(beanName, bean);

    return bean;
  }

  protected void processProvider(String beanName, Object bean) {
    
    
    // aop后,新的实例的父类可能是原class,也可能只是个proxy,父类不是原class
    // 所以,需要先取出原class,再取标注
    Class<?> beanCls = BeanUtils.getImplClassFromBean(bean);
    if (beanCls == null) {
    
    
      return;
    }
    RestSchema restSchema = beanCls.getAnnotation(RestSchema.class);
    if (restSchema != null) {
    
    
      ProducerMeta producerMeta = new ProducerMeta(restSchema.schemaId(), bean);
      producerMeta.setSchemaInterface(restSchema.schemaInterface());
      producerMetaList.add(producerMeta);
      return;
    }

    if (scanRestController && beanCls.getAnnotation(restControllerCls) != null) {
    
    
      ProducerMeta producerMeta = new ProducerMeta(beanCls.getName(), bean);
      producerMetaList.add(producerMeta);
    }
  }
}

In the implementation of cse, the RestSchema annotation is first scanned. If the RestSchema annotation is not used, the RestController annotation is scanned. The interface contract generated by the RestController annotation uses the name of the bean as the schemaId by default. In fact, this is not much different from the way Rpc is used.

3 calling method

See rpc calling method.

Guess you like

Origin blog.csdn.net/qq_32907491/article/details/131502547