关于封面:晚饭后回自习室的路上

Easyexcel 官方文档

Easyexcel | github

前言

最近也是在写的一个小练习中,需要用到这个。趁着这次就将写个整合的Demo给大家。

希望能够让大家有所收获。

阅读完本文,我想你对于使用Java配合Easyexcel操作Excel是完全没有问题的啦。

一、环境准备

1.1、导入相关依赖依赖

我使用Easyexcel的jar包是2021年10月的,说一句是最新版本,莫问题吧

easyexcel | maven

<!-- https://mvnrepository.com/artifact/com.alibaba/easyexcel -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.0.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.74</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>

1.2、项目结构

搭建个项目大家都会啦,这里放一下我自己的结构。

二、读Excel操作 readExcel

2.1、前期准备

准备好一个xslx文件模板,我就是准备了我自己了。

我们创建一个实体类,来对应xlsx中的列名。

实体类

@Data
public class DemoModel { /**
* 用名字去匹配,这里需要注意,如果名字重复,会导致只有一个字段读取到数据,所以我们常用下面这样的格式来写确定。
*/
@ExcelProperty(value = "博客名", index = 0)
private String name; @ExcelProperty(value = "社区", index = 1)
private String communityName; @ExcelProperty(value = "主页", index = 2)
private String homePageUrl; @ExcelProperty(value = "涉及领域", index = 3)
private String specialty; @ExcelProperty(value = "联系邮箱", index = 4)
private String email; /**
* 这里用string 去接日期才能格式化。我想接收年月日格式
*/
@DateTimeFormat("yyyy-MM-dd HH:mm:ss")
@ExcelProperty(value = "发布的第一篇原创文章", index = 5)
private String startDate;
}

监听器

/**
* 有个很重要的点 DocumentListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
* @author crush
*/
public class DemoListener extends AnalysisEventListener<DemoModel> { private static final Logger LOGGER = LoggerFactory.getLogger(DemoListener.class); /**
* 每隔5条存储数据库,实际使用中可以3000条,然后清理list ,方便内存回收
*/
private static final int BATCH_COUNT = 10; List<DemoModel> list = new ArrayList<DemoModel>(); /**
* 假设这个是一个DAO,当然有业务逻辑这个也可以是一个service。当然如果不用存储这个对象没用。
*/
private DemoMapper demoMapper; public DemoListener() {
// 这里是demo,所以随便new一个。实际使用如果到了spring,请使用下面的有参构造函数
demoMapper = new DemoMapper();
} /**
* 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来
*
* @param demoDAO
*/
public DemoListener(DemoMapper demoMapper) {
this.demoMapper = demoMapper;
}
/**
* 这个每一条数据解析都会来调用
*
* @param data
* one row value. Is is same as {@link AnalysisContext#readRowHolder()}
* @param context
*/
@Override
public void invoke(DemoModel data, AnalysisContext context) {
LOGGER.info("解析到一条数据:{}", JSON.toJSONString(data));
list.add(data);
// 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
if (list.size() >= BATCH_COUNT) {
saveData();
// 存储完成清理 list
list.clear();
}
} /**
* 所有数据解析完成了 都会来调用
*
* @param context
*/
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// 这里也要保存数据,确保最后遗留的数据也存储到数据库
saveData();
LOGGER.info("所有数据解析完成!"+count);
} /**
* 加上存储数据库
*/
private void saveData() {
LOGGER.info("{}条数据,开始存储数据库!", list.size());
//进行数据库层面操作
demoMapper.save(list);
LOGGER.info("存储数据库成功!");
}
}

mapper层:此处只是模拟

/**
* @Author: crush
* @Date: 2021-10-31 11:39
* version 1.0
*/
@Repository
public class DemoMapper { public void save(List<DemoModel> demoModels){
System.out.println("Mapper:"+demoModels);
}
}

类型转换

2.2、读单个Sheet

/**
* demo1 最简单的读
* 我们先操作单个的Sheet,进行读操作
* * <p>1. 创建excel对应的实体对象 参照{@link QuestionModel}
* * <p>2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DocumentListener}
* * <p>3. 直接读即可
*/
@Test
public void demo1TestRead() {
// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
// 写法1:
String fileName = "E:\\project_code\\commons-utils\\springboot-excel\\src\\main\\resources\\excel\\demo.xlsx";
// 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭
EasyExcel.read(fileName, DemoModel.class, new DemoListener()).sheet().doRead();
}

这里我们无需指定sheet,因为我们就一个工作表,直接默认就完事了。

控制台输出:

23:35:52.583 [main] INFO com.crush.excel.listener.DemoListener - 解析到一条数据:{"communityName":"掘金","email":"nzc_wyh@163.com","homePageUrl":"https://juejin.cn/user/2859142558267559","name":"宁在春","specialty":"Java后端开发","startDate":"2021-10-31 12:01:52"}
23:35:52.584 [main] INFO com.crush.excel.listener.DemoListener - 解析到一条数据:{"communityName":"CSDN","email":"nzc_wyh@163.com","homePageUrl":"https://blog.csdn.net/weixin_45821811?spm=1000.2115.3001.5343","name":"宁在春","specialty":"Java后端开发","startDate":"2020-05-11 12:01:52"}
23:35:52.585 [main] INFO com.crush.excel.listener.DemoListener - 2条数据,开始存储数据库!
Mapper:[DemoModel(name=宁在春, communityName=掘金, homePageUrl=https://juejin.cn/user/2859142558267559, specialty=Java后端开发, email=nzc_wyh@163.com, startDate=2021-10-31 12:01:52), DemoModel(name=宁在春, communityName=CSDN, homePageUrl=https://blog.csdn.net/weixin_45821811?spm=1000.2115.3001.5343, specialty=Java后端开发, email=nzc_wyh@163.com, startDate=2020-05-11 12:01:52)]
23:35:52.617 [main] INFO com.crush.excel.listener.DemoListener - 存储数据库成功!
23:35:52.618 [main] INFO com.crush.excel.listener.DemoListener - 所有数据解析完成!2

这是最简单的方式,也是读取Excel中,单个Sheet的操作,但我们平时中,一个excel是会有多个工作表的。

如下:

2.3、读多个Sheet

其实本质上还是操作单个sheet工作表。

因为各个Sheet的数据不同,因而要建立多个Model和多个监听类。

这边就不再把全部model贴出了,如果不太明白,可以去文末看看源码。

同时也要创建多个监听器。

mapper层在这里就省略了哈。

/**
* 读多个或者全部sheet,这里注意一个sheet不能读取多次,多次读取需要重新读取文件
* <p>
* 1. 创建excel对应的实体对象 参照{@link QuestionModel}
* <p>
* 2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DocumentListener}
* <p>
* 3. 直接读即可
*/
@Test
public void repeatedRead() {
String fileName = "E:\\project_code\\commons-utils\\springboot-excel\\src\\main\\resources\\excel\\Document_Success.xlsx";
// 读取需要读取的Sheet
ExcelReader excelReader = EasyExcel.read(fileName).build();
// 自己使用功能必须不同的Listener
ReadSheet readSheet1 =
EasyExcel.readSheet(0).head(DocumentModel.class).registerReadListener(new DocumentListener()).build();
ReadSheet readSheet2 =
EasyExcel.readSheet(1).head(TemplateModel.class).registerReadListener(new TemplateListener()).build(); ReadSheet readSheet3 =
EasyExcel.readSheet(2).head(SectionModel.class).registerReadListener(new SectionListener()).build(); ReadSheet readSheet4 =
EasyExcel.readSheet(3).head(QuestionModel.class).registerReadListener(new QuestionListener()).build(); ReadSheet readSheet5 =
EasyExcel.readSheet(4).head(OptionModel.class).registerReadListener(new OptionListener()).build(); ReadSheet readSheet6 =
EasyExcel.readSheet(5).head(ConditionModel.class).registerReadListener(new ConditionListener()).build(); ReadSheet readSheet7 =
EasyExcel.readSheet(6).head(QuestionTooltipModel.class).registerReadListener(new QuestionTooltipListener()).build(); ReadSheet readSheet8 =
EasyExcel.readSheet(7).head(OptionTooltipModel.class).registerReadListener(new OptionTooltipListener()).build(); // 这里注意 一定要把sheet1 sheet2 一起传进去,不然有个问题就是03版的excel 会读取多次,浪费性能
excelReader.read(readSheet1, readSheet2, readSheet3, readSheet4, readSheet5, readSheet6, readSheet7, readSheet8);
// 这里千万别忘记关闭,读的时候会创建临时文件,到时磁盘会崩的
excelReader.finish();
}

控制台输出:

23:42:19.733 [main] INFO com.crush.excel.listener.DocumentListener - 解析到一条数据:{"customerId":1,"name":"Document i"}

23:42:19.755 [main] INFO com.crush.excel.listener.TemplateListener - 解析到一条数据:{"documentType":1,"fontStyleId":1,"lobId":1,"name":"Template q","userGroupId":1}

23:42:19.785 [main] INFO com.crush.excel.listener.SectionListener - 解析到一条数据:{"index":1,"name":"Section 1","order":1}
23:42:19.785 [main] INFO com.crush.excel.listener.SectionListener - 解析到一条数据:{"index":2,"name":"Section 2","order":2} 23:42:19.823 [main] INFO com.crush.excel.listener.QuestionListener - 解析到一条数据:{"allowComments":"false","answerType":"1","enhancedField":"false","index":"1","order":"1","question":"This is Simple Question","required":"true","sectionIndex":"1"}
23:42:19.824 [main] INFO com.crush.excel.listener.QuestionListener - 解析到一条数据:{"allowComments":"false","answerType":"2","enhancedField":"false","index":"2","order":"2","question":"This is Simple Question","required":"false","sectionIndex":"1"}
23:42:19.825 [main] INFO com.crush.excel.listener.QuestionListener - 解析到一条数据:{"allowComments":"false","answerType":"10","enhancedField":"false","index":"3","order":"3","question":"This is Simple Question","required":"false","sectionIndex":"1"}
23:42:19.826 [main] INFO com.crush.excel.listener.QuestionListener - 解析到一条数据:{"allowComments":"false","answerType":"3","enhancedField":"false","index":"4","order":"1","question":"This is Simple Question","required":"true","sectionIndex":"2"}
23:42:19.827 [main] INFO com.crush.excel.listener.QuestionListener - 解析到一条数据:{"allowComments":"false","answerType":"5","enhancedField":"false","index":"5","order":"2","question":"This is Simple Question","required":"false","sectionIndex":"2"}
23:42:19.831 [main] INFO com.crush.excel.listener.QuestionListener - 解析到一条数据:{"allowComments":"false","answerType":"12","enhancedField":"false","index":"6","order":"3","question":"This is Simple Question","required":"true","sectionIndex":"2"} 23:42:19.847 [main] INFO com.crush.excel.listener.OptionListener - 解析到一条数据:{"index":1,"order":1,"questionIndex":3,"value":"Option 1"}
23:42:19.848 [main] INFO com.crush.excel.listener.OptionListener - 解析到一条数据:{"index":2,"order":2,"questionIndex":3,"value":"Option 2"}
23:42:19.849 [main] INFO com.crush.excel.listener.OptionListener - 解析到一条数据:{"index":3,"order":3,"questionIndex":3,"value":"Option 3"}
23:42:19.849 [main] INFO com.crush.excel.listener.OptionListener - 解析到一条数据:{"index":4,"order":4,"questionIndex":3,"value":"Option 4"}
23:42:19.850 [main] INFO com.crush.excel.listener.OptionListener - 解析到一条数据:{"index":5,"order":1,"questionIndex":6,"value":"Option 1"}
23:42:19.850 [main] INFO com.crush.excel.listener.OptionListener - 解析到一条数据:{"index":6,"order":2,"questionIndex":6,"value":"Option 2"}
23:42:19.851 [main] INFO com.crush.excel.listener.OptionListener - 解析到一条数据:{"index":7,"order":3,"questionIndex":6,"value":"Option 3"} 23:42:19.868 [main] INFO com.crush.excel.listener.ConditionListener - 解析到一条数据:{"action":1,"index":1,"questionIndex":1,"selectedAnswer":"Test","triggerQuestionIndex":1,"triggerSectionIndex":1}
23:42:19.869 [main] INFO com.crush.excel.listener.ConditionListener - 解析到一条数据:{"action":1,"index":2,"questionIndex":2,"selectedAnswer":"Test","triggerSectionIndex":2} 23:42:19.888 [main] INFO com.crush.excel.listener.QuestionTooltipListener - 解析到一条数据:{"endIndex":10,"index":1,"parentIndex":1,"startIndex":1,"text":"Sample Tooltip"} 23:42:19.908 [main] INFO com.crush.excel.listener.OptionTooltipListener - 解析到一条数据:{"endIndex":10,"index":1,"parentIndex":1,"startIndex":1,"text":"Sample Tooltip"}
23:42:19.910 [main] INFO com.crush.excel.listener.OptionTooltipListener - 解析到一条数据:{"endIndex":10,"index":2,"parentIndex":2,"startIndex":1,"text":"Sample Tooltip"}

我们可以在获取到数据的时候,存储进数据库。另外我们常常会上传excel,进行数据的导入,所以我们再来看看web中的excel的读。

2.4、web中的读

@AutowiredDemoMapper demoMapper;/** * 文件上传 * <p> * 1. 创建excel对应的实体对象 参照{@link UploadData} * <p> * 2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link UploadDataListener} * <p> * 3. 直接读即可 */@PostMapping("upload")@ResponseBodypublic String upload(MultipartFile file) throws IOException {    EasyExcel.read(file.getInputStream(), DemoModel.class, new DemoListener(demoMapper)).sheet().doRead();    return "success";}

我想这样的场景是我们在Web开发中最常用到的那种。

如果是内存足够,数据量不是特别大的话,像我这么写也是完全能够接受的。

如果是操作数据十万或几十万数据的大兄弟,可以前去官网看看优化方案。Easyexcel | github

三、写Excle操作 writeExcel

3.1、写到单个Sheet中

/**
* 最简单的写
* <p>1. 创建excel对应的实体对象 参照{@link DemoModel}
* <p>2. 直接写即可
*/
@Test
public void simpleWrite() {
DemoModel model = new DemoModel();
model.setName("宁在春");
model.setCommunityName("知乎");
model.setHomePageUrl("https://www.zhihu.com/creator/manage/creation/all");
model.setEmail("nza_wyh@163.com");
model.setSpecialty("SpringBoot");
model.setStartDate("2021-10-31 12:00:00");
DemoModel model2 = new DemoModel();
model2.setName("宁在春2");
model2.setCommunityName("知乎2");
model2.setHomePageUrl("2https://www.zhihu.com/creator/manage/creation/all");
model2.setEmail("nza_wyh@163.com2");
model2.setSpecialty("SpringBoot2");
model2.setStartDate("2021-10-31 12:00:00");
List<DemoModel> models = new ArrayList<DemoModel>();
models.add(model);
models.add(model2);
// 写法1
String fileName = "E:\\project_code\\commons-utils\\springboot-excel\\src\\main\\resources\\excel\\demo2.xlsx";
// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
// 如果这里想使用03 则 传入excelType参数即可
EasyExcel.write(fileName, DemoModel.class).sheet(0).doWrite(models); // // 写法2
// // 这里 需要指定写用哪个class去写
ExcelWriter excelWriter = EasyExcel.write(fileName, DemoModel.class).build();
WriteSheet writeSheet = EasyExcel.writerSheet(0).build();
excelWriter.write(models, writeSheet);
// 千万别忘记finish 会帮忙关闭流
excelWriter.finish();
}

这是操作excel文件中单个Sheet的操作,十分简单。

EasyExcel.write(fileName, DemoModel.class).sheet(0).doWrite(models);关于sheet()中的参数0,我们其实就一张Sheet工作表,填与不填其实都一样,另外,此处也可以填sheet表名字,也是一样 。

操作结果:

注意:

注意:我发现如果我是直接向这个excel文件进行写入,默认是采用覆盖的方式进行写入,即之前有的信息都会被覆盖掉。

测试:

写之前:

写之后:

补充:

如果我们直接写,是采用覆盖模式的,这肯定是不符合一些业务场景的。所以肯定有解决方式的,

easyexcel中对此也是有处理的。

它有一个根据模板写入,模板如下:

/**
* 根据模板写入
* <p>1. 创建excel对应的实体对象 参照
* <p>2. 使用 注解指定写入的列
* <p>3. 使用withTemplate 写取模板
* <p>4. 直接写即可
*/
@Test
public void templateWrite() {
DemoModel model = new DemoModel();
model.setName("宁在春");
model.setCommunityName("知乎");
model.setHomePageUrl("https://www.zhihu.com/creator/manage/creation/all");
model.setEmail("nza_wyh@163.com");
model.setSpecialty("SpringBoot");
model.setStartDate("2021-10-31 12:00:00");
DemoModel model2 = new DemoModel();
model2.setName("宁在春2");
model2.setCommunityName("知乎2");
model2.setHomePageUrl("2https://www.zhihu.com/creator/manage/creation/all");
model2.setEmail("nza_wyh@163.com2");
model2.setSpecialty("SpringBoot2");
model2.setStartDate("2021-10-31 12:00:00");
List<DemoModel> models = new ArrayList<DemoModel>();
models.add(model);
models.add(model2);
String templateFileName = "E:\\project_code\\commons-utils\\springboot-excel\\src\\main\\resources\\excel\\demo2.xlsx";
String fileName = "E:\\project_code\\commons-utils\\springboot-excel\\src\\main\\resources\\excel\\templateWrite.xlsx";
// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
EasyExcel.write(fileName, DemoModel.class).withTemplate(templateFileName).sheet().doWrite(models);
}

最后的效果如下:

写到注意那一小节的时候,我也有考虑这个是如何进行处理的。

我最开始想的是,是先把文件读出来,然后把数据拼接起来,再写进去。但是我一下就推翻这个想法,一旦那样做,内存消耗什么的都太大了,不太合适。

然后简单看了一下,它是直接new了一个File,加载进内存。(觉得自己好憨)


因为这个我又去测试了个不同的,如果把模板修改的不符合会咋样。

测试的结果就是符合我的猜测,就是copy了一份原文件,在源文件的基础上进行写操作,不管原文件格式如何,都会进行保留。

3.2、重复写入或写到多个Sheet中

重复写入:

其实就是循环了单个的操作。

// 方法1 如果写到同一个sheet
String fileName = "E:\\project_code\\commons-utils\\springboot-excel\\src\\main\\resources\\excel\\demo2.xlsx";
// // 这里 需要指定写用哪个class去写
ExcelWriter excelWriter = EasyExcel.write(fileName, DemoModel.class).build();
WriteSheet writeSheet = EasyExcel.writerSheet(0).build();
// 去调用写入,这里我调用了五次,实际使用时根据数据库分页的总的页数来
for (int i = 0; i < 5; i++) {
// 分页去数据库查询数据 这里可以去数据库查询每一页的数据
List<DemoModel> data = data();
excelWriter.write(data, writeSheet);
}
// 千万别忘记finish 会帮忙关闭流
excelWriter.finish();

data()就是生成数据的一个方法。

写到多个Sheet中:

其本质也是上面那样,只是调用不同监听类,不同的对象罢了,更简单的方式,以做到不同的处理,目前还没有找到更简单的方法。

和读多个sheet中一样的方式,在此处就不再复述了。

3.3、web中的写

既然有写就要也有读了,有始有终,这个也给大家贴出来了。

/**
* 文件下载(失败了会返回一个有部分数据的Excel)
* <p>
* 1. 创建excel对应的实体对象 参照
* <p>
* 2. 设置返回的 参数
* <p>
* 3. 直接写,这里注意,finish的时候会自动关闭OutputStream,当然你外面再关闭流问题不大
*/
@GetMapping("download")
public void download(HttpServletResponse response) throws IOException {
// 这里注意 有同学反应使用swagger 会导致各种问题,请直接用浏览器或者用postman
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
// 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
String fileName = URLEncoder.encode("测试", "UTF-8");
response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
//这里的sheet的参数就是生成后的工作表的名称
EasyExcel.write(response.getOutputStream(), DemoModel.class).sheet("模板").doWrite(data());
} /**
* 文件下载并且失败的时候返回json(默认失败了会返回一个有部分数据的Excel)
*
* @since 2.1.1
*/
@GetMapping("downloadFailedUsingJson")
public void downloadFailedUsingJson(HttpServletResponse response) throws IOException {
// 这里注意 有同学反应使用swagger 会导致各种问题,请直接用浏览器或者用postman
try {
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
// 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
String fileName = URLEncoder.encode("测试", "UTF-8");
response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
// 这里需要设置不关闭流
EasyExcel.write(response.getOutputStream(), DemoModel.class).autoCloseStream(Boolean.FALSE).sheet("模板")
.doWrite(data());
} catch (Exception e) {
// 重置response
response.reset();
response.setContentType("application/json");
response.setCharacterEncoding("utf-8");
Map<String, String> map = new HashMap<String, String>();
map.put("status", "failure");
map.put("message", "下载文件失败" + e.getMessage());
response.getWriter().println(JSON.toJSONString(map));
}
} private List<DemoModel> data(){
List<DemoModel> demoModels = new ArrayList<>();
DemoModel model = new DemoModel();
model.setName("宁在春");
model.setCommunityName("知乎");
model.setHomePageUrl("https://www.zhihu.com/creator/manage/creation/all");
model.setEmail("nza_wyh@163.com");
model.setSpecialty("SpringBoot");
model.setStartDate("2021-10-31 12:00:00");
DemoModel model2 = new DemoModel();
model2.setName("宁在春2");
model2.setCommunityName("知乎2");
model2.setHomePageUrl("2https://www.zhihu.com/creator/manage/creation/all");
model2.setEmail("nza_wyh@163.com2");
model2.setSpecialty("SpringBoot2");
model2.setStartDate("2021-10-31 12:00:00");
demoModels.add(model);
demoModels.add(model2);
return demoModels;
}

结果:

四、可能会产生的问题

  1. 在实体中已经使用了@Data注解,就不要再使用@Accessors(chain = true),否则会产生读取为空的现象。

    这一点我已经亲自踩坑了(一开始排这个错都排了会)

  2. 在读的时候Listener里面需要使用spring的@Autowired

    Listener创建成员变量,然后在构造方法里面传进去。必须不让spring管理Listener,每次读取都要new一个。

这两点是我认为十分常见,又比较容易踩的坑,给大家列出来了。

详细的可点击 Easexcel 常见问题

五、自言自语

11月更文开始,每天写写项目,做做记录,充实的每一天。

纸上得来终觉浅,绝知此事要躬行。

大家好,我是博主宁在春主页

一名喜欢文艺却踏上编程这条道路的小青年。

希望:我们,待别日相见时,都已有所成

一个可爱的猫咪小表情,实在太可爱了,没办法抵挡住诱惑啊。

最新文章

  1. iOS开发随笔
  2. jpa findOne()用法
  3. docker管理shipyard中文版v3.0.2更新
  4. Maven 使用介绍
  5. bzoj4216 Pig
  6. 提升WordPress站点速度的八个建议
  7. 安装tensorflow
  8. STM32系列命名规则
  9. asp-net-web-api 自定义URl插件
  10. Intellij IDEA 2016 mybatis 生成 mapper
  11. 7.MyBatis延时加载
  12. 为开源社区尽一份力,翻译RocketMQ官方文档
  13. python3 [爬虫实战] selenium 爬取安居客
  14. 设计模式总结篇系列:工厂方法模式(Factory Method)
  15. 【BZOJ2940】条纹(博弈论)
  16. java.lang.ClassNotFoundException: org.apache.commons.fileupload.FileItemFactory
  17. Android之网络摄像头
  18. 分布式锁--Redis小试牛刀
  19. sqlserver 2012 IDE中 Windows身份验证连接服务器报错 ,Login failed for user &#39;xxx\Administrator&#39;. 原因: 找不到与提供的名称匹配的登录名。
  20. 转:iOS7导航栏遮盖问题的解决

热门文章

  1. IO流实现GBK写入文件然后转换UTF-8
  2. 使用@EnableConfigurationProperties注册配置Bean时的命名规则
  3. POJ1804——Brainman(水题)
  4. Windows Server 2008 系统加固
  5. Charles抓包工具过滤网络请求
  6. javascript 字符串 数字反转 字母大小写互换
  7. P6122-[NEERC2016]Mole Tunnels【模拟费用流】
  8. 【MySQL数据库】MySQL5.7安装与配置、可视化工具安装和破解
  9. java文件上传工具包
  10. 第30篇-main()方法的执行