一、邮件发送
使用springboot自带的邮件系统就能实现邮件的发送,首先导入依赖:
1、新建springboot项目,添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
2、以qq邮箱为例,在application文件中配置邮箱信息
spring.mail.host=smtp.qq.com
spring.mail.username=用户名
spring.mail.password=密码 #为客户端登录授权码,并不是邮箱登录密码
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true
spring.mail.properties.mail.smtp.starttls.required=true
授权码获取:
选择开启,点击后会让你用绑定的手机号发送一条信息,发送后点击已发送就会看到自己的登录授权码,一定记得要保存修改。不然会出问题
3、编写一个简单的测试用例
@Autowired
private mailSender;//注入邮件发送对象。会自动加载配置文件中的邮件设置
@Test
public void contextLoads() {
MimeMessage mimeMessage = mailSender.createMimeMessage();
MimeMessageHelper helper;
String[] strings = new String[3];
strings[2]="email1";
strings[1]="email2";
strings[0]="email3";
try {
helper = new MimeMessageHelper(mimeMessage, true);
helper.setFrom("email");//发送人
helper.setTo("emailTo");//邮件接收人
helper.setCc(strings);//抄送邮件接收人,可以抄送多个
helper.setSubject("群发邮件");//邮件标题
FileSystemResource file = new FileSystemResource(new File("detail.jpg"));
helper.addAttachment("附件-1.jpg", file);//发送附件
helper.addInline("jpg", file);
helper.setText("<html><body> <div>群发邮件</div></body></html>", true);//可以使用html
mailSender.send(mimeMessage);
} catch (MessagingException e) {
e.printStackTrace();
}
4、上述工作完成后启动测试类,即可成功发送指定内容至指定邮箱。
在这个过程中遇到了几个问题,这里记录一下:
问题1:Caused by: javax.mail.AuthenticationFailedException: 535 Error: ÇëʹÓÃÊÚȨÂëµÇ¼¡£ÏêÇéÇë¿´
这是在application.properties中设置的密码不是授权码或者属性值后面带有空格等导致的
问题2:Caused by: com.sun.mail.smtp.SMTPAddressFailedException: 550 Mailbox not found or access denied
这是由于邮箱地址填写错误导致的。还有就是使用企业邮箱时,在发送的邮箱中由于有人离职导致该地址不存在而导致发送异常。
至此一个简单的邮件发送就完成了,然而在实际开发中仅仅这样是不能满足我们的开发需求的,所以我们要对其进行加工,让变得更加丰满。
二、进阶——定时任务
我们时常会遇到需要定时处理某个任务的情况,比如定时发送邮件至某个或几个相关人员的邮箱,那么应该怎么做呢。接下来我们就看看如何通过定时任务将邮件发送至指定邮箱。
1、添加定时任务的依赖
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.0</version>
<exclusions>
<exclusion>
<artifactId>slf4j-api</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
<exclusion>
<artifactId>HikariCP-java6</artifactId>
<groupId>com.zaxxer</groupId>
</exclusion>
</exclusions>
</dependency>
这里我没有使用springboot自带的定时任务触发器,而是使用quartz,话不多说,直接上代码。
@RestController
@RequestMapping("/job")public class JobController {
private static final Logger LOGGER = LoggerFactory.getLogger(JobController.class);
@Autowired
@Qualifier("scheduler")
private Scheduler scheduler;
@SuppressWarnings({"unchecked", "rawtypes"})
@RequestMapping("/add")
@ApiOperation(value = "新建任务", notes = "新建任务", httpMethod = "POST")
@ApiImplicitParams({
@ApiImplicitParam(paramType = "query", name = "jobName", dataType = "String", value = "任务名称"),
@ApiImplicitParam(paramType = "query", name = "jobGroup", dataType = "String", value = "任务分组"),
@ApiImplicitParam(paramType = "query", name = "jobClassName", dataType = "String", value = "任务类全路径"),
@ApiImplicitParam(paramType = "query", name = "description", dataType = "String", value = "任务描述"),
@ApiImplicitParam(paramType = "query", name = "cronExpression", dataType = "String", value = "执行时间"),
@ApiImplicitParam(paramType = "query", name = "oldJobName", dataType = "String", value = "原任务名称"),
@ApiImplicitParam(paramType = "query", name = "oldJobGroup", dataType = "String", value = "原任务分组"),
@ApiImplicitParam(paramType = "query", name = "endTime", dataType = "String", value = "任务结束时间")
})
public String save(Quartz quartz) {
LOGGER.info("新增任务");
//时间格式化
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
try {
// Date parse = sdf.parse("2019-07-25 16:30:00");
//将输入的任务过期时间格式化为特定格式
Date parse = sdf.parse(quartz.getEndTime());
//将任务开始时间格式化
Date startTime = sdf.parse("2019-07-01 22:32:12");
// 获取Scheduler实例、废弃、使用自动注入的scheduler、否则spring的service将无法注入
// Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
// 如果是修改,展示旧的任务
if (quartz.getOldJobGroup() != null) {
//生成任务唯一key
JobKey key = new JobKey(quartz.getOldJobName(), quartz.getOldJobGroup());
scheduler.deleteJob(key);
}
//任务执行类
Class cls = Class.forName(quartz.getJobClassName());
cls.newInstance();
// 构建job信息
JobDetail job = JobBuilder.newJob(cls).withIdentity(quartz.getJobName(),
quartz.getJobGroup())
.withDescription(quartz.getDescription()).build();
// 触发时间点
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(quartz.getCronExpression());
Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger" + quartz.getJobName(), quartz.getJobGroup())
.startNow().withSchedule(cronScheduleBuilder).endAt(parse).startAt(startTime).build();
// 交由scheduler安排触发
scheduler.scheduleJob(job, trigger);
} catch (Exception e) {
e.printStackTrace();
return "failure";
}
return "success";
}
@PostMapping("/remove")
@ApiOperation(value = "移除任务", notes = "移除任务", httpMethod = "POST")
@ApiImplicitParams({
@ApiImplicitParam(paramType = "query", name = "jobName", dataType = "String", value = "任务名称"),
@ApiImplicitParam(paramType = "query", name = "jobGroup", dataType = "String", value = "任务分组")
})
public String remove(Quartz quartz) {
try {
TriggerKey triggerKey = TriggerKey.triggerKey(quartz.getJobName(), quartz.getJobGroup());
// 停止触发器
scheduler.pauseTrigger(triggerKey);
// 移除触发器
scheduler.unscheduleJob(triggerKey);
// 删除任务
scheduler.deleteJob(JobKey.jobKey(quartz.getJobName(), quartz.getJobGroup()));
LOGGER.info("removeJob:" + JobKey.jobKey(quartz.getJobName()));
} catch (Exception e) {
e.printStackTrace();
return "failer";
}
return "success";
}
}
任务job:
public class MailSendJob implements Job, Serializable {
private static final Logger LOGGER = LoggerFactory.getLogger(MailSendJob.class);
private static final long serialVersionUID = 1L;
private static final String URL = "";
private static final String NAME = "";
private static final String PASSWORD = "";
@Autowired
private JavaMailSender mailSender;//启动类添加注解后,注入这个注解会自动加载配置文件中的邮件配置
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
MimeMessage mimeMessage = mailSender.createMimeMessage();
MimeMessageHelper helper;
String[] strings = new String[2];//构建需要发送邮件的邮箱数组
strings[1] = "mail3";
strings[0] = "mail";
String filePath = writeCsv("test");
try {
helper = new MimeMessageHelper(mimeMessage, true);
helper.setFrom("mail1");//发送人
helper.setTo("mail2");//邮件接收人
helper.setCc(strings);//抄送邮件接收人
helper.setSubject("发送带抄送的邮件");//邮件标题
helper.setText("<html><body> <div>发送带抄送的邮件</div></body></html>", true);//可以使用html
FileSystemResource file = new FileSystemResource(new File(filePath));//获取需要发送的附件文件
String fileName = filePath.substring(filePath.lastIndexOf(File.pathSeparator) + 1);//获取文件名
helper.addAttachment(fileName, file);//添加附件
mailSender.send(mimeMessage);//发送带附件的邮件
} catch (MessagingException e) {
e.printStackTrace();
LOGGER.error("任務執行失敗");
} finally {
//最后删除在磁盘上生成的文件
File file1 = new File(filePath);
if (file1.exists() && file1.isFile()) {
file1.delete();
}
}
}
private String writeCsv(String name) {
String home = System.getProperty("user.home") + "\\Downloads\\" + name + "." + "csv";
// String home = "d:/detail.csv";
try {
//1.加载驱动程序
Class.forName("com.mysql.jdbc.Driver");
// Class.forName("org.postgresql.Driver");
//2.获得数据库的连接
Connection conn = DriverManager.getConnection(URL, NAME, PASSWORD);
//3.通过数据库的连接操作数据库,实现增删改查
Statement stmt = conn.createStatement();
Statement statement = conn.createStatement();
// TODO 获取数据
ResultSet resultSet = statement.executeQuery(" select * from area_code order by code limit 50");
// ResultSet resultSet = statement.executeQuery("select a.*,b.* from a join b on a.id=b.id where a.id<1000");
ResultSetMetaData metaData = resultSet.getMetaData();
int columnCount = metaData.getColumnCount();
FileOutputStream out = new FileOutputStream(home);
CsvWriter csvWriter = new CsvWriter(home, ',', Charset.forName("UTF-8"));
//写入表头信息
String[] header = new String[columnCount];
for (int i = 0; i < columnCount; i++) {
header[i] = metaData.getColumnName(i + 1);
}
csvWriter.writeRecord(header);
//写入内容信息
while (resultSet.next()) {
for (int i = 0; i < columnCount; i++) {
csvWriter.write(resultSet.getString(metaData.getColumnName(i + 1)));
}
csvWriter.endRecord();
}
//关闭写入的流
csvWriter.close();
File fileLoad = new File(home);
FileInputStream in = new java.io.FileInputStream(fileLoad);
//每次写入10240个字节
byte[] b = new byte[10240];
int n;
while ((n = in.read(b)) != -1) {
out.write(b, 0, n); //每次写入out1024字节
}
out.close();
in.close();
System.out.println(home);
return home;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
需要几个配置:
@Configuration
public class QuartzConfig {
@Autowired
private SpringJobFactory springJobFactory;
@Bean(name = "schedulerFactory")
public SchedulerFactoryBean schedulerFactoryBean() throws IOException {
SchedulerFactoryBean factory = new SchedulerFactoryBean();
factory.setAutoStartup(true);
// 延时5秒启动
factory.setStartupDelay(5);
factory.setQuartzProperties(quartzProperties());
factory.setJobFactory(springJobFactory);
return factory;
}
@Bean
public Properties quartzProperties() throws IOException {
PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties"));//任务信息库表信息配置文件
propertiesFactoryBean.afterPropertiesSet();
return propertiesFactoryBean.getObject();
}
/**
* quartz初始化监听器
*
* @return
*/
@Bean
public QuartzInitializerListener executorListener() {
return new QuartzInitializerListener();
}
/**
* 通过SchedulerFactoryBean获取Scheduler的实例
*
* @return
* @throws IOException
*/
@Bean(name = "scheduler")
public Scheduler scheduler() throws IOException {
return schedulerFactoryBean().getScheduler();
}
}
@Component
public class SpringJobFactory extends AdaptableJobFactory {
@Autowired
private AutowireCapableBeanFactory capableBeanFactory;
@Override
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
// 调用父类的方法
Object jobInstance = super.createJobInstance(bundle);
// 进行注入
capableBeanFactory.autowireBean(jobInstance);
return jobInstance;
}
}
任务说明:使用定时任务定时获取数据库中的数据信息,将统计数据以excel定时发送到指定负责人的邮箱