Springboot實(shí)現(xiàn)動態(tài)定時任務(wù)管理的示例代碼
最近在做spring boot項(xiàng)目開發(fā)中,由于使用@EnableScheduling注解和@Scheduled注解來實(shí)現(xiàn)的定時任務(wù),只能靜態(tài)的創(chuàng)建定時任務(wù),不能動態(tài)修改、添加、刪除、啟/停任務(wù)。由于項(xiàng)目開發(fā)體量不大,如果引入xxl-job等開源框架處理,會導(dǎo)致項(xiàng)目過于臃腫和復(fù)雜,同時通過查找相關(guān)資料,發(fā)現(xiàn)可以通過改造spring-context.jar包中org.springframework.scheduling.ScheduledTaskRegistrar類實(shí)現(xiàn)動態(tài)增刪啟停定時任務(wù)功能,而且網(wǎng)上也有類似文章介紹,于是便動手實(shí)踐了一下,發(fā)現(xiàn)是可行的。
1、定時任務(wù)表設(shè)計(jì)
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表達(dá)式', `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ù)實(shí)體類及對應(yīng)Mapper
關(guān)于定時任務(wù)實(shí)體類一些基本的增刪改查接口代碼,這里為了簡便操作,引入了mybatis-plus中的ActiveRecord 模式,通過實(shí)體類繼承Model類實(shí)現(xiàn),關(guān)于Model類的說明,參看如下文檔:https://baomidou.com/pages/49cc81/#activerecord-%E6%A8%A1%E5%BC%8F
2.1、定時任務(wù)實(shí)體類
<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ù)實(shí)體類 * * @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表達(dá)式 */ 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ù), 作為線程池?cái)?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接口實(shí)現(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接口實(shí)現(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)管理具體實(shí)現(xiàn)工具類 * * @author 星空流年 * @date 2023/7/5 */ @Component public class TaskUtils { @Resource private CronTaskRegistrar cronTaskRegistrar; /** * 添加定時任務(wù) * * @param scheduleJob 定時任務(wù)實(shí)體類 * @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ù)實(shí)體類 * @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實(shí)現(xiàn)動態(tài)定時任務(wù)管理的文章就介紹到這了,更多相關(guān)Springboot動態(tài)定時任務(wù)管理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java實(shí)現(xiàn)讀取項(xiàng)目中文件(.json或.properties)的方法詳解
這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)讀取項(xiàng)目中文件的方法,例如.json或.properties,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下2023-04-04使用HttpClient調(diào)用接口的實(shí)例講解
下面小編就為大家?guī)硪黄褂肏ttpClient調(diào)用接口的實(shí)例講解。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-10-10springboot之security?FilterSecurityInterceptor的使用要點(diǎn)記錄
這篇文章主要介紹了springboot之security?FilterSecurityInterceptor的使用要點(diǎn)記錄,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-12-12springboot跨域過濾器fetch react Response to p
這篇文章主要介紹了springboot跨域過濾器fetch react Response to preflight request doesn‘t pass access control check問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-03-03SpringBoot項(xiàng)目中連接Gauss數(shù)據(jù)庫
本文主要介紹了SpringBoot項(xiàng)目中連接Gauss數(shù)據(jù)庫,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-06-06Spring細(xì)數(shù)兩種代理模式之靜態(tài)代理和動態(tài)代理概念及使用
代理是一種設(shè)計(jì)模式,提供了對目標(biāo)對象另外的訪問方式,即通過代理對象訪問目標(biāo)對象??梢圆恍薷哪繕?biāo)對象,對目標(biāo)對象功能進(jìn)行拓展。在我們學(xué)習(xí)Spring的時候就會發(fā)現(xiàn),AOP(面向切面編程)的底層就是代理2023-02-02Spring的@PreAuthorize注解自定義權(quán)限校驗(yàn)詳解
這篇文章主要介紹了Spring的@PreAuthorize注解自定義權(quán)限校驗(yàn)詳解,由于項(xiàng)目中,需要對外開放接口,要求做請求頭校驗(yàn),不做其他權(quán)限控制,所以準(zhǔn)備對開放的接口全部放行,不做登錄校驗(yàn),需要的朋友可以參考下2023-11-11