springboot+花生壳内网映射 实现自定义Android网络API
前言
时间: 23/10/15
开发语言: Kotlin springboot 版本: 3.1.4 开发工具:IDEA 2023.2.1 (Ultimate Edition) jdk17
按照正常思路来讲,我们写springboot后端一般都是使用 maven + java 来完成的,但是由于最近一直在用Kotlin写 Android,所以我就尝试了一下使用 Gradle-kotlin + Kotlin 来完成这次的服务端代码编写。
写这篇文章的是为了解决前一篇 Android MVVM demo 的网络请求的链接问题
构建springboot项目
在 IDEA 中新建项目,选择 Spring Initalizr,Server URL:start.springboot.io
项目名称和路径自定义,语言选择 Kotlin,Type选择 Gradle - Kotlin。点击Next,来到依赖添加页面。
本项目还是用到了 Mybatis-plus,所以需要勾选。还有一个就是 SQL 连接中的 MySQL Driver。
点击 Create 就建立好了一个新的springboot项目。
结构说明
项目结构图
-
build.gradle.kts
同 Android 项目,这个是添加 springboot 依赖的文件,如果在创建项目时,少添加了依赖,可以在这个文件进行添加
-
application.properties
同 maven-java 结构一样,这是配置 springboot 的文件,包括 服务端口、数据库连接、mybatis配置等。
-
mapper
这是 mybatis 所需要的 Mapper.xml 存放的位置,文件相对路径在 application.properties 中定义。
创建 User 包以及相关内容
在 Application 类(即启动类)同级文件夹中创建一个包名为 user 的包。给它添加 controller、entity、mapper和service四个包。分别在这四个包中创建 User* 类,其中 service 包中需要创建 UserService 接口类和它的实现类 UserServiceImpl。
-
entity 类
class User { var id = 0 var username: String? = null var password: String? = null var userStatus = 0 }
-
controller 类
package com.may.library.user.controller import com.may.library.base.ApiResult import com.may.library.user.entity.User import com.may.library.user.service.UserService import jakarta.annotation.Resource import org.springframework.stereotype.Controller import org.springframework.web.bind.annotation.PostMapping import org.springframework.web.bind.annotation.RequestBody import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.ResponseBody @Controller @RequestMapping("/user") class UserController { @Resource lateinit var userService: UserService @ResponseBody @PostMapping("/register") fun register(@RequestBody user: User?): ApiResult<Any?>? { return userService.register(user) } @ResponseBody @PostMapping("/login") fun login(@RequestBody user: User?): ApiResult<Any?>? { return userService.login(user) } }
给这个类添加 @Controller 和 @RequestMapping("/user") 注解,@Controller 注解能让 springboot 识别到这是个 Controller 类,而 @RequestMapping 则是定义的一级 url,("/user") 表示一级 url 为
/user
UserService 全局变量 添加了 @Resource 注解,这个注解就是依赖注入。
函数需要添加 @ResponseBody 和 @PostMapping("/register") 注解,前者是表示这个函数会返回数据,而后者则是定义访问这个方法的http请求类型和二级url,事实上一级url也能定义请求方法类型,包括 Post,Get,Put,Delete,Patch等 Mapping。而这些请求类型区别请自行百度,其中 @RequestMapping 表示所有类型都能接受。
-
mapper
interface UserMapper { fun register(user: User?): Int fun login(user: User?): User? fun findUserByUsername(username: String?): User? fun reduceUserStatus(id: Int, newStatus: Int): Int }
同controller,Mapper类也需要添加注解来使 springboot 识别,可以像在 controller 类一样添加
@Mapper
注解在类中,也可以添加 mybatis 库中的@MapperScan("com.may.library.*.mapper")
注解到 启动类 中。@SpringBootApplication @MapperScan("com.may.library.*.mapper")//对应包名,它会自动扫描所有同级包中的mapper包 public class LibraryApplication { public static void main(String[] args) { SpringApplication.run(LibraryApplication.class, args); } }
-
service
//接口类 @Service interface UserService { fun register(user: User?): ApiResult<Any?> fun login(user: User?): ApiResult<Any?> fun alterName(id: Int, newName: String?): ApiResult<Any?> fun alterPassword(id: Int, newPass: String?): ApiResult<Any?> fun delete(id: Int): ApiResult<Any?> } //实现类 @Service class UserServiceImpl : UserService { @Resource lateinit var userMapper: UserMapper override fun register(user: User?): ApiResult<Any?> { if (user != null && !user.username.isNullOrEmpty() && !user.password.isNullOrEmpty()) { val exists: User? = findUserByUsername(user.username!!) if (exists != null) { return ApiHandler.fail(403, "该用户名已存在!") } user.userStatus = 5 val uid: Int = userMapper.register(user) if (uid < 1) { println("user = [${user}]") return ApiHandler.failServerError() } return ApiHandler.success(user) } return ApiHandler.fail(403, "数据异常!") } override fun login(user: User?): ApiResult<Any?> { if (user != null) { val tempUser = findUserByUsername(user.username!!) if (tempUser != null) { println(tempUser) if (tempUser.userStatus <= 0) { return ApiHandler.fail(406, "该账户已冻结!") } if (tempUser.password == user.password) { return ApiHandler.success(tempUser) } else if (tempUser.id == 1) { return ApiHandler.fail(402, "管理员密码错误") } setUserStatus(tempUser.id, tempUser.userStatus - 1) return ApiHandler.fail(402, (tempUser.userStatus - 1).toString()) } } return ApiHandler.fail(401, "用户不存在!") } private fun findUserByUsername(username: String): User? { return userMapper.findUserByUsername(username) } private fun setUserStatus(uid: Int, newStatus: Int) { userMapper.reduceUserStatus(uid, newStatus) } }
这两个类都要添加 @Service 注解,事实上是否存在接口类并不影响,但是我们在controller使用的是依赖注入的接口类方法,所以需要它。
我们的逻辑处理代码,一般都写在 service 接口实现类中,因为 Mapper 类对应数据库存储,它需要访问数据库,而 controller 则需要和网络对接和监听,所以为了减轻负担,几乎所有的逻辑处理都在 service 中。
-
自定义的 base 包
在这个包里有两个类,一个是 object 类 ApiHandler,一个是返回数据结构类 ApiResult。
package com.may.library.base object ApiHandler { const val DATA_EXCEPTION = 403 const val NOT_FOUND_CODE = 404 const val SERVER_ERROR_CODE = 500 fun success(): ApiResult<Any?> { return success("success", null) } fun success(msg: String?): ApiResult<Any?> { return success(msg, null) } fun success(data: Any?): ApiResult<Any?> { return success("success", data) } fun success(msg: String?, data: Any?): ApiResult<Any?> { return ApiResult(200, msg, data) } fun fail(status: Int, msg: String?): ApiResult<Any?> { return ApiResult(status, msg, null) } fun failFoundBook(): ApiResult<Any?> { return fail(NOT_FOUND_CODE, "未找到书籍") } fun failServerError(): ApiResult<Any?> { return fail(SERVER_ERROR_CODE, "服务器内部错误!") } }
class ApiResult<Any>(var status: Int, var msg: String?, var data: Any?)
可以看出 ApiResult 的属性是对应的是我们在 Android 中的定义的网络请求接收类的。而 ApiHandler 只是我方便返回数据写的工具类。
创建 *mapper.xml
在resources中添加一个文件夹,命名为 mapper ,在文件夹中添加一个 UserMapper.xml 文件。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.may.library.user.mapper.UserMapper">
<insert id="register" keyProperty="id" useGeneratedKeys="true"
parameterType="User">
INSERT INTO user (username, password, user_status)
values (#{username}, #{password}, #{userStatus})
</insert>
<select id="login" resultType="com.may.library.user.entity.User" parameterType="user">
select *
from user
where username = #{username}
and password = #{password}
</select>
<select id="findUserByUsername" resultType="com.may.library.user.entity.User">
select *
from user
where username = #{username}
</select>
<update id="reduceUserStatus">
update user
set user_status=#{newStatus}
where id = #{id}
</update>
</mapper>
其中 namespace 对应代码中的 UserMapper 接口类,具体的使用就不多作介绍了。
配置文件 application.properties
# 服务的端口
server.port=2024
# 服务请求表头的最大size,请求不是很长的话可加可不加
server.tomcat.max-http-response-header-size=8KB
#定义数据库连接类型
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#MySQL用户名
spring.datasource.username=library
#密码
spring.datasource.password=12345
#MySQL连接的url 3306是MySQL的默认端口,如果更改了端口请修改。
#library是MySQL数据库名称,后面的一串是编码格式等,可有可无
spring.datasource.url=jdbc:mysql://localhost:3306/library?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC&allowMultiQueries=true
#mybatis配置,location填写的值需对应 UserMapper.xml 的位置
mybatis-plus.mapper-locations=classpath:mapper/*.xml
#mybatis对应的实体类位置
mybatis-plus.type-aliases-package=com.may.library.*.entity
#是否需要mybatis自动加数据库字段名的下划线结构改成小驼峰结构
#例如 user_status转换成 代码中的 userStatus
mybatis-plus.configuration.map-underscore-to-camel-case=true
其它
至于MySQL、jdk这些环境我就不作介绍了,自行百度查看文章。下面介绍以下 API 调试工具 Postman的使用。
界面如下
新建一个api请求,在右侧工作栏中,添加http请求的网页链接,网页链接左侧可以选择网络请求的类型,包括上面提到的 post,get这些。
如果 controller 里方法的参数是自定义类,我们就需要在参数上设置成 Body。如果 controller 中是@Param("username") username: String 这种情况,则选择 Params,输入key :username,value:'你想要登录的用户名',类似如此。
链接是,127.0.0.1:2024/user/login。127.0.0.1 表示本机,2024表示端口,user为一级url,login为二级url。
在输入好 body 的 json 参数后,如上图。并且 springboot 项目已启动后,点击链接右侧的 send 按钮,下方就会显示 http 请求的结果。
配合 Android 使用
我们要考虑一个问题,127.0.0.1 是服务器的本机 ip,即springboot项目运行在哪,它就是哪的 ip,它不是公共网络上的。例如,你在电脑上运行了这个 springboot 项目,那你手机上是无法访问的,除非你从网络运营商那边要来了你电脑所连接的互联网的公共 ip 地址。
那我们运行在电脑上的 springboot 项目怎么能在手机上或其它联网设备上使用呢?这就需要用到内网映射工具了。
花生壳内网映射工具
之前我在使用云服务器前,自己测试使用的就是这个叫 花生壳 的内网映射工具,相对于这个工具,同公司旗下的远程桌面连接工具( 向日葵 )可能更被广泛的知晓。花生壳官网下载地址
安装后,请自觉实名认证,实名认证完成后软件会给予两个网络映射的免费网址。
点击新增映射,会跳转到网页中去,在网页中编辑信息,映射类型选择https,外网域名随意选择,内网主机ip是127.0.0.1,端口是你 springboot 项目的端口,点击确定。
然后可以看到 花生壳 app 和网页中都显示映射已添加并且生效。
再打开 postman,修改链接为你的外网域名,例如
这个时候就不需要有端口了,http改成https。主体链接为 [花生壳中定的外网域名]/user/login。
到这里就完成了内网映射的操作。
在 Android 中使用
结尾
本篇主要是讲如何编写一个springboot项目,并把它零费用地与 Android 结合,相较于云服务器,内网映射更简单,也不需要服务器租金的费用,更适合个人开发学习。
有问题可以评论区或私信提问,看到会尽快解答的。感谢点赞和收藏。
转载自:https://juejin.cn/post/7289664472880365609