一、邮件发送
使用springboot自带的邮件系统就能实现邮件的发送,首先导入依赖:
1、新建springboot项目,添加依赖
1<dependency> 2 <groupId>org.springframework.boot</groupId> 3 <artifactId>spring-boot-starter-mail</artifactId> 4</dependency>
2、以qq邮箱为例,在application文件中配置邮箱信息
1spring.mail.host=smtp.qq.com 2spring.mail.username=用户名 3spring.mail.password=密码 #为客户端登录授权码,并不是邮箱登录密码 4spring.mail.properties.mail.smtp.auth=true 5spring.mail.properties.mail.smtp.starttls.enable=true 6spring.mail.properties.mail.smtp.starttls.required=true
授权码获取:

选择开启,点击后会让你用绑定的手机号发送一条信息,发送后点击已发送就会看到自己的登录授权码,一定记得要保存修改。不然会出问题
3、编写一个简单的测试用例
1@Autowired 2private mailSender;//注入邮件发送对象。会自动加载配置文件中的邮件设置 3 4@Test 5public void contextLoads() { 6 7 MimeMessage mimeMessage = mailSender.createMimeMessage(); 8 MimeMessageHelper helper; 9 String[] strings = new String[3]; 10 strings[2]="email1"; 11 strings[1]="email2"; 12 strings[0]="email3"; 13 try { 14 helper = new MimeMessageHelper(mimeMessage, true); 15 helper.setFrom("email");//发送人 16 helper.setTo("emailTo");//邮件接收人 17 helper.setCc(strings);//抄送邮件接收人,可以抄送多个 18 helper.setSubject("群发邮件");//邮件标题 19FileSystemResource file = new FileSystemResource(new File("detail.jpg")); 20helper.addAttachment("附件-1.jpg", file);//发送附件 21helper.addInline("jpg", file); 22 23 helper.setText("<html><body> <div>群发邮件</div></body></html>", true);//可以使用html 24 mailSender.send(mimeMessage); 25 } catch (MessagingException e) { 26 27 e.printStackTrace(); 28 }
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、添加定时任务的依赖
1<dependency> 2 <groupId>org.quartz-scheduler</groupId> 3 <artifactId>quartz</artifactId> 4 <version>2.3.0</version> 5 <exclusions> 6 <exclusion> 7 <artifactId>slf4j-api</artifactId> 8 <groupId>org.slf4j</groupId> 9 </exclusion> 10 <exclusion> 11 <artifactId>HikariCP-java6</artifactId> 12 <groupId>com.zaxxer</groupId> 13 </exclusion> 14 </exclusions> 15</dependency>
这里我没有使用springboot自带的定时任务触发器,而是使用quartz,话不多说,直接上代码。
1@RestController 2@RequestMapping("/job")public class JobController { 3 4 private static final Logger LOGGER = LoggerFactory.getLogger(JobController.class); 5 6 @Autowired 7 @Qualifier("scheduler") 8 private Scheduler scheduler; 9 10 11 @SuppressWarnings({"unchecked", "rawtypes"}) 12 @RequestMapping("/add") 13 @ApiOperation(value = "新建任务", notes = "新建任务", httpMethod = "POST") 14 @ApiImplicitParams({ 15 @ApiImplicitParam(paramType = "query", name = "jobName", dataType = "String", value = "任务名称"), 16 @ApiImplicitParam(paramType = "query", name = "jobGroup", dataType = "String", value = "任务分组"), 17 @ApiImplicitParam(paramType = "query", name = "jobClassName", dataType = "String", value = "任务类全路径"), 18 @ApiImplicitParam(paramType = "query", name = "description", dataType = "String", value = "任务描述"), 19 @ApiImplicitParam(paramType = "query", name = "cronExpression", dataType = "String", value = "执行时间"), 20 @ApiImplicitParam(paramType = "query", name = "oldJobName", dataType = "String", value = "原任务名称"), 21 @ApiImplicitParam(paramType = "query", name = "oldJobGroup", dataType = "String", value = "原任务分组"), 22 @ApiImplicitParam(paramType = "query", name = "endTime", dataType = "String", value = "任务结束时间") 23 }) 24 public String save(Quartz quartz) { 25 26 LOGGER.info("新增任务"); 27 //时间格式化 28 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 29 30 try { 31// Date parse = sdf.parse("2019-07-25 16:30:00"); 32 //将输入的任务过期时间格式化为特定格式 33 Date parse = sdf.parse(quartz.getEndTime()); 34 //将任务开始时间格式化 35 Date startTime = sdf.parse("2019-07-01 22:32:12"); 36 // 获取Scheduler实例、废弃、使用自动注入的scheduler、否则spring的service将无法注入 37 // Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); 38 // 如果是修改,展示旧的任务 39 if (quartz.getOldJobGroup() != null) { 40 //生成任务唯一key 41 JobKey key = new JobKey(quartz.getOldJobName(), quartz.getOldJobGroup()); 42 scheduler.deleteJob(key); 43 } 44 //任务执行类 45 Class cls = Class.forName(quartz.getJobClassName()); 46 cls.newInstance(); 47 // 构建job信息 48 JobDetail job = JobBuilder.newJob(cls).withIdentity(quartz.getJobName(), 49 quartz.getJobGroup()) 50 .withDescription(quartz.getDescription()).build(); 51 // 触发时间点 52 CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(quartz.getCronExpression()); 53 Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger" + quartz.getJobName(), quartz.getJobGroup()) 54 .startNow().withSchedule(cronScheduleBuilder).endAt(parse).startAt(startTime).build(); 55 // 交由scheduler安排触发 56 scheduler.scheduleJob(job, trigger); 57 } catch (Exception e) { 58 e.printStackTrace(); 59 return "failure"; 60 } 61 return "success"; 62 } 63 64 @PostMapping("/remove") 65 @ApiOperation(value = "移除任务", notes = "移除任务", httpMethod = "POST") 66 @ApiImplicitParams({ 67 @ApiImplicitParam(paramType = "query", name = "jobName", dataType = "String", value = "任务名称"), 68 @ApiImplicitParam(paramType = "query", name = "jobGroup", dataType = "String", value = "任务分组") 69 }) 70 public String remove(Quartz quartz) { 71 try { 72 73 TriggerKey triggerKey = TriggerKey.triggerKey(quartz.getJobName(), quartz.getJobGroup()); 74 // 停止触发器 75 scheduler.pauseTrigger(triggerKey); 76 // 移除触发器 77 scheduler.unscheduleJob(triggerKey); 78 // 删除任务 79 scheduler.deleteJob(JobKey.jobKey(quartz.getJobName(), quartz.getJobGroup())); 80 LOGGER.info("removeJob:" + JobKey.jobKey(quartz.getJobName())); 81 } catch (Exception e) { 82 e.printStackTrace(); 83 return "failer"; 84 } 85 return "success"; 86 } 87 88}
任务job:
1public class MailSendJob implements Job, Serializable { 2 3 private static final Logger LOGGER = LoggerFactory.getLogger(MailSendJob.class); 4 private static final long serialVersionUID = 1L; 5 6 private static final String URL = ""; 7 private static final String NAME = ""; 8 private static final String PASSWORD = ""; 9 10 @Autowired 11 private JavaMailSender mailSender;//启动类添加注解后,注入这个注解会自动加载配置文件中的邮件配置 12 13 @Override 14 public void execute(JobExecutionContext context) throws JobExecutionException { 15 16 MimeMessage mimeMessage = mailSender.createMimeMessage(); 17 MimeMessageHelper helper; 18 String[] strings = new String[2];//构建需要发送邮件的邮箱数组 19 strings[1] = "mail3"; 20 strings[0] = "mail"; 21 String filePath = writeCsv("test"); 22 try { 23 helper = new MimeMessageHelper(mimeMessage, true); 24 helper.setFrom("mail1");//发送人 25 helper.setTo("mail2");//邮件接收人 26 helper.setCc(strings);//抄送邮件接收人 27 helper.setSubject("发送带抄送的邮件");//邮件标题 28 helper.setText("<html><body> <div>发送带抄送的邮件</div></body></html>", true);//可以使用html 29 FileSystemResource file = new FileSystemResource(new File(filePath));//获取需要发送的附件文件 30 String fileName = filePath.substring(filePath.lastIndexOf(File.pathSeparator) + 1);//获取文件名 31 helper.addAttachment(fileName, file);//添加附件 32 mailSender.send(mimeMessage);//发送带附件的邮件 33 34 } catch (MessagingException e) { 35 e.printStackTrace(); 36 LOGGER.error("任務執行失敗"); 37 } finally { 38 //最后删除在磁盘上生成的文件 39 File file1 = new File(filePath); 40 if (file1.exists() && file1.isFile()) { 41 file1.delete(); 42 } 43 } 44 } 45 46 47 private String writeCsv(String name) { 48 49 String home = System.getProperty("user.home") + "\\Downloads\\" + name + "." + "csv"; 50// String home = "d:/detail.csv"; 51 try { 52 //1.加载驱动程序 53 Class.forName("com.mysql.jdbc.Driver"); 54// Class.forName("org.postgresql.Driver"); 55 //2.获得数据库的连接 56 Connection conn = DriverManager.getConnection(URL, NAME, PASSWORD); 57 //3.通过数据库的连接操作数据库,实现增删改查 58 Statement stmt = conn.createStatement(); 59 Statement statement = conn.createStatement(); 60 // TODO 获取数据 61 ResultSet resultSet = statement.executeQuery(" select * from area_code order by code limit 50"); 62// ResultSet resultSet = statement.executeQuery("select a.*,b.* from a join b on a.id=b.id where a.id<1000"); 63 ResultSetMetaData metaData = resultSet.getMetaData(); 64 int columnCount = metaData.getColumnCount(); 65 FileOutputStream out = new FileOutputStream(home); 66 67 CsvWriter csvWriter = new CsvWriter(home, ',', Charset.forName("UTF-8")); 68 //写入表头信息 69 String[] header = new String[columnCount]; 70 for (int i = 0; i < columnCount; i++) { 71 header[i] = metaData.getColumnName(i + 1); 72 } 73 csvWriter.writeRecord(header); 74 //写入内容信息 75 while (resultSet.next()) { 76 77 for (int i = 0; i < columnCount; i++) { 78 79 csvWriter.write(resultSet.getString(metaData.getColumnName(i + 1))); 80 } 81 csvWriter.endRecord(); 82 } 83 84 //关闭写入的流 85 csvWriter.close(); 86 File fileLoad = new File(home); 87 FileInputStream in = new java.io.FileInputStream(fileLoad); 88 //每次写入10240个字节 89 byte[] b = new byte[10240]; 90 int n; 91 while ((n = in.read(b)) != -1) { 92 out.write(b, 0, n); //每次写入out1024字节 93 } 94 out.close(); 95 in.close(); 96 System.out.println(home); 97 return home; 98 } catch (Exception e) { 99 100 e.printStackTrace(); 101 return null; 102 } 103 104 } 105}
需要几个配置:
1@Configuration 2public class QuartzConfig { 3 4 @Autowired 5 private SpringJobFactory springJobFactory; 6 7 @Bean(name = "schedulerFactory") 8 public SchedulerFactoryBean schedulerFactoryBean() throws IOException { 9 SchedulerFactoryBean factory = new SchedulerFactoryBean(); 10 factory.setAutoStartup(true); 11 // 延时5秒启动 12 factory.setStartupDelay(5); 13 factory.setQuartzProperties(quartzProperties()); 14 factory.setJobFactory(springJobFactory); 15 return factory; 16 } 17 18 @Bean 19 public Properties quartzProperties() throws IOException { 20 PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean(); 21 propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties"));//任务信息库表信息配置文件 22 propertiesFactoryBean.afterPropertiesSet(); 23 return propertiesFactoryBean.getObject(); 24 } 25 26 /** 27 * quartz初始化监听器 28 * 29 * @return 30 */ 31 @Bean 32 public QuartzInitializerListener executorListener() { 33 return new QuartzInitializerListener(); 34 } 35 36 /** 37 * 通过SchedulerFactoryBean获取Scheduler的实例 38 * 39 * @return 40 * @throws IOException 41 */ 42 @Bean(name = "scheduler") 43 public Scheduler scheduler() throws IOException { 44 return schedulerFactoryBean().getScheduler(); 45 } 46 47} 48 49@Component 50public class SpringJobFactory extends AdaptableJobFactory { 51 @Autowired 52 private AutowireCapableBeanFactory capableBeanFactory; 53 54 @Override 55 protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception { 56 // 调用父类的方法 57 Object jobInstance = super.createJobInstance(bundle); 58 // 进行注入 59 capableBeanFactory.autowireBean(jobInstance); 60 return jobInstance; 61 } 62}
任务说明:使用定时任务定时获取数据库中的数据信息,将统计数据以excel定时发送到指定负责人的邮箱