SpringBoot详解
Spring Boot 主要目标是:
为所有 Spring 的开发者提供一个非常快速的、广泛接受的入门体验
开箱即用(启动器starter-其实就是SpringBoot提供的一个jar包),但通过自己设置参数
(.properties),即可快速摆脱这种方式。
提供了一些大型项目中常见的非功能性特性,如内嵌服务器、安全、指标,健康检测、外部化配置等,绝对没有代码生成,也无需 XML 配置。
总结:Spring Boot在Spring框架的基础上,简化了配置、优化依赖管理、优化与常用第三方框架的集成、开箱即用。
配置SpringBoot基本环境
- 创建Maven项目
- 导入依赖
<!-- 使用jdk1.8版 -->
<properties>
<java.version>1.8</java.version>
</properties>
<!-- 导入springboot父依赖-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<!-- 导入springboot依赖项-->
<dependencies>
<!-- 导入web启动器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 导入test启动器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--导入配置文件处理器,配置文件进行绑定就会有提示-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<!-- springboot插件,将程序打包成可执行jar包的插件 -->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
- 测试
@SpringBootApplication // 表示此类为spring boot 应用,是spring boot框架的入口文件
public class HolleSpringBootController {
public static void main(String[] args){
// 在main方法中运行HolleSpringBootController类
SpringApplication.run(HolleSpringBootController.class,args);
}
}
- 浏览器运行测试 http://localhost:8080/HolleController
出现这些文字,表示springboot环境搭建成功
SpringBoot项目结构
默认生成的Spring Boot项目结构;
●主程序已经生成好了,我们只需要编写我们自己的逻辑
●resources文件夹中目录结构
- static :保存所有的静态资源; js CSS images ;
- templates :保存所有的模板页面; ( Spring Boot默认jar包使用嵌入式的Tomcat ,默认不支持JSP页面) ;可以使用模板引擎( freemarker、thymeleaf ) ;
- application.properties : Spring Boot应用的配置文件;可以修改一 些默认设置;
SpringBoot常用注解
@SpringBootApplication: // Spring Boot应用标注在某介类上说明这个类是SpringBoot的主配置类, SpringBoot就应该运行这个类的main方法来启动SpringBoot应用;
@SpringBootConfiguration: // 表示Spring Boot的配置类(是springboot的注解,是基于@Configuration的再封装)
//标注在某个类上,表示这是一个Spring Boot的配置类;
// 配置文件属性值注入相关
@ConfigurationProperties(prefix = "person")
// 默认读取全局配置文件(application*.yml,或者application.properties),从配置文件中获取指定的值(以person开头的值)和bean的属性进行绑定
@Value // 默认读取全局配置文件(application*.yml,或者application.properties),单个属性注入,不支持复杂类型的注入(如数组等等)
// 测试类注解
@SpringBootTest /*表示使用springboot内部集成的测试工具*/
@RunWith(SpringRunner.class) /*表示使用spring内核进行测试*/
public class StudentServiceImplTest {}
// 加载配置文件相关
(1)@PropertySource(value = {"classpath:person.properties"})
// 加载指定的properties配置文件,可以指定多个配置文件
(2)@ImportResource(locations = {"classpath:beans.xml"}) // 不推荐使用
// 加载指定的xml文件,让配置文件生效,可以指定多个配置文件,
(3)@Configuration // 有此注解的类为配置类
// 注意:Spring Boot里面没有Spring的配置文件,我们自己编写的配置文件,也不能自动识别;
// 想让Spring的配置文件生效,加载进来; @ImportResource标注在一个配置类上并指定spring的配置文件,让其生效
// 注意:springboot默认加载application*.*文件作为配置文件,可以使用以上三种方式导入自定义的其他的配置文件
SpringBoot配置文件
SpringBoot,默认加载application*.yml 和 application*.properties文件为全局配置文件,其他配置文件SpringBoot不会自动识别。
配置文件的属性值注入
方式一:@ConfigurationProperties(prefix = “person”)
- 默认读取全局配置文件(application*.*),主要用于将配置文件中某一类属性整体批量读取并注入到某个Bean的属性中如(person)
- 注意:必须要有set方法
application.yml配置文件 | VO类,将配置的文件的字段值进行自动注入 ,实体类中属性,必须要有set方法 | 单元测试 |
---|---|---|
![]() | ![]() | ![]() |
使用application.properties文件实现属性值注入,需要修改默认编码格式,否则中文会乱码
![]() | ![]() |
方式二:@Value 单个注入,不需要set方法,不能注入复杂的类型
@ConfigurationProperties / @Value 总结
配置文件占位符
random.uuid # 获取随机的一串字符
random.int # 获取随机的整数
person.hello:hello # 如果person对象hello属性,没有值,则使用默认值hello
SpringBoot自动配置核心
SpringBoot在自动配置很多组件的时候,先看容器中有没有用户自己配置的( @Bean、@Component )如果有就用用户配置的,如果没有,才自动配置;如果有些组件可以有多个( ViewResolver )将用户配置的和自己默认的组合起来;
@SpringBootApplication: // Spring Boot应用标注在某介类上说明这个类是SpringBoot的主配置类, SpringBoot就应该运行这个类的main方法来启动SpringBoot应用;
@EnableAutoConfiguration:// 开启自动配置功能;
// 以前我们需要配置的东西,Spring Boot帮我们自动配置;@EnableAutoConfiguration告诉SpringBoot开启自动配置功能;这样自动配置才能生效;(注意,此注解有,以下的重要的子注解)
@AutoConfigurationPackage :// 自动配置包
@lmport(AutoConfigurationPackages.Registrar.class) :
// Spring的底层注解@lmport ,给容器中导入一个组件;导入的组件由AutoConfigurationPackages.Registrar.class;这个类来指定:
AutoConfigurationPackages.Registrar.class功能:
// 将主配置类( @SpringBootApplication标注的类)的所在包及下面所有子包里面的所有组件扫描到Spring容器;
@lmport(EnableAutoConfigurationImportSelector.class) ;
/* 给容器中导入组件?
EnableAutoConfigurationImportSelector :导入哪些组件的选择器;
将所有需要导入的组件以全类名的方式返回;这些组件就会被添加到容器中;
会给容器中导入非常多的自动配置类( xxxAutoConfiguration) ; 就是给容器中导入这个场景需要的所
有组件,并配置好这些组件;
Spring Boot在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值,
将这些值作为自动配置类导入到容器中,自动配置类就生效,帮我们进行自动配置工作;以前我们需要自己配置的
东西,自动配置类都帮我们;*/
自动配置相关的注解
@Configuration //表示这是一个配置类,以前编写的配置文件一样,也可以给容器中添加组件
@EnableConfigurationProperties(HttpEncodingProperties.class)
//启动指定类的ConfigurationProperties功能;将配置文件中对应的值和HttpEncodingProperties绑定起来;并把HttpEncodingProperties加入到ioc容器中
@ConditionalOnWebApplication
//Spring底层@Conditional注解(Spring注解版),根据不同的条件,如果满足指定的条件,整个配置类里面的配置就会生效; 判断当前应用是否是web应用,如果是,当前配置类生效
@ConditionalOnClass(CharacterEncodingFilter.class)
// 这里的条件是OnClass,也就是满足以下类存在:Servlet、DispatcherServlet、WebMvcConfigurer,其中Servlet只要引入了tomcat依赖自然会有,后两个需要引入SpringMVC才会有。这里就是判断你是否引入了相关依赖,引入依赖后该条件成立,当前类的配置才会生效!
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
// 这个条件与上面不同,OnMissingBean,是说环境中不存在指定的WebMvcConfigurationSupport这个bean时,这个默认实例就生效。其实这就是自定义配置的入口,也就是说,如果我们自己配置了一个WebMVCConfigurationSupport的实现类,那么这个默认配置就会失效,而会启用我们的自定义配置类!
@ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true)
//判断配置文件中是否存在某个配置 spring.http.encoding.enabled;如果不存在,判断也是成立的
ProFile多环境配置
在实际开发中,应用程序通常需要部署到不同的运行环境中,例如开发环境、测试环境、生产环境等。不同的环境可能需要不同的环境配置,针对这种情况,不可能手动变更配置文件来适应不同的开发环境,通常需要对项目进行多环境配置,Spring Boot框架提供了两种多环境配置的方式,分别是Profile文件多环境配置和@Profile注解多环境配置。
在Spring Boot框架中,提供了使用Profile配置文件来进行多环境配置,该配置文件名必须满足application-{profile}.properties的格式,其中{profile}对应具体的环境标识。这里以开发环境、测试环境和生产环境为例,编写对应环境的配置文件,格式示例如下。
application-dev.properties // 开发环境配置文件
application-test.properties // 测试环境配置文件
application-prod.properties // 生产环境配置文件
激活开发环境配置文件
spring.profiles.active=dev # 表示使用application-dev.properties环境
注册servlet三大组件
创建三大组件【Servlet、Filter、Listener】
注册Servlet三大组件【Servlet、Filter、Listener】
由于SpringBoot默认是以jar包的方式启动嵌入式的Servlet容器来启动SpringBoot的web应用,没有web.xml文件。
注册三大组件用以下方式
//ServletRegistrationBean
@Bean # 表示将返回的对象添加到spring容器中
public ServletRegistrationBean myServlet(){
# 注册的servlet对象(MyServlet) 拦截的请求
ServletRegistrationBean registrationBean = new ServletRegistrationBean(new MyServlet(),"/myServlet");
return registrationBean;
}
//FilterRegistrationBean
@Bean
public FilterRegistrationBean myFilter(){
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(new MyFilter()); # 注册MyFilter拦截器
registrationBean.setUrlPatterns(Arrays.asList("/hello","/myServlet"));
return registrationBean;
}
//ServletListenerRegistrationBean
@Bean
public ServletListenerRegistrationBean myListener(){
ServletListenerRegistrationBean<MyListener> registrationBean = new ServletListenerRegistrationBean<>(new MyListener());
return registrationBean;
}
@WebFilter注解方式
网上有说使用这种方法配置过滤器,自己试了一下,发起请求时确实进到过滤器里来了,但是并没有按照“/test/list”路径进行过滤,而是过滤了所有URL请求。在百度之后找到解决方案如下:
解决方法:
首先,在上边TestFilter类上加@WebFilter注解是没错的,但是不要加@Component注解,然后在springboot的启动类上加上注解 @ServletComponentScan即可过滤指定路径。
原因:
如果添加了@Component或@Configuration,又添加了@WebFilter(),那么会初始化两次Filter,并且会过滤所有路径+自己指定的路径 ,便会出现对没有指定的URL也会进行过滤的情况。
注:@ServletComponentScan注解的含义:在SpringBootApplication(启动类)上使用@ServletComponentScan注解后,Servlet、Filter(过滤器)、Listener(监听器)可以直接通过@WebServlet、@WebFilter、@WebListener注解自动注册,无需其他代码!
静态资源与拦截器
Spring Boot 默认为我们提供了静态资源处理,使用 WebMvcAutoConfiguration 中的配置各种属性。
这里,我们可以通过查看ResourceProperties类来查看Spring Boot默认提供的静态资源映射:
注释说明:静态资源文件位置,默认为classpath类路径下:[/META-INF/resources/,/resources/, /static/, /public/]
从上述源码内容以及注释说明可知,Spring Boot的默认配置方式,提供的静态资源映射如下:
classpath:/META-INF/resources
classpath:/resources
classpath:/static
classpath:/public
// 静态资源的访问路径:localhsot/资源文件名
在工程里面路径是这样:
如果使用的是IDEA开发工具创建的Spring Boot项目,通常会默认提供一个classpath:/static目录,而我们的静态资源文件通常就可以直接放在该目录下。
当然,如果非要自定义静态资源文件的放置路径的话,可以在Spring Boot的配置文件application.properties中配置属性spring.resources.static-locations覆盖默认路径。示例如下:
我们可以通过修改spring.mvc.static-path-pattern来修改默认的映射,例如我改成/hopu/**,那运行的
时候访问 http://lcoalhost:8080/hopu/index.html 才对应到默认静态资源路径下的index.html页面。
SpringBoot值Web管理
- 如果Spring Boot提供的Sping MVC不符合要求,则可以通过一个配置类==(注解有@Configuration的类)加上@EnableWebMvc注解来实现完全自己控制的MVC配置(这样就会关闭Spring Boot提供的所有默认Web配置)。==
- 当然,通常情况下,Spring Boot的自动配置是符合我们大多数需求的。在你既需要保留Spring Boot提供的便利,有需要增加自己的额外的配置的时候,可以定义一个配置类并实现WebMvcConfigurer接口,无需使用@EnableWebMvc注解。
- 这里我们提到这个WebMvcConfigurer这个接口,重写这个接口中的方法可以让我们增加额外的配置,
这里展示WebMvcConfigurer接口几个常用方法: https://blog.csdn.net/zhangpower1993/article/details/89016503
/* 拦截器配置 */
void addInterceptors(InterceptorRegistry var1);
/* 视图跳转控制器 */
void addViewControllers(ViewControllerRegistry registry);
/**静态资源处理**/
void addResourceHandlers(ResourceHandlerRegistry registry);
/* 默认静态资源处理器 */
void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer);
/**这里配置视图解析器**/
void configureViewResolvers(ViewResolverRegistry registry);
/* 配置内容裁决的一些选项*/
void configureContentNegotiation(ContentNegotiationConfigurer configurer);
/** 解决跨域问题 **/
public void addCorsMappings(CorsRegistry registry) ;
从上可以看出,WebMvcConfigurer接口中提供了众多Web相关的方法,如果有扩展需要,都可以自定义一个该接口的实现类并重写相关方法。
拦截器addInterceptors
这里,我们就一开发中最常用的拦截器为例进行演示说明。
要实现拦截器功能需要完成以下2个步骤:
- 创建我们自己的拦截器类并实现 HandlerInterceptor 接口;
- 其实重写WebMvcConfigurer中的addInterceptors()方法把自定义的拦截器类添加进来即可。
拦截器
自定义拦截器
package cn.mldn.lxh.xmz.web.Intercepto;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
// 自定义拦截器
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("*************拦截成功*************");
// 返回true表示放行,false则拦截
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {}
}
配置拦截器
package cn.mldn.lxh.xmz.config;
import cn.mldn.lxh.xmz.web.Intercepto.MyInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/*springboot的web管理,需要继承WebMvcConfigurer*/
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
/*用于管理拦截器的添加*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor())
.addPathPatterns("/**") /*用于添加拦截的路径*/
.excludePathPatterns("/index.html"); /*用于排除拦截的路径*/
}
}
其他要点
springboot事务处理
@Transactional:是springboot提供的事务处理,在启动程序时,springboot会自动扫描注解,并启动事务的支持
@Service
public class BookServiceImpl implements Iservice<BookInfo> {
@Autowired
RoomServiceImpl roomService;
@Transactional(rollbackFor = Exception.class) // 开启事务,当发生异常或者手动抛出异常时进行回滚
@Override
public boolean insert(Test test) throws Exception {
roomService.insert(test);
int i = 10 / 0; // 发生异常,自动进行回滚
return false;
}
定时任务
Spring框架的定时任务调度功能支持配置和注解两种方式,Spring Boot在Spring框架的基础上实现了继承,并对其中基于注解方式的定时任务实现了非常好的支持。
定时任务注解
- @EnableScheduling
@EnableScheduling注解是Spring框架提供的,用于开启基于注解方式的定时任务支持,该注解主要用在项目启动类上。 - @Scheduled
@Scheduled注解同样是Spring框架提供的,配置定时任务的执行规则,该注解主要用在定时业务方法上。
@Scheduled注解提供有多个属性,精细化配置定时任务执行规则,这些属性及说明如表所示。
属性 | 说明 |
---|---|
cron | 类似于cron的表达式,可以定制定时任务触发的秒、分钟、小时、月中的日、月、周中的日 |
zone | 指定cron表达式将被解析的时区。默认情况下,该属性是空字符串(即使用服务器的本地时区) |
fixedDelay | 表示在上一次任务执行结束后在指定时间后继续执行下一次任务(属性值为long类型) |
fixedDelayString | 表示在上一次任务执行结束后在指定时间后继续执行下一次任务(属性值为long类型的字符串形式) |
fixedRate | 表示每隔指定时间执行一次任务(属性值为long类型) |
fixedRateString | 表示每隔指定时间执行一次任务(属性值为long类型的字符串形式) |
initialDelay | 表示在fixedRate或fixedDelay任务第一次执行之前要延迟的毫秒数(属性值为long类型) |
initialDelayString | 表示在fixedRate或fixedDelay任务第一次执行之前要延迟的毫秒数(属性值为long类型的字符串形式) |
cron属性
其属性值由类似于cron表达式,的6位数组成,可以详细地指定定时任务执行的秒、分、小时、日、月、星期。
示例代码如下
@Scheduled(cron = "0 * * * * MON-FRI")
//上述代码中,cron = "0 * * * * MON-FRI"表示周一到周五每一分钟执行一次定时任务。其中,第一位属于秒位,0表示整数时间段,按照每一分钟计算;中间的三位分别属于分、时、日、月位,*表示任意时刻;最后一位属于星期位,MON-FRI表示周一到周五时间区间。
除此之外,cron属性值的每一位还支持非常丰富的字段值,具体说明如表所示。
字段 | 可取值 | 允许的特殊字符 |
---|---|---|
秒 | 0~59 | , - * / |
分 | 0~59 | , - * / |
小时 | 0~23 | , - * / |
日 | 1~31 | , - * / ? L |
月 | 1~12、月份对应英文前三个字母(大小写均可) | , - * / |
星期 | 0~7(0和7表示SUN)、星期对应英文前三个字母(大小写均可) | , - * / ? L |
上表中列举了@Schedule注解中cron属性6位字段的可取值,这些字段值除了基本的数字之外还有一些特殊字符。其中,上述特殊字符的具体说明及示例如表所示。
特殊字符 | 说明 | 示例 |
---|---|---|
, | 表示枚举 | @Scheduled(cron = “1,3,5 * * * * *”)表示任意时间的1、3、5秒钟都会执行 |
- | 表示区间 | 参考前面已列举示例 |
* | 表示任意可取值 | 参考前面已列举示例 |
/ | 表示步长 | @Scheduled(cron = “0/5 * * * * *”)表示从任意时间的整秒开始,每隔5秒都会执行 |
? | 日/星期冲突匹配符 | @Scheduled(cron = “0 * * 26 * ?”)表示每月的26日每一分钟都执行 |
L | 最后 | @Scheduled(cron = “0 0 * L * ?”)表示每月最后一日每一小时都执行 |
zone属性
zone属性主要与cron属性配合使用,指定解析cron属性值的时区。通常情况下,不需要指定zone属性,cron属性值会自动以服务器所在区域作为本地时区进行表达式解析。例如,中国地区服务器的时区,通常默认为Asia/Shanghai。
fixedDelay和fixedDelayString属性
fixedDelay和fixedDelayString属性的作用类似,指定在上一次任务执行结束后在指定时间后继续执行 下一次任务,它们的主要区别是属性值的类型不同,其中fixedDelay属性值为long类型,而fixedDelayString属性值为long类型的字符串。
下面,通过一个具体的示例说明,示例代码如下。
@Scheduled(fixedDelay = 5000)
@Scheduled(fixedDelayString = "5000")
// 上述代码中,fixedDelay = 5000和fixedDelayString = "5000"都表示在程序启动后,会立即执行一次定时任务,然后在任务执行结束后,相隔5秒钟(5000毫秒)重复执行定时任务。
注意:fixedDelay和fixedDelayString属性:上一次任务执行完后开始计时, => 那么定时任务的相隔时间 = fixedDelay值 + 方法内执行时间
fixedRate和fixedRateString属性
fixedRate和fixedRateString属性的作用类似,指定每相隔一段时间重复执行一次定时任务,它们的主要区别是属性值的类型不同,其中fixedRate属性值为long类型,fixedRateString属性值为long类型的字符串。
下面,通过一个具体的示例说明,示例代码如下。
@Scheduled(fixedRate = 5000)
@Scheduled(fixedRateString = "5000")
// 上述代码中,fixedRate = 5000和fixedRateString = "5000"都表示在程序启动后,会立即执行一次定时任务,然后相隔5秒钟(5000毫秒)重复执行定时任务。
注意:fixedRate和fixedRateString属性:是在上一个任务开始时,就开始计时了,不会管上一个任务是否执行完毕
initialDelay和initialDelayString属性
initialDelay和initialDelayString属性的作用类似,主要是与fixedRate/fixedRateString或者fixedDelay/fixedDelayString属性配合使用,指定,定时任务第一次执行的延迟时间,然后再按照各自相隔时间重复执行任务。
下面,通过一个具体的示例说明,示例代码如下
@Scheduled(initialDelay=1000, fixedDelay=5000)
@Scheduled(initialDelay=1000, fixedRate=5000)
// 上述代码中,都表示在程序启动后,会延迟1秒(1000毫秒)后再执行第一次定时任务,然后相隔5秒钟(5000毫秒)重复执行定时任务。
总结
注意:fixedRate/fixedRateString属性与fixedDelay/fixedDelayString属性的作用有些类似,都是相隔一段时间再重复执行定时任务,它们主要区别是:
- fixedDelay/fixedDelayString属性的下一次执行时间必须是在上一次任务执行完成后开始计时;
- fixedRate/fixedRateString属性的下一次,执行时间是在上一次任务执行开始计时,如果遇到配置相隔时间小于定时任务执行时间,那么下一次任务会在上一次任务执行完成后立即重复执行。
案例
1、开启基于注解的定时任务支持
@SpringBootApplication
@EnableScheduling /*开启定时任务*/
public class SpringBootDay02 {
public static void main(String[] args) {
SpringApplication.run(SpringBootDay02.class,args);
}
}
2、编写定时业务
// 每隔10秒钟执行一次,从第一次任务开始时,开始计时,每隔5s执行,
@Scheduled(fixedRate = 5000)
public void scheduledTaskImmediately() throws InterruptedException {
System.out.println(String.format("fixedRate第%s次执行,当前时间为:%s",
count1++, dateFormat.format(new Date())));
Thread.sleep(5000);
}
// 每次任务执行完后相隔5秒再执行一次,方法中间的延迟操作有效, Thread.sleep(10000);生效,
@Scheduled(fixedDelay = 5000)
public void scheduledTaskAfterSleep() throws InterruptedException {
System.out.println(String.format("fixedDelay第%s次执行,当前时间为:%s",
count2++, dateFormat.format(new Date())));
Thread.sleep(5000);
}
// 每次任务执行完后相隔5秒再执行一次,
@Scheduled(cron = "0/5 * * * * *")/*cron表达式的含义是,每个月的每星期的每天的每小时,每分钟内,5s执行一次*/
public void scheduledTaskCron() throws InterruptedException {
System.out.println(String.format("cron第%s次执行,当前时间为:%s",
count3++, dateFormat.format(new Date())));
sendSimpleMailTest();
Thread.sleep(10000);
}
邮件服务
在实际开发中,邮件发送服务应该是网站的必备功能之一,例如用户注册验证、忘记密码、给用户发送营销信息等。在早期开发过程中,通常会使用JavaMail相关Api实现邮件发送功能,后来Spring推出了JavaMailSender简化了邮件发送的过程和实现,Spring Boot框架对Spring提出的邮件发送服务也进行了整合支持。下面,针对Spring Boot框架整合支持的邮件任务进行讲解。
发送纯文本邮件
(1)导入依赖
<!-- 邮件发送依赖启动器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
当添加上述依赖后,Spring Boot自动配置的邮件服务会生效,在邮件发送任务时,可以直接使用Spring框架提供的JavaMailSender接口或者它的实现类JavaMailSenderImpl邮件发送。
(2)添加邮件服务配置
在项目中添加邮件服务依赖启动器后,还需要在配置文件中添加邮件服务相关的配置,确保邮件服务正常发送。打开项目的application.properties全局配置文件,在该文件中添加发件人邮箱服务配置和邮件服务超时的相关配置,内容如下所示。
# 发件人邮服务器相关配置
spring.mail.host=smtp.qq.com
# 邮件端口
spring.mail.port=587
# 配置个人QQ账户和密码(密码是加密后的授权码)
spring.mail.username=2811659799@qq.com
# 授权码
spring.mail.password=hcewsvqodvsfdgfg
spring.mail.default-encoding=UTF-8
# 邮件服务超时时间配置
spring.mail.properties.mail.smtp.connectiontimeout=5000
spring.mail.properties.mail.smtp.timeout=3000
spring.mail.properties.mail.smtp.writetimeout=5000
注意:使用java程序发送邮件,必须在发件人邮箱中开启POP3/SMTP服务 开启后会给你一个授权码,根据这个授权码就可以操作这个邮箱发送邮件了
(3)编写业务
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mail.MailException;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.stereotype.Service;
// 发送邮件服务类
@Service
public class SendEmailService {
@Autowired
private JavaMailSenderImpl mailSender;
@Value("${spring.mail.username}")
private String from; //发件人地址
// 发送简单邮件,调用这个方法,传入收件人,邮件标题,邮件内容,则可以发送纯文本的邮件了
public void sendSimpleEmail(String to, String subject, String text) {
// 定制纯文本邮件信息SimpleMailMessage
SimpleMailMessage message = new SimpleMailMessage();
message.setFrom(from); /*发件人邮件地址*/
message.setTo(to); /*收件人邮件地址*/
message.setSubject(subject); /*邮件标题*/
message.setText(text); /*邮件内容*/
try {
// 发送邮件
mailSender.send(message);
System.out.println("纯文本邮件发送成功");
} catch (MailException e) {
System.out.println("纯文本邮件发送失败 " + e.getMessage());
e.printStackTrace();
}
}
}
发送thymeleaf模板邮件
编写服务
// 发送邮件服务类
@Service
public class SendEmailService {
@Autowired
private JavaMailSenderImpl mailSender;
@Value("${spring.mail.username}")
private String from; //发件人地址
/**
* 发送thymeleaf模板邮件
* @param to 收件人地址
* @param subject 邮件标题
* @param content html,thymeleaf模板页面内容
*/
public void sendTemplateEmail(String to, String subject, String content) {
MimeMessage message = mailSender.createMimeMessage();
try {
// 使用MimeMessageHelper帮助类,并设置multipart多部件使用为true
MimeMessageHelper helper = new MimeMessageHelper(message, true);
helper.setFrom(from);
helper.setTo(to);
helper.setSubject(subject);
helper.setText(content, true);
// 发送邮件
mailSender.send(message);
System.out.println("模板邮件发送成功");
} catch (MessagingException e) {
System.out.println("模板邮件发送失败 " + e.getMessage());
e.printStackTrace();
}
}
发送邮件
@Autowired
private TemplateEngine templateEngine;
// 每5秒,发送一次模板邮件
// @Scheduled(cron = "0/5 * * * * *")
public void sendTemplateEmail() {
String to = "1943260672@qq.com";
String subject = "【田波涛,你个憨憨】";
// 使用模板邮件定制邮件正文内容
Context context = new Context();
// 给emailTemplate_vercode.html模板${}传值
context.setVariable("username", "石头");
context.setVariable("code", "456123");
// 使用TemplateEngine设置要处理的模板页面
String emailContent = templateEngine.process("emailTemplate_vercode", context);
// 发送模板邮件
sendEmailService.sendTemplateEmail(to, subject, emailContent);
}
发送复杂邮件
发送邮件
// 发送邮件服务类
@Service
public class SendEmailService {
@Autowired
private JavaMailSenderImpl mailSender;
@Value("${spring.mail.username}")
private String from; //发件人地址
/**
* 发送复杂邮件(包括静态资源和附件)
* @param to 收件人地址
* @param subject 邮件标题
* @param text 邮件内容
* @param filePath 附件地址
* @param rscId 静态资源唯一标识
* @param rscPath 静态资源地址
*/
public void sendComplexEmail(String to, String subject, String text,
String filePath, String rscId, String rscPath) {
// 定制复杂邮件信息MimeMessage
MimeMessage message = mailSender.createMimeMessage();
try {
// 使用MimeMessageHelper帮助类,并设置multipart多部件使用为true
MimeMessageHelper helper = new MimeMessageHelper(message, true);
helper.setFrom(from);
helper.setTo(to);
helper.setSubject(subject);
helper.setText(text, true);
// 设置邮件静态资源
FileSystemResource res = new FileSystemResource(new File(rscPath));
helper.addInline(rscId, res);
// 设置邮件附件
FileSystemResource file = new FileSystemResource(new File(filePath));
String fileName =
filePath.substring(filePath.lastIndexOf(File.separator));
helper.addAttachment(fileName, file);
// 发送邮件
mailSender.send(message);
System.out.println("复杂邮件发送成功");
} catch (MessagingException e) {
System.out.println("复杂邮件发送失败 " + e.getMessage());
e.printStackTrace();
}
}
编写服务
// 每过5秒发送一封复杂邮件
@Scheduled(cron = "0/5 * * * * *")
public void sendComplexEmail() {
String to="1943260672@qq.com";
String subject="【邓春圆】标题";
// 定义邮件内容
StringBuilder text = new StringBuilder();
text.append("<html><head></head>");
text.append("<body><h1>祝大家元旦快乐!</h1>");
// cid为固定写法,rscId自定义的资源唯一标识
String rscId = "img001";
text.append("<img src='cid:" +rscId+"'/></body>");
text.append("</html>");
// 指定静态资源文件和附件路径
String rscPath="C:\\Users\\ssa\\Desktop\\图片\\png无背景图片\\手机图片\\A20.jpg";
String filePath="C:\\Users\\ssa\\Desktop\\day02-springboot.pdf";
// 发送复杂邮件
sendEmailService.sendComplexEmail(to,subject,text.toString(),filePath,rscId,rscPath);
}