匿名函式託管 func-spring-boot-starter

Yiur發表於2022-01-15

匿名函式託管 func-spring-boot-starter

專案地址

func-spring-boot-starter開源專案地址: https://gitee.com/yiur/func-spring-boot-starter
func-web-simple開源專案地址: https://gitee.com/yiur/func-web-simple

專案搭建

專案樹結構

func-web-simple
	│  pom.xml
	└─ src
      └─ main
          ├─ java
          │  └─ com
          │      └─ simple
          │          └─ web
          │              │  FuncApplication.java
          │              │
          │              ├─ callback
          │              │      DefaultCallback.java
          │              │
          │              ├─ config
          │              │      FuncLinkConfig.java
          │              │
          │              ├─ controller
          │              │      ResultController.java
          │              │
          │              ├─ func
          │              │      HttpLink.java
          │              │      OutMessage.java
          │              │
          │              └─ lambda
          │                      LambdaConfig.java
          │
          └─ resources
                  application.yml

pom

<?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>com.resolving.func</groupId>
    <artifactId>func-web-simple</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <!--core-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>5.3.9</version>
        </dependency>
        <!--func-->
        <dependency>
            <groupId>func.spring.boot</groupId>
            <artifactId>func-spring-boot-starter</artifactId>
            <version>1.0.4.RELEASE</version>
        </dependency>
        <!--boot-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
            <version>2.3.12.RELEASE</version>
        </dependency>
        <!--web-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>2.3.12.RELEASE</version>
        </dependency>
        <!--test-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <version>2.3.12.RELEASE</version>
        </dependency>
        <!--熱部署-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <version>2.3.12.RELEASE</version>
            <optional>true</optional>
            <scope>runtime</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

yaml

server:
  port: 7000
  tomcat:
    uri-encoding: utf-8
  servlet:
    encoding:
      charset: utf-8
spring:
  application:
    name: func-web-simple
  devtools:
    restart:
      enabled: true
      additional-paths: src/main/java
      exclude: WEB-INF/**
func:
  link:
    # 預設代理模式
    singleton: true
    # 預設開啟lambda模式
    lambda: true
    # 預設的callback回撥函式類
    call-back: com.simple.web.callback.DefaultCallback
    # logger 配置
    logger:
      # 預設false 不輸出日誌
      enable-log: false
      # 如果FuncLambda配置了turn-on-log為true的話且funcLink中沒有對應的log功能,輸出已經日誌
      message: "call method --> ?{methodName}(?{parameterSource})"

配置啟動類

@EnableFuncLambda("com.simple.web.lambda")

掃描com.simple.web.lambda包下的介面

@EnableWebMvc
@EnableFuncLambda("com.simple.web.lambda")
@SpringBootApplication
public class FuncApplication {

    public static void main(String[] args) {
        SpringApplication.run(FuncApplication.class, args);
    }

}

配置匿名函式連結

FuncFactory 獲取匿名函式連結生產工廠

@Configuration
public class FuncLinkConfig {

    @Autowired
    public FuncFactory funcFactory;

}

生成匿名函式連結

@Bean
public FuncLink funcLink() {
    return funcFactory.build();
}

配置匿名函式連結生命狀態

setObject(key, lambda表示式) 鏈式程式設計

  • key: (Bean name 或 refs) + : + FuncToolEnum類定義的可用狀態

    • 小工具: FuncToolKey<String, FuncToolType>用於setObject中的key

    • public FuncToolKey<String, FuncToolType> funcToolKey = 
          (bean, toolEnum) -> FuncString.format(FuncStringType.FUNC_LINK_FORMAT, bean, toolEnum);
      
    • funcFactory.build()
                      .<FuncLogger>setObject(funcToolKey.splice("link-log", FuncToolType.LIFE_START_KEY), set -> "link-log => ?{methodName}");
      
  • lambda: setObject 是一個泛型方法,所以前面的泛型用於約束後面的lambda表示式

@Bean
public FuncLink funcLink() {
    return funcFactory.build()
        .<FuncLogger>setObject("link-log:log", set -> "link-log => ?{methodName}");
}
@Bean
public FuncLink funcLink() {
    return funcFactory.build()
        .<FuncCallbackThen<String, String>>setObject("outMessage:callback-then", data -> FuncString.format("[ { \"key\": \"replace\", \"value\": \"?\" } ]", data));
}

全配置

@Configuration
public class FuncLinkConfig {

    @Autowired
    public FuncFactory funcFactory;

    /**
     * tool key lambda
     */
    public FuncToolKey<String, FuncToolType> funcToolKey = (bean, toolEnum) -> FuncString.format(FuncStringType.FUNC_LINK_FORMAT, bean, toolEnum);

    @Bean
    public FuncLink funcLink() {
        FuncLink build = funcFactory.build()
                .<FuncLogger>setObject("link-log:log", set -> "link-log => ?{methodName}");
        setOutMessage(build);
        return build;
    }

    private void setOutMessage(FuncLink funcLink) {
        funcLink // OutMessage State of life
                .<FuncLifeStart>setObject(funcToolKey.splice("outMessage", FuncToolType.LIFE_START_KEY), data -> {
                    if (data.size() > 1) {
                        data.put("message", FuncString.format("start() -> { ? }", data.get("message")));
                    }
                    return data;
                })

                .<FuncCallbackThen<String, String>>setObject("outMessage:callback-then", data -> FuncString.format("[ { \"key\": \"replace\", \"value\": \"?\" } ]", data))

                .<FuncCallbackError<String>>setObject("outMessage:callback-error", error -> error instanceof InvocationTargetException ? ((InvocationTargetException) error).getTargetException().getMessage() : error.getMessage())

                .<FuncLifeEnd<String, String>>setObject("outMessage:life-end", data -> data);
    }

}

匿名函式連結的用法

life 匿名函式執行前與執行完畢

life 匿名函式執行前與執行完畢生命狀態 不推薦

@Bean
public FuncLink funcLink() {
    return funcFactory.build()
        .<FuncLife<String, String>>setObject("outMessage:life", new FuncLife<String, String>() {
            @Override
            public Map<String, Object> start(Map<String, Object> map) {
                return map;
            }

            @Override
            public String end(String data) {
                return data;
            }
        });
}

life-start 匿名函式執行前生命狀態 推薦

@Bean
public FuncLink funcLink() {
    return funcFactory.build()
        .<FuncLifeStart>setObject(funcToolKey.splice("outMessage:life-start", FuncToolType.LIFE_START_KEY), data -> {
            if (data.size() > 1) {
                data.put("message", FuncString.format("start() -> { ? }", data.get("message")));
            }
            return data;
        })
}

life-end 匿名函式執行完畢生命狀態 推薦

@Bean
public FuncLink funcLink() {
    return funcFactory.build()
        .<FuncLifeEnd<String, String>>setObject("outMessage:life-end", data -> data);
}

life-start + life-end 組合 推薦

@Bean
public FuncLink funcLink() {
    return funcFactory.build()
        .<FuncLifeStart>setObject(funcToolKey.splice("outMessage:life-start", FuncToolType.LIFE_START_KEY), data -> {
            if (data.size() > 1) {
                data.put("message", FuncString.format("start() -> { ? }", data.get("message")));
            }
            return data;
        })
        .<FuncLifeEnd<String, String>>setObject("outMessage:life-end", data -> data);
}

callback 匿名函式執行成功或失敗,回撥函式

callback 匿名函式執行成功或失敗回撥 不推薦

@Bean
public FuncLink funcLink() {
    return funcFactory.build()
        .<FuncCallback<String, String>>setObject("outMessage:callback", new FuncCallback<String, String>() {
            @Override
            public String then(String s) {
                return s;
            }

            @Override
            public String error(Exception e) {
                return e.getMessage();
            }
        });
}

callback-then 匿名函式執行成功回撥 推薦

@Bean
public FuncLink funcLink() {
    return funcFactory.build()
        .<FuncCallbackThen<String, String>>setObject("outMessage:callback-then", data -> FuncString.format("[ { \"key\": \"replace\", \"value\": \"?\" } ]", data));
}

callback-error 匿名函式執行失敗回撥 推薦

@Bean
public FuncLink funcLink() {
    return funcFactory.build()
        .<FuncCallbackError<String>>setObject("outMessage:callback-error", error -> error instanceof InvocationTargetException ? ((InvocationTargetException) error).getTargetException().getMessage() : error.getMessage());
}

logger 匿名函式執行前輸出資訊

log 匿名函式執行前輸出資訊 1.0.4使用

@Bean
public FuncLink funcLink() {
    return funcFactory.build()
        .<FuncLogger>setObject("link-log:log", set -> "link-log => ?{methodName}");
}

匿名函式配置

編寫匿名函式託管的介面

  • OutMessage

  • @Component
    public interface OutMessage {
    
        /**
         * 輸出
         * @param message text
         * @param args replace
         * @return string
         */
        String out(String message, String... args);
    
        /**
         * 輸出
         * @param message text
         * @return string
         */
        String out(String message);
    
    }
    
  • HttpLink

  • @Component
    public interface HttpLink {
    
        /**
         * 返回關聯
         * @return object
         */
        Object link();
    
    }
    

匿名函式寫法

@FuncConfiguration 匿名函式配置註解

@FuncConfiguration
public class LambdaConfig {
}

@FuncLambda 宣告為匿名函式

  • 提供給匿名函式額外功能的註解為
    • @FuncParameter: 匿名函式連結想呼叫到函式引數變數,必須使用
      • value: 變數名,匿名函式連結收到Map<K, V>鍵值對
    • @EnableLog: 匿名函式呼叫前輸出函式資訊
    • @CallbackClass: 匿名函式繫結的回撥函式類 不推薦
  • @FuncLambda屬性解析
    • classFile: 匿名函式託管的介面
    • refs: 宣告一個ref,提供給匿名函式連結使用
    • bean: 匿名函式託管的介面,自動裝配物件別名,可以不用預設為首字母縮寫的類名
  • 特性:
    • 匿名函式託管的介面方法,返回值可以不一致callback成功回撥接收的值就是匿名函式返回的值,但回撥函式返回的值必須與介面函式返回的值一致
    • refs可以引用多個ref包括Bean,進行統一配置減少耦合度
@FuncLambda(classFile = OutMessage.class, refs = { "link-log" })
public String out(@FuncParameter("message") String message, @FuncParameter("args") String... args) {
    return FuncString.format(message, args);
}

全配置

@FuncConfiguration
public class LambdaConfig {

    @EnableLog
    @FuncLambda(classFile = OutMessage.class, refs = { "link-log" })
    public String out(@FuncParameter("message") String message, @FuncParameter("args") String... args) {
        return FuncString.format(message, args);
    }

    @EnableLog
    @FuncLambda(classFile = OutMessage.class, refs = { "link-log" })
    public String out(@FuncParameter("message") String message) {
        return FuncString.format(message + "/no");
    }

    @EnableLog
    @CallbackClass(DefaultCallback.class)
    @FuncLambda(classFile = HttpLink.class, refs = { "link-log" })
    public Map<String, String> link() {
        Map<String, String> map = new HashMap<>(10);
        map.put("百度", "http://www.baidu.com");
        map.put("BiliBili", "http://www.bilibili.com");
        map.put("localhost", "http://localhost:7000");
        map.put("部落格", "https://www.cnblogs.com/yiur-bgy/p/15521428.html");
        return map;
    }

}

WEB 測試

@RestController 編寫api

@RestController
public class ResultController {

    @Autowired
    public OutMessage outMessage;

    @Autowired
    public HttpLink httpLink;

    @RequestMapping("/replace")
    public String replace(String message, String... args) {
        return outMessage.out(message, args);
    }

    @RequestMapping("/replace/no")
    public String replace(String message) {
        return outMessage.out(message);
    }

    @RequestMapping("/hello/{message}")
    public String hello(@PathVariable("message") String message) {
        return outMessage.out(message);
    }

    @RequestMapping("/httpLink")
    public Object httpLink() {
        return httpLink.link();
    }

}

測試 api

相關文章