SpringBoot

SpringBoot

pom.xml

父依赖

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.5.RELEASE</version>
    <relativePath/>
</parent>

往里点

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-dependencies</artifactId>
    <version>2.2.5.RELEASE</version>
    <relativePath>../../spring-boot-dependencies</relativePath>
</parent>

这个才是真正管理SpringBoot应用里面所有依赖版本的地方,SpringBoot的版本控制中心

Spring-boot-starter 启动器

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

主启动类

//@SpringBootApplication 来标注一个主程序类
//说明这是一个Spring Boot应用
@SpringBootApplication
public class SpringbootApplication {
   public static void main(String[] args) {
      SpringApplication.run(SpringbootApplication.class, args);
   }
}

@SpringBootApplication

作用:标注在某个类上说明这个类是SpringBoot的主配置类 , SpringBoot就应该运行这个类的main方法来启动SpringBoot应用;进入这个注解:

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
    // ......
}

@ComponentScan

对应XML配置中的元素,作用 : 自动扫描并加载符合条件的组件或者bean,将这个bean定义加载到IOC容器中

@SpringBootConfiguration

作用 : SpringBoot的配置类,标注在某个类上,表示这是一个SpringBoot的配置类

进入这个注解:

@Configuration
public @interface SpringBootConfiguration {}
@Component
public @interface Configuration {}

@Configuration 说明这是一个配置类,配置类就是对应Spring的xml配置文件;

@Component 说明启动类本身也是Spring中的一个组件而已,负责启动应用

@EnableAutoConfiguration

开启自动配置功能,以前需要自己配置的东西,现在SpringBoot可以自动帮我们配置

进入@EnableAutoConfiguration中

@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    Class<?>[] exclude() default {};

    String[] excludeName() default {};
}

进入@AutoConfigurationPackage中

@Import({Registrar.class})
public @interface AutoConfigurationPackage {
}
@import

Spring底层注解,给容器中导入一个组件

Registrar.class 作用 : 将主启动类的所在包及下面所有子包里面的所有组件扫描到Spring容器

@Import({AutoConfigurationImportSelector.class})

给容器导入组件,AutoConfigurationImportSelector自动配置导入选择器。

进入这个类,有这样一个方法

// 获得候选的配置
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    //这里的getSpringFactoriesLoaderFactoryClass()方法返回的就是最开始看的启动自动导入配置文件的注解类:EnableAutoConfiguration
    List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), 	this.getBeanClassLoader());
    Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a 	custom packaging, make sure that file is correct.");
    return configurations;
}

这个方法又调用了 SpringFactoriesLoader 类的静态方法,进入这个类的loadFactoryNames()方法:

public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
    String factoryTypeName = factoryType.getName();
    // 这里又调用了loadSpringFactories()方法
    return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}

继续进入loadSpringFactories()方法:

private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
    Map<String, List<String>> result = (Map)cache.get(classLoader);
    if (result != null) {
        return result;
    } else {
        HashMap result = new HashMap();

        try {
            Enumeration urls = classLoader.getResources("META-INF/spring.factories");

            while(urls.hasMoreElements()) {
                URL url = (URL)urls.nextElement();
                UrlResource resource = new UrlResource(url);
                Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                Iterator var6 = properties.entrySet().iterator();

                while(var6.hasNext()) {
                    Entry<?, ?> entry = (Entry)var6.next();
                    String factoryTypeName = ((String)entry.getKey()).trim();
                    String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                    String[] var10 = factoryImplementationNames;
                    int var11 = factoryImplementationNames.length;

                    for(int var12 = 0; var12 < var11; ++var12) {
                        String factoryImplementationName = var10[var12];
                        ((List)result.computeIfAbsent(factoryTypeName, (key) -> {
                            return new ArrayList();
                        })).add(factoryImplementationName.trim());
                    }
                }
            }

            result.replaceAll((factoryType, implementations) -> {
                return (List)implementations.stream().distinct().collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
            });
            cache.put(classLoader, result);
            return result;
        } catch (IOException var14) {
            throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var14);
        }
    }
}

spring.factories出现了很多次,全局搜索它

image-20210722115025525

进入后看见很多配置,这就是自动配置的根源。点击 WebMvcAutoConfiguration 进入。

image-20210722115811811

可以看见这是一个javaConfig配置类,注入了很多Bean。

总之,自动配置真正实现是从classpath中搜寻所有的META-INF/spring.factories配置文件,并将其中对应的 org.springframework.boot.autoconfigure. 包下的配置置顶,通过反射实例化为对应标注了@Configuration的JavaConfig形式的IOC容器配置类,然后将这些都汇总成为一个实例并加载到IOC容器中。

总结

  • SpringBoot在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值
  • 将这些值作为自动配置类导入容器,自动配置类就生效,帮我们进行自动配置工作
  • 整个J2EE的整体解决方案和自动配置都在springboot-autoconfigure的jar包中
  • 它会给容器中导入非常多的自动配置类(xxxAutoConfiguration),就是给容器中导入这个场景需要的所有组件,并配置好这些组件
  • 有了自动配置类,免去了我们手动编写配置注入功能组件登工作

SpringApplication

这个类主要做了一下四件事情:

  • 推断应用的类型是普通的项目还是Web项目

  • 查找并加载所有可用初始化器,设置到initializers属性中

  • 找出所有的应用程序监听器,设置到listeners属性中

  • 推断并设置main方法的定义类,找到运行的主类

查看构造器:

public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) {
    // ......
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    this.setInitializers(this.getSpringFactoriesInstances();
    this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
    this.mainApplicationClass = this.deduceMainApplicationClass();
}

![SpringBoot-Run()](C:\Users\10466\Pictures\Camera Roll\SpringBoot-Run().jpg)

JSR303数据校验

@Validated 类注解 : 数据校验

@Email(message="") 只能为邮箱

@Null 验证对象是否为null
@NotNull 验证对象是否不为null, 无法查检长度为0的字符串
@NotBlank 检查约束字符串是不是Null还有被Trim的长度是否大于0,只对字符串,且会去掉前后空格.
@NotEmpty 检查约束元素是否为NULL或者是EMPTY@Max(value= ,message="") 不超过

@AssertTrue 验证 Boolean 对象是否为 true
@AssertFalse 验证 Boolean 对象是否为 false

集成swagger

添加maven依赖

<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId>
    <version>2.9.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui -->
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger-ui</artifactId>
    <version>2.9.2</version>
</dependency>

编写SwaggerConfig配置类

@Configuration //配置类
@EnableSwagger2// 开启Swagger2的自动配置
public class SwaggerConfig {  
}

访问测试 :http://localhost:8080/swagger-ui.html ,可以看到swagger的界面

配置Swagger

  • Swagger实例Bean是Docket,所以通过配置Docket实例来配置Swagger

    @Bean //配置docket以配置Swagger具体参数
    public Docket docket() {
        return new Docket(DocumentationType.SWAGGER_2);
    }
    
  • 通过apliInfo()属性配置文档信息

    //配置文档信息
    private ApiInfo apiInfo() {
        Contact contact = new Contact("联系人名字", "http://xxx.xxx.com/联系人访问链接", "联系人邮箱");
        return new ApiInfo(
                "Swagger学习", // 标题
                "学习演示如何配置Swagger", // 描述
                "v1.0", // 版本
                "http://terms.service.url/组织链接", // 组织链接
                contact, // 联系人信息
                "Apach 2.0 许可", // 许可
                "许可链接", // 许可连接
                new ArrayList<>()// 扩展
        );
    }
    
  • Docket实例关联上apliInfo()

    @Bean
    public Docket docket() {
        return new Docket(DocumentationType.SWAGGER_2)
            .apiInfo(apiInfo());
    }
    
  • 通过select()方法配置怎么扫描接口

    @Bean
    public Docket docket() {
        return new Docket(DocumentationType.SWAGGER_2)
            .apiInfo(apiInfo());
        	.select()
            .apis(RequestHandlerSelectors.basePackage("类路径"))
            .build();
    }
    
    Swagger注解 简单说明
    @Api(tags="xxx模块说明") 作用在模块类上
    @ApiOperation("xxx接口说明") 作用在接口方法上
    @ApiModel("xxxPOJO说明") 作用在模型类上:如VO、BO
    @ApiModelProperty(value = "xxx属性说明",hidden = true) 作用在类方法和属性上,hidden设置为true可以隐藏该属性
    @ApiParam("xxx参数说明") 作用在参数、方法和字段上,类似@ApiModelProperty

异步任务

在主启动类上加@EnableAsync注解,开启异步注解功能

在方法上添加@Async注解

定时任务

创建一个ScheduleService

@Service
public class ScheduledService {
    //在方法上添加@Scheduled注解
    @Scheduled(cron = "0 * * * *  0-7")
    public void hello() {
		System.out.println("hello.......");
    }
}

在主启动类上加@EnableScheduling注解,开启异步注解功能

cron表达式:http://www.bejson.com/othertools/cron/

(1)0/2 * * * * ?   表示每2秒 执行任务

(1)0 0/2 * * * ?    表示每2分钟 执行任务

(1)0 0 2 1 * ?   表示在每月的1日的凌晨2点调整任务

(2)0 15 10 ? * MON-FRI   表示周一到周五每天上午10:15执行作业

(3)0 15 10 ? 6L 2002-2006   表示2002-2006年的每个月的最后一个星期五上午10:15执行作

(4)0 0 10,14,16 * * ?   每天上午10点,下午2点,4点 

(5)0 0/30 9-17 * * ?   朝九晚五工作时间内每半小时 

(6)0 0 12 ? * WED    表示每个星期三中午12点 

(7)0 0 12 * * ?   每天中午12点触发 

(8)0 15 10 ? * *    每天上午10:15触发 

(9)0 15 10 * * ?     每天上午10:15触发 

(10)0 15 10 * * ?    每天上午10:15触发 

(11)0 15 10 * * ? 2005    2005年的每天上午10:15触发 

(12)0 * 14 * * ?     在每天下午2点到下午2:59期间的每1分钟触发 

(13)0 0/5 14 * * ?    在每天下午2点到下午2:55期间的每5分钟触发 

(14)0 0/5 14,18 * * ?     在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发 

(15)0 0-5 14 * * ?    在每天下午2点到下午2:05期间的每1分钟触发 

(16)0 10,44 14 ? 3 WED    每年三月的星期三的下午2:10和2:44触发 

(17)0 15 10 ? * MON-FRI    周一至周五的上午10:15触发 

(18)0 15 10 15 * ?    每月15日上午10:15触发 

(19)0 15 10 L * ?    每月最后一日的上午10:15触发 

(20)0 15 10 ? * 6L    每月的最后一个星期五上午10:15触发 

(21)0 15 10 ? * 6L 2002-2005   2002年至2005年的每月的最后一个星期五上午10:15触发 

(22)0 15 10 ? * 6#3   每月的第三个星期五上午10:15触发

邮件任务

引入pom依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-mail</artifactId>
</dependency>

配置文件

spring.mail.username=zj_reaper@qq.com
spring.mail.password=qq授权码
spring.mail.host=smtp.qq.com
# qq需要配置ssl
spring.mail.properties.mail.smtp.ssl.enable=true
@Autowired
JavaMailSenderImpl mailSender;
 
@Test
public void contextLoads() {
    //邮件设置1:一个简单的邮件
    SimpleMailMessage message = new SimpleMailMessage();
    message.setSubject("通知-明天不上班");
    message.setText("今晚7:30开会");
 
    message.setTo("977659733@qq.com");
    message.setFrom("977659733@qq.com");
    mailSender.send(message);
}
 
@Test
public void contextLoads2() throws MessagingException {
    //邮件设置2:一个复杂的邮件
    MimeMessage mimeMessage = mailSender.createMimeMessage();
    MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);
 
    helper.setSubject("通知-明天不上班");
    helper.setText("<b style='color:red'>今天 7:30来开会</b>",true);
 
    //发送附件
    helper.addAttachment("1.jpg",new File(""));
    helper.addAttachment("2.jpg",new File(""));
 
    helper.setTo("977659733@qq.com");
    helper.setFrom("977659733@qq.com");
 
    mailSender.send(mimeMessage);
}

富文本编辑器

基础工程搭建

数据库设计

article:文章表

字段 备注
id int 文章的唯一ID
author varchar 作者
title varchar 标题
conent varchar 文章的内容

建表sql

CREATE TABLE `article` (
  `id` int(10) NOT NULL AUTO_INCREMENT COMMENT 'int文章的唯一ID',
  `author` varchar(50) NOT NULL COMMENT '作者',
  `title` varchar(100) NOT NULL COMMENT '标题',
  `content` longtext NOT NULL COMMENT '文章的内容',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

配置数据源

spring:
  datasource:
    username: root
    password: 123456
    #?serverTimezone=UTC解决时区的报错
    url: jdbc:mysql://localhost:3306/springboot?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
    driver-class-name: com.mysql.cj.jdbc.Driver

静态资源过滤

<resources>
    <resource>
        <directory>src/main/java</directory>
        <includes>
            <include>**/*.xml</include>
        </includes>
        <filtering>true</filtering>
    </resource>
</resources>

实体类

//文章类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Article implements Serializable {
 
    private int id; //文章的唯一ID
    private String author; //作者名
    private String title; //标题
    private String content; //文章的内容

}

mapper接口

@Mapper
@Repository
public interface ArticleMapper {
    //查询所有的文章
    List<Article> queryArticles();
 
    //新增一个文章
    int addArticle(Article article);
 
    //根据文章id查询文章
    Article getArticleById(int id);
 
    //根据文章id删除文章
    int deleteArticleById(int id);
 
}

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 
<mapper namespace="com.gorilla.mapper.ArticleMapper">
 
    <select id="queryArticles" resultType="Article">
       select * from article
    </select>
    
    <select id="getArticleById" resultType="Article">
       select * from article where id = #{id}
    </select>
    
    <insert id="addArticle" parameterType="Article">
        insert into article (author,title,content) values (#{author},#{title},#{content});
    </insert>
    
    <delete id="deleteArticleById" parameterType="int">
        delete from article where id = #{id}
    </delete>
    
</mapper>

告诉SpringBoot Mybatis的映射配置文件的位置

mybatis:
  mapper-locations: classpath:com/gorilla/mapper/*.xml
  type-aliases-package: com.kuang.pojo

页面

<!DOCTYPE html>
<html class="x-admin-sm" lang="zh" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Gorilla'Blog</title>
    <meta name="renderer" content="webkit">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="viewport" content="width=device-width,user-scalable=yes, minimum-scale=0.4, initial-scale=0.8,target-densitydpi=low-dpi" />
    <!--Editor.md-->
    <link rel="stylesheet" th:href="@{/editormd/css/editormd.css}"/>
    <link rel="shortcut icon" href="https://pandao.github.io/editor.md/favicon.ico" type="image/x-icon" />
</head>
 
<body>
 
<div class="layui-fluid">
    <div class="layui-row layui-col-space15">
        <div class="layui-col-md12">
            <!--博客表单-->
            <form name="mdEditorForm">
                <div>
                    标题:<input type="text" name="title">
                </div>
                <div>
                   作者:<input type="text" name="author">
                </div>
                <div id="article-content">
                    <textarea name="content" id="content" style="display:none;"> </textarea>
                </div>
            </form>
        </div>
    </div>
</div>
</body>
<!--editormd-->
<script th:src="@{/editormd/lib/jquery.min.js}"></script>
<script th:src="@{/editormd/editormd.js}"></script>
<script type="text/javascript">
    var testEditor;
    //window.onload = function(){ }
    $(function() {
        testEditor = editormd("article-content", {
            width : "95%",
            height : 400,
            syncScrolling : "single",
            path : "../editormd/lib/",
            saveHTMLToTextarea : true,    // 保存 HTML 到 Textarea
            emoji: true,
            theme: "dark",//工具栏主题
            previewTheme: "dark",//预览主题
            editorTheme: "pastel-on-dark",//编辑主题
            tex : true,                   // 开启科学公式TeX语言支持,默认关闭
            flowChart : true,             // 开启流程图支持,默认关闭
            sequenceDiagram : true,       // 开启时序/序列图支持,默认关闭,
            //图片上传
            imageUpload : true,
            imageFormats : ["jpg", "jpeg", "gif", "png", "bmp", "webp"],
            imageUploadURL : "/article/file/upload",
            onload : function() {
                console.log('onload', this);
            },
            /*指定需要显示的功能按钮*/
            toolbarIcons : function() {
                return ["undo","redo","|",
                    "bold","del","italic","quote","ucwords","uppercase","lowercase","|",
                    "h1","h2","h3","h4","h5","h6","|",
                    "list-ul","list-ol","hr","|",
                    "link","reference-link","image","code","preformatted-text",
                    "code-block","table","datetime","emoji","html-entities","pagebreak","|",
                    "goto-line","watch","preview","fullscreen","clear","search","|",
                    "help","info","releaseIcon", "index"]
            },
            /*自定义功能按钮,下面我自定义了2个,一个是发布,一个是返回首页*/
            toolbarIconTexts : {
                releaseIcon : "<span bgcolor=\"gray\">发布</span>",
                index : "<span bgcolor=\"red\">返回首页</span>",
            },
 
            /*给自定义按钮指定回调函数*/
            toolbarHandlers:{
                releaseIcon : function(cm, icon, cursor, selection) {
                    //表单提交
                    mdEditorForm.method = "post";
                    mdEditorForm.action = "/article/addArticle";//提交至服务器的路径
                    mdEditorForm.submit();
                },
                index : function(){
                    window.location.href = '/';
                },
            }
        });
    });
</script>
 
</html>

编写Controller

@Controller
@RequestMapping("/article")
public class ArticleController {
 
    @GetMapping("/toEditor")
    public String toEditor(){
        return "editor";
    }
    
    @PostMapping("/addArticle")
    public String addArticle(Article article){
        articleMapper.addArticle(article);
        return "editor";
    }
    
}

前端js中添加图片上传配置

//图片上传
imageUpload : true,
imageFormats : ["jpg", "jpeg", "gif", "png", "bmp", "webp"],
imageUploadURL : "/article/file/upload", // //这个是上传图片时的访问地址

后端请求,接收保存这个图片,需要导入FastJson的依赖

//博客图片上传问题
@RequestMapping("/file/upload")
@ResponseBody
public JSONObject fileUpload(@RequestParam(value = "editormd-image-file", required = true) MultipartFile file, HttpServletRequest request) throws IOException {
    //上传路径保存设置
 
    //获得SpringBoot当前项目的路径:System.getProperty("user.dir")
    String path = System.getProperty("user.dir")+"/upload/";
 
    //按照月份进行分类:
    Calendar instance = Calendar.getInstance();
    String month = (instance.get(Calendar.MONTH) + 1)+"月";
    path = path+month;
 
    File realPath = new File(path);
    if (!realPath.exists()){
        realPath.mkdir();
    }
 
    //上传文件地址
    System.out.println("上传文件保存地址:"+realPath);
 
    //解决文件名字问题:我们使用uuid;
    String filename = "ks-"+UUID.randomUUID().toString().replaceAll("-", "");
    //通过CommonsMultipartFile的方法直接写文件(注意这个时候)
    file.transferTo(new File(realPath +"/"+ filename));
 
    //给editormd进行回调
    JSONObject res = new JSONObject();
    res.put("url","/upload/"+month+"/"+ filename);
    res.put("success", 1);
    res.put("message", "upload success!");
 
    return res;
}

设置虚拟目录映射,解决文件回显的问题


@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
 
    // 文件保存在真实目录/upload/下,
    // 访问的时候使用虚路径/upload,比如文件名为1.png,就直接/upload/1.png就ok了。
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/upload/**")
            .addResourceLocations("file:"+System.getProperty("user.dir")+"/upload/");
    }
 
}

手动下载表情包,放到图片路径下,修改editormd.js文件

// Emoji graphics files url path
editormd.emoji     = {
    path  : "../editormd/plugins/emoji-dialog/emoji/",
    ext   : ".png"
};

文章展示,在Controller中增加方法

@GetMapping("/{id}")
public String show(@PathVariable("id") int id,Model model){
    Article article = articleMapper.getArticleById(id);
    model.addAttribute("article",article);
    return "article";
}

编写article.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <title th:text="${article.title}"></title>
</head>
<body>
 
<div>
    <!--文章头部信息:标题,作者,最后更新日期,导航-->
    <h2 style="margin: auto 0" th:text="${article.title}"></h2>
    作者:<span style="float: left" th:text="${article.author}"></span>
    <!--文章主体内容-->
    <div id="doc-content">
        <textarea style="display:none;" placeholder="markdown" th:text="${article.content}"></textarea>
    </div>
 
</div>
 
<link rel="stylesheet" th:href="@{/editormd/css/editormd.preview.css}" />
<script th:src="@{/editormd/lib/jquery.min.js}"></script>
<script th:src="@{/editormd/lib/marked.min.js}"></script>
<script th:src="@{/editormd/lib/prettify.min.js}"></script>
<script th:src="@{/editormd/lib/raphael.min.js}"></script>
<script th:src="@{/editormd/lib/underscore.min.js}"></script>
<script th:src="@{/editormd/lib/sequence-diagram.min.js}"></script>
<script th:src="@{/editormd/lib/flowchart.min.js}"></script>
<script th:src="@{/editormd/lib/jquery.flowchart.min.js}"></script>
<script th:src="@{/editormd/editormd.js}"></script>
 
<script type="text/javascript">
    var testEditor;
    $(function () {
        testEditor = editormd.markdownToHTML("doc-content", {//注意:这里是上面DIV的id
            htmlDecode: "style,script,iframe",
            emoji: true,
            taskList: true,
            tocm: true,
            tex: true, // 默认不解析
            flowChart: true, // 默认不解析
            sequenceDiagram: true, // 默认不解析
            codeFold: true
        });});
</script>
</body>
</html>
posted @ 2021-07-22 17:07  ¹Reaper²ι  阅读(55)  评论(0编辑  收藏  举报