在系列第一篇中,使用的是mybatis得到了一个小小的项目,而该项目的用户对象是有邮件地址的,如果按照邮件地址给对方去一封邮件会不会更能体现针对性呢,所以,我在这篇准备加入发送邮件的功能,利用的就是spring 自带的JavaMail.
1, 更新pom.xml. 添加velocity,javax.mail1.5能及spring-context-support.
<dependency> <groupId>velocity</groupId> <artifactId>velocity</artifactId> <version>1.5</version> </dependency>
<dependency> <groupId>com.sun.mail</groupId> <artifactId>javax.mail</artifactId> <version>1.5.0</version> </dependency>
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>3.2.4.RELEASE</version> </dependency>
2, 新建emailtemplates文件夹,包含vm和jpg文件。因为我的邮件要包含logo的。邮件里面要包含图片显示,vm模板必须写成<img src="cid:图片服务器端全地址,如本例中使用logoName"/>,发送时
org.springframework.core.io.Resource img = new ClassPathResource(logoName);
message.addInline("logoName", img);
3, config.properties中加入
com.vanceinfo.emailtemplate/myEmail=emailtemplates/myEmail.vm
com.vanceinfo.emailtemplate/myEmaillogo=emailtemplates/FlyingBird.jpg
4,spring-mvc.xml中加入
<!-- spring mail send configuration --> <bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl"> <property name="host" value="smtp.gmail.com" /> <property name="port" value="587" /> <property name="username" value="kingnaliu@gmail.com" /> <property name="password" value="you real password" /> <property name="javaMailProperties"> <props> <prop key="mail.smtp.starttls.enable">true</prop> </props> </property> </bean> <bean id="restTemplate" class="org.springframework.web.client.RestTemplate" /> <bean id="velocityEngine" class="org.springframework.ui.velocity.VelocityEngineFactoryBean"> <property name="velocityProperties"> <value> resource.loader=class class.resource.loader.class=org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader </value> </property> </bean>
5,然后就是Java代码,分别定义一个接口,接品实现类及一个辅助实现的Helper类。这里有一个设计上的缺陷就是为了写单元测试,新开线程时写了一个实名的Runnable的类,本来用Java匿名类来实现是很优雅的一件事情,偏偏为了追求单元测试的覆盖率而白白增加这个Runnable类,而且还要在里面注入所需的参数和service名称。感觉有点搞。所以,我还是贴出我最原始的Helper类的源码吧。
package com.vanceinfo.javaserial.services; import javax.mail.MessagingException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.vanceinfo.javaserial.model.EmailInfo; import com.vanceinfo.javaserial.model.User; @Service("sendEmailHelper") public class SendEmailHelper { @Autowired private ISendEmail sendEmailService; public void sendVMLogoEmail(final User user) { /* * SendVMLogoEmailRunnable runnable = new SendVMLogoEmailRunnable(); * runnable.setSendEmailService(sendEmailService); * runnable.setUser(user); Thread sendEmailThread = new * Thread(runnable); sendEmailThread.start(); */ new Thread(new Runnable() { public void run() { try { EmailInfo email = new EmailInfo(); email.setFrom("jinliangliu@163.com"); email.setTo(new String[] { user.getEmail() }); email.setSubject("Test"); email.setEmailPlaceHolder(user); email.setTempleteName("emailtemplates/myEmail.vm"); sendEmailService.sendEmailWithTemplate(email); } catch (MessagingException e) { } catch (Exception e) { } } }).start(); } }
6,最后,我是在userService里面调用Helper,让Helper调用实现类完成发送邮件功能。为什么要用Helper,因为如果你还要发送其也类型的邮件,只需在这里加入方法即可完成。
7, 上面提到的单元测试覆盖率,我们项目使用的是EclEmma,这个插件可以直接以EclEmma在Eclipse市场查找到,安装完之后,即可使用,其写方网址是:http://www.eclemma.org/
8,添加单元测试。这次我准备用一个tokenHandler来完成这次单元测试。因为要自定义Token的tag,所以先引入javax.servlet.jsp-api
<dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>javax.servlet.jsp-api</artifactId> <version>2.3.1</version> </dependency>
作这个token的作用,是我不想每次按F5或Ctrl+F5的时候,重新发送请求,也就是常说的二次提交问题的避免。中心思想来源于Struts2的token就是说当用户加载这个页面时,这个token生成了一个Guid,分别保存进session和hidden两个地方。提交之前对这两个进行比较,内容完全一致则认为是一致的,提交上去,然后remove掉session里面的Guid,这样子话,如果用户再次提交,就会因为session里面为空,而hidden里面还有值而不一致,跳转页面不让提交。
在spring-mvc.xml中加入声明:
<mvc:interceptors> <!-- use Token to void multi post when user press F5 or Ctrol+F5 --> <mvc:interceptor> <mvc:mapping path="/index.do" /> <bean class="com.vanceinfo.javaserial.handlerinterceptors.TokenHandlerInterceptor" /> </mvc:interceptor> </mvc:interceptors>
由于我更改了spring-mvc.xml里面对模型视图的解析,由原来的
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" p:prefix="/" p:suffix=".jsp" />
变成了如下:
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/jsp/" /> <property name="suffix" value=".jsp" /> <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" /> </bean>
所以还要加上jstl-api1.2.1
<dependency> <groupId>javax.servlet.jsp.jstl</groupId> <artifactId>javax.servlet.jsp.jstl-api</artifactId> <version>1.2.1</version> </dependency>
如果不知道如何自定义jsp标签的话,可以参考这里,我这里的步骤:a,定义tag类,让其扩展TagSupport类. b,在WEB-INF下面创建标签库描述文件.tld(Tag Library Description). c,在web.xml里面定义
<jsp-config>
<taglib>
<taglib-uri>/mytaglib</taglib-uri>
<taglib-location>/WEB-INF/tlds/Token.tld</taglib-location>
</taglib>
</jsp-config>
d,在需引用的jsp里声明:
<%@ taglib uri="/mytaglib" prefix="ck"%>
并写上
<ck:token/>
即可。
值得一提的是这次的单元测试,还是使用的是参数化单元测试的,当写到这里的时候,我想还是另开一篇专门讲这一小节的好了。请点击这里
9,修改后的源码我更新了,可以在这里下载到。