学习邮件,利用Spring+Velocity

简介:

Velocity 是 apache 组织下的一个 基于 java  模板引擎( template engine ),而这种 Apache velocity 模板引擎的简单使用可以更好的将样式设计和 java 开发分离开来。

       举个简单例子,在给一些客户发送邮件时,邮件正文往往带有一些较规范的表格或链接,而在发送邮件时,我们当然可以在 java 代码中写正文时加入 html 标签修饰达到如下目的:


        但如果在业务上遇到更为复杂,项目较为庞大,这就要求项目组分工较为明确,而此时的邮件开发人员( java 开发)对表单设计并不在行,换句话说 java 开发人员在开发邮件发送功能时即便花费较长时间用在利用 html 标签来修饰邮件正文内容,但其也很难达到专业的 Web designers 设计出来的水平。那么最能达到用户要求的方式是什么呢?是让每个人仅作自己最擅长的事情: java 开发人员仅作邮件功能, web designer 仅作邮件的类似 web 表单的正文。

       上面所示的邮件中的表格( table )及其中的样式和连接( <a></a> )效果是web designer 使用 html 实现的一个模板,而 java 开发人员则提供了相关表格( table)单元格填充所需要的内容,模板引擎利用模板语言将两者进行完美结合,最终形成一个美观邮件内容。 Velocity 则提供了这种方式的实现,如下图所示:

         Velocity 提供的这种方式的实现,其思想来源于 MVC 。 Web designer 设计好一个模板,里面使用 html 标签和模板语言( Velocity Template Language ),而 velocity模板引擎起到了控制作用, java 开发人员只需要将相关数据作为一个 map 或其他变量等信息提供给这个模板,即可完成相关工作。代码示意如下所示:

模板: templateDemo.vm :

</head>
<center>
<TABLE width=850 border=1 bordercolor="#000000" id="tabID">
  <TBODY align="left">
    <TR rowspan="2">
      <TD colSpan=4><B><FONT color=blue>$map.get("applyFormId")</FONT></B></TD>
    </TR>
    <TR>
      <TD align=middle colSpan=4><FONT size=5><B>templateDemo</B></FONT></TD>
    </TR>
    <TR>
      <TD nowrap width="140"><B>标题:</B></TD>
      <TD> $map.get("Name")</TD>
      <TD nowrap><B>关键词</B></TD>
      <TD> $map.get("key")</TD>
    </TR>
  </TBODY>
</TABLE>
</center>
</html>

 

VelocityEngine的使用

VelocityEngine ve = new VelocityEngine();
ve.setProperty(Velocity.RESOURCE_LOADER, "class");
ve.setProperty("class.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader"); // 设置类路径加载模板
ve.setProperty(Velocity.INPUT_ENCODING, "utf-8");// 设置输入字符集
ve.setProperty(Velocity.OUTPUT_ENCODING, "utf-8");// 设置输出字符集
ve.init();// 初始化模板引擎
Template t = ve.getTemplate("/templateDemo.vm");// 加载模板,相对于classpath路径
VelocityContext context = new VelocityContext();

HashMap<String, Object> result = new HashMap<String, Object>();
result.put("Name", "模板");
result.put("Key", "语言");

context.put("map", result);

StringWriter writer = new StringWriter();
t.merge(context, writer); // 转换
writer.toString();	//形成最终结果

 

       虽然模板引擎不仅仅使用在复杂的邮件内容的业务上,但这个简单例子让我们体会到了界面和业务数据分离的实现方式,这也是模板引擎的主要作用,即它 可以让界面与数据分离,从而大大提高开发效率,也使得项目更加容易重用优良的界面设计。



分隔符----------------------------------------------------------------------------------

发送邮件(Spring+Velocity)

最近有需实现邮件发送的功能,为了让邮件界面和业务数据分离,故采用Velocity模板引擎。需引入velocity的时候,在Maven配置文件中添加如下依赖:

?
1
2
3
4
5
6
7
8
9
10
< dependency >
     < groupId >velocity</ groupId >
     < artifactId >velocity</ artifactId >
     < version >1.5</ version >
</ dependency >
< dependency >
     < groupId >velocity-tools</ groupId >
     < artifactId >velocity-tools</ artifactId >
     < version >2.0-beta1</ version >
</ dependency >

接着在Spring的配置文件中定义添加如下配置:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
< bean  id = "velocityEngine"  class = "org.springframework.ui.velocity.VelocityEngineFactoryBean" >
     < property  name = "velocityProperties" >
         < props >
             < prop  key = "resource.loader" >file</ prop >
             < prop  key = "file.resource.loader.class" >org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader</ prop >
             < prop  key = "file.resource.loader.cache" >false</ prop >
             < prop  key = "file.resource.loader.modificationCheckInterval" >3</ prop >
             < prop  key = "input.encoding" >UTF-8</ prop >
             < prop  key = "output.encoding" >UTF-8</ prop >
         </ props >
     </ property >
</ bean >
 
< bean  id = "javaMailSender"   class = "org.springframework.mail.javamail.JavaMailSenderImpl" >  
         < property  name = "host" >  
             < value >${mail.host}</ value >  
         </ property >
         < property  name = "port" >
             < value >${mail.port}</ value >
         </ property >  
         < property  name = "javaMailProperties" >  
             < props >  
                 < prop  key = "mail.smtp.auth" >${mail.smtp.auth}</ prop >  
                 < prop  key = "mail.smtp.timeout" >${mail.smtp.timeout}</ prop >  
             </ props >  
         </ property >  
         < property  name = "username" >  
             < value >${mail.username}</ value >  
         </ property >  
         < property  name = "password" >  
             < value >${mail.password}</ value >  
         </ property >  
</ bean >

需要在spring配置文件中加入如下配置:

?
1
2
3
4
5
<bean id= "propertyConfigurer"  class = "org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" >
     <property name= "location" >
         <value>classpath:config.properties</value>
     </property>
</bean>

并在src/main/resources目录下的config.properties文件中加入配置:

?
1
2
3
4
5
6
mail.host=mail.ylmob.com
mail.port= 25
mail.username=pay @ylmob .com
mail.password=*************
mail.smtp.auth= true
mail.smtp.timeout= 25000

有时习惯查看源码,但是Maven私服上面并没有源码,只能下载velocity-1.5.zip,并在解压,按照图中的指示操作就可以附加源码:

附加源码之后,选中你想看源码的类,按下F3键,就进入源码中。

org.apache.velocity.runtime.RuntimeConstants中定义了key值信息,部门key值如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
     /**
      * Key used to retrieve the names of the resource loaders to be used. In a properties file they may appear as the following:
      *
      * <p>resource.loader = classpath</p>
      */
     String RESOURCE_LOADER =  "resource.loader" ;
 
     /** The public handle for setting a path in the FileResourceLoader. */
     String FILE_RESOURCE_LOADER_PATH =  "file.resource.loader.path" ;
 
     /** The public handle for turning the caching on in the FileResourceLoader. */
     String FILE_RESOURCE_LOADER_CACHE =  "file.resource.loader.cache" ;

    定义模板的输入和输出模板编码的配置是:

?
1
2
<prop key= "input.encoding" >UTF- 8 </prop>
<prop key= "output.encoding" >UTF- 8 </prop>

    配置检查模板更改时间间隔:

?
1
< prop  key = "file.resource.loader.modificationCheckInterval" >3</ prop >

    配置是否启用模板缓存:

?
1
< prop  key = "file.resource.loader.cache" >false</ prop >

    配置模板路径:   

?
1
< prop  key = "file.resource.loader.path" >${path}</ prop >

   配置模板加载器类型:

?
1
< prop  key = "resource.loader" >file</ prop >

    配置加载器类:

?
1
< prop  key = "file.resource.loader.class" >org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader</ prop >

    定义一个用于发送邮件接口IMailSenderService,具体代码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package  com.astep.yunpay.service;
 
import  java.util.Map;
 
/**
  * @Author DJM
  * @CreateDate:2015-05-06 13:57:20
  * @UpdateDate:2015-05-06 13:57:20
  * @Deprecated:Send e-mail
  */
public  interface  IMailSenderService {
 
     public  void  sendMailWithVelocityTemplate(String mailSubject, String templateLocation, Map<String, Object> velocityContext);
         
}

再写一个实现IMailSenderService接口的实现类MailSenderService,具体代码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
package  com.astep.yunpay.service.impl;
 
import  java.util.Map;
 
import  javax.annotation.Resource;
import  javax.mail.MessagingException;
import  javax.mail.internet.MimeMessage;
 
import  org.apache.velocity.app.VelocityEngine;
import  org.springframework.beans.factory.annotation.Autowired;
import  org.springframework.mail.javamail.JavaMailSender;
import  org.springframework.mail.javamail.MimeMessageHelper;
import  org.springframework.stereotype.Service;
import  org.springframework.ui.velocity.VelocityEngineUtils;
 
import  com.astep.yunpay.service.IMailSenderService;
import  com.astep.yunpay.service.ISsPropertyService;
 
/**
  * @Author DJM
  * @CreateDate:2015-05-06 13:57:20
  * @UpdateDate:2015-05-06 13:57:20
  * @Deprecated:Send e-mail
  */
@Service ( "mailSenderService" )
public  class  MailSenderService  implements  IMailSenderService {
     
     @Autowired
     private  ISsPropertyService    ssPropertyService;
     
     @Resource
     private  JavaMailSender javaMailSender;
     
     @Resource
     private  VelocityEngine velocityEngine;
     
     @Override
     public  void  sendMailWithVelocityTemplate(String mailSubject, String templateLocation, Map<String, Object> velocityContext){
         
         MimeMessage mailMessage = javaMailSender.createMimeMessage();   
         
         MimeMessageHelper messageHelper;
         try  {
             messageHelper =  new  MimeMessageHelper(mailMessage,  true "utf-8" );
             // 设置邮件接收人
             if (ssPropertyService.getByKey( "system.mail.to" )== null ||ssPropertyService.getByKey( "system.mail.to" ).equals( "" )){
                 return ;
             }
             messageHelper.setTo(ssPropertyService.getByKey( "system.mail.to" ).trim().split( ";" ));
             // 设置邮件发送人
             messageHelper.setFrom(ssPropertyService.getByKey( "mail.from" ).trim());
             messageHelper.setSubject(mailSubject);
             String mailText = VelocityEngineUtils.mergeTemplateIntoString(velocityEngine, templateLocation,  "utf-8" , velocityContext);
             messageHelper.setText(mailText,  true );
             javaMailSender.send(mailMessage);
         catch  (MessagingException e) {
             e.printStackTrace();
         }
         
     }
 
}

    需要在代码中注意代码中,

?
1
messageHelper.setFrom(ssPropertyService.getByKey( "mail.from" ).trim());

   可以自由配置为shabi@shabi.com、laopozhaoxue@laopo.com、meinv@img.com等,只会影响下图中红色框框中的显示部分,邮件实际上是由pay@ylmob.com发送的。假如我们配置duoshengwa@laopo.com,红色框框中显示将是duoshengwa,就相当于给pay@ylmob.com穿上一层皮,即使发垃圾邮件,一般也不会被别人知道。随便说句,“mail.from”和"system.mail.to"系统后台配置的属性,存放于数据库中的数据。

    

    这次需求是每天早晨八点发送排名前十位的内容提供商的排名以及金额统计详细和应用排名以及金额统计详细邮件。有了MailSenderService类,只需要传入邮件主题、模板文件的路径和一个Map<String, Object>类型的对象model,源码中的注释对model给出了说明

?
1
@param  model the Map that contains model names as keys and model objects as values
?
1
2
3
4
5
6
7
8
9
package  com.astep.yunpay.task;
 
/**
  * @Author DJM
  */
public  interface  ITopTenCpAndAppDailyCensusSendEmailTaskService {
 
     public  void  email();
}

实现该接口的类为:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
package  com.astep.yunpay.task.impl;
 
import  java.util.ArrayList;
import  java.util.Date;
import  java.util.HashMap;
import  java.util.List;
import  java.util.Map;
 
import  org.springframework.beans.factory.annotation.Autowired;
import  org.springframework.scheduling.annotation.Scheduled;
import  org.springframework.stereotype.Service;
 
import  com.astep.common.util.CalculateUtil;
import  com.astep.common.util.DateUtil;
import  com.astep.common.util.DigitUtil;
import  com.astep.yunpay.service.IMailSenderService;
import  com.astep.yunpay.service.ISsPropertyService;
import  com.astep.yunpay.service.comprehensive.IComprehensiveService;
import  com.astep.yunpay.task.ITopTenCpAndAppDailyCensusSendEmailTaskService;
 
/**
  * @Author DJM
  * @Date:2015年03月18日
  * @Time:上午11:10:25
  * @Description:邮件发送每日TOP10的CP和应用统计报表
  */
@Service
public  class  TopTenCpAndAppDailyCensusSendEmailTaskService  implements  ITopTenCpAndAppDailyCensusSendEmailTaskService {
     
     @Autowired
     ISsPropertyService     ssPropertyService;
     
     @Autowired
     IComprehensiveService    comprehensiveService;
     
     @Autowired
     private  IMailSenderService mailSenderService;
     
     private  static  boolean     isRunning    =  false ;
     
     @Scheduled (cron =  "0 0 8 * * *" //开发调试所采用的调度时间
     //@Scheduled(cron = "0/30 * * * * *") //开发调试所采用的调度时间
     public  void  email() {
         if  (isRunning) {
             return ;
         }
         isRunning =  true ;
         
         Map<String, Object> velocityCpParameter =  new  HashMap<String, Object>();
         velocityCpParameter = enchaseParameter(velocityCpParameter,  false );
         mailSenderService.sendMailWithVelocityTemplate( "每日TOP10的CP统计报表" "velocity/VelocityTemplateOfTopTenCpDailyCensusMail.vm" , velocityCpParameter);
         
         Map<String, Object> velocityAppParameter =  new  HashMap<String, Object>();
         velocityAppParameter = enchaseParameter(velocityAppParameter,  true );
         mailSenderService.sendMailWithVelocityTemplate( "每日TOP10的APP统计报表" "velocity/VelocityTemplateOfTopTenAppDailyCensusMail.vm" , velocityAppParameter);
         
         isRunning =  false ;
     }
     
     public  Map<String, Object> enchaseParameter(Map<String, Object> velocityParameter,  boolean  isGourpApp){
         velocityParameter.put( "mailPlatformFlag" , ssPropertyService.getByKey( "mail.platform.flag" ) ==  null  "" : ssPropertyService.getByKey( "mail.platform.flag" ));
         velocityParameter.put( "date" , DateUtil.formatDate(DateUtil.addDays( new  Date(), - 1 )));
         velocityParameter.put( "comprehensiveList" , getComprehensiveList(isGourpApp));
         velocityParameter.put( "summationMap" , getSummationMap(getComprehensiveList(isGourpApp)));
         return  velocityParameter;
     }
     
     public  List<Map<String, Object>> getComprehensiveList( boolean  isGourpApp){
         Map<String, Object> condition =  new  HashMap<String, Object>();
         Date addTimeStart = DateUtil.getDayBegin(DateUtil.addDays( new  Date(), - 1 ));
         Date addTimeEnd = DateUtil.getDayEnd(addTimeStart);
         if (!isGourpApp){
             condition.put( "screening" true );
         }
         condition.put( "screeningTop" true );
         condition.put( "addTimeStart" , addTimeStart);
         condition.put( "addTimeEnd" , addTimeEnd);
         List<Map<String, Object>> comprehensiveList =  new  ArrayList<Map<String, Object>>(); 
         comprehensiveList =    comprehensiveService.census(condition,  "cpDailyCensus" );
         return  comprehensiveList;
     }
     
     public  Map<String, Object> getSummationMap(List<Map<String, Object>> comprehensiveList){
         Map<String, Object> summationMap =  new  HashMap<String, Object>();
         int  coSuccessSummation= 0 , poSuccessSummation= 0 , moSuccessSummation= 0 , mrSuccessSummation= 0 ;
         if (comprehensiveList!= null  && comprehensiveList.size()> 0 ){
             for (Map<String, Object> record: comprehensiveList){
                 coSuccessSummation+=DigitUtil.obtainWithoutDecimal(record.get( "coS" ));
                 poSuccessSummation+=DigitUtil.obtainWithoutDecimal(record.get( "poS" ));
                 moSuccessSummation+=DigitUtil.obtainWithoutDecimal(record.get( "moS" ));
                 mrSuccessSummation+=DigitUtil.obtainWithoutDecimal(record.get( "mrS" ));
             }
         }
         summationMap.put( "coSuccessSummation" , coSuccessSummation);
         summationMap.put( "poSuccessSummation" , poSuccessSummation);
         summationMap.put( "moSuccessSummation" , moSuccessSummation);
         summationMap.put( "mrSuccessSummation" , mrSuccessSummation);
         summationMap.put( "summationPercent" , CalculateUtil.quotient(mrSuccessSummation, poSuccessSummation));
         
         return  summationMap;
     }
     
     public  String toYuan(Object object){
         int  number = DigitUtil.obtainWithoutDecimal(object);
         Integer result = number/ 100 ;
         return  result.toString();
     }
}

velocity模板文件为:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
##author: DJM
<! DOCTYPE  html>
< html >
   < head >
     < meta  charset = "utf-8" >
     < meta  http-equiv = "X-UA-Compatible"  content = "IE=edge" >
     < meta  name = "viewport"  content = "width=device-width, initial-scale=1" >
     < title >每日TOP10的APP统计报表</ title >
     < style >
         #include("velocity/css/bootstrap.css")
     </ style >
   </ head >
   < body >
       < h3 >< span  class = "label label-success" >${mailPlatformFlag}-每日TOP10的APP统计报表(${date})</ span ></ h3 >
       < div  class = "panel panel-danger" >
           < div ></ div >
           < div >
             < table  class = "table table-bordered table-hover" >
                 < tr >
                     < td  title = "时间" >时间</ td >
                     < td  title = "应用名称" >应用名称</ td >
                     < td  title = "所属CP" >所属CP</ td >
                     < td  title = "确认订单金额" >确认订单金额</ td >
                     < td  title = "PO成功金额" >PO成功金额</ td >
                     < td  title = "MO成功金额" >MO成功金额</ td >
                     < td  title = "MR成功金额" >MR成功金额</ td >
                     < td  title = "计费转化率" >计费转化率</ td >
                 </ tr >
             #foreach($comprehensive in ${comprehensiveList})
                 < tr >
                     < td >${comprehensive.censusDay}</ td >
                     < td  title = "${comprehensive.appId}" >${comprehensive.appName}</ td >
                     < td >${comprehensive.shortName}</ td >
                     #set($coS=${comprehensive.coS}/100)
                     < td >${coS}元</ td >
                     #set($poS=${comprehensive.poS}/100)
                     < td >${poS}元</ td >
                     #set($moS=${comprehensive.moS}/100)
                     < td >${moS}元</ td >
                     #set($mrS=${comprehensive.mrS}/100)
                     < td >${mrS}元</ td >
                     #if(${comprehensive.mrS} <= 0 || ${comprehensive.poS} <= 0)
                         < td >0%</ td >
                     #else
                         #set($comprehensivePercent=${comprehensive.mrS}*10000/${comprehensive.poS}/100)
                         < td >${comprehensivePercent}%</ td >
                     #end
                 </ tr >
             #end
                 < tr >
                     < td >合计</ td >
                     < td >-</ td >
                     < td >-</ td >
                     #set($coSuccessSummation=${summationMap.coSuccessSummation}/100)
                     < td >${coSuccessSummation}元</ td >
                     #set($poSuccessSummation=${summationMap.poSuccessSummation}/100)
                     < td >${poSuccessSummation}元</ td >
                     #set($moSuccessSummation=${summationMap.moSuccessSummation}/100)
                     < td >${moSuccessSummation}元</ td >
                     #set($mrSuccessSummation=${summationMap.mrSuccessSummation}/100)
                     < td >${mrSuccessSummation}元</ td >
                     < td >${summationMap.summationPercent}%</ td >
                 </ tr >
             </ table >
           </ div >
       </ div >
       < h3 >
           < span  class = "glyphicon glyphicon-link"  aria-hidden = "true" ></ span >
           < span  class = "label label-info" >< a  href = "http://p.918ja.com:9020/login.do" >进入移动支付统一合作平台</ a ></ span >
       </ h3 >
   </ body >
</ html >

    使用#include命令引入bootstrap.css文件:

?
1
2
3
< style >       
      #include("velocity/css/bootstrap.css")
</ style >

  发送的邮件内容为:

    

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值