石一歌的SpringBoot笔记
运行原理探究(2.6.2)
基于狂神的springboot运行原理探究,自己操作一下 2.6.2版本
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.6.2</version> <relativePath/> </parent>
前置知识
java元注解
- @Target 表示该注解用于什么地方。
- @Retention 表示在什么级别保存该注解信息。
- @Documented 将此注解包含在 javadoc 中 ,它代表着此注解会被javadoc工具提取成文档。
- @Inherited 允许子类继承父类中的注解。
@SpringBootApplication
主启动类,主要目的是开启自动配置
@Target({ElementType.TYPE})// 元注解,该注解可修饰类,接口(包括注解类型)或enum声明
@Retention(RetentionPolicy.RUNTIME)// 元注解,注解保留级别 VM将在运行期保留注释,可通过反射读取注解的信息
@Documented// 元注解 注解会被javadoc工具提取成文档
@Inherited// 元注解 允许子类继承父类中的注解
@SpringBootConfiguration// springboot配置注解
@EnableAutoConfiguration// 自动配置注解 核心
@ComponentScan(// 组件扫描注解
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
...
}
@SpringBootConfiguration
SpringBoot的配置类,继承了@Configuration,功能类似
@Target({ElementType.TYPE})// 元注解,该注解可修饰类,接口(包括注解类型)或enum声明
@Retention(RetentionPolicy.RUNTIME)// 元注解,注解保留级别 VM将在运行期保留注释,可通过反射读取注解的信息
@Documented// 元注解 注解会被javadoc工具提取成文档
@Configuration// 配置注解 将当前类标注为配置类,配合@bean使用
@Indexed// 索引注解,为Spring的模式注解添加索引,以提升应用启动性能。
public @interface SpringBootConfiguration {
...
}
@EnableAutoConfiguration
开启自动配置
@Target({ElementType.TYPE})// 元注解,该注解可修饰类,接口(包括注解类型)或enum声明
@Retention(RetentionPolicy.RUNTIME)// 元注解,注解保留级别 VM将在运行期保留注释,可通过反射读取注解的信息
@Documented// 元注解 注解会被javadoc工具提取成文档
@Inherited// 元注解 允许子类继承父类中的注解
@AutoConfigurationPackage// 自动配置包注解,启动类注解所在包的自包
@Import({AutoConfigurationImportSelector.class})// 导入自动配置导入选择器组件
public @interface EnableAutoConfiguration {
...
}
AutoConfigurationImportSelector
自动配置导入选择器
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
return configurations;
}
调用SpringFactoriesLoader.loadFactoryNames()方法
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
ClassLoader classLoaderToUse = classLoader;
if (classLoader == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
String factoryTypeName = factoryType.getName();
return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}
调用SpringFactoriesLoader.loadSpringFactories()方法
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
Map<String, List<String>> result = (Map)cache.get(classLoader);
if (result != null) {
return result;
} else {
HashMap result = new HashMap();
try {
Enumeration urls = classLoader.getResources("META-INF/spring.factories");
while(urls.hasMoreElements()) {
URL url = (URL)urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
Iterator var6 = properties.entrySet().iterator();
while(var6.hasNext()) {
Entry<?, ?> entry = (Entry)var6.next();
String factoryTypeName = ((String)entry.getKey()).trim();
String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
String[] var10 = factoryImplementationNames;
int var11 = factoryImplementationNames.length;
for(int var12 = 0; var12 < var11; ++var12) {
String factoryImplementationName = var10[var12];
((List)result.computeIfAbsent(factoryTypeName, (key) -> {
return new ArrayList();
})).add(factoryImplementationName.trim());
}
}
}
result.replaceAll((factoryType, implementations) -> {
return (List)implementations.stream().distinct().collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
});
cache.put(classLoader, result);
return result;
} catch (IOException var14) {
throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var14);
}
}
}
源头
@AutoConfigurationPackage
@Target({ElementType.TYPE})// 元注解,该注解可修饰类,接口(包括注解类型)或enum声明
@Retention(RetentionPolicy.RUNTIME)// 元注解,注解保留级别 VM将在运行期保留注释,可通过反射读取注解的信息
@Documented// 元注解 注解会被javadoc工具提取成文档
@Inherited// 元注解 允许子类继承父类中的注解
@Import({Registrar.class})// 导入注册组件,将主启动类的所在包及包下面所有子包里面的所有组件扫描到Spring容器
public @interface AutoConfigurationPackage {
...
}
@ComponentScan
自动扫描并加载符合条件的组件或bean
总结
-
@SpringBootApplication 主配置类
-
@SpringBootConfiguration SpringBoot的配置类
- Configuration 配置类对应spring的xml
-
-
@EnableAutoConfiguration springboot自动配置
-
@AutoConfigurationPackage 自动配置包
- @import(Registrar.class) 将主启动类的所在包及包下面所有子包里面的所有组件扫描到Spring容器
-
@Import({AutoConfigurationImportSelector.class}) :自动配置导入选择器
- AutoConfigurationImportSelector.getCandidateConfigurations 调用
- SpringFactoriesLoader.loadFactoryNames 调用
- SpringFactoriesLoader.loadSpringFactories 调用
- META-INF/spring.factories 配置源文件
- SpringFactoriesLoader.loadSpringFactories 调用
- SpringFactoriesLoader.loadFactoryNames 调用
- AutoConfigurationImportSelector.getCandidateConfigurations 调用
-
-
@ComponentScan 自动扫描并加载符合条件的组件或bean
结论:
- SpringBoot在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值
- 将这些值作为自动配置类导入容器 , 自动配置类生效 ,进行自动配置工作
- 整个J2EE的整体解决方案和自动配置都在springboot-autoconfigure的jar包中
- 它会给容器中导入非常多的自动配置类 (xxxAutoConfiguration), 就是给容器中导入这个场景需要的所有组件 , 并配置好这些组件
自动装配原理
//表明为配置类
@Configuration(
proxyBeanMethods = false
)
//判断 有DataSource.class, EmbeddedDatabaseType.class,实例化
@ConditionalOnClass({DataSource.class, EmbeddedDatabaseType.class})
//判断 无io.r2dbc.spi.ConnectionFactory,实例化
@ConditionalOnMissingBean(
type = {"io.r2dbc.spi.ConnectionFactory"}
)
//自动配置之前加载SqlInitializationAutoConfiguration.class
@AutoConfigureBefore({SqlInitializationAutoConfiguration.class})
//将配置文件中对应的值和DataSourceProperties绑定起来;并把DataSourceProperties加入到ioc容器中
@EnableConfigurationProperties({DataSourceProperties.class})
//导入DataSourcePoolMetadataProvidersConfiguration.class, InitializationSpecificCredentialsDataSourceInitializationConfiguration.class, SharedCredentialsDataSourceInitializationConfiguration.class
@Import({DataSourcePoolMetadataProvidersConfiguration.class, InitializationSpecificCredentialsDataSourceInitializationConfiguration.class, SharedCredentialsDataSourceInitializationConfiguration.class})
public class DataSourceAutoConfiguration {
public DataSourceAutoConfiguration() {
}
...
}
总结
- 一但这个配置类生效;这个配置类就会给容器中添加各种组件;
- 这些组件的属性是从对应的properties类中获取的,这些类里面的每一个属性又是和配置文件绑定的;
- 所有在配置文件中能配置的属性都是在xxxxProperties类中封装着;
- 配置文件能配置什么就可以参照某个功能对应的这个属性类
Springboot启动流程
-
SpringBoot启动加载大量的自动配置类(spring.factories)
-
SpringBoot默认写好的自动配置类
-
自动配置类中配置组件
-
给容器中自动配置类添加组件的时候,会从properties类中获取某些属性(用配置文件可修改)。
Yaml注入配置文件
用@ConfigurationProperties(prefix = "person")代替@Value
此注解需要导入配置文件处理器
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
配置javabean Person
@PropertySource(value = "classpath:person.yaml")//指定配置文件
@Component //注册bean
@ConfigurationProperties(prefix = "person")//与配置文件下person属性对应
public class Person {
private String name;
private Integer age;
private Boolean happy;
private Date birth;
private Map<String,Object> maps;
private List<Object> lists;
private Dog dog;
//有参无参构造、get、set方法、toString()方法
}
yaml文件 person.yaml
person:
name: qinjiang${random.uuid} # 随机uuid 占位符
age: ${random.int} # 随机int 占位符
happy: false
birth: 2000/01/01
maps: {k1: v1,k2: v2}
lists:
- code
- girl
- music
dog:
name: 旺财
age: 1
数据校验
类注解@Validated
参数注解如下
空检查
@Null 验证对象是否为null
@NotNull 验证对象是否不为null, 无法查检长度为0的字符串
@NotBlank 检查约束字符串是不是Null还有被Trim的长度是否大于0,只对字符串,且会去掉前后空格.
@NotEmpty 检查约束元素是否为NULL或者是EMPTY.
Booelan检查
@AssertTrue 验证 Boolean 对象是否为 true
@AssertFalse 验证 Boolean 对象是否为 false
长度检查
@Size(min=, max=) 验证对象(Array,Collection,Map,String)长度是否在给定的范围之内
@Length(min=, max=) string is between min and max included.
日期检查
@Past 验证 Date 和 Calendar 对象是否在当前时间之前
@Future 验证 Date 和 Calendar 对象是否在当前时间之后
@Pattern 验证 String 对象是否符合正则表达式的规则
多配置文件切换
2.4版本之后spring.profiles.active弃用升级到spring.config.activate.on-profile
properties
-
application.properties主配置文件选择具体配置文件
- spring.config.activate.on-profile=dev
-
命名格式:application-{profile}.properties)
yaml
-
文件块区分
-
spring: profiles: active: test #选择要激活那个环境块 --- server: port: 8082 spring: config: activate: on-profile: dev --- server: port: 8083 spring: config: activate: on-profile: test
配置文件优先级
优先级自上而下
-
项目路径下的config文件夹配置文件
-
项目路径下配置文件
-
资源路径下的config文件夹配置文件
-
资源路径下配置文件
-
properties
-
yaml
自定义Starter
数据源
Hikari(默认)
spring:
datasource:
username: root
password: root
url: jdbc:mysql://localhost:3306/mybatis_plus?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.zaxxer.hikari.HikariDataSource #Spring Boot 2.0 默认,无需配置
hikari:
maximum-pool-size: 15 #最大连接数
minimum-idle: 5 #最小空闲连接
connection-timeout: 60000 #连接超时时间(毫秒)
idle-timeout: 600000 #连接在连接池中空闲的最长时间
max-lifetime: 3000000 #连接最大存活时间
connection-test-query: select 1 #连接测试查询
auto-commit: true #自动提交行为
pool-name: HikariCPDataSource
Druid
传统配置
pom
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.6</version>
</dependency>
application.yaml
spring:
datasource:
username: root
password: root
url: jdbc:mysql://127.0.0.1:3306/mybatis?useUnicode=true&characterEncoding=UTF-8&useSSL=true&serverTimezone=UTC
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
#druid 数据源专有配置,Spring Boot无法解析,自己绑定
initialSize: 5
minIdle: 5
maxActive: 20
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
#配置监控统计拦截的filters,stat:监控统计 log4j:日志记录 wall:防御sql注入
#如果允许时报错 java.lang.ClassNotFoundException: org.apache.log4j.Priority
#则导入 log4j 依赖即可,Maven 地址:https://mvnrepository.com/artifact/log4j/log4j
filters: stat,wall,log4j
maxPoolPreparedStatementPerConnectionSize: 20
useGlobalDataSourceStat: true
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
DruidConfig.java
Druid配置文件:绑定druid专有配置,设置监控及其过滤器
@Configuration
public class DruidConfig {
//绑定配置文件
@ConfigurationProperties(prefix = "spring.datasource")
@Bean
public DataSource druidDataSource() {
return new DruidDataSource();
}
//配置 Druid 监控管理后台的Servlet;
//内置 Servlet 容器时没有web.xml文件,所以使用 Spring Boot 的注册 Servlet 方式
@Bean
public ServletRegistrationBean statViewServlet() {
ServletRegistrationBean bean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");
// 这些参数可以在 com.alibaba.druid.support.http.StatViewServlet
// 的父类 com.alibaba.druid.support.http.ResourceServlet 中找到
Map<String, String> initParams = new HashMap<>();
initParams.put("loginUsername", "admin"); //后台管理界面的登录账号
initParams.put("loginPassword", "123456"); //后台管理界面的登录密码
//后台允许谁可以访问
//initParams.put("allow", "localhost"):表示只有本机可以访问
//initParams.put("allow", ""):为空或者为null时,表示允许所有访问
initParams.put("allow", "");
//deny:Druid 后台拒绝谁访问
//initParams.put("kuangshen", "192.168.1.20");表示禁止此ip访问
//设置初始化参数
bean.setInitParameters(initParams);
return bean;
}
//配置 Druid 监控 之 web 监控的 filter
//WebStatFilter:用于配置Web和Druid数据源之间的管理关联监控统计
@Bean
public FilterRegistrationBean webStatFilter() {
FilterRegistrationBean bean = new FilterRegistrationBean();
bean.setFilter(new WebStatFilter());
//exclusions:设置哪些请求进行过滤排除掉,从而不进行统计
Map<String, String> initParams = new HashMap<>();
initParams.put("exclusions", "*.js,*.css,/druid/*,/jdbc/*");
bean.setInitParameters(initParams);
//"/*" 表示过滤所有请求
bean.setUrlPatterns(Arrays.asList("/*"));
return bean;
}
}
starter
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
url: jdbc:mysql://localhost:3306/test01
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
druid:
# Druid数据源配置
# 初始连接数
initialSize: 5
# 最小连接池数量
minIdle: 10
# 最大连接池数量
maxActive: 20
# 配置获取连接等待超时的时间
maxWait: 60000
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
timeBetweenEvictionRunsMillis: 60000
# 配置一个连接在池中最小生存的时间,单位是毫秒
minEvictableIdleTimeMillis: 300000
# 配置一个连接在池中最大生存的时间,单位是毫秒
maxEvictableIdleTimeMillis: 900000
# 配置检测连接是否有效
validationQuery: SELECT 1
#申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。
testWhileIdle: true
#配置从连接池获取连接时,是否检查连接有效性,true每次都检查;false不检查。做了这个配置会降低性能。
testOnBorrow: false
#配置向连接池归还连接时,是否检查连接有效性,true每次都检查;false不检查。做了这个配置会降低性能。
testOnReturn: false
#打开PsCache,并且指定每个连接上PSCache的大小
poolPreparedStatements: true
maxPoolPreparedStatementPerConnectionSize: 20
#配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
filters: stat,wall,log4j
#合并多个DruidDatasource的监控数据
useGlobalDataSourceStat: true
#通过connectProperties属性来打开mergesql功能罗慢sQL记录
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500;
#开启监控页,设置账号密码
stat-view-servlet:
enabled: true
login-password: 123456
login-username: admin
#设置web过滤,过滤一些不必要的网页
web-stat-filter:
enabled: true
url-pattern: /*
exclusions: *.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*
整合mybatis
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>
mybatis:
config-locations: classpath:mybatis/mybatis-config.xml
mapper-locations: classpath:mybatis/mapper/*.xml
type-aliases-package: com.nuc.pojo
Web开发
静态资源处理
默认配置(resources文件夹之下)
"classpath:/META-INF/resources/"
"classpath:/resources/"
"classpath:/static/"
"classpath:/public/"
手动指定
spring:
resources:
static-locations: classpath:/coding/,classpath:/kuang/
首页处理
静态资源文件夹下的所有 index.html 页面;被 /** 映射
图标
-
静态资源文件夹下自定义favicon.ico, 常放置在public文件夹下
-
关闭springboot默认图标
spring: mvc: favicon: enabled: false
Thymeleaf
无需配置
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
语法
-
命名空间
<html lang="en" xmlns:th="http://www.thymeleaf.org">
-
类比jsp
国际化
-
resources文件夹新建i18n文件夹
-
其中新建properties文件
命名格式 xxx.properties, xxx_en_US.properties, xxx_zh_CN.properties
-
右击资源包,可以添加新的属性文件
-
双击资源包,进入国际化视图,可以快速添加新属性
-
配置路径
spring: messages: basename: i18n.login
-
Thymeleaf获取国际化值 #
-
前端链接修改
<a class="btn btn-sm" th:href="@{/index.html(l='zh_CN')}">中文</a> <a class="btn btn-sm" th:href="@{/index.html(l='en_US')}">English</a>
-
后端组件处理
public class MyLocaleResolver implements LocaleResolver { //解析请求 @Override public Locale resolveLocale(HttpServletRequest request) { String language = request.getParameter("l"); Locale locale = Locale.getDefault(); // 如果没有获取到就使用系统默认的 //如果请求链接不为空 if (!StringUtils.isEmpty(language)){ //分割请求参数 String[] split = language.split("_"); //国家,地区 locale = new Locale(split[0],split[1]); } return locale; } @Override public void setLocale(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Locale locale) { } }
-
web配置中注册bean
@Bean public LocaleResolver localeResolver() { return new MyLocaleResolver(); }
Swagger
swagger-ui
-
pom
<dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.9.2</version> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>2.9.2</version> </dependency>
-
swaggerConfig
@Configuration //配置类 @EnableSwagger2// 开启Swagger2的自动配置 public class SwaggerConfig { @Bean public Docket docket(Environment environment) { Profiles of = Profiles.of("dev", "test"); // 判断当前是否处于该环境 // 通过 enable() 接收此参数判断是否要显示 boolean swaggerEnable = environment.acceptsProfiles(of); return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .enable(swaggerEnable) // 配置是否启用Swagger,如果是false,在浏览器将无法访问 .select()// 通过.select()方法,去配置扫描接口,RequestHandlerSelectors配置如何扫描接口 .apis(RequestHandlerSelectors.basePackage("com.nuc.controller.controller"))// RequestHandlerSelectors配置如何扫描接口 .paths(PathSelectors.ant("/nuc/**")) // 配置如何通过path过滤,即这里只扫描请求以/nuc开头的接口 .build(); } private ApiInfo apiInfo() { Contact contact = new Contact("石一歌", "http://xxx.xxx.com/shiyge", "联系人邮箱"); return new ApiInfoBuilder() .title("XX服务后端API") .description("XX服务专用API,其他系统请勿调用!") .version("1.0") .termsOfServiceUrl("http://www.springboot.vue.com") .contact(contact) .license("Apach 2.0 许可") .licenseUrl("许可链接").build(); } }
-
搭配使用的注解
- @Api:作用于类上,标识此类是Swagger的资源。
- @ApiOperation:主要作用于方法上,用于对一个接口进行说明。
- @ApiParam:用于方法,参数,字段上,用于对参数进行说明。
- @ApiModel:作用于类上,主要用于实体类当接口参数时使用,对实体类进行说明。
- @ApiModelProperty:作用于方法和字段上,一般用于实体类的属性说明。
-
举例
-
实体类
@ApiModel(value = "管理员实体") @Data public class AdminDTO { @ApiModelProperty(value = "用户ID", required = true, example = "1548w4dwf7as1a21cv4") private String personId; @ApiModelProperty(value = "用户名", example = "陈皮") private String name; @ApiModelProperty(value = "用户年龄", required = true, example = "18") private Integer age; }
-
接口类
@Api(tags = {"管理员相关"}) @RestController @RequestMapping("admin") public class AdminController { @ApiOperation(value = "添加管理员", notes = "注意:只有管理员权限才能添加管理员", produces = "application/json", consumes = "application/json") @PostMapping("add") public AdminDTO add(@ApiParam(value = "参数体", required = true) @RequestBody AdminDTO adminDTO) { return adminDTO; } @ApiOperation(value = "管理员列表", notes = "注意:只有管理员权限才能获取管理员列表", produces = "application/json") @GetMapping("list") public List<AdminDTO> list( @ApiParam(value = "页码,从1开始,1,2,3...", required = true, example = "1") @RequestParam Integer pageIndex, @ApiParam(value = "页量", required = true, example = "10") @RequestParam Integer pageSize) { return new ArrayList<>(); } }
-
Knife4J(推荐)
目前已经发行的Knife4j版本,其本身已经引入了springfox,所以我们不需要再单独引入Springfox的具体版本,否则会导致版本冲突。注意,使用Knife4j2.0.6及以上的版本,SpringBoot的版本必须大于等于2.2.x
pom
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>
Layui-ui
pom
<dependency>
<groupId>com.github.caspar-chen</groupId>
<artifactId>swagger-ui-layer</artifactId>
<version>1.1.3</version>
</dependency>
mg-ui
pom
<dependency>
<groupId>com.zyplayer</groupId>
<artifactId>swagger-mg-ui</artifactId>
<version>2.0.1</version>
</dependency>
异步任务
-
@Async
该注解加在异步方法上,告诉springboot,该方法为异步方法
-
@EnableAsync
该注解加在主启动类上,开启异步注解功能
定时任务
-
@Scheduled
该注解加在定时方法上,告诉springboot,该方法为定时方法
配合cron表达式使用,插一句,云服务器上跑脚本也常常用cron表达式
@Scheduled(cron = "0 * * * * 0-7")
-
@EnableScheduling
该注解加在主启动类上,开启定时任务功能
邮件任务
-
pom
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-mail</artifactId> </dependency>
-
yaml
# qq需要配置ssl spring: mail: username: 24736743@qq.com password: qq授权码 host: smtp.qq.com properties: mail: smtp: ssl: enable: true
-
简单使用
@Autowired JavaMailSenderImpl mailSender; @Test public void contextLoads() { //简单邮件 SimpleMailMessage message = new SimpleMailMessage(); message.setSubject("标题"); message.setText("正文"); message.setTo("收信地址"); message.setFrom("发信地址"); mailSender.send(message); } @Test public void contextLoads2() throws MessagingException { //复杂邮件 MimeMessage mimeMessage = mailSender.createMimeMessage(); MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true); helper.setSubject("标题"); helper.setText("正文(可使用富文本)",true); helper.addAttachment("1.jpg",new File(""));//发送附件 helper.setTo("收信地址"); helper.setFrom("发信地址"); mailSender.send(mimeMessage); }