国庆7天,写了一个轻量级API框架

7,047 阅读5分钟

前言

有些项目是我们自己或者为朋友所开发的,功能比较简单,接口也比较少,但通常使用SpringBoot开发后,少说也得20M,传输部署也不方便,且这个jar中很可能有80%-90%的代码是永远得不到执行的,但他可能被虚拟机所加载,占用一部分内存。

所以在国庆7天写了一个小型的后端框架,称为mini-api,他的源码只有141KB,但由于引入了其他框架,所以最终大小为10M,可以使用下面方式将min-api引入到你的项目。

Gradle

implementation 'com.houxinlin:mini-api:1.0.3'

Maven

<dependency>
    <groupId>com.houxinlin</groupId>
    <artifactId>mini-api</artifactId>
    <version>1.0.3</version>
</dependency>

注意,1.0.01.0.2由于测试不周到,存在一些问题,需要从1.0.3开始。

框架本身依赖的第三方库如下

  1. gson:作用于json解析
  2. asm:作用于class文件解析
  3. kotlin,由于项目是kotlin方法,所以会包含一些kotlin必备的库
  4. mybatis:数据库查询
  5. mysql:mysql驱动

项目地址:

https://github.com/houxinlin/mini-api

示例

创建实例

CoolMini的构造方法需要一个端口号,且调用start后表示启动服务,他的参数同SpringBoot启动方法一样,会扫描目标Class所在包下的所有子包,这些子包下应包含标有@RestController注解的类,同SpringBoot一样。

public class Main {
    public static void main(String[] args) throws Throwable {
    
        CoolMini coolMini = new CoolMini(7070);
        coolMini.start(Main.class);
    }
}

简单请求

创建请求同SpringBoot中一样,mini-api提供了@GetMapping@PostMapping@PutMapping@DeleteMapping映射。

获取参数也一样,mini-api提供了以下几个注解用于从请求中获取参数。

  1. @RequestParam

    用于从请求url、请求体中Content-Type类型为application/x-www-form-urlencoded、form-data中获取参数,参数类型可以根据实际参数类型改变,不一定是String类型,但为了方便,提供了一个HttpParameterTypeConverter参数转换器,在后面会说。

@RestController
public class IndexController {
    @GetMapping("/get")
    public String index(@RequestParam("name") String name,@RequestParam("age") int age) {
        System.out.println(name +"  "+age);
        return name;
    }
}
  1. @PathVariable 同SpringBoot
@GetMapping("/get/{user}")
public String index(@PathVariable("user") String user) {
   return  user;
}
  1. @RequestUri 获取请求url地址
@RestController
public class IndexController {
    @GetMapping("/get/{user}")
    public String index(@RequestUri String url) {
        return  url;
    }
}
  1. @RequestBody获取请求体,参数类型不一定是String,可以是具体对象,也可以是List,解析使用Gson解析,但也可以自定义json解析器。
@PostMapping("/get")
public String index(@RequestBody String body) {
    return  body;
}

文件请求

由于不是基于Servlet规范,以往的Servlet方法在mini-api中都无法使用,而对于文件请求,可以直接使用下面方式获取,如果多个文件,可以使用List<FilePart>接收。

@RestController
public class IndexController {

    @PostMapping("/get")
    public String index(
            @RequestParam("name")String name,
            @RequestParam("file") FilePart filePart) throws IOException {
        byte[] buffer =new byte[((int) filePart.getContentLength())];
        filePart.inputStream.read(buffer);
        Files.write(Paths.get("/home/HouXinLin/temp-files/temp.txt"),buffer);
        return  "OK";
    }
}

Session

mini-api提供了简单的session功能,用来在服务端存储一些数据,可以用来做认证。

@RestController
public class IndexController {

    @GetMapping("/get")
    public String get(Session session) {
        return  session.getAttibute("name","").toString();
    }
    @GetMapping("/set")
    public String set(Session session)  {
        session.setTnvalidTime(1000*10);
        session.setAttribute("name","张三");
        return  "OK";
    }
}

HttpParameterTypeConverter接口

用于把请求参数转换为自定义数据类型,比如url中有个参数为name=张三,如果你想通过下面方法接收。

@RestController
public class IndexController {
    @GetMapping("/get")
    public String index(@RequestParam("name") User name,@RequestParam("age") int age) {
        System.out.println(name +"  "+age);
        return name.toString();
    }
}

那么需要添加一个参数转换器

public class Main {
    public static void main(String[] args) throws Throwable {

        CoolMini coolMini = new CoolMini(7070);
        coolMini.addHttpParameterTypeConverter(true,new HttpParameterTypeConverter<User>(){
            @Override
            public boolean canConvert(@NotNull MethodParameter methodParameter, @NotNull String s) {
            //返回是否能转换此参数
                return User.class.equals(methodParameter.param.getType());
            }

            @Nullable
            @Override
            public User typeConvert(String value) {
                return new User(value);
            }
        });
        coolMini.start(Main.class);
    }
}

HttpIntercept接口

用于拦截所有请求,intercept方法如果返回true则表示拦截,那么postHandler方法将会被调用,可以通过httpRequestAdapter.setResponse设置响应。

CoolMini coolMini = new CoolMini(7070);
coolMini.addHttpIntercept(new HttpIntercept() {
    @Override
    public boolean intercept(@NotNull HttpRequestAdapter httpRequestAdapter) {
        return false;
    }
    @Override
    public void postHandler(@NotNull HttpRequestAdapter httpRequestAdapter) {
        httpRequestAdapter.setResponse("拦截");
    }
});
coolMini.start(Main.class);

ArgumentResolver接口

用于参数转换,不同于HttpParameterTypeConverter,HttpParameterTypeConverter用于已知参数名,但无法把String类型参数转换为目标方法中的实际参数类型。

ArgumentResolver接口则可以最大能力进行参数转换,多数用于从请求体中进行获取。

 coolMini.addArgumentResolvers(true, new ArgumentResolver() {
     @Override
     public boolean support(@NotNull MethodParameter methodParameter, @NotNull HttpRequestAdapter httpRequestAdapter) {
         return methodParameter.param.getType()== User.class;
     }
     @Nullable
     @Override
     public Object resolver(@NotNull MethodParameter methodParameter, @NotNull HttpRequestAdapter httpRequestAdapter, @NotNull MappingInfo mappingInfo) {
         String requestBody = new String(httpRequestAdapter.getRequestBody());
         return new User(requestBody);
     }
 });
 
@RestController
public class IndexController {
    @PostMapping("/get")
    public String index( User user) {
        return  user.toString();
    }
}

全局认证器

mini-api提供了一个全局认证器,所有请求都会拦截(如果被设置了的话),所以就需要提供一个登录接口地址,用于认证。

 coolMini.setAuthorization(new MiniAuthentication("/login", new AuthenticationIntercept() {
     @Override
     public boolean intercept(@NotNull HttpRequestAdapter httpRequestAdapter) {
     // 返回true则表示拦截
         return false;
     }
     @Override
     public void postHandler(@NotNull HttpRequestAdapter httpRequestAdapter) {
     }
 }));

DataSource

mini-api结合了mybatis进行数据库查询,扩展了动态sql,但也保留了原来mybatis通过接口+注解的方式查询,但在查询之前,需要提供一个数据源。

CoolMini coolMini = new CoolMini(7070);
coolMini.setDataSource(new MysqlDataSource("root","pass-+","jdbc:mysql://localhost:3306/day"));
coolMini.start(Main.class);

这里的动态sql不是指mybatis的动态标签,而是可以直接以字符方式进行查询,如下。

@RestController
public class IndexController {
    @AutowriteCrud
    MybatisCrudRepository crudRepository;
    @GetMapping("/get")
    public List<User> index( ) {
        List<User>users =crudRepository.list("select * from aunt_day", User.class);
        return  users;
    }
}

在设置了数据源后,就可以通过@AutowriteCrud注解自动注入一个BaseCrudRepository实例,默认实现是MybatisCrudRepository,未来可能会加入其他,如果不想通过字符串这种方式,可以使用mybatis原生Mapper接口方式,但不支持xml方式。

@RestController
public class IndexController {
    interface Mapper{
        @Select("select * from aunt_day")
        List<User> listUser();
    }
    @AutowriteCrud
    MybatisCrudRepository crudRepository;

    private Mapper mapper;

    public void init(){
        mapper =crudRepository.getMapper(Mapper.class);
    }
    @GetMapping("/get")
    public List<User> index( ) {
        List<User>users =mapper.listUser();
        return  users;
    }
}