这篇文章是我写毕设后端遇到的一些问题。
一技术栈
最近在写毕业设计。对于后端的技术栈我是由以下技术完成的。对于毕设内容不作介绍了。说一说遇到的一些问题和如何解决的。
java1.8 springboot2.1.3 maven mysql 5.7 redis 2.18 mybatis tk-mapper jetcache freemaker webSocket git
在编码后使用jenkins 和 阿里云的一台ECS 进行服务器的部署。ECS是ubuntu16.04 在业务中调用了阿里云的短信接口服务进行手机验证码的分发。
虽然编码都是我自己编的。但是我还是把代码放到了我的GitHub私有仓库中了。模拟真实开发场景。下面附一下pom文件。对于一些配置和细节这篇文章就不作细说了。只使用了mybatis的一个生成插件。
对于项目的初始构建具体配置我也写了一个博客。
<?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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>zy.healthy</groupId>
<artifactId>healthy-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>healthy-service</name>
<description>this is healthy project</description>
<properties>
<java.version>1.8</java.version>
<mysql.version>5.1.37</mysql.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
<scope>runtime</scope>
</dependency>
<!--getter setter-->
<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>
<dependency>
<groupId>com.alicp.jetcache</groupId>
<artifactId>jetcache-starter-redis</artifactId>
<version>2.4.4</version>
</dependency>
<!--HTTP invoke-->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>3.9.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.47</version>
</dependency>
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper-spring-boot-starter</artifactId>
<version>2.0.0</version>
</dependency>
<!--aliyun sms-->
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-core</artifactId>
<version>4.0.3</version>
</dependency>
<!--添加freeMarker-->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.28</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<!--<plugin>
<!–Mybatis-generator插件,用于自动生成Mapper和POJO–>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.7</version>
<configuration>
<!–配置文件的位置–>
<configurationFile>src/main/resources/mybatis-generator-config.xml</configurationFile>
<verbose>true</verbose>
<overwrite>true</overwrite>
</configuration>
<executions>
<execution>
<id>Generate MyBatis Artifacts</id>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.6</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
</dependencies>
</plugin>-->
</plugins>
</build>
</project>
二Java
在这我使用的是Java1.8 也算是很长时间的一个jdk了。
主要用到的是lambda表达式和stream流。这个在集合的一些过滤分组筛选时有很好的效果。当然我觉得这个东西写的代码比较好看。而对于一些其他的关于Java1.8就没什么使用了。就是该怎么用Java语言就怎么用。没遇到什么问题。
三springboot
至于Java web 编程。我目前学习过的有如下几种吧。
1.servlet+jsp
2.springMvc/struts + jsp + mybatis/hibernate
3.springboot + 其他
我个人比较懒。对于1.2都需要一些配置,而且技术用到的比较旧。我在做的时候就把1.2给拒绝了。选择3.也就是springboot
由于我之前也没怎么写过前端。这时选择的也是springboot这个技术,之前接触到的都是与jsp交互。这次实践也算是一个突破吧。前端我选择的是vue 因为听说比较简单我就尝试了一下。最后毕设也是用这个写的。也还不错。哈哈哈。
对于springboot我之前也有使用过。比较简单。也没遇到什么问题。
下图是我的项目目录结构。(我讨厌8080这个端口)
四.maven
maven我在官网下载的3.6 是比较新的一个版本。我在上文引入了pom文件。这个配置一下maven镜像。在把maven配置到这个项目中就好了。我没有模拟nexus私服的使用。因为觉得没必要吧。等后续我会自己搭着玩。
在这遇到了一个小问题。就是IDEA不是用管理员身份启动的。导致我在terminal中写mvn 命令不好使。用管理员启动idea就好使了。这个也不算困难。
还有一个小问题。就是我run起来的项目编译出的target有问题,导致一些东西没找到。然后我看有东西没找到,就看了一下target中编译好的文件。发现没有。然后我mvn clean package 还是不好使。我就很纳闷。于是我把target直接删了。
在mvn package就好用了。按理说clean 就清理了。但是最后还是用这种暴力的方式解决的。以后也没出现过这个问题。mvn clean package 好用。 至于其他使用maven的问题就没有了。因为我这个项目目录也比较简单。也用不上分布式。
五.MySQL
MySQL我使用的是阿里云服务器上的。
在这我遇到了一个问题。就是乱码问题。我是数据库录入数据时 汉字全是 ??? 然后我就一步一步的排查问题、
整体数据插入环节是客户端发起请求,请求调用dao,链接数据库,执行SQL插入。我就从dao开始尝试。执行一个SQL,然后发现并不好使,就是乱码。所以可以把之前得到环节先略过。然后我检查了一下编辑器的文件编码和数据库连接时jdbc的链接参数、都没问题。然后查数据库的编码。也没问题。最后查数据库服务的编码。也就是mysql server 我发现编码是latin1
然后下一步就是改这个编码为utf-8 改完之后就好用了。至于其他问题暂时还没遇到。
六.redis + jetcache
这个我在这个博客有介绍。
用来缓存了一下service查询的结果。这样能快很多。至于其他问题暂时没有遇到。因为使用场景并不多。
七.mybatis+tk-mapper
这个的使用还是出了不少错的。在这个博客也做了介绍。这个博客是我构建项目的博客。
八.FreeMaker
我在做的时候把一个数据分析结果用word导出来了。使用的是这个模板引擎。还有一种是使用Apache poi 由于之前用过freemaker在这就用的freemaker 。由于没有博客单独写freemaker 的过程。就在这做出介绍。
以word为例。我先将要导出的word样式调好。在需要导出的数据的地方使用占位符 ${name} 来代替。
然后将文件保存为word xml版本 。然后将拓展名改成ftl 下一步是检查你的ftl 因为会有一些错误产生。你的变量被分开了。你需要做的就是将这个变量变回去。将没用的删掉、然后配置freeMaker。注意下面的代码getter setter 我用了idea的lombok插件。
package zy.healthy.util;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.springframework.util.ClassUtils;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.URLDecoder;
import java.util.*;
/**
* @Author zhangyong
* @Date 2019/5/1 11:01
*/
@Getter
@Setter
@ToString //使用了idea的 lombok插件
@SuppressWarnings("serial")
public class OperateWord {
private String filePath; //文件路径
private String fileName; //文件名称
private String fileOnlyName; //文件唯一名称
public String createWord(Map<String, Object> dataMap,String reportName,String phone) {
//todo 文件的存储路径是本地写死的。放到服务器上会存在异常。
//文件路径
// filePath = ClassUtils.getDefaultClassLoader().getResource("").getPath();
filePath = "E:/tmp/";
//文件唯一名称
fileOnlyName = phone+"_" + reportName + ".doc";
/** 生成word */
WordUtil.createWord(dataMap, "healthy.ftl", filePath, fileOnlyName);
return "success";
}
/**
* 返回最终生成的word文档 文件流
* 下载生成的word文档
*/
public InputStream getWordFile() {
try {
//解决中文乱码
fileName = URLDecoder.decode(fileName, "UTF-8");
/** 返回最终生成的word文件流 */
return new FileInputStream(filePath + File.separator + fileOnlyName);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
package zy.healthy.util;
import freemarker.template.Configuration;
import freemarker.template.Template;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.util.Map;
/**
* @Author zhangyong
* @Date 2019/5/1 11:01
*/
public class WordUtil {
/**
* 生成word文件
* @param dataMap word中需要展示的动态数据,用map集合来保存
* @param templateName word模板名称,例如:test.ftl
* @param filePath 文件生成的目标路径,例如:D:/wordFile/
* @param fileName 生成的文件名称,例如:test.doc
*/
public static void createWord(Map dataMap,String templateName,String filePath,String fileName){
try {
//创建配置实例 freemarker.template.Configuration;
Configuration configuration = new Configuration();
//设置编码
configuration.setDefaultEncoding("UTF-8");
//ftl模板文件
configuration.setClassForTemplateLoading(WordUtil.class,"/");
//获取模板
Template template = configuration.getTemplate(templateName);
//输出文件
File outFile = new File(filePath+File.separator+fileName);
//如果输出目标文件夹不存在,则创建
if (!outFile.getParentFile().exists()){
outFile.getParentFile().mkdirs();
}
//将模板和数据模型合并生成文件
Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outFile),"UTF-8"));
//生成文件
template.process(dataMap, out);
//关闭流
out.flush();
out.close();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 文件流扔到response中。设置相应的MIME
* @param request
* @param response
* @param inputStream
* @param fileName
*/
public static void download(HttpServletRequest request, HttpServletResponse response, InputStream inputStream, String fileName){
BufferedOutputStream bos = null;
try {
byte[] buffer = new byte[10240];
String userAgent = request.getHeader("user-agent").toLowerCase();
if (userAgent.contains("msie") || userAgent.contains("like gecko")) {
fileName = URLEncoder.encode(fileName, "UTF-8");
} else {
fileName = new String(fileName.getBytes("UTF-8"), "iso-8859-1");
}
response.setCharacterEncoding("utf-8");
// response.setContentType("multipart/form-data");
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition", "attachment; filename=" + fileName);
bos = new BufferedOutputStream(response.getOutputStream());
int bytesRead = 0;
while ((bytesRead = inputStream.read(buffer)) != -1) {
bos.write(buffer, 0, bytesRead);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (bos != null) {
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
*****重点:以上的操作是将文件写入到response的流中。以流的形式返回的,此时你要注意的是你使用的是否是ajax。如果是那么会出现问题。因为ajax不能捕捉流。我查了很久,最后使用的是blob这个对象接收的。下面是前端代码。
this.$http.post('http://127.0.0.1:8088/word', {
}, {
headers: {
},
responseType: 'blob',
emulateJSON: true
}).then(function (response,request) {
// 这里是处理正确的回调
console.log("下载报告 success")
console.log(response);
const blob = new Blob([response.data]);
const fileName = this.formInline.reportName+'.doc';
const elink = document.createElement('a');
elink.download = fileName;
elink.style.display = 'none';
elink.href = URL.createObjectURL(blob);
document.body.appendChild(elink);
elink.click();
URL.revokeObjectURL(elink.href); // 释放URL 对象
document.body.removeChild(elink);
}, function (response) {
// 这里是处理错误的回调
console.log("下载报告 error")
});
九. Git
我自己写也没有冲突。只有一个master分支,也不用合并。所有没遇到什么错误。关于git 本地保存密码这个是一个问题。
十.Jenkins
在实习时学到了这个Jenkins的这个东西。发现是个好东西。所有我私下也自己搭建了一下