Spring
Bean
IoC
控制反转(Inversion of Control),是 Spring 框架的核心之一
作用
- 管理对象的创建和生命周期:对象的创建和管理交由 Spring 容器 来完成,开发者只需要配置好依赖关系
- 解耦:不需要主动去依赖某个实现类,而是依赖于接口。具体的实现由容器在运行时依赖注入(Dependency Injection)
Bean的加载方式
1.<bean/>标签
通过xml配置来注入bean
1 | |
获取bean
1 | |
结果
1 | |
2.注解定义bean
可以使用的注解有@Component以及三个衍生注解@Service、@Controller、@Repository。
1 | |
加载第三方bean时可通过@bean来注入
1 | |
最后记得开启注解扫描
1 | |
3.@ComponentScan扫描bean
使用FactoryBean接口
造出来的bean并不是DogFactoryBean,而是Dog
1 | |
定义一个类并使用**@ComponentScan**替代原始xml配置中的包扫描这个动作
1 | |
通过AnnotationConfigApplicationContext获取bean
1 | |
补充
可用**@ImportResource**将xml配置文件中的bean和配置类中的bean融合在一起,在配置类上直接写上要被融合的xml配置文件名即可,算的上一种兼容性解决方案
1 | |
@Configuration注解的proxyBeanMethods属性,此属性默认值为true,若设为false,则表示注入到容器中的对象不再是唯一的
1 | |
4.@Import指定bean
通过@Import可指定要注入的bean,不需要在bean上加注解
1 | |
获取bean
1 | |
结果
1 | |
5.容器初始化完成后手动加载bean
通过AnnotationConfigApplicationContext的register方法,可以在容器初始化完成后手动加载bean
1 | |
也可以用registerBean方法指定bean的名称和参数
1 | |
6.在容器初始化过程中加载bean
(1)ImportSelector接口
实现ImportSelector接口,selectImports方法的返回值会被注入到容器中
1 | |
导入Selector配置类
1 | |
获取bean
1 | |
(2)ImportBeanDefinitionRegistrar接口
bean的加载不是一个简简单单的对象,spring中定义了一个叫做BeanDefinition的东西,它才是控制bean初始化加载的核心
ImportBeanDefinitionRegistrar相比于ImportSelector,对注入的bean有更大的操作空间
实现ImportBeanDefinitionRegistrar接口
1 | |
导入Registrar配置类
1 | |
获取bean
1 | |
7.对容器中bean的最终裁定
postProcessor为最后执行的对容器中bean的注册
实现BeanDefinitionRegistryPostProcessor接口
1 | |
导入PostProcessor配置类
1 | |
获取bean
1 | |
FactoryBean
BeanFactory的作用是完成了Bean的初始化、自动装配、存储单例Bean等功能
FactoryBean本身是一个Bean,的作用是会将自身和getObject()方法的返回值作为Bean注入到容器中
读原文FactoryBean——Spring的扩展点之一 - 掘金 (juejin.cn)做的笔记
基本使用
- 实现FactoryBean接口,将自定义的FactoryBean注入容器
- 从容器中获取
1 | |
1 | |
1 | |
1 | |
注意:
自定义的FactoryBean会向容器中注入两个Bean,一个是自身,一个是getObject()方法的返回值;
要想获取自定义的FactoryBean本身,需要在BeanName前面加上
&符号,applicationContext.getBean("&customFactoryBean")
源码
refresh()
创建容器的
finishBeanFactoryInitialization步骤,会调用到beanFactory.preInstantiateSingletons()来实例化单例Bean
preInstantiateSingletons()
如果是FactoryBean的实例,就通过
getBean()来获取或创建Bean,且在BeanName前加了个&之后会判断
isEagerInit来创建getObject()里的Bean
getObjectForBeanInstance()
在
getBean()中会调用doGetBean(),然后调用getObjectForBeanInstance()在这个方法中,会判断Bean是否是
FactoryBean的实例,如果是则判断缓存中是否有对应的实例,如果没有才会调用getObjectFromFactoryBean()
getObjectFromFactoryBean()
通过
doGetObjectFromFactoryBean()来调用FactoryBean的getObject()方法,并且判断是否为单例来决定要不要放到缓存中
doGetObjectFromFactoryBean()
最终在这个方法中调用了
getObject()
总结
通过判断
FactoryBean、getObject()里返回的对象是否被创建过,只有都没有创建过才会最终创建Bean,保证单例性
在Mybatis里的使用场景
Spring中使用Mybatis
- mybatis中,通过
sqlSessionFactoryBean来创建sqlSessionFactory MapperScannerRegistrar会将Mapper文件扫描出来生成BeanDefinition,然后会生成一个MapperFactoryBean,通过MapperFactoryBean最终会生成一个Mapper文件对应的代理对象
SpringBoot整合Mybatis
- 在SpringBoot中Mybatis的自动装配由
MybatisAutoConfiguration这个类完成,里面调用了SqlSessionFactoryBean的getObject()方法来注入了sqlSessionFactory
Bean的加载控制
@ConditionalOnClass / @ConditionalOnMissingClass
当虚拟机中有指定类时加载bean
1 | |
当虚拟机中有没有指定类时加载bean
1 | |
@ConditionalOnBean / @ConditionalOnMissingBean
当加载了指定名称的bean时加载bean
1 | |
当没有加载了指定名称的bean时加载bean
1 | |
还有一系列的条件控制注解

Bean默认是单例还是多例
在Spring框架中,Bean默认是单例的。也就是说,当应用程序中的多个地方都需要调用某个Bean时,Spring容器只会创建一个Bean的实例,并在需要时共享使用这个实例。
如果需要将Bean设置成多例,可以通过在Bean类上添加@Scope("prototype")注解来实现
1 | |
当容器需要创建这个Bean时,每次都会创建一个新的实例
Bean的生命周期
BV1L14y1S7cf的笔记
步骤
五步骤版本
- 实例化:创建对象
- 依赖注入:给成员变量赋值
- 初始化:执行自定义的初始化方法
- 使用
- 销毁
例子
Dog类,有成员变量name,自定义的初始化和销毁方法
1 | |
通过配置文件将Dog类交给Spring管理,并指定初始化和销毁方法,还有给成员变量赋值
1 | |
使用
1 | |
七步骤版本
实例化:创建对象
依赖注入:给成员变量赋值
初始化前执行 BeanPostProcessor before方法
初始化:执行自定义的初始化方法
初始化前执行 BeanPostProcessor after方法
使用
销毁
例子
创建自定义的Bean后置处理器
1 | |
交给Spring管理
1 | |
使用
1 | |
十步骤版本
实例化:创建对象
依赖注入:给成员变量赋值
执行回调函数…Aware
初始化前执行 BeanPostProcessor before方法
执行正在初始化bean函数
初始化:执行自定义的初始化方法
初始化前执行 BeanPostProcessor after方法
使用
执行销毁bean方法
销毁
例子
实现BeanNameAware, InitializingBean, DisposableBean接口
1 | |
使用
1 | |
InitializingBean作用
InitializingBean的afterPropertiesSet()是在依赖注入完、执行完回调、执行完后处理器的before后调用的。这个时候成员变量已经实例化完,可以通过afterPropertiesSet()方法来给变量进行一些初始化操作
如:
- 数据库连接池的初始化
在连接池的初始化过程中,可以通过实现InitializingBean接口,在Bean初始化完成后自动调用afterPropertiesSet()方法,进行连接池的初始化工作。 - 配置文件加载
在Spring中,可以通过实现InitializingBean接口,使用afterPropertiesSet()方法,在Bean属性设置完成后,进行配置文件的加载工作,从而减少了代码量和配置文件的加载时间。 - 线程池初始化
在Java中创建线程池时,可以通过实现InitializingBean接口,使用afterPropertiesSet()方法,在Bean属性设置完成后,进行线程池的初始化工作。 - 数据初始化
在某些场景下,需要在Bean初始化完成后,进行数据初始化的操作。可以通过实现InitializingBean接口,在Bean属性设置完成后,使用afterPropertiesSet()方法,进行相关数据的初始化工作。
BeanPostProcessor作用
BeanPostProcessor是Spring IoC容器提供的一个扩展接口,主要作用是在Spring IoC容器实例化Bean对象后,对其进行加工和装饰,添加功能
如:
- AOP代理
通过before或after方法,在Bean对象装配之后,对Bean对象进行AOP代理,从而在不修改原有代码的情况下,实现对Bean对象的增强或切面操作,如事务管理、权限控制、日志记录、性能监控等。
定制Bean
单例多例
默认单例Bean
通过@Scope("prototype")声明为多例Bean,每次都返回新的实例
注入所有同类型的Bean
1 | |
注入指定的Bean
1 | |
在注入时通过@Qualifier(beanName)指定
Bean的依赖属性注入
在配置文件中设置值
1 | |
定义一个类,通过ConfigurationProperties注解来读取和封装配置中的属性
1 | |
在需要使用配置的地方使用EnableConfigurationProperties注解来注入配置类
1 | |
在需要使用属性类的位置通过注解EnableConfigurationProperties注解加载bean,而不要直接在属性配置类上定义bean,减少资源加载的数量,因需加载而不要饱和式加载。
AOP
OOP:Object Oriented Programming,面向对象变成
AOP:Aspect Orient Programming,面向切面编程
作用:实现在不修改源代码的情况下给程序动态统一添加额外功能的一种技术,减少对业务代码的侵入
使用场景:日志、事务、权限校验、性能监控等
概念
名词
- Aspect:切面,即一个横跨多个核心逻辑的功能,或者称之为系统关注点;
- Joinpoint:连接点,指可以被动态代理拦截目标类的方法
- Pointcut:切入点,被拦截的连接点
- Advice:通知,对切入点增强的内容
- Target Object:代理的目标对象
- Weaving:织入,把增强代码应用到目标上,生成代理对象的过程
- AOP Proxy:AOP代理,生成的代理对象
通知类别
| 注解 | 含义 | 类比 |
|---|---|---|
@Before |
方法执行前 | “准备阶段” |
@After |
方法执行后(无论成功失败) | “清理阶段” |
@AfterReturning |
正常返回后 | “成功收尾” |
@AfterThrowing |
抛出异常后 | “出错应对” |
@Around |
包裹整个方法,可控制执行与否 | “万能钩子” |
切点表达式
| 表达式 | 说明 |
|---|---|
execution(* com.example.service..*(..)) |
匹配 service 包及子包下的所有方法 |
execution(public * com.example.service.UserService.*(..)) |
匹配 UserService 类的所有 public 方法 |
@annotation(org.springframework.transaction.annotation.Transactional) |
匹配被某个注解标注的方法 |
within(com.example.controller..*) |
匹配某个包下的所有类中的方法 |
使用步骤
Spring中使用
SpringBoot中使用
例:记录方法的执行时间、参数、方法名等信息
导入依赖
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- 序列化、反序列化 -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>定义日志实体类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22package com.xw.springboottest.bo;
import lombok.Data;
/**
* 系统日志bo(Business Object)
*/
@Data
public class SysLogBO {
private String className;
private String methodName;
private String params;
private Long exeuTime;
private String remark;
private String createDate;
}定义日志注解
1
2
3
4
5
6
7
8
9
10
11
12
13package com.xw.springboottest.anno;
import java.lang.annotation.*;
/**
* 定义系统日志注解
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SysLog {
String value() default "";
}(重点)定义切面
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93package com.xw.springboottest.aspect;
import com.google.gson.Gson;
import com.xw.springboottest.anno.SysLog;
import com.xw.springboottest.bo.SysLogBO;
import com.xw.springboottest.service.SysLogService;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@Aspect
@Component
public class SysLogAspect {
@Autowired
private SysLogService sysLogService;
// 定义切点
@Pointcut("@annotation(com.xw.springboottest.anno.SysLog)")
public void logPointCut() {}
/**
* 环绕通知
* @param point
* @return
* @throws Throwable
*/
@Around("logPointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
// 运行开始时间
long beginTime = System.currentTimeMillis();
// 执行目标方法
Object result = point.proceed();
// 运行结束时间
long endTime = System.currentTimeMillis();
try {
saveLog(point, endTime - beginTime);
} catch (Exception e) {
}
return result;
}
/**
* 保存日志
*/
private void saveLog(ProceedingJoinPoint joinPoint, long time) {
// 拿到当前执行方法的签名信息
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
// 创建日志对象
SysLogBO sysLogBO = new SysLogBO();
sysLogBO.setExeuTime(time);
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
sysLogBO.setCreateDate(dateFormat.format(new Date()));
// 判断目标方法是否有 @SysLog 注解
SysLog sysLog = method.getAnnotation(SysLog.class);
if(sysLog != null){
// 保存注解上的描述
sysLogBO.setRemark(sysLog.value());
}
//请求的 类名、方法名
String className = joinPoint.getTarget().getClass().getName();
String methodName = signature.getName();
sysLogBO.setClassName(className);
sysLogBO.setMethodName(methodName);
// 请求的参数
Object[] args = joinPoint.getArgs();
try{
List<String> list = new ArrayList<>();
for (Object o : args) {
// 将参数转化为json字符串
list.add(new Gson().toJson(o));
}
sysLogBO.setParams(list.toString());
}catch (Exception e){ }
// 保存日志
sysLogService.save(sysLogBO);
}
}测试
1
2
3
4
5
6
7
8
9@RestController
public class TestController {
@SysLog("测试")
@GetMapping("/test")
public String test(@RequestParam("name") String name){
return name;
}
}
实现原理
采用JDK动态代理和CGLib动态代理实现
- 如果目标对象有实现接口,则使用JDK动态代理
- 如果没有实现接口,则使用CGLib动态代理
JDK动态代理
通过反射实现代理
步骤
- 定义实现InvocationHandler接口的处理器
- 通过**Proxy.newInstance(类加载器,所有代理目标实现的接口,处理器)**来获取代理对象
- 使用代理对象的方法
优点:
- 原生,不需要其他依赖
- 生成代理类的速度快
缺点:
- 通过接口的方法来织入增强方法,因此目标必须需要实现接口
- 执行方法的速度相对较慢
1 | |
1 | |
CGLib动态代理
通过操作字节码实现代理
Cglib的实现是在字节码的基础上的,并且使用了开源的ASM读取字节码,对类实现增强功能的。
SpringMVC
MVC(Model-View-Controller),模型-视图-控制器
作用
SpringMVC底层就是Servlet,SpringMVC就是对Servlet进行深层次的封装
MVC模式:
Model表示数据和应用逻辑,可能包括数据库操作等。View是用户所看到的界面,它展示Model中的数据并让用户与其进行交互。Controller负责处理用户请求,从View中接收输入并在Model里面读取或修改数据,负责把模块和视图结合起来

执行流程

- 用户发送请求到前端调度器(DispatcherServlet)
- 调度器收到请求后调用处理器映射器(HandlerMapping)
- 处理器映射器根据url找到具体的处理器和拦截器,生成执行链,返回给调度器
- 调度器通过处理器适配器(HandlerAdapter)调用具体的处理器(Handler),返回ModelAndView
- 调度器将ModelAndView传给视图解析器(ViewReslover)解析,返回视图对象(View)
- 调度器渲染视图,把视图和数据结合,响应给用户
组件
- 前端调度器(DispatcherServlet):接收请求,响应数据,需要程序员配置
- 处理器映射器(HandlerMapping):通过url查找Handler
- 处理器适配器(Handler Adapter):执行Handler中的方法
- 处理器(Handler):业务方法,需要程序员编写
- 视图解析器(ViewResolver):把ModelAndView解析出给用户看的视图
- 视图(View):展示给用户看的,需要程序员编写
问题
调度器(DispatcherServlet)如何获取处理器(Handler)?
调度器里有一个doDispatch()方法,里面调用了getHandler()方法;在getHandler()里面调用了处理器映射器(HandlerMapping)的getHandler()方法来获取处理器
调度器如何执行处理器?
调度器里的doDispatch()方法里,首先通过getHandlerAdapter()方法获取当前处理器的处理器适配器(HandlerAdapter)来做适配,然后执行适配器的handler()方法
调度器如何解析视图对象(ModelAndView)的?
调度器里的doDispatch()方法里,调用processDispatcherResult()方法,方法里调用render()方法,render()方法调用resolveViewName(),通过遍历的方式来找和viewName相同的View
三大组件(HandlerMapping、HandlerAdapter、ViewResolver)是谁初始化的?
SpringMVC中通过DispatcherServlet中的initStrategies()方法,通过默认配置来找到类所在位置,来初始化组件
Spring Cache
通过注解来实现缓存,底层可以切换不同的cache实现,通过CacheManager接口来统一不同的缓存技术
对不同的缓存技术需要实现不同的CacheManager
| CacheManager | 描述 |
|---|---|
| EhCacheCacheManager | 使用EhCache缓存技术 |
| GuavaCacheManager | 使用Guava缓存技术 |
| RedisCacheManager | 使用Redis缓存技术 |
常用注解
| 注解 | 说明 |
|---|---|
| @EnableCaching | 开启缓存注解功能 |
| @Cacheable | 优先从缓存中取数据,没有去数据库取 |
| @CachePut | 将方法返回值放到内存中 |
| @CacheEvict | 将一条或多条数据从缓存中删除 |
实例1
导入坐标
1 | |
配置相应技术
1 | |
启动类加@EnableCaching注解
在要缓存的方法上加注解, 返回值会被存到redis中, 返回值必须是可序列化的
1 | |
实例2
在微服务中使用spring cache
例子来自尚医通
在common组件中添加依赖
1
2
3
4
5
6
7
8
9
10
11
12<!-- redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- spring2.X集成redis所需common-pool2-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.7.0</version>
</dependency>在common组件中添加redis配置类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85package com.xw.config;
@Configuration
@EnableCaching
public class RedisConfig {
/**
* 自定义key规则
* @return
*/
@Bean
public KeyGenerator keyGenerator() {
return new KeyGenerator() {
@Override
public Object generate(Object target, Method method, Object... params) {
StringBuilder sb = new StringBuilder();
sb.append(target.getClass().getName());
sb.append(method.getName());
for (Object obj : params) {
sb.append(obj.toString());
}
return sb.toString();
}
};
}
/**
* 设置RedisTemplate规则
* @param redisConnectionFactory
* @return
*/
@Bean
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
//解决查询缓存转换异常的问题
ObjectMapper om = new ObjectMapper();
// 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和public
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
// 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会跑出异常
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
//序列号key value
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
/**
* 设置CacheManager缓存规则
* @param factory
* @return
*/
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory) {
RedisSerializer<String> redisSerializer = new StringRedisSerializer();
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
//解决查询缓存转换异常的问题
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
// 配置序列化(解决乱码的问题),过期时间600秒
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofSeconds(600))
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
.disableCachingNullValues();
RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
.cacheDefaults(config)
.build();
return cacheManager;
}
}在使用到缓存的模块中添加配置
1
2
3
4
5
6
7
8
9
10spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.database= 0
spring.redis.timeout=1800000
spring.redis.lettuce.pool.max-active=20
spring.redis.lettuce.pool.max-wait=-1
#最大阻塞等待时间(负数表示没限制)
spring.redis.lettuce.pool.max-idle=5
spring.redis.lettuce.pool.min-idle=0在需要缓存的方法上添加注解
1
2
3
4
5
6
7
8
9
10
11
12
13// 根据id查询子数据列表
@Cacheable(value = "dict",keyGenerator = "keyGenerator")
@Override
public List<Dict> findChildData(Long id) {
LambdaQueryWrapper<Dict> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(Dict::getParentId, id);
List<Dict> dictList = baseMapper.selectList(wrapper);
for (Dict dict : dictList) {
boolean hasChildren = this.isChildren(dict.getId());
dict.setHasChildren(hasChildren);
}
return dictList;
}
SpringTask
SpringTask是Spring自主研发的轻量级定时任务工具
Cron表达式
格式:秒 分 时 日 月 星期
| 时间元素 | 可出现的字符 | 有效数值范围 |
|---|---|---|
| Seconds | , - * / | 0-59 |
| Minutes | , - * / | 0-59 |
| Hours | , - * / | 0-23 |
| DayofMonth | , - * / ? L W | 0-31 |
| Month | , - * / | 1-12 |
| DayofWeek | , - * / ? L # | 1-7或SUN-SAT |
说明:
| 字符 | 作用 | 举例 |
|---|---|---|
| , | 列出枚举值 | 在Minutes域使用5,10,表示在5分和10分各触发一次 |
| - | 表示触发范围 | 在Minutes域使用5-10,表示从5分到10分钟每分钟触发一次 |
| * | 匹配任意值 | 在Minutes域使用*, 表示每分钟都会触发一次 |
| / | 起始时间开始触发,每隔固定时间触发一次 | 在Minutes域使用5/10,表示5分时触发一次,每10分钟再触发一次 |
| ? | 在DayofMonth和DayofWeek中,用于匹配任意值 | 在DayofMonth域使用?,表示每天都触发一次 |
| # | 在DayofMonth中,确定第几个星期几 | 1#3表示第三个星期日 |
| L | 表示最后 | 在DayofWeek中使用5L,表示在最后一个星期四触发 |
| W | 表示有效工作日(周一到周五) | 在DayofMonth使用5W,如果5日是星期六,则将在最近的工作日4日触发一次 |
示例:
每两秒执行一次
1
0/2 * * * * *每个二、四、六星期的下午两点执行一次
1
* * 14 * * 2,4,6
使用
SpringTask已经存在于Spring框架中,无需添加新的依赖
@EnableScheduling开启定时任务功能1
2
3
4@Configuration
@EnableScheduling
public class SpringTaskConfig {
}使用示例
每十分钟执行一次
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17package com.xw.mallLearning.component;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class OrderTimeOutCancelTask {
@Scheduled(cron = "0 0/10 * * * *")
private void cancelTimeOutOrder() {
// 定时任务
log.info("task");
}
}