Spring+Quartz實(shí)現(xiàn)動(dòng)態(tài)任務(wù)調(diào)度詳解
一、定時(shí)任務(wù)的實(shí)現(xiàn)
1、配置文件方式
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <bean id="userTimer" class="com.kevin.timer.XmlTimer"></bean> <bean id="userTask" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean"> <property name="targetObject"> <ref bean="userTimer" /> </property> <property name="targetMethod"> <value>execute</value> </property> </bean> <!-- 每隔5秒鐘執(zhí)行一次,我要在運(yùn)行時(shí)動(dòng)態(tài)修改為每小時(shí)執(zhí)行一次 --> <bean id="userTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean"> <property name="jobDetail"> <ref bean="userTask" /> </property> <property name="cronExpression"> <value>0/5 * * * * ?</value> </property> </bean> <bean id="startQuertz" lazy-init="false" autowire="no" class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> <property name="triggers"> <list> <ref bean="userTrigger" /> </list> </property> </bean> </beans>
2、注解方式
我最近經(jīng)?;趕pring boot寫定時(shí)任務(wù),并且是使用注解的方式進(jìn)行實(shí)現(xiàn),分成的方便將自己的類注入spring容器(@conponent等),
并在要實(shí)現(xiàn)父方法上添加注解 @Scheduled(cron="0/10 * * * * ?") ,告知任務(wù)調(diào)度器質(zhì)性方法中的業(yè)務(wù)邏輯的質(zhì)性時(shí)間。
二、任務(wù)調(diào)用的工作原理

三、動(dòng)態(tài)任務(wù)調(diào)用的實(shí)現(xiàn)
首先定義任務(wù)實(shí)體應(yīng)該具有的屬性:
package com.kevin.schedule.entity;
/**
* 調(diào)度任務(wù)定義
* @author Tom
*
*/
public class Task {
private String id; //任務(wù)ID,默認(rèn)系統(tǒng)時(shí)間戳
private String parentId = ""; //父級(jí)任務(wù)ID
private String name = ""; //任務(wù)名稱
private String desc = ""; //任務(wù)描述
private int planExe = 0; //計(jì)劃執(zhí)行次數(shù),默認(rèn)為0,表示滿足條件循環(huán)執(zhí)行
private String group = ""; //任務(wù)組名稱
private String groupDesc = ""; //任務(wù)組描述
private String cron = ""; //任務(wù)表達(dá)式
private String cronDesc = ""; //表達(dá)式描述
private String trigger = ""; //觸發(fā)器
private String triggerDesc = "";//觸發(fā)器描述
private int execute = 0; //任務(wù)被執(zhí)行過多少次
private Long lastExeTime = 0L; //最后一次開始執(zhí)行時(shí)間
private Long lastFinishTime = 0L;//最后一次執(zhí)行完成時(shí)間
private int state = 1; //任務(wù)狀態(tài)0禁用、1啟動(dòng)、2刪除
private int deply = 0; //延時(shí)啟動(dòng),默認(rèn)為0,表示不延時(shí)啟動(dòng)
public Task(String taskId){
this.id = taskId;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getParentId() {
return parentId;
}
public void setParentId(String parentId) {
this.parentId = parentId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCron() {
return cron;
}
public void setCron(String cron) {
this.cron = cron;
}
public String getCronDesc() {
return cronDesc;
}
public void setCronDesc(String cronDesc) {
this.cronDesc = cronDesc;
}
// public List<ScheduleJob> getChildren() {
// return children;
// }
// public void setChildren(List<ScheduleJob> children) {
// this.children = children;
// }
public int getExecute() {
return execute;
}
public void setExecute(int execute) {
this.execute = execute;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
public String getGroup() {
return group;
}
public void setGroup(String group) {
this.group = group;
}
public String getGroupDesc() {
return groupDesc;
}
public void setGroupDesc(String groupDesc) {
this.groupDesc = groupDesc;
}
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
}
public Long getLastExeTime() {
return lastExeTime;
}
public void setLastExeTime(Long lastExeTime) {
this.lastExeTime = lastExeTime;
}
public String getTrigger() {
return trigger;
}
public void setTrigger(String trigger) {
this.trigger = trigger;
}
public String getTriggerDesc() {
return triggerDesc;
}
public void setTriggerDesc(String triggerDesc) {
this.triggerDesc = triggerDesc;
}
public int getDeply() {
return deply;
}
public void setDeply(int deply) {
this.deply = deply;
}
public int getPlanExe() {
return planExe;
}
public void setPlanExe(int planExe) {
this.planExe = planExe;
}
// public String getTriggerGroup() {
// return triggerGroup;
// }
// public void setTriggerGroup(String triggerGroup) {
// this.triggerGroup = triggerGroup;
// }
// public String getTriggerGroupDesc() {
// return triggerGroupDesc;
// }
// public void setTriggerGroupDesc(String triggerGroupDesc) {
// this.triggerGroupDesc = triggerGroupDesc;
// }
public Long getLastFinishTime() {
return lastFinishTime;
}
public void setLastFinishTime(Long lastFinishTime) {
this.lastFinishTime = lastFinishTime;
}
}package com.kevin.schedule.proxy;
import java.lang.reflect.Method;
import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import com.gupaoedu.schedule.entity.Task;
/**
*
* @author Tom
*
*/
public class TriggerProxy implements Job{
public static final String DATA_TARGET_KEY = "target"; //目標(biāo)對(duì)象,實(shí)例
public static final String DATA_TRIGGER_KEY = "trigger"; //方法名
public static final String DATA_TRIGGER_PARAMS_KEY = "trigger_params";//方法的參數(shù)值
public static final String DATA_TASK_KEY = "task"; //自己封裝的任務(wù)對(duì)象
private ThreadLocal<Entry> local = new ThreadLocal<Entry>();
//是由調(diào)度器自動(dòng)調(diào)用的
public void execute(JobExecutionContext context) throws JobExecutionException {
// TriggerProxy.class.getResource("")
try {
local.set(new Entry());
//獲取參數(shù)信息
JobDataMap data = context.getTrigger().getJobDataMap();
Object target = data.get(DATA_TARGET_KEY);
Method method = (Method)data.get(DATA_TRIGGER_KEY);
Object[] params = (Object[])data.get(DATA_TRIGGER_PARAMS_KEY);
//修改任務(wù)執(zhí)行次數(shù)
Task task = (Task)data.get(DATA_TASK_KEY);
//任務(wù)沒執(zhí)行一次,需要累加1
task.setExecute(task.getExecute() + 1);
local.get().start = System.currentTimeMillis();
//調(diào)用觸發(fā)器,用反射調(diào)用我們自己定義的方法
method.invoke(target,params);
local.get().end = System.currentTimeMillis();
//記錄任務(wù)的最后一次執(zhí)行時(shí)間
task.setLastExeTime(local.get().start);
//記錄任務(wù)完成的時(shí)間
task.setLastFinishTime(local.get().end);
} catch (Exception e) {
e.printStackTrace();
}
}
class Entry{
public long start = 0L;
public long end = 0L;
}
}@Component
public class AnnotationTimer {
Logger LOG = Logger.getLogger(this.getClass());
@Scheduled(cron="0/10 * * * * ?")
@Async
public void a(){
LOG.info("annotation配置的任務(wù)執(zhí)行");
}
}定義動(dòng)態(tài)任務(wù)調(diào)度需要的方法,在web項(xiàng)目中,通過界面的方式調(diào)用方法以動(dòng)態(tài)控制和查看任務(wù)調(diào)度的執(zhí)行情況:
package com.kevin.schedule.service;
import java.util.List;
import com.gupaoedu.schedule.entity.Task;
public interface IScheduleService {
/**
* 獲取任務(wù)列表
* @return
*/
public List<Task> getAllTask();
/**
* 根據(jù)任務(wù)ID獲取一個(gè)任務(wù)
* @return
*/
public Task getTask(String taskId);
/**
* 新建一個(gè)任務(wù)
* @param taskName 任務(wù)名稱
* @param taskClassName 任務(wù)Class名稱
* @param triggerName 觸發(fā)器名稱
* @param cron 執(zhí)行表達(dá)式
* @throws Exception
*/
public Task createTask(String taskName,String taskClassName,String triggerName,String cron) throws Exception;
/**
* 修改一個(gè)任務(wù)的觸發(fā)時(shí)間(使用默認(rèn)的任務(wù)組名,觸發(fā)器名,觸發(fā)器組名)
*
*/
public Task modifyTaskCron(String taskId, String cron);
/**
* 移除一個(gè)任務(wù)(使用默認(rèn)的任務(wù)組名,觸發(fā)器名,觸發(fā)器組名)
*
*/
public Task removeTask(String taskId);
/**
* 重啟任務(wù)
* @param taskId
* @return
*/
public Task restartTask(String taskId);
/**
* 暫停定時(shí)任務(wù)
* @param taskId
* @return
*/
public Task pauseTask(String taskId);
/**
* 關(guān)閉定時(shí)任務(wù)
* @param taskId
* @return
*/
public Task shutdownTask(String taskId);
/**
* 啟動(dòng)所有定時(shí)任務(wù)
*
*/
public void startAllTask();
/**
* 關(guān)閉所有定時(shí)任務(wù)
*/
public void shutdownAllTask();
}實(shí)現(xiàn)方法:
package com.kevin.schedule.service.impl;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.quartz.CronTrigger;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.stereotype.Service;
import com.gupaoedu.schedule.entity.Task;
import com.gupaoedu.schedule.proxy.TriggerProxy;
import com.gupaoedu.schedule.service.IScheduleService;
/**
* 任務(wù)管理,負(fù)責(zé)輸出和管理日志
* @author Tom
*/
@Service
public class ScheduleService implements IScheduleService,ApplicationContextAware{
//自己不能再重新搞一個(gè)工廠出來,必須和Spring引用的調(diào)度工廠是同一個(gè)
//才能實(shí)現(xiàn)無縫集成,而且可以動(dòng)態(tài)修改配置文件已經(jīng)配置好的任務(wù)
@Autowired private SchedulerFactoryBean schedulerFactory;
//保存所有任務(wù)表達(dá)式的描述
private Map<String,String> cronDesc = new LinkedHashMap<String,String>();
//所有的任務(wù)都放到任務(wù)池中
private static Map<String,Task> taskPool = new LinkedHashMap<String,Task>();
private static ApplicationContext app;
public ScheduleService(){}
/**
* 創(chuàng)建一個(gè)調(diào)度任務(wù)
* @param m
* @return
* @throws Exception
*/
private Task createTask(Method m) throws Exception{
//任務(wù)ID就是時(shí)間戳,再加上一個(gè)隨機(jī)數(shù)
Task task = new Task("" + System.currentTimeMillis());
//通過方法,可以獲取到方法所在的class
Class clazz = m.getDeclaringClass();
//設(shè)置任務(wù)組
task.setGroup(clazz.getName());
//設(shè)置觸發(fā)器信息
//一個(gè)觸發(fā)器對(duì)應(yīng)一個(gè)方法,在此處,所謂的觸發(fā)器還只是一個(gè)概念
//概念是一個(gè)字符描述,它記錄了觸發(fā)的目標(biāo)信息
task.setTrigger(clazz.getName() + "." + m.getName());
Annotation sc = m.getAnnotation(Scheduled.class);
Method cronM = sc.getClass().getMethod("cron",null);
String cron = cronM.invoke(sc, null).toString();
task.setTriggerDesc(cronDesc.get(cron));
task.setCron(cron);
task.setCronDesc(cronDesc.get(cron));
//此時(shí),就已經(jīng)完成一個(gè)task的封裝
return task;
}
@Override
public void setApplicationContext(ApplicationContext app) throws BeansException {
this.app = app;
//加載所有任務(wù)到任務(wù)隊(duì)列
for(String name : app.getBeanDefinitionNames()){
try{
Class<?> c = app.getBean(name).getClass();
for(Method m : c.getMethods()){
//只要是添加了Scheduled注解的都給他添加到調(diào)度工廠中
if(m.isAnnotationPresent(Scheduled.class)){
Task task = createTask(m);
//將任務(wù)加入隊(duì)列準(zhǔn)備啟動(dòng)
createTask(task);
}
}
}catch(Exception e){
continue;
}
}
}
public Task getTask(String taskId) {
return taskPool.get(taskId);
}
@Override
public List<Task> getAllTask() {
if(taskPool.size() == 0){
return new ArrayList<Task>();
}
List<Task> r = new ArrayList<Task>();
r.addAll(taskPool.values());
return r;
}
private Task createTask(Task task) throws Exception{
if(null == task.getGroup() || task.getGroup().trim().length() == 0){ return null; }
//還是拿到字節(jié)碼
Class<?> clazz = Class.forName(task.getGroup());
//先從容器中獲取
Object target = null;
try{
//從Spring容器中提取已經(jīng)創(chuàng)建好的對(duì)象引用
target = app.getBean(clazz);
}catch(Exception e){
}
//如果Spring容器沒有幫我們創(chuàng)建,那么就自己創(chuàng)建實(shí)例
if(target == null){
target = clazz.newInstance();
}
//把觸發(fā)器需要調(diào)用的方法找出來,還是用反射
Method m = clazz.getMethod(task.getTrigger().replaceAll(task.getGroup() + ".", ""));
//把任務(wù)ID取出來,時(shí)間戳
String taskId = task.getId();
//================ 事前準(zhǔn)備 ====================
//拿到Quartz中的調(diào)度器
Scheduler sched = schedulerFactory.getScheduler();
//創(chuàng)建一個(gè)Detail
JobDetail taskDetail = new JobDetail(taskId, task.getGroup(), TriggerProxy.class);// 任務(wù)名,任務(wù)組,任務(wù)執(zhí)行類
// 觸發(fā)器
CronTrigger trigger = new CronTrigger(taskId, task.getTrigger());// 觸發(fā)器名,觸發(fā)器組
//在這里設(shè)置CronExpression表達(dá)式
trigger.setCronExpression(task.getCron());// 觸發(fā)器時(shí)間設(shè)定
//JobDataMap 用來存儲(chǔ)附加信息
//利用這么一個(gè)API,把自定義的信息添加到Map中
trigger.getJobDataMap().put(TriggerProxy.DATA_TARGET_KEY, target);
trigger.getJobDataMap().put(TriggerProxy.DATA_TRIGGER_KEY, m);
// m.getParameterTypes()
trigger.getJobDataMap().put(TriggerProxy.DATA_TRIGGER_PARAMS_KEY, new Object[]{});
trigger.getJobDataMap().put(TriggerProxy.DATA_TASK_KEY, task);
sched.scheduleJob(taskDetail, trigger);
// 如果這個(gè)任務(wù)沒有被主動(dòng)關(guān)閉,我們就給他啟動(dòng)
if (!sched.isShutdown()) {
sched.start();
}
//放入我們的任務(wù)池
if(!taskPool.containsKey(taskId)){
taskPool.put(taskId, task);
}
return task;
}
/**
* 添加一個(gè)定時(shí)任務(wù),使用默認(rèn)的任務(wù)組名,觸發(fā)器名,觸發(fā)器組名
*/
public Task createTask(String taskName,String taskClassName,String triggerName,String cron) throws Exception{
return createTask(taskName,null,taskClassName,null,triggerName,cron);
}
/**
* 添加一個(gè)定時(shí)任務(wù)
*/
private Task createTask(String taskName, String taskGroupName, String taskClassName,String triggerGroupName, String triggerName,String cron) throws Exception{
//根據(jù)類名,利用反射機(jī)制獲取到類的字節(jié)碼
//約定優(yōu)于配置
Class<?> clazz = Class.forName(taskClassName); //就是類名全程,包名.類名
Method m = clazz.getMethod(triggerName); //顯然就是方法名
Task task = createTask(m);
task.setName(taskName);
if(null != taskGroupName){
task.setGroup(taskGroupName);
}
task.setCron(cron);
return createTask(task);
}
/**
* 修改一個(gè)任務(wù)的觸發(fā)時(shí)間(使用默認(rèn)的任務(wù)組名,觸發(fā)器名,觸發(fā)器組名)
*
*/
public Task modifyTaskCron(String taskId, String cron) {
Task task = taskPool.get(taskId);
try {
Scheduler sched = schedulerFactory.getScheduler();
CronTrigger trigger = (CronTrigger) sched.getTrigger(taskId,task.getTrigger());
String oldTime = trigger.getCronExpression();
if (!oldTime.equalsIgnoreCase(cron)) {
JobDetail taskDetail = sched.getJobDetail(taskId,task.getGroup());
Class objJobClass = taskDetail.getJobClass();
removeTask(taskId);
//重新生成ID
task.setId("" + System.currentTimeMillis());
task.setCron(cron);
createTask(task);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
return task;
}
/**
* 移除一個(gè)任務(wù)(使用默認(rèn)的任務(wù)組名,觸發(fā)器名,觸發(fā)器組名)
*
*/
public Task removeTask(String taskId) {
Task task = taskPool.get(taskId);
try {
Scheduler sched = schedulerFactory.getScheduler();
sched.pauseTrigger(taskId, task.getTrigger());// 停止觸發(fā)器
sched.unscheduleJob(taskId, task.getGroup());// 移除觸發(fā)器
sched.deleteJob(taskId, task.getGroup());// 刪除任務(wù)
taskPool.remove(taskId);
} catch (Exception e) {
throw new RuntimeException(e);
}
return task;
}
/**
* 暫停任務(wù)
* @param taskId
*/
public Task pauseTask(String taskId){
Task task = taskPool.get(taskId);
try {
Scheduler sched = schedulerFactory.getScheduler();
sched.pauseTrigger(task.getId(), task.getTrigger());// 停止觸發(fā)器
} catch (Exception e) {
throw new RuntimeException(e);
}
return task;
}
/**
*
*
*/
public Task restartTask(String taskId) {
Task task = taskPool.get(taskId);
try {
Scheduler sched = schedulerFactory.getScheduler();
// 重啟觸發(fā)器
sched.resumeTrigger(task.getId(),task.getTrigger());
} catch (Exception e) {
throw new RuntimeException(e);
}
return task;
}
/**
* 關(guān)閉任務(wù)
* @param taskId
*/
public Task shutdownTask(String taskId){
Task task = taskPool.get(taskId);
try {
Scheduler sched = schedulerFactory.getScheduler();
sched.pauseTrigger(taskId, task.getTrigger());// 停止觸發(fā)器
sched.unscheduleJob(taskId, task.getGroup());// 移除觸發(fā)器
sched.deleteJob(taskId, task.getGroup());// 刪除任務(wù)
} catch (Exception e) {
throw new RuntimeException(e);
}
return task;
}
/**
* 啟動(dòng)所有定時(shí)任務(wù)
*
*/
public void startAllTask() {
try {
Scheduler sched = schedulerFactory.getScheduler();
sched.start();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 關(guān)閉所有定時(shí)任務(wù)
*/
public void shutdownAllTask() {
try {
Scheduler sched = schedulerFactory.getScheduler();
if (!sched.isShutdown()) {
sched.shutdown();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}到此這篇關(guān)于Spring+Quartz實(shí)現(xiàn)動(dòng)態(tài)任務(wù)調(diào)度詳解的文章就介紹到這了,更多相關(guān)Spring+Quartz動(dòng)態(tài)任務(wù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Springboot-admin整合Quartz實(shí)現(xiàn)動(dòng)態(tài)管理定時(shí)任務(wù)的過程詳解
- SpringBoot+Quartz實(shí)現(xiàn)動(dòng)態(tài)定時(shí)任務(wù)
- 淺談SpringBoot集成Quartz動(dòng)態(tài)定時(shí)任務(wù)
- Springboot整個(gè)Quartz實(shí)現(xiàn)動(dòng)態(tài)定時(shí)任務(wù)的示例代碼
- Quartz+Spring Boot實(shí)現(xiàn)動(dòng)態(tài)管理定時(shí)任務(wù)
- springboot整合Quartz實(shí)現(xiàn)動(dòng)態(tài)配置定時(shí)任務(wù)的方法
相關(guān)文章
Java object wait notify notifyAll代碼解析
這篇文章主要介紹了Java object wait notify notifyAll代碼解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-11-11
SpringBoot配置MongoDB多數(shù)據(jù)源的方法步驟
這篇文章主要介紹了SpringBoot配置MongoDB多數(shù)據(jù)源的方法步驟,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-10-10
Java序列化問題:“Serialized class has not impl
在Java開發(fā)中,序列化(Serialization)是一個(gè)常見的操作,尤其是在分布式系統(tǒng)、網(wǎng)絡(luò)通信或數(shù)據(jù)持久化場(chǎng)景中,然而,序列化過程中可能會(huì)遇到各種問題,其中最常見的一個(gè)錯(cuò)誤是Serialized class has not implement Serializable interface,本文給大家介紹了相關(guān)的解決方法2025-02-02
Android?Studio中創(chuàng)建java工程的完整步驟
Android?Studio創(chuàng)建java工程是非常麻煩的,因?yàn)锳ndroid?Studio沒有提供直接創(chuàng)建java工程的方法,下面這篇文章主要給大家介紹了關(guān)于Android?Studio中創(chuàng)建java工程的完整步驟,需要的朋友可以參考下2024-01-01
spring中@Autowired自動(dòng)注入依賴項(xiàng)的使用
當(dāng)使用@Autowired注解時(shí),它可以自動(dòng)注入依賴項(xiàng),例如其他類的實(shí)例,本文就來詳細(xì)的介紹一下,具有一定的參考價(jià)值,感興趣的可以了解一下2023-09-09
Javaweb開發(fā)中通過Servlet生成驗(yàn)證碼圖片
這篇文章主要為大家詳細(xì)介紹了Javaweb開發(fā)中通過Servlet生成驗(yàn)證碼圖片的相關(guān)資料,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-05-05
SpringBoot如何使用Template請(qǐng)求http接口
在Spring?Boot中,如果你想要通過模板(template)的方式連接HTTP服務(wù),并發(fā)送HTTP請(qǐng)求,有幾種不同的方式可以實(shí)現(xiàn),但最直接和常用的方式之一是使用RestTemplate,這篇文章主要介紹了SpringBoot使用Template請(qǐng)求http接口,需要的朋友可以參考下2024-08-08
SpringSecurityOAuth2 如何自定義token信息
這篇文章主要介紹了SpringSecurityOAuth2 自定義token信息的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-06-06
Java把多個(gè)對(duì)象的list的數(shù)據(jù)合并的方法示例
在Java中合并多個(gè)List可以使用遍歷、Stream API或Apache Commons Collections,通過遍歷各個(gè)List并將元素添加到新List實(shí)現(xiàn)合并,Java 8提供了Stream API,使用Stream.of()和flatMap()可以簡(jiǎn)潔地合并List,文中給出了多種方法示例,需要的朋友可以參考下2024-09-09

