在系列第一篇中,使用的是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>
velocity1.5
<dependency>
<groupId>com.sun.mail</groupId>
<artifactId>javax.mail</artifactId>
<version>1.5.0</version>
</dependency>
javax.mail1.5.0
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>3.2.4.RELEASE</version>
</dependency>
spring-context-support3.2.4-RELEASE
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>
spring mail send configuration
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();
}
}
SendEmailHelper
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>
javax.servlet.jsp-api2.3.1
作这个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>
mvc:interceptor
由于我更改了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>
javax.servlet.jsp.jstl-api1.2.1
如果不知道如何自定义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,修改后的源码我更新了,可以在这里下载到。