springboot邮件管理-动态

创数据库建表

 
  1. /*

  2. Navicat MySQL Data Transfer

  3. Source Server : mysql

  4. Source Server Version : 50720

  5. Source Host : localhost:3306

  6. Source Database : blog_system

  7. Target Server Type : MYSQL

  8. Target Server Version : 50720

  9. File Encoding : 65001

  10. Date: 2018-12-13 16:54:34

  11. */

  12. SET FOREIGN_KEY_CHECKS=0;

  13. -- ----------------------------

  14. -- Table structure for `t_article`

  15. -- ----------------------------

  16. DROP TABLE IF EXISTS `t_article`;

  17. CREATE TABLE `t_article` (

  18. `id` int(11) NOT NULL AUTO_INCREMENT,

  19. `title` varchar(50) NOT NULL COMMENT '文章标题',

  20. `content` longtext COMMENT '文章具体内容',

  21. `created` date NOT NULL COMMENT '发表时间',

  22. `modified` date DEFAULT NULL COMMENT '修改时间',

  23. `categories` varchar(200) DEFAULT '默认分类' COMMENT '文章分类',

  24. `tags` varchar(200) DEFAULT NULL COMMENT '文章标签',

  25. `allow_comment` tinyint(1) NOT NULL DEFAULT '1' COMMENT '是否允许评论',

  26. `thumbnail` varchar(200) DEFAULT NULL COMMENT '文章缩略图',

  27. PRIMARY KEY (`id`)

  28. ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

  29. -- ----------------------------

  30. -- Records of t_article

  31. -- ----------------------------

  32. INSERT INTO `t_article` VALUES ('1', '2018新版Java学习路线图', '    播妞深知广大爱好Java的人学习是多么困难,没视频没资源,上网花钱还老担心被骗。因此专门整理了新版的学习路线图,不管你是不懂电脑的小白,还是已经步入开发的大牛,这套路线图绝对不容错过!12年传智播客黑马程序员分享免费视频教程长达10余万小时,累计下载量3000余万次,受益人数达千万。2018年我们不忘初心,继续前行。 路线图的宗旨就是分享,专业,便利,让喜爱Java的人,都能平等的学习。从今天起不要再找借口,不要再说想学Java却没有资源,赶快行动起来,Java等你来探索,高薪距你只差一步!\r\n\r\n**一、2018新版Java学习路线图---每阶段市场价值及可解决的问题**\r\n![alt](/article_img/roadmap/1.jpg)\r\n\r\n**二、2018新版Java学习路线图---学习大纲及各阶段知识点**\r\n![alt](/article_img/roadmap/2.jpg)\r\n\r\n![alt](/article_img/roadmap/3.jpg)\r\n\r\n**三、2018新版Java学习路线图---升级后新增知识点一览**\r\n![alt](/article_img/roadmap/4.jpg)\r\n\r\n\r\n\r\n', '2018-10-10', null, '默认分类', '2018,Java,学习路线图', '1', null);

  33. INSERT INTO `t_article` VALUES ('2', '2018新版Python学习线路图', '    12年历经风雨,传智播客黑马程序员已免费分享视频教程长达10万余小时,累计下载量超2000万次,受益人数达千万。2018年我们不忘初心,继续前行。学习路线图的宗旨就是分享,专业,便利,让喜爱Python的人,都能平等的学习。据悉,Python已经入驻小学生教材,未来不学Python不仅知识会脱节,可能与小朋友都没有了共同话题~~所以,从今天起不要再找借口,不要再说想学Python却没有资源,赶快行动起来,Python等你来探索,高薪距你只差一步!\r\n\r\n**一、2018新版Python学习路线图---每阶段市场价值及可解决的问题**\r\n![alt](/article_img/phmap/1.jpg)\r\n\r\n![alt](/article_img/phmap/2.jpg)\r\n\r\n**二、2018新版Python学习路线图---学习大纲及各阶段知识点**\r\n![alt](/article_img/phmap/3.jpg)\r\n\r\n![alt](/article_img/phmap/4.jpg)\r\n\r\n**三、2018新版Python学习路线图---升级版Python成长路径**\r\n![alt](/article_img/phmap/5.jpg)\r\n\r\n![alt](/article_img/phmap/6.jpg)', '2018-10-24', null, '默认分类', '2018,Python,学习线路图', '1', null);

  34. INSERT INTO `t_article` VALUES ('3', '2018新版前端与移动开发学习线路图', '    传智播客黑马程序员作为一个IT学习平台,历经12年的成长,免费分享视频教程长达10万余小时,累计下载量超3000万次,受益人数达千万。2018年我们不忘初心,继续前行!路线图的宗旨就是分享,专业,便利,让更多想要学习IT的人都能系统的学习!从今天起不要再找借口,不要再说想学却没有资源,赶快行动起来,前端与移动开发等你来探索,高薪距你只差一步!注:新版前端与移动开发学习大纲于2018年2月26日完成升级,本学习路线图依据最新升级后的规划制作!\r\n\r\n**一、2018新版前端与移动开发学习路线图---每阶段可掌握的能力及可解决的问题**\r\n![alt](/article_img/frmap/1.jpg)\r\n\r\n![alt](/article_img/frmap/2.jpg)\r\n\r\n**二、2018新版前端与移动开发学习路线图---学习大纲及各阶段知识点**\r\n![alt](/article_img/frmap/3.jpg)\r\n\r\n![alt](/article_img/frmap/4.jpg)\r\n\r\n**三、2018新版前端与移动开发学习路线图--升级后新增知识点设计理念**\r\n![alt](/article_img/frmap/5.jpg)', '2018-11-13', null, '默认分类', '2018,前端与移动,学习线路图', '1', null);

  35. INSERT INTO `t_article` VALUES ('4', '2018新版PHP学习线路图', '    传智播客黑马程序员作为一个IT学习平台,历经12年的成长,免费分享视频教程长达10万余小时,累计下载量超3000万次,受益人数达千万。2018年我们不忘初心,继续前行!路线图的宗旨就是分享,专业,便利,让更多喜爱PHP的人都能系统的学习!从今天起不要再找借口,不要再说想学PHP却没有资源,赶快行动起来,PHP等你来探索,高薪距你只差一步!\r\n\r\n**一、2018新版PHP学习路线图---每阶段市场价值及可解决的问题**\r\n![alt](/article_img/phpmap/1.jpg)\r\n\r\n![alt](/article_img/phpmap/2.jpg)\r\n\r\n**二、2018新版PHP学习路线图---学习大纲及各阶段知识点**\r\n![alt](/article_img/phpmap/3.jpg)\r\n\r\n![alt](/article_img/phpmap/4.jpg)\r\n\r\n**三、2018新版PHP学习路线图---升级后新增知识点设计理念**\r\n![alt](/article_img/phpmap/5.jpg)\r\n', '2018-11-16', null, '默认分类', '2018,PHP,学习线路图', '1', null);

  36. INSERT INTO `t_article` VALUES ('5', '2018版Go语言+区块链学习线路图', '    12年传智播客黑马程序员分享免费视频教程长达10万余小时,累计下载量超3000万次,受益人数达千万。2018年我们不忘初心,继续前行。 路线图的宗旨就是分享,专业,便利,让喜爱Go语言+区块链的人,都能平等的学习。从今天起不要再找借口,不要再说想学Go语言+区块链却没有资源,赶快行动起来,Go语言+区块链等你来探索,高薪距你只差一步!\r\n\r\n**一、2018新版Go语言+区块链学习路线图---每阶段市场价值及可解决的问题**\r\n![alt](/article_img/gomap/1.jpg)\r\n\r\n![alt](/article_img/gomap/2.jpg)\r\n\r\n**二、2018新版Go语言+区块链学习路线图---每阶段可掌握的核心能力**\r\n![alt](/article_img/gomap/3.jpg)\r\n\r\n**三、2018新版Go语言+区块链学习路线图---每阶段的设计理念**\r\n![alt](/article_img/gomap/4.jpg)\r\n\r\n**四、2018新版Go语言+区块链学习路线图---学习大纲及各阶段知识点**\r\n![alt](/article_img/gomap/5.jpg)\r\n\r\n![alt](/article_img/gomap/6.jpg)', '2018-11-27', null, '默认分类', '2018,Go语言,区块链,学习线路图', '1', null);

  37. INSERT INTO `t_article` VALUES ('6', 'JDK 8——Lambda表达式介绍', '  Lambda表达式是JDK 8中一个重要的新特性,它使用一个清晰简洁的表达式来表达一个接口,同时Lambda表达式也简化了对集合以及数组数据的遍历、过滤和提取等操作。下面,本篇文章就对Lambda表达式进行简要介绍,并进行演示说明。\r\n\r\n**1. Lambda表达式入门** \r\n\r\n  匿名内部类存在的一个问题是,如果匿名内部类的实现非常简单,例如只包含一个抽象方法的接口,那么匿名内部类的语法仍然显得比较冗余。为此,JDK 8中新增了一个特性Lambda表达式,这种表达式只针对有一个抽象方法的接口实现,以简洁的表达式形式实现接口功能来作为方法参数。 \r\n  一个Lambda表达式由三个部分组成,分别为参数列表、“->”和表达式主体,其语法格式如下:\r\n```js\r\n ([数据类型 参数名,数据类型 参数名,...]) -> {表达式主体}\r\n``` \r\n  从上述语法格式上看,Lambda表达式的书写非常简单,下面针对Lambda表达式的组成部分进行简单介绍,具体如下: \r\n  (1)([数据类型 参数名,数据类型 参数名,...]):用来向表达式主体传递接口方法需要的参数,多个参数名中间必须用英文逗号“,”进行分隔;在编写Lambda表达式时,可以省略参数的数据类型,后面的表达式主体会自动进行校对和匹配;同时,如果只有一个参数,则可以省略括号“()”。 \r\n  (2)->:表示Lambda表达式箭牌,用来指定参数数据指向,不能省略,且必须用英文横线和大于号书写。 \r\n  (3){表达式主体}:由单个表达式或语句块组成的主体,本质就是接口中抽象方法的具体实现,如果表达式主体只有一条语句,那么可以省略包含主体的大括号;另外,在Lambda表达式主体中允许有返回值,当只有一条return语句时,也可以省略return关键字。 \r\n  了解了Lambda表达式的语法后,接下来编写一个示例文件对Lambda表达式的基本使用进行介绍,具体代码如下所示。\r\n```js\r\n 1 // 定义动物类接口\r\n 2 interface Animal { \r\n 3 void shout(); // 定义方法shout()\r\n 4 }\r\n 5 public class Example22 {\r\n 6 public static void main(String[] args) {\r\n 7 String name = \"小花\"; \r\n 8 // 1、匿名内部类作为参数传递给animalShout()方法\r\n 9 animalShout(new Animal() { \r\n 10 public void shout() { \r\n 11 System.out.println(\"匿名内部类输出:\"+name+\"喵喵...\");\r\n 12 }\r\n 13 });\r\n 14 // 2、使用Lambda表达式作为参数传递给animalShout()方法\r\n 15 animalShout(()-> System.out.println(\"Lambda表达式输出:\"\r\n 16 +name+\"喵喵...\"));\r\n 17 }\r\n 18 // 创建一个animalShout()静态方法,接收接口类型的参数\r\n 19 public static void animalShout(Animal an) {\r\n 20 an.shout(); \r\n 21 }\r\n 22 }\r\n``` \r\n  运行结果下图所示。\r\n![alt](/article_img/lambda/1.jpg)\r\n  上述代码示例中,先定义了只有一个抽象方法的接口Animal,然后分别使用匿名内部类和Lambda表达式的方式实现了接口方法。从图中可以看出,使用匿名内部类和Lambda表达式都能实现接口中方法,但很显然使用Lambda表达式更加简洁和清晰。', '2018-11-27', null, '默认分类', '2018,Lambda表达式', '1', null);

  38. INSERT INTO `t_article` VALUES ('7', '函数式接口', '  虽然Lambda表达式可以实现匿名内部类的功能,但在使用时却有一个局限,即接口中有且只有一个抽象方法时才能使用Lamdba表达式代替匿名内部类。这是因为Lamdba表达式是基于函数式接口实现的,所谓函数式接口是指有且仅有一个抽象方法的接口,Lambda表达式就是Java中函数式编程的体现,只有确保接口中有且仅有一个抽象方法,Lambda表达式才能顺利地推导出所实现的这个接口中的方法。 \r\n  在JDK 8中,专门为函数式接口引入了一个@FunctionalInterface注解,该注解只是显示的标注了接口是一个函数式接口,并强制编辑器进行更严格的检查,确保该接口是函数式接口,如果不是函数式接口,那么编译器就会报错,而对程序运行并没有实质上的影响。 \r\n  接下来通过一个案例来演示函数式接口的定义与使用,示例代码如下所示。\r\n```js\r\n 1 // 定义无参、无返回值的函数式接口\r\n 2 @FunctionalInterface\r\n 3 interface Animal {\r\n 4 void shout();\r\n 5 }\r\n 6 // 定义有参、有返回值的函数式接口\r\n 7 interface Calculate {\r\n 8 int sum(int a, int b);\r\n 9 }\r\n 10 public class Example23 {\r\n 11 public static void main(String[] args) {\r\n 12 // 分别两个函数式接口进行测试\r\n 13 animalShout(() -> System.out.println(\"无参、无返回值的函数式接口调用\"));\r\n 14 showSum(10, 20, (x, y) -> x + y);\r\n 15 }\r\n 16 // 创建一个动物叫的方法,并传入接口对象Animal作为参数\r\n 17 private static void animalShout(Animal animal) {\r\n 18 animal.shout();\r\n 19 }\r\n 20 // 创建一个求和的方法,并传入两个int类型以及接口Calculate类型的参数\r\n 21 private static void showSum(int x, int y, Calculate calculate) {\r\n 22 System.out.println(x + \"+\" + y + \"的和为:\" + calculate.sum(x, y));\r\n 23 }\r\n 24 }\r\n``` \r\n  运行结果如下图所示。\r\n\r\n![alt](/article_img/lambda/2.jpg)\r\n  上述代码示例中,先定义了两个函数式接口Animal和Calculate,然后在测试类中分别编写了两个静态方法,并将这两个函数式接口以参数的形式传入,最后在main()方法中分别调用这两个静态方法,并将所需要的函数式接口参数以Lambda表达式的形式传入。从图中可以看出,程序中函数式接口的定义和使用完全正确。\r\n', '2018-12-01', null, '默认分类', '接口,函数式接口', '1', null);

  39. INSERT INTO `t_article` VALUES ('8', 'JDK 8——聚合操作', '&emsp;&emsp;在Java8版本中,JDK包含许多聚合操作(如平均值,总和,最小,最大,和计数),返回一个计算流stream的聚合结果。这些聚合操作被称为聚合操作。JDK除返回单个值的聚合操作外,还有很多聚合操作返回一个collection集合实例。很多的reduce操作执行特定的任务,如求平均值或按类别分组元素。 \r\n\r\n**1. 聚合操作简介**\r\n\r\n  在开发中,多数情况下会涉及到对集合、数组中元素的操作,在JDK 8之前都是通过普通的循环遍历出每一个元素,然后还会穿插一些if条件语句选择性的对元素进行查找、过滤、修改等操作,这种原始的操作方法虽然可行,但是代码量较大并且执行效率较低。 \r\n  为此,JDK 8中新增了一个Stream接口,该接口可以将集合、数组的中的元素转换为Stream流的形式,并结合Lambda表达式的优势来进一步简化集合、数组中元素的查找、过滤、转换等操作,这一新功能就是JDK 8中的聚合操作。 \r\n  在程序中,使用聚合操作没有绝对的语法规范,根据实际操作流程,主要可以分为以下3个步骤: \r\n  (1)将原始集合或者数组对象转换为Stream流对象; \r\n  (2)对Stream流对象中的元素进行一系列的过滤、查找等中间操作(Intermediate Operations),然后仍然返回一个Stream流对象; \r\n  (3)对Stream流进行遍历、统计、收集等终结操作(Terminal Operation),获取想要的结果。 \r\n  接下来,就根据上面聚合操作的3个步骤,通过一个案例来演示聚合操作的基本用法,具体示例代码如下所示。\r\n```js\r\n 1 import java.util.*;\r\n 2 import java.util.stream.Stream;\r\n 3 public class Example31 {\r\n 4 public static void main(String[] args) {\r\n 5 // 创建一个List集合对象\r\n 6 List<String> list = new ArrayList<>(); \r\n 7 list.add(\"张三\");\r\n 8 list.add(\"李四\");\r\n 9 list.add(\"张小明\");\r\n 10 list.add(\"张阳\");\r\n 11 // 1、创建一个Stream流对象\r\n 12 Stream<String> stream = list.stream();\r\n 13 // 2、对Stream流中的元素分别进行过滤、截取操作\r\n 14 Stream<String> stream2 = stream.filter(i -> i.startsWith(\"张\"));\r\n 15 Stream<String> stream3 = stream2.limit(2);\r\n 16 // 3、对Stream流中的元素进行终结操作,进行遍历输出\r\n 17 stream3.forEach(j -> System.out.println(j));\r\n 18 System.out.println(\"=======\");\r\n 19 // 通过链式表达式的形式完成聚合操作\r\n 20 list.stream().filter(i -> i.startsWith(\"张\"))\r\n 21 .limit(2)\r\n 22 .forEach(j -> System.out.println(j));\r\n 23 }\r\n 24 }\r\n``` \r\n   运行结果如下图所示。\r\n![alt](/article_img/lambda/3.jpg)\r\n  上述示例代码中,先创建了一个List集合,然后根据聚合操作的3个步骤实现了集合对象的聚合操作,对集合中的元素使用Stream流的形式进行过滤(filter)、截取(limit),并进行遍历输出。其中第12~17行代码分步骤详细展示了聚合操作,而第20~22行代码是使用了链式表达式(调用有返回值的方法时不获取返回值而是直接再调用另一个方法)实现了聚合操作,该表达式的语法格式更简洁、高效,这种链式调用也被称为操作管道流。\r\n\r\n**2. 创建Stream流对象** \r\n  在上一小节中,介绍了聚合操作的主要使用步骤,其中首要解决的问题就是创建Stream流对象。聚合操作针对的就是可迭代数据进行的操作,如集合、数组等,所以创建Stream流对象其实就是将集合、数组等通过一些方法转换为Stream流对象。 \r\n  在Java中,集合对象有对应的集合类,可以通过集合类提供的静态方法创建Stream流对象,而数组数据却没有对应的数组类,所以必须通过其他方法创建Stream流对象。针对不同的源数据,Java提供了多种创建Stream流对象的方式,分别如下: \r\n  (1)所有的Collections集合都可以使用stream()静态方法获取Stream流对象; \r\n  (2)Stream接口的of()静态方法可以获取基本类型包装类数组、引用类型数组和单个元素的Stream流对象; \r\n  (3)Arrays数组工具类的stream()静态方法也可以获取数组元素的Stream流对象。 \r\n  接下来,通过一个案例来学习聚合操作中如何创建Stream流对象,具体示例代码如下所示。\r\n```js\r\n 1 import java.util.*;\r\n 2 import java.util.stream.Stream;\r\n 3 public class Example32 {\r\n 4 public static void main(String[] args) {\r\n 5 // 创建一个数组\r\n 6 Integer[] array = { 9, 8, 3, 5, 2 }; \r\n 7 // 将数组转换为List集合\r\n 8 List<Integer> list = Arrays.asList(array); \r\n 9 // 1、使用集合对象的stream()静态方法创建Stream流对象\r\n 10 Stream<Integer> stream = list.stream();\r\n 11 stream.forEach(i -> System.out.print(i+\" \"));\r\n 12 System.out.println();\r\n 13 // 2、使用Stream接口的of()静态方法创建Stream流对象\r\n 14 Stream<Integer> stream2 = Stream.of(array);\r\n 15 stream2.forEach(i -> System.out.print(i+\" \"));\r\n 16 System.out.println();\r\n 17 // 3、使用Arrays数组工具类的stream()静态方法创建Stream流对象\r\n 18 Stream<Integer> stream3 = Arrays.stream(array);\r\n 19 stream3.forEach(i -> System.out.print(i+\" \"));\r\n 20 }\r\n 21 }\r\n``` \r\n  运行结果如下图所示。\r\n![alt](/article_img/lambda/4.jpg)\r\n  上述示例代码中,先创建了一个数组和一个集合,然后通过三种方式实现了Stream流对象的创建,并通过Stream流对象的forEach()方法结合Lambda表达式完成了集合和数组中元素的遍历。 \r\n\r\n**小提示:** \r\n  在JDK 8中,只针对单列集合Collections接口对象提供了stream()静态方法获取Stream流对象,并未对Map集合提供相关方法获取Stream流对象,所以想要用Map集合创建Stream流对象必须先通过Map集合的keySet()、values()、entrySet()等方法将Map集合转换为单列Set集合,然后再使用单列集合的stream()静态方法获取对应键、值集合的Stream流对象。\r\n\r\n', '2018-12-02', null, '默认分类', 'JDK 8,聚合操作', '1', null);

  40. INSERT INTO `t_article` VALUES ('9', '虚拟化容器技术——Docker运行机制介绍', '  Docker是一个开源的应用容器引擎,它基于go语言开发,并遵从Apache2.0开源协议。使用Docker可以让开发者封装他们的应用以及依赖包到一个可移植的容器中,然后发布到任意的Linux机器上,也可以实现虚拟化。Docker容器完全使用沙箱机制,相互之间不会有任何接口,这保证了容器之间的安全性。 \r\n\r\n**1. Docker的引擎介绍**\r\n\r\n  Docker Engine(Docker引擎)是Docker的核心部分,使用的是客户端-服务器(C/S)架构模式,其主要组成部分如下图所示。\r\n![alt](/article_img/docker/1.png)\r\n  从上图可以看出,Docker Engine中包含了三个核心组件(docker CLI、REST API和docker daemon),这三个组件的具体说明如下: \r\n  ①docker CLI(command line interface):表示Docker命令行接口,开发者可以在命令行中使用Docker相关指令与Docker守护进程进行交互,从而管理诸如image(镜像)、container(容器)、network(网络)和data volumes(数据卷)等实体。 \r\n  ②REST API:表示应用程序API接口,开发者通过该API接口可以与Docker的守护进程进行交互,从而指示后台进行相关操作。 \r\n  ③docker daemon:表示Docker的服务端组件,他是Docker架构中运行在后台的一个守护进程,可以接收并处理来自命令行接口及API接口的指令,然后进行相应的后台操作。 \r\n  对于开发者而言,既可以使用编写好的脚本文件通过REST API来实现与Docker进程交互,又可以直接使用Docker相关指令通过命令行接口来与Docker进程交互,而其他一些Docker应用则是通过底层的API和CLI进行交互的。\r\n\r\n**2. Docker的架构介绍**\r\n\r\n  了解了Docker内部引擎及作用后,我们还需要通过Docker的具体架构,来了解Docker的整个运行流程。接下来借助Docker官网的架构图来对Docker架构进行详细说明,如下图所示。\r\n![alt](/article_img/docker/2.jpg)\r\n  从图中可以看出,Docker架构主要包括Client、DOCKER_HOST和Register三部分,关于这三部分的具体说明如下。 \r\n&emsp;&emsp;**(1)Client(客户端)** \r\n  Client即Docker客户端,也就是上一小节Docker Engine中介绍的docker CLI。开发者通过这个客户端使用Docker的相关指令与Docker守护进程进行交互,从而进行Docker镜像的创建、拉取和运行等操作。 \r\n&emsp;&emsp;**(2)DOCKER_HOST(Docker主机)** \r\n  DOCKER_HOST即Docker内部引擎运行的主机,主要指Docker daemon(Docker守护进程)。可以通过Docker守护进程与客户端还有Docker的镜像仓库Registry进行交互,从而管理Images(镜像)和Containers(容器)等。 \r\n&emsp;&emsp;**(3)Registry(注册中心)** \r\n  Registry即Docker注册中心,实质就是Docker镜像仓库,默认使用的是Docker官方远程注册中心Docker Hub,也可以使用开发者搭建的本地仓库。Registry中包含了大量的镜像,这些镜像可以是官网基础镜像,也可以是其他开发者上传的镜像。 \r\n  我们在实际使用Docker时,除了会涉及到图中的三个主要部分外,还会涉及到很多Docker Objects(Docker对象),例如Images(镜像)、Containers(容器)、Networks(网络)、Volumes(数据卷)、Plugins(插件)等。其中常用的两个对象Image和Containers的说明如下。 \r\n  ①Images(镜像) \r\n  Docker 镜像就是一个只读的模板,包含了一些创建Docker容器的操作指令。通常情况下,一个Docker镜像是基于另一个基础镜像创建的,并且新创建的镜像会额外包含一些功能配置。例如:开发者可以依赖于一个Ubuntu的基础镜像创建一个新镜像,并可以在新镜像中安装Apache等软件或其它应用程序。 \r\n  ②Containers(容器) \r\n  Docker容器属于镜像的一个可运行实例(镜像与容器的关系其实与Java中的类与对象相似),开发者可以通过API接口或者CLI命令行接口来创建、运行、停止、移动、删除一个容器,也可以将一个容器连接到一个或多个网络中,将数据存储与容器进行关联。\r\n\r\n\r\n\r\n', '2018-12-03', null, '默认分类', '虚拟化容器,Docker,运行机制', '1', null);

  41. INSERT INTO `t_article` VALUES ('10', 'Docker常用客户端指令介绍', '  在使用Docker之前,首先会为对应的项目编写Dockerfile镜像构建文件,然后通过Docker的相关指令进行镜像构建,完成镜像的构建后,就可以使用这些项目镜像进行启动测试了。所以要想知道如何使用Docker来执行这些Dockerfile镜像构建文件,还需要学习Docker客户端的常用指令,本篇文章将对Docker客户端的常用指令进行详细讲解。 \r\n\r\n**1.列出镜像** \r\n  通过docker images指令可以查看本地镜像列表中已有的镜像,具体使用方式如下。\r\n```js\r\n$ docker images\r\n``` \r\n  执行上述指令后,系统会将所有本地镜像都展示出来,如下图所示。\r\n![alt](/article_img/docker/3.png)\r\n  从图中可以看出,系统终端将本地镜像列表中的3个镜像分5列进行了展示,每一列的具体含义如下。 \r\n  ●REPOSITORY:镜像名称。 \r\n  ●TAG:镜像的参数,类似于版本号,默认是latest。 \r\n  ●IMAGE ID:镜像ID,是唯一值。此处看到的是一个长度为12的字符串,实际上它是64位完整镜像ID的缩写形式。 \r\n  ●CREATED:距今创建镜像的时间。 \r\n  ●SIZE:镜像大小。 \r\n\r\n**2.搜索镜像** \r\n  想知道在Docker Hub中包含了哪些镜像,除了可以登录Docker Hub,在官网中心进行查看外,还可以直接在Docker客户端进行查询。例如想要查询Ubuntu镜像,可以使用如下指令。\r\n```js\r\n$ docker search ubuntu\r\n``` \r\n  执行上述指令后,系统终端就会将搜索到的有关Ubuntu的镜像展示出来,如下图所示。\r\n![alt](/article_img/docker/4.png)\r\n  从图所示的结果可以看出,系统终端分5列将搜索到的Ubuntu相关镜像都列举出来了,这5列的具体含义如下。 \r\n  ●NAME:表示镜像的名称,这里有两种格式的名称,其中不带有“/”的表示官方镜像,而带有“/”的表示其他用户的公开镜像。公开镜像“/”前面是用户在Docker Hub上的用户名(唯一),后面是对应的镜像名;官方镜像与用户镜像,除了从镜像名称上可以区分外,还可以通过第4列的OFFICIAL声明中看出(该列下内容为OK表示官方镜像)。 \r\n  ●DESCRIPTION:表示镜像的描述,这里只显示了一小部分。 \r\n  ●STARS:表示该镜像的收藏数,用户可以在Docker Hub上对镜像进行收藏,一般可以通过该数字反应出该镜像的受欢迎程度。 \r\n  ●OFFICIAL:表示是否为官方镜像。 \r\n  ●AUTOMATED:表示是否自动构建镜像。例如,用户可以将自己的Docker Hub绑定到如Github上,当代码提交更新后,可以自动构建镜像。 \r\n \r\n**3.拉取镜像** \r\n  通过docker pull指令可以拉取仓库镜像到本地(默认都是拉取Docker Hub仓库镜像,也可以指定“IP+端口”拉取某个Docker机器上的私有仓库镜像),具体使用方式如下。\r\n```js\r\n$ docker pull ubuntu\r\n``` \r\n  执行上述指令后,Docker会自动从Docker Hub上下载最新版本的Ubuntu到本地,当然也可以使用以下指令拉取指定版本的镜像到本地,具体指令如下。\r\n```js\r\n$ docker pull ubuntu:14.04\r\n``` \r\n**4.构建镜像** \r\n  除了可以通过docker pull指令从仓库拉取镜像外,还可以通过docker build指令构建Docker镜像,通常情况下都是通过Dockerfile文件来构建镜像的。 \r\n  这里假设linux系统home目录下/shitou/workspace/dockerspace文件夹中编写有对应的Dockerfile文件,则构建镜像直立示例如下所示。 \r\n```js\r\n$ docker build -t hellodocker3 /home/shitou/workspace/dockerspace/.\r\n```\r\n**5.删除镜像** \r\n  当本地存放过多不需要的镜像时,可以通过docker rmi指令将其删除。在删除镜像时,需要指定镜像名称或镜像ID。删除镜像的使用方式如下。\r\n```js\r\n$ docker rmi -f hellodocker2 hellodocker3\r\n``` \r\n  上述指令中,docker rmi表示删除镜像,-f表示进行强制删除,而hellodocker2和hellodocker3分别表示需要删除的镜像名称,这里同时删除两个镜像。除了根据名称删除镜像外,还也可以根据镜像ID来删除镜像,只是这里如果指定了删除ID为23c617a866d4的镜像后,会同时删除hellodocker、hellodocker2和hellodocker3三个镜像。 \r\n  需要特别强调的是,在进行镜像删除操作时,如果是通过镜像ID进行镜像删除,那么由该镜像创建的容器必须提前删除或停止。另外,在通过镜像名称操作镜像时,如果出现镜像重名的情况,必须在镜像名称后面指定镜像标签tag参数来确保唯一性。\r\n\r\n**6.创建并启动容器** \r\n  Docker镜像主要用于创建容器,可以使用docker run指令创建并启动容器,具体使用方式如下。\r\n```js\r\n$ docker run -d -p 5000:80 --name test hellodocker\r\n``` \r\n  上述创建并启动容器的指令略微复杂,具体分析如下。 \r\n  ●docker run:表示创建并启动一个容器,而后面的hellodocker就表示要启动运行的镜像名称; \r\n  ●-d:表示容器启动时在后台运行; \r\n  ●-p 5000:80:表示将容器内暴露的80端口映射到宿主机指定的5000端口,也可以将-p 5000:80更改为-P来映射主机的随机端口(注意p字母的大小写); \r\n  ●--name test:表示为创建后的容器指定名称为test,如果没有该参数配置,则生成的容器会设置一个随机名称。 \r\n  docker run命令是Docker操作中较为复杂的一个,它可以携带多个参数和参数,我们可以通过docker run --help指令进行查看,其中有些参数如-e、-v和-w等都可以在Dockerfile文件中预先声明。 \r\n \r\n**7.列出容器** \r\n  生成容器后,可以通过docker ps指令查看当前运行的所有容器,具体使用方式如下。\r\n```js\r\n$ docker ps\r\n``` \r\n  执行上述命令后,会将所有当前运行的容器都展示出来,具体如下图所示。\r\n![alt](/article_img/docker/5.png)\r\n  从图中可以看出,系统终端通过7列对当前的正在运行的一个容器进行了展示,图中每一列的具体含义如下。 \r\n  ●CONTAINER ID:表示生成的容器ID; \r\n  ●IMAGE:表示生成该容器的镜像名称; \r\n  ●COMMAND:表示启动容器时运行的命令,Docker要求在启动容器时必须运行一个命令; \r\n  ●CREATED:表示容器创建的时间; \r\n  ●STATUS:表示容器运行状态,例如Up表示运行中,Exited表示已停止; \r\n  ●PORTS:表示容器内部暴露的端口映射到主机的端口; \r\n  ●NAMES:表示生成容器的名称,由Docker引擎自动生成,可以像上述示例中使用--name参数指定生成容器的名称。 \r\n  另外,docker ps指令运行过程中可以指定多个参数,还可以通过docker ps --help指令对ps指令的其他信息进行查看。\r\n\r\n**8.删除容器** \r\n  当不需要使用容器时,则可以使用docker rm指令删除已停止的容器,具体使用方式如下。\r\n```js\r\n$ docker rm f0c9a8b6e8c5\r\n``` \r\n  需要注意的是,上述指令只能删除已经停止运行的容器,而不能删除正在运行的容器。如果想要删除正在运行的容器,则需要添加-f参数强制删除,具体使用方式如下。\r\n```js\r\n$ docker rm -f f0c9a8b6e8c5\r\n``` \r\n  当需要删除的容器过多时,如果还一个个的删除就略显麻烦了,此时可以通过如下指令将全部容器删除。\r\n```js\r\n$ docker rm -f $(docker ps -aq)\r\n``` \r\n  上述指令中,首先会通过$(docker ps -aq)获取所有容器的ID,然后通过docker rm -f指令进行强制删除。如果开发者有自己特殊的删除需求,可以根据前面docker ps指令进行组装来获取需要删除的容器ID。 \r\n  Docker提供的操作指令远不止这些,这里就不一一列举了,想要了解更多Docker的操作指令,可以通过docker --help指令进行查看。\r\n\r\n\r\n', '2018-12-05', null, '默认分类', 'Docker,客户端指令', '1', null);

  42. INSERT INTO `t_article` VALUES ('11', 'Docker数据管理介绍', '  当我们对容器进行相关操作时,产生的一系列数据都会存储在容器中,而Docker内部又是如果管理这些数据的呢?本篇文章将针对Docker数据管理的一些知识进行介绍。\r\n \r\n**1. Docker数据存储机制** \r\n  使用Docker时,我们操作的都是镜像和由镜像生成的容器,所以想要更好的了解Docker内部的数据存储机制,就必须从镜像、容器与数据存储的关系出发。 \r\n  Docker镜像是通过读取Dockerfile文件中的指令构建的,Dockerfile中的每条指令都会创建一个镜像层,并且每层都是只读的,这一系列的镜像层就构成了Docker镜像。接下来以一个Dockerfile文件为例进行说明,具体如下列代码示例所示。\r\n```js\r\n 1 FROM ubuntu:16.04\r\n 2 COPY . /app\r\n 3 RUN make /app\r\n 4 CMD python /app/app.py\r\n``` \r\n   上述文件示例中的Dockerfile包含了4条指令,每条指令都会创建一个镜像层,其中每一层与前一层都有所不同,并且是层层叠加的。通过镜像构建容器时,会在镜像层上增加一个容器层(即可写层),所有对容器的更改都会写入容器层,这也是Docker默认的数据存储方式。 \r\n  下面通过一个效果图进行说明,具体如下图所示。\r\n![alt](/article_img/docker/6.png)\r\n  从图中可以看出,Docker容器和镜像之间的主要区别是顶部的容器层,而所有对容器中数据的添加、修改等操作都会被存储在容器层中。当容器被删除时,容器层也会被删除,其中存储的数据会被一同删除,而下面的镜像层却保持不变。 \r\n  由于所有的容器都是通过镜像构建的,所以每个容器都有各自的容器层,对于容器数据的更改就会保存在各自的容器层中。也就是说,由同一个镜像构建的多个容器,它们会拥有相同的底部镜像层,而拥有不同的容器层,多个容器可以访问相同的镜像层,并且有自己的独立数据状态。具体说明如下图所示。 \r\n![alt](/article_img/docker/7.png)\r\n  从图中可以看出,基于同一个镜像构建的多个容器可以共享该镜像层,但是多个容器想要共享相同的数据,就需要将这些数据存储到容器之外的地方,而这种方式就是下一节要提到的Docker volume数据外部挂载机制。 \r\n\r\n**2. Docker数据存储方式** \r\n  在默认情况下,Docker中的数据都是存放在容器层的,但是这样存储数据却有较多的缺陷,具体表现如下。 \r\n  ●当容器不再运行时,容器中的数据无法持久化保存,如果另一个进程需要这些数据,那么将很难从容器中获取数据; \r\n  ●容器层与正在运行的主机紧密耦合,不能轻易地移动数据; \r\n  ●容器层需要一个存储驱动程序来管理文件系统,存储驱动程序提供了一个使用Linux内核的联合文件系统,这种额外的抽象化降低了性能。 \r\n  基于上述种种原因,多数情况下Docker数据管理都不会直接将数据写入容器层,而是使用另一种叫做Docker volume数据外部挂载的机制进行数据管理。 \r\n  针对Docker volume数据外部挂载机制,Docker提供了三种不同的方式将数据从容器映射到Docker宿主机,他们分别为:volumes(数据卷)、bind mounts(绑定挂载)和tmpfs mounts(tmpfs挂载)。这三种数据管理方式的具体选择,需要结合实际情况进行考虑,其中的volumes数据卷是最常用也是官方推荐的数据管理方式。无论选择使用哪种数据管理方式,数据在容器内看起来都一样的,而在容器外则会被被挂载到文件系统中的某个目录或文件中。 \r\n  下面通过一张图来展示数据卷、绑定挂载和tmpfs挂载之间的差异,如下图所示。 \r\n![alt](/article_img/docker/8.png)\r\n  从图中可以看出,Docker提供的三种数据管理方式略有不同,具体分析如下。 \r\n  ●volumes:存储在主机文件系统中(在Linux系统下是存在于/var/lib/Docker/volumes/目录),并由Docker管理,非Docker进程无法修改文件系统的这个部分。 \r\n  ●bind mounts:可以存储在主机系统的任意位置,甚至可能是重要的系统文件或目录,在Docker主机或容器上的非Docker进程可以对他们进行任意修改。 \r\n  ●tmpfs mounts:只存储在主机系统的内存中,并没有写入到主机的文件系统中。\r\n\r\n\r\n', '2018-12-07', null, '默认分类', 'Docker,数据管理', '1', null);

  43. INSERT INTO `t_article` VALUES ('12', 'Spring Boot 2 权威发布', '  如果这两天登录 [https://start.spring.io/ ](https://start.spring.io/ )就会发现,Spring Boot 默认版本已经升到了 2.1.0。这是因为 Spring Boot 刚刚发布了 2.1.0 版本,我们来看下 Spring Boot 2 发布以来第一个子版本都发布了哪些内容? \r\n\r\n**2.1 中的新特性** \r\n  ●将spring-boot-starter-oauth2-oidc-client重命名为spring-boot-starter-oauth2-client命名更简洁 \r\n  ●添加 OAuth2 资源服务 starter,OAuth2 一个用于认证的组件 \r\n  ●支持 ConditionalOnBean 和 ConditionalOnMissingBean 下的参数化容器 \r\n  ●自动配置 applicationTaskExecutor bean 的延迟加载来避免不必要的日志记录 \r\n  ●将 DatabaseDriver#SAP 重命名为 DatabaseDriver \r\n  ●跳过重启器不仅适用于 JUnit4,也适用于 JUnit5 \r\n  ●在 Jest HealthIndicator 中使用集群端点 \r\n  ●当 DevTools 禁用重启时添加日志输出 \r\n  ●添加注解:@ConditionalOnMissingServletFilter提高 Servlet Filters 的自动装配。\r\n \r\n**2.1 中的组件升级** \r\n  ●升级 Hibernate 5.3,Hibernate 的支持升级到了 5.3 \r\n  ●升级 Tomcat 9 ,支持最新的 tomcat 9 \r\n  ●支持 Java 11,Java 现在更新越来越快,Spring 快赶不上了 \r\n  ●升级 Thymeleaf Extras Springsecurity 到 3.0.4.RELEASE ,thymeleaf-extras-springsecurity 是 Thymeleaf 提供集成 Spring Security 的组件 \r\n  ●升级 Joda Time 2.10.1,Joda-Time, 面向 Java 应用程序的日期/时间库的替代选择,Joda-Time 令时间和日期值变得易于管理、操作和理解。 \r\n  ●升级 Lettuce 5.1.2.RELEASE ,Lettuce 前面说过,传说中 Redis 最快的客户端。 \r\n  ●升级 Reactor Californium-SR2 ,Californium 是物联网云服务的 Java COAP 实现。因此,它更专注的是可扩展性和可用性而不是像嵌入式设备那样关注资源效率。不过,Californium 也适合嵌入式的 JVM。 \r\n  ●升级 Maven Failsafe Plugin 2.22.1 ,Maven 中的测试插件。 \r\n  ●升级 Flyway 5.2.1 , Flyway是一款开源的数据库版本管理工具 \r\n  ●升级 Aspectj 1.9.2 ,AspectJ 是 Java 中流行的 AOP(Aspect-oriented Programming)编程扩展框架,是 Eclipse 托管给 Apache 基金会的一个开源项目。 \r\n  ●升级 Mysql 8.0.13 ,Mysql 支持到 8。 \r\n  ●... \r\n&emsp;&emsp;更多的详细内容可以参考这里:[Spring Boot 2.1 Release Notes](https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.1-Release-Notes)\r\n', '2018-12-12', null, '默认分类', 'Spring Boot 2', '1', null);

  44. -- ----------------------------

  45. -- Table structure for `t_comment`

  46. -- ----------------------------

  47. DROP TABLE IF EXISTS `t_comment`;

  48. CREATE TABLE `t_comment` (

  49. `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '评论id',

  50. `article_id` int(11) NOT NULL COMMENT '关联的文章id',

  51. `created` date NOT NULL COMMENT '评论时间',

  52. `ip` varchar(200) DEFAULT NULL COMMENT '评论用户登录的ip地址',

  53. `content` text NOT NULL COMMENT '评论内容',

  54. `status` varchar(200) NOT NULL DEFAULT 'approved' COMMENT '评论状态',

  55. `author` varchar(200) NOT NULL COMMENT '评论用户用户名',

  56. PRIMARY KEY (`id`)

  57. ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

  58. -- ----------------------------

  59. -- Records of t_comment

  60. -- ----------------------------

  61. INSERT INTO `t_comment` VALUES ('1', '12', '2018-12-13', '0:0:0:0:0:0:0:1', '很不错,不过这文章排版不太好看啊', 'approved', '李四');

  62. INSERT INTO `t_comment` VALUES ('2', '11', '2018-12-13', '0:0:0:0:0:0:0:1', '很不错的原理分析,受用了!', 'approved', '李四');

  63. INSERT INTO `t_comment` VALUES ('3', '10', '2018-12-13', '0:0:0:0:0:0:0:1', '关于Docker虚拟容器的讲解挺好的额,学习中', 'approved', '李四');

  64. INSERT INTO `t_comment` VALUES ('9', '1', '2018-12-13', '0:0:0:0:0:0:0:1', '非常不错,赞一个!', 'approved', '李四');

  65. INSERT INTO `t_comment` VALUES ('10', '1', '2018-12-13', '0:0:0:0:0:0:0:1', '博主,这资料怎么弄的?有相关资源和教材推荐吗?', 'approved', '李四');

  66. INSERT INTO `t_comment` VALUES ('11', '1', '2018-12-13', '0:0:0:0:0:0:0:1', '很详细,感谢...', 'approved', '东方不败');

  67. INSERT INTO `t_comment` VALUES ('12', '1', '2018-12-13', '0:0:0:0:0:0:0:1', '很全,努力学习中...', 'approved', '东方不败');

  68. INSERT INTO `t_comment` VALUES ('13', '1', '2018-12-13', '0:0:0:0:0:0:0:1', '好东西,先收藏起来,哈哈', 'approved', 'tom');

  69. INSERT INTO `t_comment` VALUES ('14', '8', '2018-12-13', '0:0:0:0:0:0:0:1', 'very good blog', 'approved', 'tom');

  70. -- ----------------------------

  71. -- Table structure for `t_authority`

  72. -- ----------------------------

  73. DROP TABLE IF EXISTS `t_authority`;

  74. CREATE TABLE `t_authority` (

  75. `id` int(11) NOT NULL AUTO_INCREMENT,

  76. `authority` varchar(200) DEFAULT NULL COMMENT '权限',

  77. PRIMARY KEY (`id`)

  78. ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

  79. -- ----------------------------

  80. -- Records of t_authority

  81. -- ----------------------------

  82. INSERT INTO `t_authority` VALUES ('1', 'ROLE_admin');

  83. INSERT INTO `t_authority` VALUES ('2', 'ROLE_common');

  84. -- ----------------------------

  85. -- Table structure for `t_statistic`

  86. -- ----------------------------

  87. DROP TABLE IF EXISTS `t_statistic`;

  88. CREATE TABLE `t_statistic` (

  89. `id` int(11) NOT NULL AUTO_INCREMENT,

  90. `article_id` int(11) NOT NULL COMMENT '关联的文章id',

  91. `hits` int(11) NOT NULL DEFAULT '0' COMMENT '文章点击总量',

  92. `comments_num` int(11) NOT NULL DEFAULT '0' COMMENT '文章评论总量',

  93. PRIMARY KEY (`id`)

  94. ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

  95. -- ----------------------------

  96. -- Records of t_statistic

  97. -- ----------------------------

  98. INSERT INTO `t_statistic` VALUES ('1', '1', '91', '5');

  99. INSERT INTO `t_statistic` VALUES ('2', '2', '3', '0');

  100. INSERT INTO `t_statistic` VALUES ('3', '3', '4', '0');

  101. INSERT INTO `t_statistic` VALUES ('4', '4', '4', '0');

  102. INSERT INTO `t_statistic` VALUES ('5', '5', '4', '0');

  103. INSERT INTO `t_statistic` VALUES ('6', '6', '15', '0');

  104. INSERT INTO `t_statistic` VALUES ('7', '7', '6', '0');

  105. INSERT INTO `t_statistic` VALUES ('8', '8', '23', '1');

  106. INSERT INTO `t_statistic` VALUES ('9', '9', '18', '0');

  107. INSERT INTO `t_statistic` VALUES ('10', '10', '18', '1');

  108. INSERT INTO `t_statistic` VALUES ('11', '11', '10', '1');

  109. INSERT INTO `t_statistic` VALUES ('12', '12', '39', '1');

  110. -- ----------------------------

  111. -- Table structure for `t_user`

  112. -- ----------------------------

  113. DROP TABLE IF EXISTS `t_user`;

  114. CREATE TABLE `t_user` (

  115. `id` int(11) NOT NULL AUTO_INCREMENT,

  116. `username` varchar(200) DEFAULT NULL,

  117. `password` varchar(200) DEFAULT NULL,

  118. `email` varchar(200) DEFAULT NULL,

  119. `created` date DEFAULT NULL,

  120. `valid` tinyint(1) NOT NULL DEFAULT '1',

  121. PRIMARY KEY (`id`)

  122. ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

  123. -- ----------------------------

  124. -- Records of t_user

  125. -- ----------------------------

  126. INSERT INTO `t_user` VALUES ('1', 'admin', '$2a$10$5ooQI8dir8jv0/gCa1Six.GpzAdIPf6pMqdminZ/3ijYzivCyPlfK', '2127269781@qq.com', '2018-10-01', '1');

  127. INSERT INTO `t_user` VALUES ('2', '李四', '$2a$10$5ooQI8dir8jv0/gCa1Six.GpzAdIPf6pMqdminZ/3ijYzivCyPlfK', '1768653040@qq.com', '2018-11-13', '1');

  128. INSERT INTO `t_user` VALUES ('3', '东方不败', '$2a$10$5ooQI8dir8jv0/gCa1Six.GpzAdIPf6pMqdminZ/3ijYzivCyPlfK', '13718391550@163.com', '2018-12-18', '1');

  129. INSERT INTO `t_user` VALUES ('4', 'tom', '$2a$10$5ooQI8dir8jv0/gCa1Six.GpzAdIPf6pMqdminZ/3ijYzivCyPlfK', 'asexeees@sohu.com', '2018-12-03', '1');

  130. -- ----------------------------

  131. -- Table structure for `t_user_authority`

  132. -- ----------------------------

  133. DROP TABLE IF EXISTS `t_user_authority`;

  134. CREATE TABLE `t_user_authority` (

  135. `id` int(11) NOT NULL AUTO_INCREMENT,

  136. `user_id` int(11) NOT NULL COMMENT '关联的用户id',

  137. `authority_id` int(11) NOT NULL COMMENT '关联的权限id',

  138. PRIMARY KEY (`id`)

  139. ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

  140. -- ----------------------------

  141. -- Records of t_user_authority

  142. -- ----------------------------

  143. INSERT INTO `t_user_authority` VALUES ('1', '1', '1');

  144. INSERT INTO `t_user_authority` VALUES ('2', '2', '2');

  145. INSERT INTO `t_user_authority` VALUES ('3', '3', '2');

  146. INSERT INTO `t_user_authority` VALUES ('4', '4', '2');

 
  1. # 选择使用数据库

  2. USE blog_system;

  3. # 创建表t_customer并插入相关数据

  4. DROP TABLE IF EXISTS `t_customer`;

  5. CREATE TABLE `t_customer` (

  6. `id` int(20) NOT NULL AUTO_INCREMENT,

  7. `username` varchar(200) DEFAULT NULL,

  8. `password` varchar(200) DEFAULT NULL,

  9. `valid` tinyint(1) NOT NULL DEFAULT '1',

  10. PRIMARY KEY (`id`)

  11. ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

  12. INSERT INTO `t_customer` VALUES ('1', 'admin', '$2a$10$5ooQI8dir8jv0/gCa1Six.GpzAdIPf6pMqdminZ/3ijYzivCyPlfK', '1');

  13. INSERT INTO `t_customer` VALUES ('2', '李四', '$2a$10$5ooQI8dir8jv0/gCa1Six.GpzAdIPf6pMqdminZ/3ijYzivCyPlfK', '1');

  14. # 创建表t_authority并插入相关数据

  15. DROP TABLE IF EXISTS `t_authority`;

  16. CREATE TABLE `t_authority` (

  17. `id` int(20) NOT NULL AUTO_INCREMENT,

  18. `authority` varchar(20) DEFAULT NULL,

  19. PRIMARY KEY (`id`)

  20. ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

  21. INSERT INTO `t_authority` VALUES ('1', 'ROLE_admin');

  22. INSERT INTO `t_authority` VALUES ('2', 'ROLE_common');

  23. # 创建表t_customer_authority并插入相关数据

  24. DROP TABLE IF EXISTS `t_customer_authority`;

  25. CREATE TABLE `t_customer_authority` (

  26. `id` int(20) NOT NULL AUTO_INCREMENT,

  27. `customer_id` int(20) DEFAULT NULL,

  28. `authority_id` int(20) DEFAULT NULL,

  29. PRIMARY KEY (`id`)

  30. ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;

  31. INSERT INTO `t_customer_authority` VALUES ('1', '1', '1');

  32. INSERT INTO `t_customer_authority` VALUES ('2', '2', '2');

  33. # 记住我功能中创建持久化Token存储的数据表

  34. create table persistent_logins (username varchar(64) not null,

  35. series varchar(64) primary key,

  36. token varchar(64) not null,

  37. last_used timestamp not null);

 
  1. /*

  2. Navicat MySQL Data Transfer

  3. Source Server : localhost

  4. Source Server Version : 80031

  5. Source Host : 127.0.0.1:3306

  6. Source Database : blog_system

  7. Target Server Type : MYSQL

  8. Target Server Version : 80031

  9. File Encoding : 65001

  10. Date: 2023-10-30 15:59:43

  11. */

  12. SET FOREIGN_KEY_CHECKS=0;

  13. -- ----------------------------

  14. -- Table structure for t_schedule_email

  15. -- ----------------------------

  16. DROP TABLE IF EXISTS `t_schedule_email`;

  17. CREATE TABLE `t_schedule_email` (

  18. `id` int NOT NULL AUTO_INCREMENT,

  19. `toaddress` varchar(255) DEFAULT NULL COMMENT '收件人邮箱地址',

  20. `schedule` bigint DEFAULT NULL COMMENT '定时参数(5位数字,第1位是星期几,第2-3位是几时,第4-5位是几分',

  21. `subject` varchar(255) DEFAULT NULL COMMENT '邮件主题',

  22. `content` text COMMENT '邮件内容',

  23. `status` char(1) DEFAULT NULL COMMENT '0未发送 1发送成功 2发送失败',

  24. `error` text COMMENT '若邮件发送失败,存储失败具体信息',

  25. `create_time` datetime DEFAULT NULL COMMENT '记录创建时间',

  26. PRIMARY KEY (`id`)

  27. ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb3;

html文件

 
  1. <!--模板-->

  2. 欢迎 加入我们公司

  3. <table border="1" style="width:500px;">

  4. <tr>

  5. <td width="100">

  6. 姓名

  7. </td>

  8. <td>$input</td>

  9. </tr>

  10. <tr>

  11. <td>

  12. 职位

  13. </td>

  14. <td>JAVA开发工程师</td>

  15. </tr>

  16. <tr>

  17. <td>

  18. 职称

  19. </td>

  20. <td>高级工程师</td>

  21. </tr>

  22. <tr>

  23. <td>

  24. 部门

  25. </td>

  26. <td>研发部</td>

  27. </tr>

  28. </table>

  29. <p>

  30. 希望在未来的日子里,携手共进!

  31. </p>

 
  1. <!--附件邮件-->

  2. <script>

  3. let filesize=-1;

  4. let maxsize=10*1024*1024;//最大文件大小 10M

  5. function checkform(){

  6. console.log('1111');

  7. let address=val('to');

  8. if(address==''){

  9. alert('邮箱不能为空');

  10. return false;

  11. }

  12. if(!isEmail(address)){

  13. alert('邮箱格式输入错误');

  14. return false;

  15. }

  16. if(val('subject')==''){

  17. alert('邮件主题不能为空');

  18. return false;

  19. }

  20. let file=$('#file')[0];

  21. if(file.value==''||filesize==-1){

  22. alert('附件不能为空');

  23. return false;

  24. }

  25. //获取文件扩展名

  26. let ext=file.value;

  27. ext=ext.lastIndexOf('.')<0?ext:(ext.substring(ext.lastIndexOf('.')+1));

  28. ext=ext.toLowerCase();

  29. if(ext!='txt' && ext!='doc' && ext!='pptx')

  30. {

  31. alert('请选择正确的附件类型');

  32. return false;

  33. }

  34. if(filesize>maxsize){

  35. alert('附件大小不能超过10M');

  36. return false;

  37. }

  38. return true;

  39. }

  40. //获取上传文件大小

  41. function filechange(e){

  42. filesize=e.target.files[0].size;

  43. }

  44. </script>

  45. <form method="post" th:action="@{/email/sendAttachment}" onsubmit="return checkform();" enctype="multipart/form-data">

  46. <input type="hidden" name="page" value="2"/>

  47. <div>

  48. <label for="to">邮箱地址(收件人):</label>

  49. <input id="to" name="to" type="text">

  50. </div>

  51. <div>

  52. <label for="subject">邮件主题:</label>

  53. <input id="subject" name="subject" type="text">

  54. </div>

  55. <div>

  56. <label for="content">附件:</label>

  57. <input id="file" name="file" type="file" onchange="filechange(event)">

  58. </div>

  59. <button type="submit">发送邮件</button>

  60. </form>

 
  1. <!--图片邮件-->

  2. <script>

  3. let filesize=-1;

  4. let maxsize=1*1024*1024;//最大文件大小 1M

  5. function checkform(){

  6. let address=val('to');

  7. if(address==''){

  8. alert('邮箱不能为空');

  9. return false;

  10. }

  11. if(!isEmail(address)){

  12. alert('邮箱格式输入错误');

  13. return false;

  14. }

  15. if(val('subject')==''){

  16. alert('邮件主题不能为空');

  17. return false;

  18. }

  19. let file=$('#file')[0];

  20. if(file.value==''||filesize==-1){

  21. alert('图片不能为空');

  22. return false;

  23. }

  24. //获取文件扩展名

  25. let ext=file.value;

  26. ext=ext.lastIndexOf('.')<0?ext:(ext.substring(ext.lastIndexOf('.')+1));

  27. ext=ext.toLowerCase();

  28. if(ext!='jpg' /*&& ext!='gif' && ext!='png' && ext!='bmp' && ext!='jpeg'*/)

  29. {

  30. alert('请选择正确的图片类型');

  31. return false;

  32. }

  33. if(filesize>maxsize){

  34. alert('图片大小不能超过1M');

  35. return false;

  36. }

  37. return true;

  38. }

  39. //获取上传文件大小

  40. function filechange(e){

  41. filesize=e.target.files[0].size;

  42. }

  43. </script>

  44. <form method="post" th:action="@{/email/sendImage}" onsubmit="return checkform();" enctype="multipart/form-data">

  45. <input type="hidden" name="page" value="3"/>

  46. <div>

  47. <label for="to">邮箱地址(收件人):</label>

  48. <input id="to" name="to" type="text">

  49. </div>

  50. <div>

  51. <label for="subject">邮件主题:</label>

  52. <input id="subject" name="subject" type="text">

  53. </div>

  54. <div>

  55. <label for="content">图片:</label>

  56. <input id="file" name="file" type="file" onchange="filechange(event)">

  57. </div>

  58. <button type="submit">发送邮件</button>

  59. </form>

 
  1. <!--邮件功能主页-->

  2. <!DOCTYPE html>

  3. <html xmlns:th="http://www.thymeleaf.org">

  4. <head>

  5. <meta charset="UTF-8">

  6. <title>邮件管理</title>

  7. <link href="/user/css/emailTemplate.css" rel="stylesheet">

  8. <script th:src="@{/assets/js/jquery.min.js}"></script>

  9. <style>

  10. .btn-panel{

  11. display: flex;

  12. align-items: center;

  13. justify-content: center;

  14. }

  15. button {

  16. margin-right: 10px;

  17. }

  18. button.selected{

  19. background-color: red;

  20. }

  21. </style>

  22. <script>

  23. function show(c){

  24. location.href='index?page='+c;

  25. }

  26. function val(id){

  27. return trim($('#'+id).val());

  28. }

  29. function trim(str) {

  30. return str.replace(/(^\s*)|(\s*$)/g, '');

  31. }

  32. function isEmail(v){

  33. let reg = /^[a-zA-Z0-9]+([-_.][A-Za-zd]+)*@([a-zA-Z0-9]+[-.])+[A-Za-zd]{2,5}$/

  34. return reg.test(v);

  35. }

  36. window.onload=function(){

  37. let message='[[${message}]]';

  38. if(message!='')

  39. alert(message);

  40. }

  41. </script>

  42. </head>

  43. <body>

  44. <div class="btn-panel">

  45. <button onclick="show(1)" th:class="${page=='1'?'selected':''}">发纯文本邮件</button>

  46. <button onclick="show(2)" th:class="${page=='2'?'selected':''}">发附件邮件</button>

  47. <button onclick="show(3)" th:class="${page=='3'?'selected':''}">发图片邮件</button>

  48. <button onclick="show(4)" th:class="${page=='4'?'selected':''}">发模板邮件</button>

  49. <button onclick="show(5)" th:class="${page=='5'?'selected':''}">发定时邮件</button>

  50. </div>

  51. <div class="page-content" th:switch="${page}">

  52. <th:block th:case="'1'" th:include="client/email/text"></th:block>

  53. <th:block th:case="'2'" th:include="client/email/attach"></th:block>

  54. <th:block th:case="'3'" th:include="client/email/image"></th:block>

  55. <th:block th:case="'4'" th:include="client/email/template"></th:block>

  56. <th:block th:case="'5'" th:include="client/email/schedule"></th:block>

  57. <th:block th:case="*">

  58. <div style="text-align: center;padding:20px;">请选择功能</div>

  59. </th:block>

  60. </div>

  61. </body>

  62. </html>

 
  1. <!--定时邮件-->

  2. <script>

  3. function checkform(){

  4. let address=val('to');

  5. if(address==''){

  6. alert('邮箱不能为空');

  7. return false;

  8. }

  9. if(!isEmail(address)){

  10. alert('邮箱格式输入错误');

  11. return false;

  12. }

  13. if(val('week')==''||val('hour')==''||val('minute')==''){

  14. alert('请设置定时发送时间');

  15. return false;

  16. }

  17. return true;

  18. }

  19. </script>

  20. <form method="post" th:action="@{/email/sendScheduledText}" onsubmit="return checkform();">

  21. <input type="hidden" name="page" value="5"/>

  22. <div>

  23. <label for="to">邮箱地址(收件人):</label>

  24. <input id="to" name="to" type="text">

  25. </div>

  26. <div>

  27. <label for="subject">邮件内容:</label>

  28. 博客系统访问量信息

  29. </div>

  30. <div>

  31. <label for="content">定时:</label>

  32. <select id="week" name="week">

  33. <option value="">请选择..</option>

  34. <option value="1">星期一</option>

  35. <option value="2">星期二</option>

  36. <option value="3">星期三</option>

  37. <option value="4">星期四</option>

  38. <option value="5">星期五</option>

  39. <option value="6">星期六</option>

  40. <option value="7">星期天</option>

  41. </select>

  42. <input type="number" id="hour" name="hour" min="0" max="23" maxlength="2" onchange="if(this.value>23) this.value=23">小时

  43. <input type="number" id="minute" name="minute" min="0" max="59" maxlength="2" onchange="if(this.value>59) this.value=59">分

  44. </div>

  45. <button type="submit">发送邮件</button>

  46. </form>

 
  1. <!--模板邮件-->

  2. <script>

  3. function checkform(){

  4. let address=val('to');

  5. if(address==''){

  6. alert('邮箱不能为空');

  7. return false;

  8. }

  9. if(!isEmail(address)){

  10. alert('邮箱格式输入错误');

  11. return false;

  12. }

  13. if(val('subject')==''){

  14. alert('邮件主题不能为空');

  15. return false;

  16. }

  17. /* if(!$('#templateEditor').attr('contenteditable')=='false'){

  18. alert('请选择模板邮件');

  19. return false;

  20. }*/

  21. let editor=$('#templateEditor');

  22. let empty=false;

  23. let value='';

  24. editor.find("input").each(function(){

  25. if($(this).val()==''){

  26. empty=true;

  27. //模板内的输入没有填写

  28. }else

  29. value=$(this).val();

  30. });

  31. if(empty){

  32. alert('请填写用户姓名');

  33. return false;

  34. }

  35. let html=editor.data('template');

  36. html=html.replaceAll('$input',value);

  37. $('#content').val(html);

  38. return true;

  39. }

  40. //加载模板

  41. function loadTemplate(name){

  42. if(name=='') return;

  43. $.get('/email/template?name='+name,function(html){

  44. let html2=html.replaceAll('$input',"<input type='text'/>");

  45. $('#templateEditor').html(html2).data('template',html)//设置编辑区的html内容

  46. });

  47. }

  48. $(function(){

  49. loadTemplate('resume');

  50. })

  51. </script>

  52. <form method="post" th:action="@{/email/sendTemplate}" onsubmit="return checkform();">

  53. <input type="hidden" name="page" value="4"/>

  54. <input type="hidden" id="content" name="content" />

  55. <div>

  56. <label for="to">邮箱地址(收件人):</label>

  57. <input id="to" name="to" type="text">

  58. </div>

  59. <div>

  60. <label for="subject">邮件主题:</label>

  61. <input id="subject" name="subject" type="text">

  62. </div>

  63. <!--<div>

  64. <label for="content">邮件模板:</label>

  65. <select id="templateSelect" onchange="loadTemplate(this.value)">

  66. <option value="">请选择...</option>

  67. <option value="resume">个人介绍</option>

  68. </select>

  69. </div>-->

  70. <div style="text-align: center">模板内容编辑</div>

  71. <div id="templateEditor" style="border:2px solid #aaa;height: 500px;padding:30px;" contenteditable="false"><div style="height: 100%;color:#9999;display: flex;align-items: center;justify-content: center">请选择模板</div></div>

  72. <button type="submit">发送邮件</button>

  73. </form>

 
  1. <!--纯文本邮件-->

  2. <script>

  3. function checkform(){

  4. let address=val('to');

  5. if(address==''){

  6. alert('邮箱不能为空');

  7. return false;

  8. }

  9. if(!isEmail(address)){

  10. alert('邮箱格式输入错误');

  11. return false;

  12. }

  13. if(val('subject')==''){

  14. alert('邮件主题不能为空');

  15. return false;

  16. }

  17. let content=val('content');

  18. if(content==''){

  19. alert('内容不能为空');

  20. return false;

  21. }

  22. if(content.length>200){

  23. alert('内容不能超过200字');

  24. return false;

  25. }

  26. return true;

  27. }

  28. </script>

  29. <form method="post" th:action="@{/email/sendText}" onsubmit="return checkform();">

  30. <input type="hidden" name="page" value="1"/>

  31. <div>

  32. <label for="to">邮箱地址(收件人):</label>

  33. <input id="to" name="to" type="text">

  34. </div>

  35. <div>

  36. <label for="subject">邮件主题:</label>

  37. <input id="subject" name="subject" type="text">

  38. </div>

  39. <div>

  40. <label for="content">邮件内容:</label>

  41. <textarea cols="50" name="content" id="content" rows="10"></textarea>

  42. </div>

  43. <button type="submit">发送邮件</button>

  44. </form>

代码,邮箱接口

 
  1. package com.itheima.dao;

  2. import com.itheima.model.domain.ScheduleEmail;

  3. import org.apache.ibatis.annotations.*;

  4. import java.util.List;

  5. /**

  6. * 邮件管理

  7. */

  8. @Mapper

  9. public interface EmailMapper {

  10. // 创建定时邮件

  11. @Insert("INSERT INTO t_schedule_email(toaddress, schedule, subject,content,status,error,create_time) VALUES(#{toaddress}, #{schedule}, #{subject},#{content},#{status},#{error},#{createTime})")

  12. @Options(useGeneratedKeys = true, keyProperty = "id")

  13. public int insertScheduledEmail(ScheduleEmail email);

  14. @Select("select * from t_schedule_email where status='0' and schedule>=#{schedule}")

  15. public List<ScheduleEmail> queryScheduledEmail(@Param("schedule") int schedule);

  16. @Update("update t_schedule_email set status=#{status} where id=#{id}")

  17. public void updateScheduledEmailStatus(@Param("id") int id,@Param("status") String status);

  18. @Update("update t_schedule_email set status=#{status},error=#{error} where id=#{id}")

  19. public void updateScheduledEmailStatusWithError(@Param("id") int id,@Param("status") String status, @Param("error") String error);

  20. }

 
  1. package com.itheima.domain;

  2. import java.util.Date;

  3. /**

  4. * @date 2023/10/26 1:50

  5. */

  6. public class ScheduleEmail {

  7. private int id;

  8. private String toaddress;

  9. private int schedule;

  10. private String subject;

  11. private String content;

  12. private String status;

  13. private String error;

  14. private Date createTime;

  15. public ScheduleEmail(){}

  16. public ScheduleEmail(String toaddress, int schedule, String subject, String content, String status, String error, Date createTime) {

  17. this.toaddress = toaddress;

  18. this.schedule = schedule;

  19. this.subject = subject;

  20. this.content = content;

  21. this.status = status;

  22. this.error = error;

  23. this.createTime = createTime;

  24. }

  25. public int getId() {

  26. return id;

  27. }

  28. public void setId(int id) {

  29. this.id = id;

  30. }

  31. public String getToaddress() {

  32. return toaddress;

  33. }

  34. public void setToaddress(String toaddress) {

  35. this.toaddress = toaddress;

  36. }

  37. public int getSchedule() {

  38. return schedule;

  39. }

  40. public void setSchedule(int schedule) {

  41. this.schedule = schedule;

  42. }

  43. public String getSubject() {

  44. return subject;

  45. }

  46. public void setSubject(String subject) {

  47. this.subject = subject;

  48. }

  49. public String getContent() {

  50. return content;

  51. }

  52. public void setContent(String content) {

  53. this.content = content;

  54. }

  55. public String getStatus() {

  56. return status;

  57. }

  58. public void setStatus(String status) {

  59. this.status = status;

  60. }

  61. public String getError() {

  62. return error;

  63. }

  64. public void setError(String error) {

  65. this.error = error;

  66. }

  67. public Date getCreateTime() {

  68. return createTime;

  69. }

  70. public void setCreateTime(Date createTime) {

  71. this.createTime = createTime;

  72. }

  73. }

 
  1. package com.itheima.server.impl;

  2. import com.itheima.dao.EmailMapper;

  3. import com.itheima.dao.StatisticMapper;

  4. import com.itheima.model.domain.ScheduleEmail;

  5. import com.itheima.server.IEmailService;

  6. import org.springframework.beans.factory.annotation.Autowired;

  7. import org.springframework.beans.factory.annotation.Value;

  8. import org.springframework.mail.MailException;

  9. import org.springframework.mail.SimpleMailMessage;

  10. import org.springframework.mail.javamail.JavaMailSender;

  11. import org.springframework.scheduling.annotation.Scheduled;

  12. import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;

  13. import org.springframework.scheduling.support.CronTrigger;

  14. import org.springframework.stereotype.Service;

  15. import org.springframework.transaction.annotation.Transactional;

  16. import javax.annotation.PostConstruct;

  17. import java.util.Calendar;

  18. import java.util.List;

  19. /**

  20. * @date 2023/10/26 1:45

  21. * 定时任务管理

  22. */

  23. /**

  24. * 邮件管理

  25. */

  26. @Service

  27. @Transactional

  28. public class EmailServiceImpl implements IEmailService {

  29. @Autowired

  30. private EmailMapper emailMapper;

  31. @Autowired

  32. private StatisticMapper statisticMapper;

  33. @Autowired

  34. private JavaMailSender mailSender;

  35. @Value("${spring.mail.username}")

  36. private String emailfrom;

  37. //spring 定时管理器

  38. private ThreadPoolTaskScheduler taskScheduler;

  39. private final static String[] WEEK_NAMES=new String[]{"MON","TUES","WED","THUR","FRI","SAT","SUN"};

  40. /**

  41. * 初始化spring定时管理器,并动态配置邮件定时器

  42. */

  43. @PostConstruct

  44. public void initTaskScheduler(){

  45. System.out.println("初始化邮件定时管理器....");

  46. taskScheduler=new ThreadPoolTaskScheduler();

  47. taskScheduler.initialize();

  48. //获取当前时间,然后查询当前时间点,以及之前为执行发送的邮件列表

  49. Calendar cal=Calendar.getInstance();

  50. int week=cal.get(Calendar.DAY_OF_WEEK);

  51. if(week==1){

  52. //如果0,则星期天,需要修改为7,与界面上设置的一致

  53. week=7;

  54. }else

  55. week-=1;

  56. int hour=cal.get(Calendar.HOUR_OF_DAY);

  57. int minute=cal.get(Calendar.MINUTE);

  58. int schedule=Integer.valueOf(week+(hour<10?"0":"")+hour+(minute<10?"0":"")+minute);

  59. List<ScheduleEmail> scheduledEmailList=emailMapper.queryScheduledEmail(schedule);

  60. if(scheduledEmailList.size()>0){

  61. System.out.println("动态配置邮件定时器....");

  62. for(ScheduleEmail email :scheduledEmailList){

  63. setupScheduledEmail(email);

  64. }

  65. }

  66. }

  67. //保存定时邮件

  68. @Override

  69. public void saveScheduledEmail(ScheduleEmail email) {

  70. emailMapper.insertScheduledEmail(email);

  71. setupScheduledEmail(email);

  72. }

  73. //发送定时邮件

  74. public void sendScheduledEmail(ScheduleEmail email){

  75. System.out.println("执行定时邮件发送 > "+email.getId()+" > "+email.getSchedule());

  76. SimpleMailMessage mailMessage = new SimpleMailMessage();

  77. mailMessage.setFrom(emailfrom);

  78. mailMessage.setTo(email.getToaddress());

  79. mailMessage.setSubject(email.getSubject());

  80. //mailMessage.setText(email.getContent());

  81. StringBuffer content = new StringBuffer();

  82. long totalvisit = statisticMapper.getTotalVisit();

  83. long totalComment = statisticMapper.getTotalComment();

  84. content.append("博客系统总访问量为:"+totalvisit+"人次").append("\n");

  85. content.append("博客系统总评论量为:"+totalComment+"人次").append("\n");

  86. mailMessage.setText(content.toString());

  87. String message=null;

  88. try {

  89. //发送邮件

  90. mailSender.send(mailMessage);

  91. emailMapper.updateScheduledEmailStatus(email.getId(),"1");

  92. } catch (MailException e) {

  93. emailMapper.updateScheduledEmailStatusWithError(email.getId(),"2","发送失败 > "+e.getMessage());

  94. }

  95. }

  96. //动态配置定时器

  97. public void setupScheduledEmail(ScheduleEmail email){

  98. String scheduleConfig=email.getSchedule()+"";

  99. int week=Integer.valueOf(scheduleConfig.substring(0,1));

  100. int hour=Integer.valueOf(scheduleConfig.substring(1,3));

  101. int minute=Integer.valueOf(scheduleConfig.substring(3));

  102. taskScheduler.schedule(new EmailSendTask(email),new CronTrigger("0 "+minute+" "+hour+" ? * "+WEEK_NAMES[week-1]));

  103. }

  104. public class EmailSendTask implements Runnable{

  105. private ScheduleEmail email;

  106. public EmailSendTask(ScheduleEmail email){

  107. this.email=email;

  108. }

  109. @Override

  110. public void run() {

  111. sendScheduledEmail(email);

  112. }

  113. }

  114. }

 
  1. package com.itheima.server;

  2. import com.itheima.model.domain.ScheduleEmail;

  3. import com.itheima.model.domain.User;

  4. /**

  5. * 邮件管理

  6. */

  7. public interface IEmailService {

  8. public void saveScheduledEmail(ScheduleEmail email);

  9. }

 
  1. package com.itheima.web.client;

  2. import com.itheima.model.domain.ScheduleEmail;

  3. import com.itheima.server.IEmailService;

  4. import org.springframework.beans.factory.annotation.Autowired;

  5. import org.springframework.beans.factory.annotation.Value;

  6. import org.springframework.mail.MailException;

  7. import org.springframework.mail.SimpleMailMessage;

  8. import org.springframework.mail.javamail.JavaMailSender;

  9. import org.springframework.mail.javamail.MimeMessageHelper;

  10. import org.springframework.stereotype.Controller;

  11. import org.springframework.web.bind.annotation.*;

  12. import org.springframework.web.multipart.MultipartFile;

  13. import javax.mail.MessagingException;

  14. import javax.mail.internet.MimeMessage;

  15. import javax.servlet.http.HttpServletRequest;

  16. import java.io.File;

  17. import java.util.Date;

  18. /**

  19. * @date 2023/11/23 1:35

  20. * 定时任务管理

  21. */

  22. /**

  23. * 邮件管理

  24. */

  25. @Controller

  26. @RequestMapping("/email")

  27. public class EmailSendController {

  28. @Autowired

  29. private JavaMailSender mailSender;

  30. @Autowired

  31. private IEmailService emailService;

  32. @Value("${spring.mail.username}")

  33. private String emailfrom;

  34. //访问首页

  35. @GetMapping("/index")

  36. public String index(HttpServletRequest request){

  37. String page=request.getParameter("page");//页面类型

  38. if(page!=null)

  39. request.setAttribute("page",page);

  40. return "client/email/index";

  41. }

  42. //加载模板

  43. @GetMapping("/template")

  44. public String loadTemplate(HttpServletRequest request){

  45. String name=request.getParameter("name");

  46. return "client/email/moban/"+name;

  47. }

  48. //发送纯文本

  49. @PostMapping("/sendText")

  50. private String sendTextEmail(@RequestParam("to") String to,

  51. @RequestParam("subject") String subject,

  52. @RequestParam("content") String content,

  53. HttpServletRequest request) {

  54. SimpleMailMessage mailMessage = new SimpleMailMessage();

  55. mailMessage.setFrom(emailfrom);

  56. mailMessage.setTo(to);

  57. mailMessage.setSubject(subject);

  58. mailMessage.setText(content);

  59. String message=null;

  60. try {

  61. //发送邮件

  62. mailSender.send(mailMessage);

  63. message="纯文本邮件发送成功";

  64. } catch (MailException e) {

  65. message="纯文本邮件发送失败 " + e.getMessage();

  66. System.out.println(message);

  67. e.printStackTrace();

  68. }

  69. request.setAttribute("message",message);

  70. return index(request);

  71. }

  72. //发送上传附件

  73. @PostMapping("sendAttachment")

  74. public String sendAttachment(@RequestParam("file") MultipartFile file, HttpServletRequest request) throws MessagingException {

  75. String dirName="c:\\upload\\";

  76. String fileName = file.getOriginalFilename();

  77. File uploadDir = new File(dirName);

  78. if (!uploadDir.exists())

  79. uploadDir.mkdirs();

  80. String path = dirName + fileName;

  81. // 新建一个文件

  82. File tempFile = new File(path);

  83. try {

  84. // 将上传的文件写入新建的文件中

  85. file.transferTo(tempFile);

  86. } catch (Exception e) {

  87. request.setAttribute("message","上传文件存储失败 "+e.getMessage());

  88. e.printStackTrace();

  89. }

  90. //-------发送邮件附件

  91. MimeMessage message=mailSender.createMimeMessage();

  92. MimeMessageHelper helper = new MimeMessageHelper(message, true);

  93. helper.setFrom(emailfrom);

  94. helper.setTo(request.getParameter("to"));

  95. helper.setSubject(request.getParameter("subject"));

  96. helper.setText("");

  97. helper.addAttachment(fileName,tempFile);

  98. String result;

  99. try {

  100. //发送邮件

  101. mailSender.send(message);

  102. result="附件邮件发送成功";

  103. } catch (MailException e) {

  104. result="附件邮件发送失败 " + e.getMessage();

  105. System.out.println(result);

  106. e.printStackTrace();

  107. }

  108. request.setAttribute("message",result);

  109. return index(request);

  110. }

  111. //发送图片

  112. @PostMapping("sendImage")

  113. public String sendImage(@RequestParam("file") MultipartFile file, HttpServletRequest request) throws MessagingException {

  114. String dirName="c:\\upload\\";

  115. String fileName = file.getOriginalFilename();

  116. File uploadDir = new File(dirName);

  117. if (!uploadDir.exists())

  118. uploadDir.mkdirs();

  119. String path = dirName + fileName;

  120. // 新建一个文件

  121. File tempFile = new File(path);

  122. try {

  123. // 将上传的文件写入新建的文件中

  124. file.transferTo(tempFile);

  125. } catch (Exception e) {

  126. request.setAttribute("message","图片文件存储失败 "+e.getMessage());

  127. e.printStackTrace();

  128. }

  129. //-------发送邮件图片

  130. MimeMessage message=mailSender.createMimeMessage();

  131. MimeMessageHelper helper = new MimeMessageHelper(message, true);

  132. helper.setFrom(emailfrom);

  133. helper.setTo(request.getParameter("to"));

  134. helper.setSubject(request.getParameter("subject"));

  135. String fileKey=System.currentTimeMillis()+"";

  136. String html="<img src=\"cid:"+fileKey+"\"/>"; //邮件内容 为html, filekey必须跟下面的图片对应

  137. helper.setText(html,true);

  138. helper.addInline(fileKey,tempFile); //file key对应图片文件

  139. String result;

  140. try {

  141. //发送邮件

  142. mailSender.send(message);

  143. result="图片邮件发送成功";

  144. } catch (MailException e) {

  145. result="图片邮件发送失败 " + e.getMessage();

  146. System.out.println(result);

  147. e.printStackTrace();

  148. }

  149. request.setAttribute("message",result);

  150. return index(request);

  151. }

  152. //发送模板邮件

  153. @PostMapping("sendTemplate")

  154. public String sendImage(HttpServletRequest request) throws MessagingException {

  155. //-------发送模板邮件

  156. MimeMessage message=mailSender.createMimeMessage();

  157. MimeMessageHelper helper = new MimeMessageHelper(message, true);

  158. helper.setFrom(emailfrom);

  159. helper.setTo(request.getParameter("to"));

  160. helper.setSubject(request.getParameter("subject"));

  161. helper.setText(request.getParameter("content"),true);

  162. String result;

  163. try {

  164. //发送邮件

  165. mailSender.send(message);

  166. result="模板邮件发送成功";

  167. } catch (MailException e) {

  168. result="模板邮件发送失败 " + e.getMessage();

  169. System.out.println(result);

  170. e.printStackTrace();

  171. }

  172. request.setAttribute("message",result);

  173. return index(request);

  174. }

  175. //发送定时邮件

  176. @PostMapping("/sendScheduledText")

  177. private String sendScheduledTextTextEmail(@RequestParam("to") String to,

  178. @RequestParam("week") int week,

  179. @RequestParam("hour") int hour,

  180. @RequestParam("minute") int minute,

  181. HttpServletRequest request) {

  182. //定时参数(5位数字,第1位是星期几,第2-3位是几时,第4-5位是几分

  183. String schedule=week+(hour<10?"0":"")+hour+(minute<10?"0":"")+minute;

  184. ScheduleEmail email=new ScheduleEmail(to,Integer.valueOf(schedule),"个人博客系统流量统计情况","博客访问量","0","",new Date());

  185. emailService.saveScheduledEmail(email);

  186. request.setAttribute("message", "定时邮件提交成功");

  187. return index(request);

  188. }

  189. }

全局配置

 
  1. spring.mail.host=smtp.qq.com

  2. spring.mail.port=587

  3. spring.mail.username=1111111111@qq.com

  4. spring.mail.password=lizmpuvxtuoyddhc

  5. spring.servlet.multipart.max-file-size=10MB

  6. spring.servlet.multipart.max-request-size=10MB

  • 16
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SpringBoot可以很方便地与XXL-JOB进行整合,实现任务调度和定时任务的功能。你可以仿照com.xxl.job.executor.service.jobhandler.SampleXxlJob类的实现,在你的Spring Boot项目中定义一个类,并使用@XxlJob注解来标记你的任务处理方法。在这个方法中,你可以编写你的任务逻辑代码。同时,你可以通过XxlJobHelper.log方法打印执行日志,并通过XxlJobHelper.handleFail和XxlJobHelper.handleSuccess方法来自主设置任务结果。 为了使用XXL-JOB,你需要启动xl-job-admin服务。首先,你需要创建数据库并执行相应的SQL文件。然后,修改yml配置文件,配置好数据库连接等信息。最后,启动xl-job-admin服务,通过访问http://127.0.0.1:8080/xxl-job-admin/并使用admin/123456登录,如果能看到相应的界面则说明配置成功。接着,你可以根据xxl-job-executor-sample-springboot项目模仿,创建一个自己的Spring Boot服务进行开发。 整合完成后,你的Spring Boot项目就可以使用XXL-JOB进行任务调度和定时任务的管理了。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [SpringBoot整合XXL-JOB项目,以及附带使用xxl-job邮件定时发送。](https://download.csdn.net/download/qq_42217363/86402558)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [springboot整合xxl-job](https://blog.csdn.net/qq_32419139/article/details/131187884)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值