❝ flexmark-java 是 java 版的 Markdown 转换工具,基本支持 Markdown 所有的语法,而且扩展性也不错;本文主要是通过扩展形式给链接添加 target 属性本文的扩展还将支持 Spring Properties 来动态配置, 支持域名排除、支持相对路径排除、支持自定义 target 属性的值. ❞
定义Properties配置类 LinkTargetProperties
@Configuration@ConfigurationProperties(prefix = "markdown.link")public class LinkTargetProperties{ /** * 排除添加 target 属性的链接 */ private List excludes; /** * target 属性的值 */ private String target = "_target"; /** * 相对路径排除 */ private boolean relativeExclude = true; // get 和 set 方法省略}
实现 AttributeProvider的类 LinkTargetAttributeProvider
❝
扩展 flexmark-java 主要是通过实现 AttributeProvider 进行修改
❞
public class LinkTargetAttributeProvider implements AttributeProvider { // 用于获取配置的数据 private final DataHolder dataHolder; // 绝对路径正则匹配 private final Pattern pattern = Pattern.compile("^[a-zA-z]+://[^s]*"); public LinkTargetAttributeProvider(DataHolder dataHolder) { this.dataHolder = dataHolder; } @Override public void setAttributes(@NotNull Node node, @NotNull AttributablePart part, @NotNull Attributes attributes) { // 只处理 Link if (node instanceof Link && part == AttributablePart.LINK) { // 获取 href 标签 Attribute hrefAttr = attributes.get("href"); if (hrefAttr == null) { return; } // 值也不能为空 String href = hrefAttr.getValue(); if (StringUtils.isEmpty(href)) { return; } // 获取配置参数 // 注意此处不能直接使用 Spring Boot 的依赖注入 // 但可以使用ApplicatonContext.getBean的形式获取 LinkTargetProperties dataKey = FlexmarkExtensions.LINK_TARGET.get(this.dataHolder); // 判断是否是绝对路径 if (!pattern.matcher(href).matches()) { if (dataKey.isRelativeExclude()) { // 如果是相对路径,则排除 return; } } else { // 获取域名/host Optional host = ServletUtil.getHost(href); if (host.isEmpty()) { return; } List excludes = dataKey.getExcludes(); if (excludes != null && !excludes.isEmpty()) { // 如果包含当前的host则排除 if (excludes.contains(host.get())) { return; } } } String target = dataKey.getTarget(); if (StringUtils.isEmpty(target)) { target = ""; } // 设置target 属性 attributes.replaceValue("target", target); } } static AttributeProviderFactory factory() { return new IndependentAttributeProviderFactory() { @Override public @NotNull AttributeProvider apply(@NotNull LinkResolverContext linkResolverContext) { // 在此处获取dataHolder return new LinkTargetAttributeProvider(linkResolverContext.getOptions()); } }; }}
❝
在旧版本中也可以通过 DataHoler.get(DataKey) 获取配置参数,现在已经不推荐使用了,这里推荐使用 linkResolverContext.getOptions()的形式
❞
注册 Provider
❝
自定义的Provider需要通过 HtmlRenderer.Builder.attributeProviderFactory 的方式注册才能使用
❞
public class LinkTargetExtensions implements HtmlRenderer.HtmlRendererExtension { // 定义配置参数 // 并设置默认值 public static final DataKey LINK_TARGET = new DataKey<>("LINK_TARGET", new LinkTargetProperties()); @Override public void rendererOptions(@NotNull MutableDataHolder mutableDataHolder) { } @Override public void extend(HtmlRenderer.@NotNull Builder builder, @NotNull String s) { builder.attributeProviderFactory(LinkTargetAttributeProvider.factory()); } public static LinkTargetExtensions create() { return new LinkTargetExtensions(); }}
Markdown 工具类
public class MarkdownUtil{ private static final MutableDataSet OPTIONS = new MutableDataSet( PegdownOptionsAdapter.flexmarkOptions( true, // 所有的特性 Extensions.ALL, // 自定义的 Link Target 扩展 LinkTargetExtensions.create() )) .set(HtmlRenderer.SOFT_BREAK, "
"); // 解析器 private static final Parser PARSER = Parser.builder(OPTIONS).build(); // 渲染器 private static final HtmlRenderer htmlRender = HtmlRenderer.builder(OPTIONS).build(); /** * 渲染 Markdown * @param markdown 文档 * @param JDK8的Consumer的,用于动态改变 LinkTargetAttributeProvider的配置参数 * @return html */ public static String renderHtml(String markdown, Consumer accept) { if (Util.isEmpty(markdown)) { return ""; } Document document = PARSER.parse(markdown); if (accept != null) { accept.accept(document); } return htmlRender.render(document); }}
使用示例
public class TestMarkdownRender(){ public static void main(String[] args) { String markdown = "[测试1](http://www.itlangzi.com/test1 '测试1') [测试2](/test2 '测试2') [测试3](https://www.google.com/test3 '测试3')"; System.out.println(MarkdownUtil.renderHtml(markdown, doc -> { doc.set(LinkTargetExtensions.LINK_TARGET, new LinkTargetProperties( // 需要过滤的域名 Arrays.asList("www.itlangzi.com","www.baidu.com"), // target 属性的值 "_target", // 排除相对路径 true )); })); }}
结果
![83800ecc126ce8b3a25ec6d17f80e02a.png](https://i-blog.csdnimg.cn/blog_migrate/206f8840da10099004f8ea0928802e7c.jpeg)
「码砖不易,多多关注」
原文链接 IT浪子の博客 > flexmark-java markdown给链接添加target属性