Spring Boot专栏七:Spring Boot的依赖包的介绍
本节专栏来填一个坑,就是项目的相关依赖包的介绍。之前介绍过Spring Boot的依赖都是写在主目录demo下的pom.xml中,那么下面就来讲讲,我们目前用到了哪些依赖,它们分别有什么作用,另外,以后可能还要用到哪些依赖,又有什么作用?
目前已经用到的依赖及其作用
打开项目的pom.xml文件,我们可以从中看到一个<dependencies></dependencies>对,这个对里包含了我们目前用到的所有的依赖。我先把它们拷过来:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
这是我目前的pom.xml文件的状况,大家的依赖应该和我的一样,最多是依赖的顺序有些调整罢了。
我们首先可以看一下每个依赖的结构,在每个<dependency></dependency>对中,肯定有一个<groupId>标签和<artifactId>标签。前者表示该依赖在哪个包下,后者表示该依赖的id是什么,这两者确定了这个依赖的属性。其他标签我们不讲了,有兴趣的朋友可以钻研。
接下来我们来看看它们的作用,每个依赖我们用它的artifactId来表示。
- spring-boot-starter-web:
用来支持全栈式Web开发,包括Tomcat和spring-webmvc。 - spring-boot-starter-jdbc:
用来支持JDBC数据库。 - mysql-connector-java:
用来连接mysql数据库。 - mybatis-spring-boot-starter:
自动将mybatis配置到我们的项目中。 - spring-boot-starter-thymeleaf:
支持Thymeleaf模板引擎,包括与Spring的集成。Thymeleaf模板引擎用于视图层,可以方便我们的项目与网页、前端等进行交互,它是Spring Boot支持最好的模板引擎。 - spring-boot-devtools:
spring-boot-devtools为应用提供一些开发时特性,包括默认值设置,自动重启,livereload 等。另外,Spring Boot项目的热启动(当我们对项目进行了修改,只需要保存,就可以更改项目,而不需要重新启动项目)也与该依赖有关。 - lombok:
提供了简单的注解的形式来帮助我们简化消除一些必须有但显得很臃肿的 java 代码,最明显的就是在entity文件夹下的那些POJO类中。 - spring-boot-starter-test:
支持常规的测试依赖,包括JUnit、Hamcrest、Mockito以及spring-test模块。我们的项目部署后,就不能用到这个依赖了,这个依赖是方便我们测试用的。
再添加几个依赖
与Json字符串相关的依赖
使用json字符串的一个好处,就是能将我们的类,包装成一个字符串;也能根据一个合乎类的构造方法的字符串,构造出一个类。
还记不记得之前我们传给网页全部用户的信息时,网页上显示的类是什么样子的?在专栏第二节中,我们输出的是如下的图片:
这个输出的类型是List<Map<String, Object>>的。如果显示到网页还好,要是返回给前端的开发者,那就有问题了,它可不知道我们的返回类型是什么,有可能我们的某个功能只是根据用户id搜索用户名,那么返回类型是String,可能我们的另一个功能是根据用户id搜索余额,那么返回类型又变成了double。要是前端的返回类型,永远固定成String,那不就轻松了?使用Json字符串就可以解决这个问题。
那首先我们就要添加相关依赖。其实,在spring-boot-starter-web中,已经有一个jackson-databind可以实现该功能,不过有点缺陷。我个人目前是使用阿里巴巴的fastjson来作依赖。我们在pom.xml更改两个地方。
在spring-boot-starter-web依赖中,我们除去jackson-databind依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!-- 除去jackson-databind依赖 -->
<exclusions>
<exclusion>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</exclusion>
</exclusions>
</dependency>
(提示:在Vscode中可以使用快捷键ctrl+/对java、xml文件进行注释)
然后,添加fastjson依赖:
<!-- 添加fastjson依赖 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.47</version>
</dependency>
另外,我们还需要做一点小配置。在main.java.com.example.demo下新建一个config文件夹,并在其中新建一个MyFastjsonConfig.java文件,在其中写:
package com.example.demo.config;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
@Configuration
public class MyFastjsonConfig {
@Bean
FastJsonHttpMessageConverter fastJsonHttpMessageConverter(){
FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
FastJsonConfig config = new FastJsonConfig();
//升级之后的fastjson(1.2.28之后的版本)需要手动配置MediaType,否则会报错
List<MediaType> supportedMediaTypes = new ArrayList<>();
supportedMediaTypes.add(MediaType.APPLICATION_JSON);
// supportedMediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
supportedMediaTypes.add(MediaType.APPLICATION_ATOM_XML);
supportedMediaTypes.add(MediaType.APPLICATION_FORM_URLENCODED);
supportedMediaTypes.add(MediaType.APPLICATION_OCTET_STREAM);
supportedMediaTypes.add(MediaType.APPLICATION_PDF);
supportedMediaTypes.add(MediaType.APPLICATION_RSS_XML);
supportedMediaTypes.add(MediaType.APPLICATION_XHTML_XML);
supportedMediaTypes.add(MediaType.APPLICATION_XML);
supportedMediaTypes.add(MediaType.IMAGE_GIF);
supportedMediaTypes.add(MediaType.IMAGE_JPEG);
supportedMediaTypes.add(MediaType.IMAGE_PNG);
supportedMediaTypes.add(MediaType.TEXT_EVENT_STREAM);
supportedMediaTypes.add(MediaType.TEXT_HTML);
supportedMediaTypes.add(MediaType.TEXT_MARKDOWN);
supportedMediaTypes.add(MediaType.TEXT_PLAIN);
supportedMediaTypes.add(MediaType.TEXT_XML);
converter.setSupportedMediaTypes(supportedMediaTypes);
config.setDateFormat("yyyy-MM-dd");
config.setCharset(Charset.forName("UTF-8"));
config.setSerializerFeatures(
//json格式化
SerializerFeature.PrettyFormat,
//输出value为null的数据
SerializerFeature.WriteMapNullValue
);
converter.setFastJsonConfig(config);
return converter;
}
}
这样我们就完全配置好了fastjson,并且可以在项目中使用了。
通过Json字符串,可以将一个类转为为一个字符串。下面我们修改一下我们的项目代码,看看效果,只需改变UserController.java中的public List show_user_all()方法即可:
@RequestMapping(value = "/show_user_all", method = {RequestMethod.GET})
public String show_user_all() {
List<User> result = userService.show_user_all();
int len = result.size();
System.out.println("共查询到"+len+"条记录");
for (int i = 0;i < len;i++) {
User user = result.get(i);
String user_name = user.get_user_name();
String user_password = user.get_user_password();
String register_time = user.get_register_time();
String login_time = user.get_login_time();
System.out.println("第"+(i+1)+"条记录的用户名为"+user_name+",密码为"+user_password+",注册时间为"+register_time+",最后一次的登录时间为"+login_time);
}
return JSON.toJSONString(result);
}
其实更改的地方,就只是返回值类型(由List<User>变为了String),和return处(由result变为了JSON.toJSONString(result))。
然后我们重新调用这个方法,看到在浏览器中输出了:
现在来看,其实就是给这个列表加了个引号,并把类中的引号通过""符号转义了——其实,把类包装成字符串的意义就在这里,这样前后端才方便通信。
所以,其实使用Json字符串不止是方便前端,更重要的还是方便了后端——前端不用输入很多很多个参数,只要把它们包装成类,就可以输入给后端,后端也不用傻乎乎地一个一个接收,还要重写一大堆数据类型。
不过本专栏讲授的项目不包括前端,大家可能无法直观感受到Json字符串的威力。我在这里也建议大家找一个做前端的伙伴,可以让他/她用vue框架写前端,你用Spring Boot写后端,一起学习探讨进步。
上传图片——接收Base64型的图片所用到的commons-codec依赖
在写项目时,我们经常会遇到前端传来一张图片的情况,比如商城系统,用户进行评论时上传图片;比如后台管理系统,商家上架商品时上传图片……由于我们保存图片的方式,是将图片存入到resources/static中,那么问题来了:图片是以什么形式传过来的?
这个问题的答案是,一般而言,前端传给我们后端的图片格式是一个字符串,字符串的内容很长,受篇幅限制,我们只列出前面一些长度(有需要的朋友去我的github项目中找,项目地址见博客最下方):
data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEASABIAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCAH0AfQDASIAAhEBAxEB/8QAHAAAAwEBAQEBAQAAAAAAAAAAAAECAwQFBgcI/8QAOhAAAgIBAwMDAgQEBAYCAwAAAAECEQMEITEFEkETUWEGIhQycYEHQpGhIzOx0RUkUmJy8eHwJUPB/8QAGAEBAQEBAQAAAAAAAAAAAAAAAAECAwT/xAAdEQEBAQEBAQEBAQEAAAAAAAAAARECEiExQQ…
其中最重要的就是第一行的"base64,"的部分。我们现在使用一个split函数,就能将其分为两部分,前一部分,表示图片的属性信息,其中的jpeg就是图片的后缀名;后一部分,是一个字节数组字符串,表示图片内容。
因此我们收到这样的图片信息后,需要做两件事:1.得到前面后缀名信息;2.将后面的字节数组字符串转化成图片。相关内容可以参考博客:java实现Base64数据图片和数据间的互相转换。在该博客中,作者是用sun.misc.BASE64Decoder来实现字符串->图片的,但是该包不是java本地有的,调用起来也麻烦。为此,我建议换成另外一个依赖,commons-codec. 在pom.xml文件中的导入方式为:
<!-- 为了使用Base64 -->
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.10</version>
</dependency>
然后我们再在config文件夹下添加一个Base64ToPic.java文件,专门用于处理图像,代码为:
package com.example.demo.config;
import java.io.FileOutputStream;
import java.io.OutputStream;
import org.apache.commons.codec.binary.Base64;
public class Base64ToPic {
//对字节数组字符串进行Base64解码并生成图片
public static boolean GenerateImage(String imgStr,String imageName) {
if (imgStr == null) {
//图像数据为空
return false;
}
try {
//Base64解码
byte[] b = Base64.decodeBase64(new String(imgStr).getBytes());
for(int i=0;i<b.length;++i) {
if(b[i]<0) {
//调整异常数据
b[i]+=256;
}
}
//生成图片
String imgFilePath = "{你自己的项目路径}/demo/src/main/resources/static"+imageName; //新生成的图片的路径
OutputStream out = new FileOutputStream(imgFilePath);
out.write(b);
out.flush();
out.close();
return true;
}
catch (Exception e) {
return false;
}
}
// 从字符串中读取图片后缀名和base64字节数组字符串
public static String[] split(String base64Str) {
String[] s = base64Str.split(";base64,");
if (s.length != 2) {
return null;
}
String type = s[0].substring(s[0].lastIndexOf("/")+1); // 获取图片后缀名
System.out.println(type);
String base = s[1]; // 获取base64字符串
String[] result = {type, base};
return result;
}
}
我们在TestController.java中添加一个测试用例,我将Base64字符串放入"picture.txt"文件中,并放在resources文件夹下,以供读取使用:
@RequestMapping(value = "save_picture", method = {RequestMethod.GET})
public String save_picture() {
String picture_txt_path = "{你自己的项目路径}/demo/src/main/resources/picture.txt";
FileInputStream fin;
try {
fin = new FileInputStream(picture_txt_path);
InputStreamReader reader = new InputStreamReader(fin);
BufferedReader buffReader = new BufferedReader(reader);
String picture_string = "";
String line;
while ((line = buffReader.readLine()) != null) {
picture_string+=line;
}
buffReader.close();
String[] picture_type_base = Base64ToPic.split(picture_string);
if (picture_type_base == null) {
return "图片上传出错";
}
String imageName = "test."+picture_type_base[0]; // 要保存的图片的文件名
boolean b = Base64ToPic.GenerateImage(picture_type_base[1], imageName);
if (b) {
return "图片上传成功";
}
else {
return "图片上传失败";
}
} catch (FileNotFoundException e) {
e.printStackTrace();
return "文件未找到";
} catch (IOException e) {
e.printStackTrace();
return "文件读写出错";
}
}
执行该方法后,将在resources文件夹下生成一张图片,证明该依赖有效。
该图片应该为:
注意,在mac系统上这个依赖是可以直接运行的,但在windows系统下,该依赖可能不太好用,可能还需要下载该依赖的包,参考:下载commons-codec。大家也可以换一种依赖,只要能将Base64的字节数组字符串转成图片就行。
Api和ApiOperation,接口文档注解
下面介绍两个用来创建接口文档的注解,Api和ApiOperation。可能我们自己写项目不需要用到它,但是写着它是个好习惯。这里简单介绍一下它的用法。
首先导入依赖,它来自Swagger,这是当前最好用的Restful API文档生成的开源项目,通过swagger-spring项目,实现了与SpingMVC框架的无缝集成功能,方便生成spring restful风格的接口文档,同时swagger-ui还可以测试spring restful风格的接口功能。
依赖导入如下:
<!-- 添加swagger2依赖,用于使用ApiOperation和Api注解 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.2.2</version>
</dependency>
在Spring Boot项目中,一般就是用在Controller文件中,在Controller类前可以这样写:
@Api(tags = "用户管理", description = "进行与用户相关的操作")
@RestController
@RequestMapping("/user")
public class UserController {
// 类的内容
}
在Controller类中的某个具体方法前可以这样写:
@ApiOperation(value = "显示所有用户的所有信息")
@RequestMapping(value = "/show_user_all", method = {RequestMethod.GET})
public String show_user_all() {
// 方法的内容
}
本项目中由于只做教学、记录使用,因此不添加此接口文档注解。大家有兴趣的可以自己查阅资料。
本节专栏的内容到此结束,为了方便大家拷贝代码,我已经将我的项目放到了github上,项目地址为https://github.com/DTSSDUTDeepLearning/Spring-Boot-Dante,有需求的朋友们自行前往下载。
谢谢大家的阅读。
专栏第八节已经更新啦,传送门:Spring Boot专栏八:与前端的交互,以及注解的解释(二)