
# Spring Cloud Function适配流程
#### 前提条件
已有开发完成的Spring Cloud Function项目。
#### 步骤一：改造Java项目
1. 将下载的两个[SDK](https://support.huaweicloud.com/bestpractice-functiongraph/functiongraph_05_1166.html#functiongraph_05_1166__section1984154414312)文件放在本地目录，例如d:\\\\runtime中。
   1. 在Spring Cloud Function项目的命令行窗口中执行以下命令，将Spring Cloud Function的SDK安装到本地的maven仓库中。
      **请根据实际本地目录修改命令行。**
      ```
      mvn install:install-file -Dfile=d:\runtime\spring-cloud-function-adapter-1.0.0.jar -DgroupId=com.huawei.function -DartifactId=spring-cloud-function-adapter -Dversion=1.0.0 -Dpackaging=jar
      ```
      
   
   2. 在项目的命令行窗口中执行以下命令，将Java的Runtime SDK安装到本地的maven仓库中。
      **请根据实际本地目录修改命令行。**
      ```
      mvn install:install-file -Dfile=d:\runtime\RunTime-2.0.5.jar -DgroupId=RunTime -DartifactId=RunTime -Dversion=2.0.5 -Dpackaging=jar
      ```
      
    
2. 在Spring Cloud Function项目中的"pom.xml"文件中添加以下dependency内容：
   1. 添加Spring Cloud Function相关依赖。
      ```
      <dependency>
           <groupId>com.huawei.function</groupId>
           <artifactId>spring-cloud-function-adapter</artifactId>
           <version>1.0.0</version>
           <exclusions>
               <exclusion>
                   <groupId>com.huawei.function</groupId>
                   <artifactId>core-runtime-sdk</artifactId>
               </exclusion>
           </exclusions>
       </dependency>
      ```
      
   
   2. 添加runtime-sdk。
      ```
      <dependency>
          <groupId>Runtime</groupId>
          <artifactId>Runtime</artifactId>
          <version>2.0.5</version>
      </dependency>
      ```
      
   
   3. 在pom.xml中添加插件用来将代码和依赖包打包到一起。**请把mainClass替换为@SpringBootApplication下的类。**
      ```
      <build>
        <plugins>
          <plugin>
            <artifactId>maven-assembly-plugin</artifactId>
            <configuration>
              <descriptorRefs>
                <descriptorRef>jar-with-dependencies</descriptorRef>
              </descriptorRefs>
              <archive>
                <manifest>
                  <mainClass>com.example.springcloudfunctionadapterdemo.SpringCloudFunctionAdapterDemoApplication</mainClass>
                </manifest>
              </archive>
              <finalName>${project.name}</finalName>
            </configuration>
            <executions>
              <execution>
                <id>make-assembly</id>
                <phase>package</phase>
                <goals><goal>single</goal>
                </goals>
              </execution>
            </executions>
          </plugin>
        </plugins>
      </build>
      ```
      
    
 
#### 步骤二：配置函数代码
1. 配置Spring Cloud Function部分代码。 支持Function、Consumer、Supplier方法，入参即为事件函数的事件类型，可参考[函数支持的触发事件](https://support.huaweicloud.com/devg-functiongraph/functiongraph_02_0102.html)自行修改需要的触发事件类型，此处以kafka事件为例。
   ```
   package com.example.springcloudfunctionadapterdemo;
    
   import com.huawei.services.runtime.Context;
   import com.huawei.services.runtime.entity.kafka.KAFKATriggerEvent;
   import org.springframework.context.annotation.Bean;
   import org.springframework.context.annotation.Configuration;
   import org.springframework.messaging.Message;
    
   import java.util.Arrays;
   import java.util.function.Consumer;
   import java.util.function.Function;
   import java.util.stream.Collectors;
    
   @Configuration
   public class FunctionConfig {
       @Bean
       public Function<KAFKATriggerEvent, String> echo() {
           System.out.println("echo");
           return payload -> String.valueOf(payload);
       }
    
       @Bean
       public Consumer<KAFKATriggerEvent> kafkaEventConsumer(){
           return (input)->{
               System.out.println("***************");
               System.out.println(Arrays.stream(input.getRecords()).map(record -> Arrays.toString(record.getMessages())).collect(Collectors.joining()));
               System.out.println("***************");
           };
       }
    
       @Bean
       public Consumer<Message<KAFKATriggerEvent>> kafkaEventMessageConsumer(){
           return (inputMessage)->{
               KAFKATriggerEvent input = inputMessage.getPayload();
               Context functionContext = (Context) inputMessage.getHeaders().get("functionContext");
    
               System.out.println("***************");
               System.out.println(functionContext.getMemorySize());
               System.out.println(Arrays.stream(input.getRecords()).map(record -> Arrays.toString(record.getMessages())).collect(Collectors.joining()));
               System.out.println("***************");
           };
          }
   }
   ```
   
2. 编写适配Spring Cloud Function的函数入口代码。
   - 继承FunctionInvoker类，需要设置两个类，第一个为Function的入参类型，第二个为Function的返回类型。
   
   - 编写函数执行入口：
     ```
     public String execute(KAFKATriggerEvent request,
                               Context context) throws NoSuchMethodException {
             return handleRequest(request, context, "echo");
         }
     ```
     
   
   
   Spring Cloud Function的函数入口完整示例代码：
   ```
   package com.example.springcloudfunctionadapterdemo;
    
   import com.huawei.services.runtime.Context;
   import com.huawei.services.runtime.entity.kafka.KAFKATriggerEvent;
   import com.huawei.springframework.cloud.function.adapter.FunctionInvoker;
    
   public class Handler extends FunctionInvoker<KAFKATriggerEvent, String> {
       public String execute(KAFKATriggerEvent request,
                             Context context) throws NoSuchMethodException {
           return handleRequest(request, context, "echo");
       }
    
       public String execute2(KAFKATriggerEvent request,
                              Context context) throws NoSuchMethodException {
           return handleRequest(request, context, "pageEventConsumer");
       }
    
       public String execute3(KAFKATriggerEvent request,
                              Context context) throws NoSuchMethodException {
           return handleRequest(request, context, "kafkaEventMessageConsumer");
       }
   }
   ```
   
3. 编写main函数代码。
   ```
   import org.springframework.boot.SpringApplication;
   import org.springframework.boot.autoconfigure.SpringBootApplication;
   @SpringBootApplication
   public class SpringCloudFunctionAdapterDemoApplication {
       public static void main(String[] args) {
           SpringApplication.run(SpringCloudFunctionAdapterDemoApplication.class, args);
       }
   }
   ```
   
4. 执行以下命令打包工程，生成一个名为"jar-with-dependencies.jar"的文件。
   ```
   mvn package assembly:single
   ```
   
 
#### 测试函数
1. 登录[函数工作流控制台](https://console.huaweicloud.com/functiongraph/?locale=zh-cn)，左侧导航栏选择"函数 \> 函数列表"，单击右上角"创建函数"进入创建函数页面，选择"创建空白函数"。
2. 如[图1]所示，配置函数基本信息，配置完成后单击右下角"创建函数"完成创建。
   图1创建Java函数   
   ![](https://support.huaweicloud.com/bestpractice-functiongraph/zh-cn_image_0000002406951205.png "点击放大")
   
   
3. 进入函数详情页，在"代码"页签下，单击右侧"上传代码 \> JAR文件 "，添加[4]的JAR文件上传。
4. 上传成功后，选择"设置 \> 常规设置"，修改需要测试的"函数执行入口"参数，本例可修改为"com.example.springcloudfunctionadapterdemo.Handler.execute"，单击"保存"。
5. 回到"代码"页签，单击代码编辑区的"测试"，选择"创建新的测试事件"，在云事件模板列表中选择需要测试的事件模板，例如本示例中使用Kafka事件，则可选择Kafka事件模板，单击"创建"。
6. 单击"测试"，查看函数执行结果。 函数执行结果分为三部分，分别为函数返回（由callback返回）、执行摘要、日志输出（由console.log或getLogger()方法获取的日志方法输出），参考[表1]查看结果说明。
    表1执行结果说明 
   | 参数项  | 执行成功                          | 执行失败                                                                                                                                                                                                                  |
   |:---|:---|:---|
   | 函数返回 | 返回函数中定义的返回信息。                 | 返回包含错误信息和堆栈异常报错信息的JSON文件。格式如下： ``` { "errorMessage": "", "stackTrace": [] } ``` errorMessage：Runtime返回的错误信息 stackTrace：Runtime返回的堆栈异常报错信息 |
   | 执行摘要 | 显示请求ID、配置内存、执行时长、实际使用内存和收费时长。 | 显示请求ID、配置内存、执行时长、实际使用内存和收费时长。                                                                                                                                                                                         |
   | 日志   | 打印函数日志，最多显示4KB的日志。            | 打印报错信息，最多显示4KB的日志。                                                                                                                                                                                                    |
      
   
 
