Springboot项目热部署
一、引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional><!-- 防止将devtools依赖传递到其他模块中 -->
</dependency>
二、IDEA中的设置
1、File | Settings | Build, Execution, Deployment | Compiler -----> build project automatically 对勾;
2、ctrl+shift+alt+/ Registry;
3、选择compiler.automake.allow.when.app.running在后面的方框中打上√;
4、在IDEA工具类上方的Application
再点击Edit Configurations...
springboot中Running Application Update Policies,下方两个选项,选择Update classes and resources。
springboot项目中加载properties文件
//confpath为properties文件的地址 如: "/license/licenseMakeConf.properties"
public LicenseMake(String confPath) {
// 获取参数
Properties prop = new Properties();
try (InputStream in = getClass().getResourceAsStream(confPath)) {
prop.load(in);
} catch (IOException e) {
log.error("CreateLicense Properties load inputStream error.", e);
}
//common param 使用参数
priAlias = prop.getProperty("private.key.alias");
privateKeyPwd = prop.getProperty("private.key.pwd");
keyStorePwd = prop.getProperty("key.store.pwd");
subject = prop.getProperty("subject");
priPath = prop.getProperty("priPath");
}
@RequestBody、@RequestParam
@RequestBody主要用来接收前端传递给后端的json字符串中的数据的(请求体中的数据的);使用@RequestBody接收数据时,一般都用POST方式进行提交。
在后端的同一个接收方法里,@RequestBody与@RequestParam()可以同时使用,@RequestBody最多只能有一个,而@RequestParam()可以有多个。
使用@requestBody注解时:
一般传递对象,以JSON的形式进行传递,请求用POST方式提交;
如果对象某个参数没有值,直接不写这个属性即可。
@RequestParam的使用方法:
public String requestparam(
@RequestParam(value="username", required=true, defaultValue="zhang") String username)
如果传递多个值,我们可以适当改变username的类型。
三、springboot项目打war包,部署到tomcat
1.配置项目
a.修改启动类
继承SpringBootServletInitializer类,并重写覆盖configure方法,代码如下:
public class Application extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(Application.class);
}
public static void main(String[] args) {
new SpringApplicationBuilder(Application.class).web(true).run(args);
}
}
b.剔除依赖
pom文件中添加一下依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-start-tomcat</artifactId>
<scope>provided</scope>
</dependency>
设置项目打包方式
为打war包,注意添加第四行到对应位置就好
<groupId>com.zony</groupId>
<artifactId>PEP_PDFSecurity</artifactId>
<version>1.0.0</version>
<packaging>war</packaging>
<name>PEP_PDFSecurity</name>
2.通过Maven的clean package进行打包
3.在第2步过程中,可能会出现错误
解决方案: 在pom.xml文件中引入以下依赖
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
4.部署
把target目录下的war包放到tomcat的webapps目录下,
去bin文件夹startup.bat启动tomcat,即可自动解压war包,并部署。
5.测试
localhost:8080/Ingship/user/info?token=admin-token
Ingship是war包解析后的文件夹的名称,相当与IDEA项目中配置的content-path
四、日志
1.配置文件中添加配置
logging:
level:
root: INFO #日志文件名称
config: classpath:logback-spring.xml
2.新建日志文件配置信息
和yml配置文件在统一目录下
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
<!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径-->
<!-- <property name="LOG_HOME" value="C:/log" />D:\log-->
<property name="LOG_HOME" value="./my-logs" />
<!-- 控制台输出 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!--设置控制台的输出级别为“ERROR”级别-->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</filter>
<!--设置控制台的日志输出格式-->
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
<pattern>%d{yyyy-MM-dd HH} [%thread] %-5level %logger{50} - %msg %n</pattern>
</encoder>
</appender>
<!--info级别的日志文件设置-->
<appender name="INFO_FILE"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<!--日志级别设置-->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</filter>
<!--临时文件的保存位置及名称-->
<File>${LOG_HOME}/my-info.log</File>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--临时文件大小保存到一定要求后,更改文件名,成为历史日志文件-->
<fileNamePattern>${LOG_HOME}/info-%d{yyyyMMdd}.log.%i</fileNamePattern>
<!--保存文件的要求,达到要求后,创建新的日志文件-->
<timeBasedFileNamingAndTriggeringPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>20MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<maxHistory>30</maxHistory>
</rollingPolicy>
<!--控制日志信息保存格式-->
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%d{yyyy-MM-dd HH} %-5level %logger -%msg%n</pattern>
</layout>
</appender>
<!--error级别的日志文件设置-->
<appender name="ERROR_FILE"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERROR</level>
</filter>
<File>${LOG_HOME}/my-error.log</File>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/error-%d{yyyyMMdd}.log.%i
</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>20MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<maxHistory>30</maxHistory>
</rollingPolicy>
<!--控制日志信息保存格式-->
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%d{yyyy-MM-dd HH} %-5level %logger -%msg%n</pattern>
</layout>
</appender>
<!-- 日志输出级别 -->
<root level="ERROR">
<appender-ref ref="STDOUT" />
<appender-ref ref="ERROR_FILE"/>
<appender-ref ref="INFO_FILE"/>
</root>
</configuration>
五、IE浏览器下ajax请求不走后台
1.问题出现原因
1、在ie浏览器下,chrome浏览器没有此问题
2、请求的url和参数和上一次的请求一样
3、请求方式为get而不是post
2.解决办法
与出现原因对应的解决方法
1、不使用ie浏览器(不太符合实际情况)
2、在正常已有的请求参数后加一个随机数或时间戳
3、将请求类型设为post (通常选择这种方式解决此问题)
4、将ajax的cache属性设为false(默认为true),这样就不会使用浏览器的缓存
六、项目中读取配置文件的绝对路径
//获取项目的文件夹绝对路径 GlobalsUtil当前类名称
//path不以"/"开头:当前类所在的包目录
//path以"/"开头:class path,即当前项目
sysRootDir = GlobalsUtil.class.getResource("/").getPath();
try {
//解决路径中文乱码
sysRootDir = URLDecoder.decode(sysRootDir, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
System.out.println("-------------------路径:"+sysRootDir);
七、文件流读取文件并作为参数传递
当文件下载后保存在本地服务器,其他服务器想要获取本地的文件时,可以使用此方法将文件作为流传递出去。
public void downloadFile(HttpServletRequest request, HttpServletResponse response, String filePath) {
//++++++此处为返回对象+++++++
ServletOutputStream ouStream = null;
InputStream inStream = null;
String responseFileName;// 显示文件名
String responseContentType;// 显示文件类型
int responseContentLength;// 显示字节数
try {
//++++++此处为返回对象+++++++
ouStream = response.getOutputStream();
File tmpFile = new File(filePath);
responseFileName = tmpFile.getName();
responseContentType = ConvertType.getType(tmpFile.getName());
responseContentLength = (int) tmpFile.length();
inStream = new FileInputStream(tmpFile);
response.setContentType(responseContentType);
response.setHeader("Content-disposition", "attach;filename=" + UriUtils.encode(responseFileName, "UTF-8"));
response.setHeader("Pragma", "no-cache");
response.setContentLength(responseContentLength);
byte[] buffer = new byte[5242880];
int len;
while ((len = inStream.read(buffer)) > 0) {
//++++++将文件写入+++++++
ouStream.write(buffer, 0, len);
}
} catch (Exception e) {
log.error(e.getMessage(), e);
} finally {
if (inStream != null) {
try {
inStream.close();
} catch (IOException e) {
log.error(e.getMessage(), e);
}
}
if (ouStream != null) {
try {
ouStream.flush();
ouStream.close();
} catch (IOException e) {
log.error(e.getMessage(), e);
}
}
}
八、MultipartFile和FIle类型相互转换
MultipartFile转换为FIle
file为MultipartFile,转换完成后在本地c盘下可看到对应的文件
String filePath = "c:\\";
File receiveFile = new File(filePath);
//MultipartFile转换为File
file.transferTo(receiveFile);
File转换为MultipartFIle
引入依赖
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.3</version>
</dependency>
//根据文件路径获取MultipartFile文件
FileItem fileItem = getMultipartFile(file,"tempItem");
CommonsMultipartFile multipartFile = new CommonsMultipartFile(fileItem);
九、清空某个文件夹
public static boolean deleteDir(String path){
File file = new File(path);
if(!file.exists()){//判断是否待删除目录是否存在
System.err.println("The dir are not exists!");
return false;
}
String[] content = file.list();//取得当前目录下所有文件和文件夹
for(String name : content){
File temp = new File(path, name);
if(temp.isDirectory()){//判断是否是目录
deleteDir(temp.getAbsolutePath());//递归调用,删除目录里的内容
temp.delete();//删除空目录
}else{
if(!temp.delete()){//直接删除文件
System.err.println("Failed to delete " + name);
}
}
}
return true;
}
十、springboot集成数据库mysql和hibernate
1.导入依赖
<!--连接mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!--添加JDBC依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- jpa配置 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>persistence-api</artifactId>
<version>1.0</version>
</dependency>
<!--lombok依赖-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
2.在springboot的yml配置文件里配置mysql的连接信息
spring:
datasource:
url: jdbc:mysql://localhost:3306/pep_pdfsecurity?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=UTC
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
jpa:
show-sql: true
servlet:
multipart:
#上传每个文件的配置最大为1GB
max-file-size: 1GB
#单次请求的文件的总数不能大于4GBMB
max-request-size: 4GB
3.创建工具类Result
package com.zony.lngship.util;
/**
*@Description 返回结果数据
*@Param
*@Return
*@Author ChuXu
*@Date 2021/11/18
*@Time 9:45
*/
public class Result {
//相应码
private Integer code;
//信息
private String message;
//返回数据
private Object data;
public Result() {
}
public Result(Integer code, String message, Object data) {
this.code = code;
this.message = message;
this.data = data;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
4.创建实体类对应数据库表
package com.zony.app.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.*;
/**
*@Description 文件表
*@Param
*@Return
*@Author ChuXu
*@Date 2022/3/24
*@Time 17:08
*/
@Entity //表名
@Table(name = "file_info")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class FileInfo {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private String id;
@Column(name = "file_name")
private String fileName;
@Column(name = "file_path")
private String filePath;
@Column(name = "file_size")
private String fileSize;
@Column(name = "page")
private String page;
@Override
public String toString() {
return "File{" +
"id='" + id + '\'' +
", fileName='" + fileName + '\'' +
", filePath='" + filePath + '\'' +
", fileSize='" + fileSize + '\'' +
", page='" + page + '\'' +
'}';
}
}
5.创建Repository 数据库的访问接口
package com.zony.app.respository;
import com.zony.app.entity.FileInfo;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.transaction.annotation.Transactional;
//表名称 主键类型
public interface FileInfoDao extends JpaRepository<FileInfo,String> {
//根据id查询文件信息
@Query(value = "select * from file_info where id = ?1",nativeQuery = true)
FileInfo seleteFileInfoById(String id);
//根据id删除文件信息
@Transactional
@Modifying
@Query(value = "delete from file_info where id = ?1",nativeQuery = true)
void deleteFileInfoById(String id);
//修改文件信息
@Transactional
@Modifying
@Query(value = "update file_info f set f.file_name = ?2,f.file_path = ?3,f.file_size = ?4,f.page = ?5 where f.id = ?1",nativeQuery = true)
void updateFileInfo(String id,String fileName,String filePath,String fileSize,String page);
}
6.创建服务层service
package com.zony.app.service;
import com.zony.app.entity.FileInfo;
import java.util.List;
public interface FileInfoService {
public List<FileInfo> selectAllFileInfo();
public FileInfo seleteFileInfoById(String id);
public void insertFileInfo(FileInfo fileInfo);
public void deleteFileInfoById(String id);
public void updateFileInfo(FileInfo fileInfo);
}
7.创建实现类impl
package com.zony.app.service.serviceimpl;
import com.zony.app.entity.FileInfo;
import com.zony.app.respository.FileInfoDao;
import com.zony.app.service.FileInfoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class FileInfoServiceImpl implements FileInfoService {
@Autowired
private FileInfoDao fileInfoDao;
@Override
public List<FileInfo> selectAllFileInfo() {
List<FileInfo> fileInfoList = fileInfoDao.findAll();
return fileInfoList;
}
@Override
public FileInfo seleteFileInfoById(String id) {
FileInfo fileInfo = fileInfoDao.seleteFileInfoById(id);
return fileInfo;
}
@Override
public void insertFileInfo(FileInfo fileInfo) {
fileInfoDao.save(fileInfo);
}
@Override
public void deleteFileInfoById(String id) {
fileInfoDao.deleteFileInfoById(id);
}
@Override
public void updateFileInfo(FileInfo fileInfo) {
fileInfoDao.updateFileInfo(fileInfo.getId(),fileInfo.getFileName(),fileInfo.getFilePath(),fileInfo.getFileSize(),fileInfo.getPage());
}
}
8.创建controller层
package com.zony.app.controller;
import com.zony.app.entity.FileInfo;
import com.zony.app.service.FileInfoService;
import com.zony.app.util.Result;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
/**
*@Description 文件信息记录到数据库中
*@Param
*@Return
*@Author ChuXu
*@Date 2022/3/24
*@Time 17:47
*/
@RestController
@RequestMapping("/FileInfoController")
public class FileInfoController {
private final static Logger log = LoggerFactory.getLogger(FileInfoController.class);
@Autowired
private FileInfoService fileInfoService;
/**
*@Description 向文件信息表中添加一条信息
*@Param [variables]
*@Return com.zony.app.util.Result
*@Author ChuXu
*@Date 2022/3/25
*@Time 10:56
*/
@RequestMapping(value = "/insertFileInfo", method = RequestMethod.POST)
public Result insertFileInfo(@RequestBody Map<String, Object> variables) {
Result result = new Result();
FileInfo fileInfo = new FileInfo();
fileInfo.setFileName((String) variables.get("fileName"));
fileInfo.setFilePath((String) variables.get("filePath"));
fileInfo.setFileSize((String) variables.get("fileSize"));
fileInfo.setPage((String) variables.get("page"));
fileInfoService.insertFileInfo(fileInfo);
result.setCode(200);
result.setMessage("success");
return result;
}
/**
*@Description 根据id删除文件信息
*@Param [id]
*@Return com.zony.app.util.Result
*@Author ChuXu
*@Date 2022/3/25
*@Time 10:59
*/
@RequestMapping(value = "/deleteFileInfoById", method = RequestMethod.POST)
public Result deleteFileInfoById(@RequestParam(value = "id") String id) {
Result result = new Result();
fileInfoService.deleteFileInfoById(id);
result.setCode(200);
result.setMessage("success");
return result;
}
/**
*@Description 修改文件信息
*@Param [variables]
*@Return com.zony.app.util.Result
*@Author ChuXu
*@Date 2022/3/25
*@Time 11:04
*/
@RequestMapping(value = "/updateFileInfo", method = RequestMethod.POST)
public Result updateFileInfo(@RequestBody Map<String, Object> variables) {
Result result = new Result();
FileInfo fileInfo = new FileInfo();
fileInfo.setId((String) variables.get("id"));
fileInfo.setFileName((String) variables.get("fileName"));
fileInfo.setFilePath((String) variables.get("filePath"));
fileInfo.setFileSize((String) variables.get("fileSize"));
fileInfo.setPage((String) variables.get("page"));
fileInfoService.updateFileInfo(fileInfo);
result.setCode(200);
result.setMessage("success");
return result;
}
/**
*@Description 查询单个文件信息
*@Param [id]
*@Return com.zony.app.util.Result
*@Author ChuXu
*@Date 2022/3/25
*@Time 11:05
*/
@RequestMapping(value = "/seleteFileInfoById", method = RequestMethod.POST)
public Result seleteFileInfoById(@RequestParam(value = "id") String id) {
Result result = new Result();
FileInfo fileInfo = fileInfoService.seleteFileInfoById(id);
result.setCode(200);
result.setMessage("success");
result.setData(fileInfo);
return result;
}
/**
*@Description 查询所有文件信息
*@Param []
*@Return com.zony.app.util.Result
*@Author ChuXu
*@Date 2022/3/25
*@Time 11:07
*/
@RequestMapping(value = "/selectAllFileInfo", method = RequestMethod.POST)
public Result selectAllFileInfo() {
Result result = new Result();
List<FileInfo> fileInfoList = fileInfoService.selectAllFileInfo();
result.setCode(200);
result.setMessage("success");
result.setData(fileInfoList);
return result;
}
}
十一、controller层传参
1.map集合
一般用来传递实体类的JSON,使用postman传参直接传递一个JSON至后端。
@RequestBody Map<String, Object> variables
2.单个参数
一般用来传递单个参数,如人员的ID等。
使用postman传参直接在param中添加key,value即可。
@RequestParam(value = "id") String id
@RequestBody和@RequestParam的作用都是用来将前端传递过来的参数,直接绑定到后端controller控制器设置的参数中,区别在于:
@RequestBody限定了前端传递的参数必须为JSON格式,并且前端不能使用GET方式提交数据,而是使用POST方式提交,而且@RequestBody只能有一个。
@RequestParam用于接收url中key-value传递的参数,通常用于GET请求,POST请求也可以使用,可以有很多个。
3.文件流
传递文件流,后台一般用MultipartFile类接收,postman设置好后直接选择本地文件即可。
postman:Body–>form-data–>添加key,value即可,key类型选择file。
@RequestPart(name = "file") MultipartFile file
十一、controller层传参
1.map集合
一般用来传递实体类的JSON,使用postman传参直接传递一个JSON至后端。
@RequestBody Map<String, Object> variables
2.单个参数
一般用来传递单个参数,如人员的ID等。
使用postman传参直接在param中添加key,value即可。
@RequestParam(value = "id") String id
@RequestBody和@RequestParam的作用都是用来将前端传递过来的参数,直接绑定到后端controller控制器设置的参数中,区别在于:
@RequestBody限定了前端传递的参数必须为JSON格式,并且前端不能使用GET方式提交数据,而是使用POST方式提交,而且@RequestBody只能有一个。
@RequestParam用于接收url中key-value传递的参数,通常用于GET请求,POST请求也可以使用,可以有很多个。
3.文件流
传递文件流,后台一般用MultipartFile类接收,postman设置好后直接选择本地文件即可。
postman:Body–>form-data–>添加key,value即可,key类型选择file。
@RequestPart(name = "file") MultipartFile file