Springboot實現(xiàn)動態(tài)定時任務(wù)管理的示例代碼
最近在做spring boot項目開發(fā)中,由于使用@EnableScheduling注解和@Scheduled注解來實現(xiàn)的定時任務(wù),只能靜態(tài)的創(chuàng)建定時任務(wù),不能動態(tài)修改、添加、刪除、啟/停任務(wù)。由于項目開發(fā)體量不大,如果引入xxl-job等開源框架處理,會導致項目過于臃腫和復雜,同時通過查找相關(guān)資料,發(fā)現(xiàn)可以通過改造spring-context.jar包中org.springframework.scheduling.ScheduledTaskRegistrar類實現(xiàn)動態(tài)增刪啟停定時任務(wù)功能,而且網(wǎng)上也有類似文章介紹,于是便動手實踐了一下,發(fā)現(xiàn)是可行的。
1、定時任務(wù)表設(shè)計
CREATE TABLE IF NOT EXISTS `schedule_setting` ( `job_id` int NOT NULL AUTO_INCREMENT COMMENT '任務(wù)ID', `bean_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'bean名稱', `method_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '方法名稱', `method_params` varchar(8192) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '方法參數(shù)', `cron_expression` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'cron表達式', `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '備注', `job_status` tinyint(1) DEFAULT NULL COMMENT '狀態(tài)(1為啟用,0為停用)', `create_time` datetime DEFAULT NULL COMMENT '創(chuàng)建時間', `update_time` datetime DEFAULT NULL COMMENT '修改時間', PRIMARY KEY (`job_id`) ) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
2、定時任務(wù)實體類及對應(yīng)Mapper
關(guān)于定時任務(wù)實體類一些基本的增刪改查接口代碼,這里為了簡便操作,引入了mybatis-plus中的ActiveRecord 模式,通過實體類繼承Model類實現(xiàn),關(guān)于Model類的說明,參看如下文檔:https://baomidou.com/pages/49cc81/#activerecord-%E6%A8%A1%E5%BC%8F
2.1、定時任務(wù)實體類
<strong>import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Date;
/**
* 定時任務(wù)實體類
*
* @author 星空流年
* @date 2023/7/5
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class ScheduleSetting extends Model<ScheduleSetting> {
/**
* 任務(wù)ID
*/
@TableId(type = IdType.AUTO)
private Integer jobId;
/**
* bean名稱
*/
private String beanName;
/**
* 方法名稱
*/
private String methodName;
/**
* 方法參數(shù)
*/
private String methodParams;
/**
* cron表達式
*/
private String cronExpression;
/**
* 狀態(tài)(1為啟用, 0為停用)
*/
private Integer jobStatus;
/**
* 備注
*/
private String remark;
/**
* 創(chuàng)建時間
*/
private Date createTime;
/**
* 更新時間
*/
private Date updateTime;
}</strong>2.2、定時任務(wù)狀態(tài)枚舉類
<strong>/**
* 定時任務(wù)啟用、停用枚舉類
*
* @author 星空流年
* @date 2023/7/5
*/
public enum ScheduleJobEnum {
/**
* 啟用
*/
ENABLED(1),
/**
* 停用
*/
DISABLED(0);
private final int statusCode;
ScheduleJobEnum(int code) {
this.statusCode = code;
}
public int getStatusCode() {
return statusCode;
}
}</strong>2.3、定時任務(wù)Mapper類
<strong>import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import xxx.entity.ScheduleSetting;
import org.apache.ibatis.annotations.Mapper;
/**
* ScheduleSetting表數(shù)據(jù)庫訪問層
*
* @author 星空流年
* @date 2023/7/5
*/
@Mapper
@SuppressWarnings("all")
public interface ScheduleSettingMapper extends BaseMapper<ScheduleSetting> {
}</strong>3、定時任務(wù)線程池相關(guān)類
3.1、定時任務(wù)線程池配置類
<strong>import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
/**
* 執(zhí)行定時任務(wù)的線程池配置類
*
* @author 星空流年
* @date 2023/7/5
*/
@Configuration
public class SchedulingConfig {
@Bean
public TaskScheduler taskScheduler() {
// 獲取系統(tǒng)處理器個數(shù), 作為線程池數(shù)量
int corePoolSize = Runtime.getRuntime().availableProcessors();
ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
// 定時任務(wù)執(zhí)行線程池核心線程數(shù)
taskScheduler.setPoolSize(corePoolSize);
taskScheduler.setRemoveOnCancelPolicy(true);
taskScheduler.setThreadNamePrefix("TaskSchedulerThreadPool-");
return taskScheduler;
}
}</strong>3.2、獲取Bean工具類
<strong>import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
/**
* 獲取Spring中Bean工具類
*
* @author 星空流年
* @date 2023/7/5
*/
@Component
@SuppressWarnings("all")
public class SpringContextUtils implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
SpringContextUtils.applicationContext = applicationContext;
}
public static Object getBean(String name) {
return applicationContext.getBean(name);
}
public static <T> T getBean(Class<T> requiredType) {
return applicationContext.getBean(requiredType);
}
public static <T> T getBean(String name, Class<T> requiredType) {
return applicationContext.getBean(name, requiredType);
}
public static boolean containsBean(String name) {
return applicationContext.containsBean(name);
}
public static boolean isSingleton(String name) {
return applicationContext.isSingleton(name);
}
public static Class<? extends Object> getType(String name) {
return applicationContext.getType(name);
}
}</strong>3.3、Runnable接口實現(xiàn)類
<strong>import lombok.extern.slf4j.Slf4j;
import xxx.util.SpringContextUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.ReflectionUtils;
import java.lang.reflect.Method;
import java.util.Objects;
/**
* Runnable接口實現(xiàn)類
* 被定時任務(wù)線程池調(diào)用, 用來執(zhí)行指定bean里面的方法
*
* @author 星空流年
* @date 2023/7/5
*/
@Slf4j
@SuppressWarnings("all")
public class SchedulingRunnable implements Runnable {
private final String beanName;
private final String methodName;
private final String params;
private final Integer jobId;
public SchedulingRunnable(String beanName, String methodName, String params, Integer jobId) {
this.beanName = beanName;
this.methodName = methodName;
this.params = params;
this.jobId = jobId;
}
@Override
public void run() {
log.info("定時任務(wù)開始執(zhí)行 - bean: {}, 方法: {}, 參數(shù): {}, 任務(wù)ID: {}", beanName, methodName, params, jobId);
long startTime = System.currentTimeMillis();
try {
Object target = SpringContextUtils.getBean(beanName);
Method method;
if (StringUtils.isNotEmpty(params)) {
method = target.getClass().getDeclaredMethod(methodName, String.class);
} else {
method = target.getClass().getDeclaredMethod(methodName);
}
ReflectionUtils.makeAccessible(method);
if (StringUtils.isNotEmpty(params)) {
method.invoke(target, params);
} else {
method.invoke(target);
}
} catch (Exception ex) {
log.error("定時任務(wù)執(zhí)行異常 - bean: {}, 方法: {}, 參數(shù): {}, 任務(wù)ID: {}", beanName, methodName, params, jobId, ex);
}
long times = System.currentTimeMillis() - startTime;
log.info("定時任務(wù)執(zhí)行結(jié)束 - bean: {}, 方法: {}, 參數(shù): {}, 任務(wù)ID: {}, 耗時: {}毫秒", beanName, methodName, params, jobId, times);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (Objects.isNull(obj) || getClass() != obj.getClass()) {
return false;
}
SchedulingRunnable that = (SchedulingRunnable) obj;
if (Objects.isNull(params)) {
return beanName.equals(that.beanName) &&
methodName.equals(that.methodName) &&
that.params == null;
}
return beanName.equals(that.beanName) &&
methodName.equals(that.methodName) &&
params.equals(that.params) &&
jobId.equals(that.jobId);
}
@Override
public int hashCode() {
if (Objects.isNull(params)) {
return Objects.hash(beanName, methodName, jobId);
}
return Objects.hash(beanName, methodName, params, jobId);
}
}</strong>3.4、ScheduledFuture包裝類
<strong>import java.util.Objects;
import java.util.concurrent.ScheduledFuture;
/**
* 定時任務(wù)包裝類
* <p>
* ScheduledFuture是ScheduledExecutorService定時任務(wù)線程池的執(zhí)行結(jié)果
* </p>
*
* @author 星空流年
* @date 2023/7/5
*/
@SuppressWarnings("all")
public final class ScheduledTask {
volatile ScheduledFuture<?> future;
/**
* 取消定時任務(wù)
*/
public void cancel() {
ScheduledFuture<?> scheduledFuture = this.future;
if (Objects.nonNull(scheduledFuture)) {
scheduledFuture.cancel(true);
}
}
}</strong>3.5、定時任務(wù)注冊類
<strong>import javax.annotation.Resource;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.config.CronTask;
import org.springframework.stereotype.Component;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
/**
* 定時任務(wù)注冊類
* <p>
* 定時任務(wù)注冊類, 主要用于增加、刪除定時任務(wù)
* </p>
*
* @author 星空流年
* @date 2023/7/5
*/
@Component
@SuppressWarnings("all")
public class CronTaskRegistrar implements DisposableBean {
private final Map<Runnable, ScheduledTask> scheduledTasks = new ConcurrentHashMap<>(16);
@Resource
private TaskScheduler taskScheduler;
public void addCronTask(Runnable task, String cronExpression) {
addCronTask(new CronTask(task, cronExpression));
}
public void addCronTask(CronTask cronTask) {
if (Objects.nonNull(cronTask)) {
Runnable task = cronTask.getRunnable();
if (this.scheduledTasks.containsKey(task)) {
removeCronTask(task);
}
this.scheduledTasks.put(task, scheduleCronTask(cronTask));
}
}
public void removeCronTask(Runnable task) {
ScheduledTask scheduledTask = this.scheduledTasks.remove(task);
if (Objects.nonNull(scheduledTask)) {
scheduledTask.cancel();
}
}
public ScheduledTask scheduleCronTask(CronTask cronTask) {
ScheduledTask scheduledTask = new ScheduledTask();
scheduledTask.future = this.taskScheduler.schedule(cronTask.getRunnable(), cronTask.getTrigger());
return scheduledTask;
}
@Override
public void destroy() {
this.scheduledTasks.values().forEach(ScheduledTask::cancel);
this.scheduledTasks.clear();
}
}</strong>4、定時任務(wù)增刪改工具類
<strong>import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import javax.annotation.Resource;
import net.cnki.nxgp.metric.recording.rule.entity.pojo.ScheduleJobEnum;
import net.cnki.nxgp.metric.recording.rule.task.component.CronTaskRegistrar;
import net.cnki.nxgp.metric.recording.rule.task.component.SchedulingRunnable;
import net.cnki.nxgp.metric.recording.rule.task.entity.ScheduleSetting;
import org.springframework.stereotype.Component;
import java.util.Date;
/**
* 定時任務(wù)動態(tài)管理具體實現(xiàn)工具類
*
* @author 星空流年
* @date 2023/7/5
*/
@Component
public class TaskUtils {
@Resource
private CronTaskRegistrar cronTaskRegistrar;
/**
* 添加定時任務(wù)
*
* @param scheduleJob 定時任務(wù)實體類
* @return boolean
*/
public ScheduleSetting insertTaskJob(ScheduleSetting scheduleJob) {
scheduleJob.setCreateTime(new Date());
scheduleJob.setUpdateTime(new Date());
boolean insert = scheduleJob.insert();
if (!insert) {
return null;
}
// 添加成功, 并且狀態(tài)是啟用, 則直接放入任務(wù)器
if (scheduleJob.getJobStatus().equals(ScheduleJobEnum.ENABLED.getStatusCode())) {
SchedulingRunnable task = new SchedulingRunnable(scheduleJob.getBeanName(), scheduleJob.getMethodName(), scheduleJob.getMethodParams(), scheduleJob.getJobId());
cronTaskRegistrar.addCronTask(task, scheduleJob.getCronExpression());
}
return scheduleJob;
}
/**
* 更新定時任務(wù)
*
* @param scheduleJob 定時任務(wù)實體類
* @return boolean
*/
public boolean updateTaskJob(ScheduleSetting scheduleJob) {
scheduleJob.setCreateTime(new Date());
scheduleJob.setUpdateTime(new Date());
// 查詢修改前任務(wù)
ScheduleSetting existedSysJob = new ScheduleSetting();
LambdaQueryWrapper<ScheduleSetting> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(ScheduleSetting::getJobId, scheduleJob.getJobId());
existedSysJob = existedSysJob.selectOne(queryWrapper);
// 修改任務(wù)
LambdaUpdateWrapper<ScheduleSetting> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.eq(ScheduleSetting::getJobId, scheduleJob.getJobId());
boolean update = scheduleJob.update(updateWrapper);
if (!update) {
return false;
}
// 修改成功, 則先刪除任務(wù)器中的任務(wù), 并重新添加
SchedulingRunnable preTask = new SchedulingRunnable(existedSysJob.getBeanName(), existedSysJob.getMethodName(), existedSysJob.getMethodParams(), existedSysJob.getJobId());
cronTaskRegistrar.removeCronTask(preTask);
// 如果修改后的任務(wù)狀態(tài)是啟用, 就加入任務(wù)器
if (scheduleJob.getJobStatus().equals(ScheduleJobEnum.ENABLED.getStatusCode())) {
SchedulingRunnable task = new SchedulingRunnable(scheduleJob.getBeanName(), scheduleJob.getMethodName(), scheduleJob.getMethodParams(), scheduleJob.getJobId());
cronTaskRegistrar.addCronTask(task, scheduleJob.getCronExpression());
}
return true;
}
/**
* 刪除定時任務(wù)
*
* @param jobId 定時任務(wù)id
* @return boolean
*/
public boolean deleteTaskJob(Integer jobId) {
// 先查詢要刪除的任務(wù)信息
ScheduleSetting existedJob = new ScheduleSetting();
LambdaQueryWrapper<ScheduleSetting> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(ScheduleSetting::getJobId, jobId);
existedJob = existedJob.selectOne(queryWrapper);
// 刪除
boolean delete = existedJob.delete(queryWrapper);
if (!delete) {
return false;
}
// 刪除成功, 并刪除定時任務(wù)器中的對應(yīng)任務(wù)
SchedulingRunnable task = new SchedulingRunnable(existedJob.getBeanName(), existedJob.getMethodName(), existedJob.getMethodParams(), jobId);
cronTaskRegistrar.removeCronTask(task);
return true;
}
/**
* 停止/啟動定時任務(wù)
*
* @param jobId 定時任務(wù)id
* @param jobStatus 定時任務(wù)狀態(tài)
* @return boolean
*/
public boolean changeStatus(Integer jobId, Integer jobStatus) {
// 修改任務(wù)狀態(tài)
ScheduleSetting scheduleSetting = new ScheduleSetting();
scheduleSetting.setJobStatus(jobStatus);
boolean update = scheduleSetting.update(new LambdaUpdateWrapper<ScheduleSetting>().eq(ScheduleSetting::getJobId, jobId));
if (!update) {
return false;
}
// 查詢修改后的任務(wù)信息
ScheduleSetting existedJob = new ScheduleSetting();
existedJob = existedJob.selectOne(new LambdaQueryWrapper<ScheduleSetting>().eq(ScheduleSetting::getJobId, jobId));
// 如果狀態(tài)是啟用, 則添加任務(wù)
SchedulingRunnable task = new SchedulingRunnable(existedJob.getBeanName(), existedJob.getMethodName(), existedJob.getMethodParams(), jobId);
if (existedJob.getJobStatus().equals(ScheduleJobEnum.ENABLED.getStatusCode())) {
cronTaskRegistrar.addCronTask(task, existedJob.getCronExpression());
} else {
// 反之, 則刪除任務(wù)
cronTaskRegistrar.removeCronTask(task);
}
return true;
}
}</strong>5、定時任務(wù)執(zhí)行類
<strong>import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
* 定時任務(wù)類
*
* @author 星空流年
* @date 2023/7/5
*/
@Slf4j
@Component("jobTaskTest")
public class JobTask {
/**
* 此處為需要執(zhí)行定時任務(wù)的方法, 可以根據(jù)需求自行添加對應(yīng)的定時任務(wù)方法
*/
public void upsertTask(String params) {
// ...
log.info("定時任務(wù)執(zhí)行啦...");
}
}</strong>6、定時任務(wù)測試類
<strong>import javax.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@Slf4j
@SpringBootTest
@RunWith(SpringRunner.class)
public class ScheduleJobApplicationTests {
@Resource
private TaskUtils taskUtils;
@Test
public void testInsertTask() {
ScheduleSetting scheduleJob = new ScheduleSetting();
// 此處beanName, methodName對應(yīng)定時任務(wù)執(zhí)行類中定義的beanName、方法名
scheduleJob.setBeanName("jobTaskTest");
scheduleJob.setMethodName("upsertTask");
// 方法參數(shù)由于定時任務(wù)Runnable接口包裝類中定義為字符串類型, 如果為其他類型,注意轉(zhuǎn)換
scheduleJob.setMethodParams("params");
scheduleJob.setJobStatus(ScheduleJobEnum.ENABLED.getStatusCode());
String cron = "*/30 * * * * ?";
scheduleJob.setCronExpression(cron);
scheduleJob.setRemark("定時任務(wù)新增");
ScheduleSetting scheduleTask = taskUtils.insertTaskJob(scheduleJob);
if (Objects.isNull(scheduleTask)) {
log.error("定時任務(wù)新增失敗");
}
}
@Test
public void testUpdateTask() {
ScheduleSetting scheduleJob = new ScheduleSetting();
scheduleJob.setJobId(1);
// 此處beanName, methodName對應(yīng)定時任務(wù)執(zhí)行類中定義的beanName、方法名
scheduleJob.setBeanName("jobTaskTest");
scheduleJob.setMethodName("upsertTask");
// 方法參數(shù)由于定時任務(wù)Runnable接口包裝類中定義為字符串類型, 如果為其他類型,注意轉(zhuǎn)換
scheduleJob.setMethodParams("params");
scheduleJob.setJobStatus(ScheduleJobEnum.ENABLED.getStatusCode());
String cron = "*/60 * * * * ?";
scheduleJob.setCronExpression(cron);
scheduleJob.setRemark("定時任務(wù)更新");
ScheduleSetting scheduleTask = taskUtils.updateTaskJob(scheduleJob);
if (Objects.isNull(scheduleTask)) {
log.error("定時任務(wù)更新失敗");
}
}
@Test
public void testChangeTaskStatus() {
boolean changeFlag = taskUtils.changeStatus(1, 0);
if (!changeFlag) {
log.error("定時任務(wù)狀態(tài)更新失敗");
}
}
@Test
public void testDeleteTask() {
boolean deleteFlag = taskUtils.deleteTaskJob(1);
if (!deleteFlag) {
log.error("定時任務(wù)刪除失敗");
}
}
}</strong>7、初始化定時任務(wù)
<strong>import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import javax.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import xxx.entity.pojo.ScheduleJobEnum;
import xxx.entity.ScheduleSetting;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* 初始化數(shù)據(jù)庫中啟用狀態(tài)下的定時任務(wù)
*
* @author 星空流年
* @date 2023/7/5
*/
@Slf4j
@Component
public class TaskJobInitRunner implements CommandLineRunner {
@Resource
private CronTaskRegistrar cronTaskRegistrar;
@Override
public void run(String... args) {
// 初始化加載數(shù)據(jù)庫中狀態(tài)為啟用的定時任務(wù)
ScheduleSetting existedSysJob = new ScheduleSetting();
List<ScheduleSetting> jobList = existedSysJob.selectList(new LambdaQueryWrapper<ScheduleSetting>().eq(ScheduleSetting::getJobStatus, ScheduleJobEnum.ENABLED.getStatusCode()));
if (CollectionUtils.isNotEmpty(jobList)) {
jobList.forEach(job -> {
SchedulingRunnable task = new SchedulingRunnable(job.getBeanName(), job.getMethodName(), job.getMethodParams(), job.getJobId());
cronTaskRegistrar.addCronTask(task, job.getCronExpression());
});
log.info("~~~~~~~~~~~~~~~~~~~~~ 定時任務(wù)初始化完成 ~~~~~~~~~~~~~~~~~~~~~");
}
}
}</strong>到此這篇關(guān)于Springboot實現(xiàn)動態(tài)定時任務(wù)管理的文章就介紹到這了,更多相關(guān)Springboot動態(tài)定時任務(wù)管理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java實現(xiàn)讀取項目中文件(.json或.properties)的方法詳解
這篇文章主要為大家詳細介紹了Java實現(xiàn)讀取項目中文件的方法,例如.json或.properties,文中的示例代碼講解詳細,感興趣的小伙伴可以了解一下2023-04-04
springboot之security?FilterSecurityInterceptor的使用要點記錄
這篇文章主要介紹了springboot之security?FilterSecurityInterceptor的使用要點記錄,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-12-12
springboot跨域過濾器fetch react Response to p
這篇文章主要介紹了springboot跨域過濾器fetch react Response to preflight request doesn‘t pass access control check問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-03-03
SpringBoot項目中連接Gauss數(shù)據(jù)庫
本文主要介紹了SpringBoot項目中連接Gauss數(shù)據(jù)庫,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2024-06-06
Spring細數(shù)兩種代理模式之靜態(tài)代理和動態(tài)代理概念及使用
代理是一種設(shè)計模式,提供了對目標對象另外的訪問方式,即通過代理對象訪問目標對象??梢圆恍薷哪繕藢ο?,對目標對象功能進行拓展。在我們學習Spring的時候就會發(fā)現(xiàn),AOP(面向切面編程)的底層就是代理2023-02-02
Spring的@PreAuthorize注解自定義權(quán)限校驗詳解
這篇文章主要介紹了Spring的@PreAuthorize注解自定義權(quán)限校驗詳解,由于項目中,需要對外開放接口,要求做請求頭校驗,不做其他權(quán)限控制,所以準備對開放的接口全部放行,不做登錄校驗,需要的朋友可以參考下2023-11-11

