SpringBoot使用Velocity发送电子邮件

SpringBoot使用Velocity发送电子邮件

简介

Velocity能用来干什么?

我认为大家熟知的什么用Velocity页面当做MVC的页面来呈现这种方式,但在当今前后端分离的时代,我觉得很大一部分公司前端页面都是使用的React,Vue等纯前端框架。使用各大模板引擎当做页面应该很少

除了这个,官方文档提到了一个很重要的一个应用场景,就是发送电子邮件。当应用需要给用户发送电子邮件时,并且有统一的邮件模板,只需要针对不同用户展现不同的内容。这个时候,页面需要同Java代码分离,并且页面的模板是不需要经常改变的,这就用到了Velocity

入门

Velocity工作原理简单概括为,在页面中使用Velocity的语法设置变量,取值。而Java代码负责设值、同时渲染模板

页面

页面主要分为以下两大类

引用

引用相关文档即可知道,这里只做简单记录

  1. 命名规则

    引用由$+变量名/方法名组成。必须以字母开头,后面的部分可以是字母、数字、下划线_

  2. 访问属性和方法

    通过命名+ 的方式

    ## 访问order的customerName
    $order.customerName
    

    关于是访问属性还是访问方法,参考查找规则

  3. 引用的正式写法

    当变量后面跟着其他单词时,Velocity可能会把后面的单词也当做变量去查找,为了防止这种情况发生,需要使用正式写法

    Jack is a ${vice}maniac
    

    这里只会去查找vice

指令

指令相关文档

指令以#开头,常用的主要有以下几个

  1. set,设值

    #set($temList = $order.items)
    

    将items设值给定义的变量temList

  2. If / ElseIf / Else,判断

    #if( $foo < 10 )
        Go North
    #elseif( $foo == 10 )
        Go East
    #elseif( $bar == 6 )
        Go South
    #else
        Go West
    #end
    

    foo可以是对象、集合、数组等,判断条件为
    在这里插入图片描述

  3. foreah,循环

    #set($temList = $order.items)
    #foreach($item in $temList)
            $item
    #end
    
  4. include和parse,重用其他页面

    <html>
    ## 这里要写resources下的全路径
        #parse("templates/emails/header.vm")
    <body>
    </body>
    </html>
    

关于其他使用,如转义、数学计算等,移步相关文档

Java代码

页面写完之后,我们需要给页面中的变量设值,开发文档

新建一个springboot应用

可以使用Velocity和VelocityEngine,看个人喜好,主要使用Context进行变量的设置

示例页面

<html>
    #parse("templates/emails/header.vm")
<body>
Hi, $order.customerName<br>
## 使用正式语法
您在${order.paymentTime}时完成的订单详情如下
<table border="1">
    <tr>
        <th>宝贝</th>
    </tr>
    #set($temList = $order.items)
    #foreach($item in $temList)
        <tr>
            <td>$item</td>
        </tr>
    #end
</table>
<br>
合计金额为:$order.paymentAmount<br>
配送方式为:$order.deliveryMethod
</body>
</html>

给上述页面设值

@Service
@Log
public class SendEmailService {
    
    private static final VelocityEngine ve = new VelocityEngine();

    /**
     * https://velocity.apache.org/engine/2.3/configuration.html#resource-management
     *
     * https://velocity.apache.org/engine/2.3/configuration.html#configuration-examples
     * 注意最后一段话
     * Node that the three names 'file', 'class', and 'jar' are merely for your convenience and sanity.
     * They can be anything you want - they are just used to associate a set of properties together.
     * However, it is recommended that you use names that give some hint of the function
     *
     * 说明可以将file,class,jar的位置替换成其他,所以下面的写法是resource.loader.class.class,这里使用class
     */
    @PostConstruct
    public void initVelocity() {
        ve.setProperty(RuntimeConstants.RESOURCE_LOADERS, "class");
        ve.setProperty("resource.loader.class.class", ClasspathResourceLoader.class.getName());
        ve.setProperty("resource.loader.class.cache", true);
        ve.init();
    }

    public boolean sendOrderDetailEmail() {
        Order order = new Order();
        order.setCustomerName("jack");
        List<String> items = Arrays.asList("猪肉", "牛肉", "鱼肉");
        order.setItems(items);
        order.setPaymentAmount(BigDecimal.valueOf(78.365));
        order.setPaymentTime(LocalDateTime.now());
        order.setDeliveryMethod("顺丰");
        // 设值
        VelocityContext context = new VelocityContext();
        context.put("order", order);
        context.put("header", "OrderDetail");
        // 获取模板
        Template template = ve.getTemplate(SendEmailUtil.obtainTemplateRealPath("orderDetail"));
        StringWriter writer = new StringWriter();
        // 将模板和变量值进行渲染
        template.merge(context, writer);
        return true;
    }
}

注意 :这里主要注意Velocity时读取模板位置的设置,ResourceLoader文档

什么时候需要设置ResourceLoader?

  1. 通过velocityEngine.mergeTemplate或者template.merge 的方式,需要在初始化的时候配置ResourceLoader.
  2. 如果.vm文件中含有#include或#parse指令,也需要配置ResourceLoader
/**
 * 两种方式:https://velocity.apache.org/engine/devel/developer-guide.html#using-velocity
 * 一种是直接使用字符串 + velocityEngine.evaluate
 * 另外一种是使用模板文件 + velocityEngine.mergeTemplate, 这种方式需要在初始化的时候配置resourceLoader
 *
 * velocity配置: https://velocity.apache.org/engine/devel/configuration.html#configuring-velocity
 * 默认配置文件位置:org/apache/velocity/runtime/defaults/velocity.properties
 * Any values specified before init() time will replace the default values.
 * Therefore, you only have to configure velocity with the values for the keys    that you need to change,
 * and not worry about the rest
 */
@Log
@Component
public class VelocityUsageService {
    
    /**
     * 字符串中含有#parse或#include指令,需要配置resourceLoader
     * @return
     */
    public String velocityEvaluateHasParseOrIncludeDirective() {
        StringWriter writer = new StringWriter();
        String template = "<html>\n" + "## 这里要写resources下的全路径\n" + "    #parse(\"templates/emails/header.vm\")\n" + "<body>\n" + "Hi, $order.customerName<br>\n" + "## 使用正式语法\n" +
            "您在${order.paymentTime}时完成的订单详情如下\n" + "<table border=\"1\">\n" + "    <tr>\n" + "        <th>宝贝</th>\n" + "    </tr>\n" + "    #set($temList = $order.items)\n" +
            "    #foreach($item in $temList)\n" + "        <tr>\n" + "            <td>$item</td>\n" + "        </tr>\n" + "    #end\n" + "</table>\n" + "<br>\n" + "合计金额为:$order.paymentAmount<br>\n" +
            "配送方式为:$order.deliveryMethod\n" + "</body>\n" + "</html>";
        VelocityEngine velocityEngine = new VelocityEngine();
        velocityEngine.setProperty(RuntimeConstants.RESOURCE_LOADERS, "class");
        velocityEngine.setProperty("resource.loader.class.class", ClasspathResourceLoader.class.getName());
        velocityEngine.setProperty("resource.loader.class.cache", true);
        VelocityContext velocityContext = new VelocityContext();
        Order order = new Order();
        order.setCustomerName("jack");
        List<String> items = Arrays.asList("猪肉", "牛肉", "鱼肉");
        order.setItems(items);
        order.setPaymentAmount(BigDecimal.valueOf(78.365));
        order.setPaymentTime(LocalDateTime.now());
        order.setDeliveryMethod("顺丰");
        velocityContext.put("order", order);
        velocityContext.put("header", "OrderDetail");
//        velocityContext.put("name", "sparrow");
        velocityEngine.evaluate(velocityContext, writer, UUID.randomUUID().toString(), template);
        log.info(writer.toString());
        return writer.toString();
    }
    
    public String velocityEvaluateNoParseAndIncludeDirective() {
        StringWriter writer = new StringWriter();
        String template = "hello $name";
        VelocityEngine velocityEngine = new VelocityEngine();
        VelocityContext velocityContext = new VelocityContext();
        velocityContext.put("name", "sparrow");
        velocityEngine.evaluate(velocityContext, writer, UUID.randomUUID().toString(), template);
        log.info(writer.toString());
        return writer.toString();
    }
    
    /**
     * https://velocity.apache.org/engine/devel/developer-guide.html#resource-loaders
     * 通过velocityEngine.mergeTemplate或者template.merge 的方式,需要在初始化的时候配置ResourceLoader.
     * 如果.vm文件中含有#include或#parse指令,也需要配置ResourceLoader
     *
     * because the resource management system will also handle non-template reasources, specifically things that are loaded via the #include() directive
     */
    public String velocityMergeTemplate() {
        StringWriter writer = new StringWriter();
        VelocityEngine velocityEngine = new VelocityEngine();
        velocityEngine.setProperty(RuntimeConstants.RESOURCE_LOADERS, "class");
        velocityEngine.setProperty("resource.loader.class.class", ClasspathResourceLoader.class.getName());
        velocityEngine.setProperty("resource.loader.class.cache", true);
        VelocityContext velocityContext = new VelocityContext();
        velocityContext.put("name", "sparrow");
        // 方式一
//        Template template = velocityEngine.getTemplate(SendEmailUtil.obtainTemplateRealPath("registerSuccess"));
//        template.merge(velocityContext, writer);
        // 方式二
        velocityEngine.mergeTemplate(SendEmailUtil.obtainTemplateRealPath("registerSuccess"), Charset.defaultCharset().name(), velocityContext, writer);
        log.info(writer.toString());
        return writer.toString();
    }
}

集成发送电子邮件

这里使用163邮箱发送电子邮件

关于收发邮件所用的协议,基本是两个

  • SMTP协议用来发邮件,全称是Simple Mail Transfer Protocol,即简单邮件传输协议。它是用于在计算机网络中传输电子邮件的标准协议。
  • IMAP协议用来收邮件,全称Internet Message Access Protocol,即互联网邮件访问协议。它是一种用于接收和管理电子邮件的协议。IMAP允许用户通过电子邮件客户端(如Outlook、Thunderbird等)连接到邮件服务器,查看、下载、组织和管理邮件
    在这里插入图片描述

使用springboot封装好的mail工具-JavaMailSender,相关文档1相关文档2

前提

在163邮箱上将POP3/SMTP/IMAP开启,并拿到授权码

编码

配置文件
server:
  port: 18080
  servlet:
    context-path: /v
spring:
  mail:
    host: smtp.163.com
    username: 自己的邮箱
    # 163邮箱授权码
    password: 填写你自己的
    properties:
      mail:
        smtp:
          auth: true
          starttls:
            enable: true
            required: true
  # https://velocity.apache.org/engine/devel/developer-guide.html#logging
  logging:
    level:
      org.apache.velocity: trace
发送邮件
@Service
@Log
public class SendEmailService {
    
    private static final VelocityEngine ve = new VelocityEngine();

    @Autowired
    private JavaMailSender javaMailSender;

    @PostConstruct
    public void initVelocity() {
        ve.setProperty(RuntimeConstants.RESOURCE_LOADERS, "class");
        ve.setProperty("resource.loader.class.class", ClasspathResourceLoader.class.getName());
        ve.setProperty("resource.loader.class.cache", true);
        ve.init();
    }

    public boolean sendOrderDetailEmail() {
        VelocityContext context = new VelocityContext();
        Order order = new Order();
        order.setCustomerName("jack");
        List<String> items = Arrays.asList("猪肉", "牛肉", "鱼肉");
        order.setItems(items);
        order.setPaymentAmount(BigDecimal.valueOf(78.365));
        order.setPaymentTime(LocalDateTime.now());
        order.setDeliveryMethod("顺丰");
        context.put("order", order);
        context.put("header", "OrderDetail");
        Template template = ve.getTemplate(SendEmailUtil.obtainTemplateRealPath("orderDetail"));
        StringWriter writer = new StringWriter();
        template.merge(context, writer);
        javaMailSender.send(buildMessage("OrderDetail Email", writer.toString()));
        return true;
    }

    @SneakyThrows
    private MimeMessage buildMessage(String subject, String emailContent) {
        MimeMessage message = javaMailSender.createMimeMessage();
        MimeMessageHelper helper = new MimeMessageHelper(message);
        helper.setFrom("发送方邮箱地址");
        helper.setTo("接收方邮箱地址");
        helper.setSubject(subject);
        message.setText(emailContent, Charset.defaultCharset().name(), "html");
        return message;
    }
}
发送结果

在这里插入图片描述

官方文档

官方文档的阅读主要分为两大部分
在这里插入图片描述

  • 如果需要写页面,则参考User’s Guide
  • 如果需要写Java代码,则参考Developer’s Guide

示例代码

  • 完整的示例代码仓库, 更详细地使用,请注意代码中的注释
要在 Spring Boot 中整合 Velocity使用自定义标签,可以按照以下步骤进行: 1. 导入相关依赖 在 `pom.xml` 文件中添加以下依赖: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-velocity</artifactId> </dependency> <dependency> <groupId>org.apache.velocity</groupId> <artifactId>velocity-tools</artifactId> <version>2.0</version> </dependency> ``` 2. 配置 Velocity 在 `application.properties` 文件中添加以下配置: ```properties spring.velocity.resource-loader-path=classpath:/templates/ spring.velocity.toolbox-config-location=velocity.toolbox.xml ``` 其中,`resource-loader-path` 配置 Velocity 模板所在的目录,`toolbox-config-location` 配置 Velocity 工具箱的配置文件路径。 3. 定义自定义标签 在 `velocity.toolbox.xml` 文件中定义自定义标签,例如: ```xml <tools> <toolbox scope="application"> <tool key="myTag" class="com.example.MyTag"/> </toolbox> </tools> ``` 其中,`key` 是标签名,`class` 是标签类的完整路径。 4. 实现自定义标签类 在项目中创建 `MyTag` 类,并实现 `org.apache.velocity.tools.generic.SafeConfig` 接口和 `org.apache.velocity.tools.generic.Tool` 接口,例如: ```java public class MyTag implements SafeConfig, Tool { private String name; @Override public void configure(Map<String, Object> map) { this.name = (String) map.get("name"); } public String execute() { return "Hello, " + name + "!"; } } ``` 其中,`configure` 方法用于获取配置信息,`execute` 方法用于执行标签的逻辑。 5. 在模板中使用自定义标签 在模板中使用自定义标签,例如: ``` <myTag name="world"/> ``` 这样就可以在模板中使用自定义标签了。 以上就是 Spring Boot 整合 Velocity使用自定义标签的步骤。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值