SpringBoot进阶
@Schedule
在SpringBoot入口或配置类中增加@EnableScheduling注解
有三种调度器
1、Cron表达式
2、固定间隔任务 fixedDelay 单位ms
下一次的任务执行时间,是从方法最后一次任务执行结束时间开始计算。并以此规则开始周期性的执行任务。
3、固定频率任务 fixedRate 单位ms
设定一个频率,任务每隔一个频率时间执行一次,若上一个任务在这个频率时间内未执行完成,则等待上一个任务执行完成后立即执行下次任务。
@Schedule默认开启一个单线程池,可通过重写configureTasks方法设置自定义线程池。
@Configuration
public class ScheduleConfig implements SchedulingConfigurer {
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setScheduler(taskExecutor());
}
public Executor taskExecutor() {
return Executors.newScheduledThreadPool(20);
}
}
或者可以直接在properties文件中加入如下配置
spring.task.scheduling.pool.size=20
此时不同的Schedule会并行执行,但是单个任务仍然会被自己堵塞。
如果需要单个任务被堵塞的情况下,该任务在下一个触发时间仍然执行,则同时使用@Async
至于为什么单个任务仍然会被自己堵塞,而不使用Schedule线程池的其他线程来执行,有时间再看看源码。
@Async
启动加上@EnableAsync,需要执行的异步方法上加上@Async
可为@Async配置线程池
如果异步方法是当前类的方法,AOP会失效。需要经过代理类调用接口,所以需要将异步的代码单独抽取成一个类调用接口。
@Retry
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
<version>1.3.0</version>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>
启动加上 @EnableRetry
@Retryable(recover = "recover", value = Exception.class, maxAttempts = 5, backoff = @Backoff(delay = 3000, multiplier = 1, maxDelay = 5000))
public void retry(Integer userId, String message) {
client.sendMessage(userId)
}
//value:触发重试的异常
//include:和value一样,默认空,当exclude也为空时,所有异常都重试
//exclude:指定异常不重试,默认空,当include也为空时,所有异常都重试
//maxAttempts:重试次数(包括第一次调用)
//backoff:重试补偿机制,默认无
//delay:重试的间隔时间
//multiplier:每次延迟是上一次延迟的倍数
//maxDelay:重试次数之间的最大时间间隔,默认为0,如果小于delay的设置,则默认为30000L
//recover:指定补偿方法执行
@Recover
public void recover(Exception e, Integer userId, String message){
MessageException message = new MessageException();
message.setUserId(userId);
message.setMessage(message);
//异常消息插入异常表
messageExceptionMapper.insert(noticeException);
}
@Slf4j
log.warn("code={},msg={}", 500, "异常", e);
//在log中用{}占位即可
@Autowired & @Resource
(1)提供方:@Autowired是由org.springframework.beans.factory.annotation.Autowired提供,换句话说就是由Spring提供;@Resource是由javax.annotation.Resource提供,即J2EE提供,需要JDK1.6及以上。
(2)注入方式:@Autowired只按照byType 注入;@Resource默认按byName自动注入,也提供按照byType 注入;
(3)属性:@Autowired按类型装配依赖对象,默认情况下它要求依赖对象必须存在,如果允许null值,可以设置它required属性为false。如果我们想使用按名称装配,可以结合@Qualifier注解一起使用。
@Resource装配顺序
-
如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常
-
如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常
-
如果指定了type,则从上下文中找到类型匹配的唯一bean进行装配,找不到或者找到多个,都会抛出异常
-
如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配;
@RequestMapping中的params(请求参数映射限定)
@RequestMapping(value="/test",params= {"name"})
//必须包含参数name,无论name有没有值
@RequestMapping(value="/test",params= {"!name"})
//不可包含参数name
@RequestMapping(value="/test",params= {"name=yu"})
//必须包含name=yu
@RequestMapping(value="/test",params= {"name!=yu"})
//没有name或有name但name不为yu
@RequestMapping(value="/test",params= {"id", "name!=yu"})
//多条件
FilterRegistrationBean
public class TestFilter implements Filter {
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
throws IOException, ServletException {
// 相关拦截操作
chain.doFilter(req,resp);
}
}
@Configuration
public class filterConfiguration {
@Bean(name = "xxx")
public FilterRegistrationBean<TestFilter> filterTest(){
FilterRegistrationBean<TestFilter> bean = new FilterRegistrationBean<TestFilter>();
bean.setFilter(new TestFilter());//注册自定义过滤器
bean.setName("myFilter");//过滤器名称
bean.addUrlPatterns("/*");//过滤所有路径
bean.addUrlPattern(".html")//过滤所有html文件
bean.setOrder(1);//过滤优先级,最顶级
return bean;
}
}
Quartz
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
@Configuration
public class QuartzConfig {
@Bean
public JobDetail taskJobDetail() {
return JobBuilder.newJob(Task.class)
.withIdentity("task") //生成JobKey
.storeDurably(true)
.build();
}
@Bean
public Trigger taskTrigger() {
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule("*/4 * * * * ?");
return TriggerBuilder.newTrigger()
.forJob(taskJobDetail())
.withIdentity("task") //生成triggerKey
.withSchedule(scheduleBuilder)
.build();
}
}
@Component
@DisallowConcurrentExecution //使quartz的单任务串行执行,多任务并行执行
public class Task extends QuartzJobBean {
@Override
protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Quartz可以动态设置定时任务,也可持久化定时任务
@SuppressWarnings
抑制警告,让编译器编译完成后不提示相关警告。如果使用IDEA的话,最直观的一点就是可以把代码页右边的黄色小横线给去掉。
参考Intellij idea的抑制警告(SuppressWarnings)的使用方法
Google Guava
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>18.0</version>
</dependency>
// 缓存接口这里是LoadingCache,LoadingCache在缓存项不存在时可以自动加载缓存
LoadingCache<Integer, Student> studentCache
// CacheBuilder的构造函数是私有的,只能通过其静态方法newBuilder()来获得CacheBuilder的实例
= CacheBuilder.newBuilder()
// 设置并发级别为8,并发级别是指可以同时写缓存的线程数
.concurrencyLevel(8)
// 设置写缓存后8秒钟过期
.expireAfterWrite(8, TimeUnit.SECONDS)
// 设置缓存容器的初始容量为10
.initialCapacity(10)
// 设置缓存最大容量为100,超过100之后就会按照LRU最近虽少使用算法来移除缓存项
.maximumSize(100)
// 设置要统计缓存的命中率
.recordStats()
// 设置缓存的移除通知
.removalListener(new RemovalListener<Object, Object>() {
public void onRemoval(RemovalNotification<Object, Object> notification) {
System.out.println(
notification.getKey() + " was removed, cause is " + notification.getCause());
}
})
// build方法中可以指定CacheLoader,在缓存不存在时通过CacheLoader的实现自动加载缓存
.build(new CacheLoader<Integer, Student>() {
@Override
public Student load(Integer key) throws Exception {
Student student = new Student();
student.setId(key);
student.setName("name " + key);
return student;
}
});
@Accessors
@Accessors(chain=true)
链式访问,生成setter方法返回this(*也就是返回的是对象*),代替了默认的返回void。
@Accessors(fluent = true)
区别在于getter和setter不带set和get前缀。
class student {
String Name;
}
student.name("张三")
@Accessors(prefix = "f")
set方法忽略指定的前缀。
class student {
String fName;
}
student.setName("张三")
Integer
Integer == 比较是比较地址,equals比较是比较值。
Integer 和 int == 比较,Integer自动拆箱
Integer 缓存策略 Integer cache[] 数组中创建了 -128至127的Integer对象,因此在这个范围内的Integer用 == 比较是 true
Integer 之间用 >,>=,<,<= 比较时是比较的值
...
可变长参数,这个位置可以传入任意个该类型参数,简单来说就是个数组。
void test(Integer... arr); 可看作 void test(Integer[] arr)
test(1,2,3)
test(new Integer[]{1,2,3})
@PostConstruct
类初始化调用顺序:
(1)构造方法Constructor
(2)@Autowired
(3)@PostConstruct
@Data
@Data相当于@Getter @Setter @RequiredArgsConstructor @ToString @EqualsAndHashCode这5个注解的合集。
如果dto中没有重写toString, 且使用了@Data注解, 调用toString时只会打印子类本身的属性值, 如果想要打印父类的属性:
方式一: 重写tostring
方式二: 子类再加上@ToString(callSuper = true)两个注解, 父类也使用注解@Data
@EqualsAndHashCode(callSuper = true) 可使父类的equals方法考虑子类。
但是许多涉及比较的方法会调用equals方法,因此不建议使用这个注解。
建议重写equals以防造成比较血案。重写后lombok将不会自动生成相应方法。
@Mapper & @MapperScan
mapper的接口上使用@Mapper注解,在编译之后会生成相应的接口实现类
@MapperScan扫描包,包下面的所有接口在编译之后都会生成相应的实现类
@MapperScan(basePackages= {"com.example.project.mapper.mysql","com.example.project.mapper.clickhouse"})
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 因为Apifox不支持离线,我果断选择了Apipost!
· 通过 API 将Deepseek响应流式内容输出到前端