SpringBoot集成Quartz

SpringBoot整合Quartz定时任务

文章目录

1.加载properties方式

1.pom依赖

      <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-quartz</artifactId>
        </dependency>

       <!--quartz-->
            <dependency>
                <groupId>org.quartz-scheduler</groupId>
                <artifactId>quartz</artifactId>
                <version>2.2.1</version>
            </dependency>
            <!--quartz-jobs-->
            <dependency>
                <groupId>org.quartz-scheduler</groupId>
                <artifactId>quartz-jobs</artifactId>
                <version>2.2.1</version>
            </dependency>
    

      2.quartz.properties配置文件

      #Main Scheduler Settings
      #配置集群时,quartz调度器的id,由于配置集群时,只有一个调度器,必须保证每个服务器该值都相同,可以不用修改,只要每个ams都一样就行
      org.quartz.scheduler.instanceName=quartzScheduler_ym
      org.quartz.scheduler.rmi.export=false
      org.quartz.scheduler.rmi.proxy=false
      org.quartz.scheduler.wrapJobExecutionInUserTransaction=false
      #集群中每台服务器自己的id,AUTO表示自动生成,无需修改
      org.quartz.scheduler.instanceId=AUTO
      org.quartz.scheduler.threadsInheritContextClassLoaderOfInitializer=true
      org.quartz.scheduler.skipUpdateCheck=true
      #一次性取出的任务数,默认值是1,适合负载均衡,但不适合大量的短时任务
      org.quartz.scheduler.batchTriggerAcquisitionMaxCount=25
      
      #Configure ThreadPool
      #quartz线程池的实现类,无需修改
      org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool 
      #quartz线程池中线程数,可根据任务数量和负载度来调整
      org.quartz.threadPool.threadCount=25
      #quartz线程优先级
      org.quartz.threadPool.threadPriority=5
      org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread=true
      
      #Configure JobStore
      org.quartz.jobStore.acquireTriggersWithinLock=true
      #表示如果某个任务到达执行时间,而此时线程池中没有可用线程时,任务等待的最大时间,如果等待时间超过下面配置的值(毫秒),本次就不在执行,而等待下一次执行时间的到来,可根据任务量和负责程度来调整
      org.quartz.jobStore.misfireThreshold=60000
      #实现集群时,任务的存储实现方式,org.quartz.impl.jdbcjobstore.JobStoreTX表示数据库存储,无需修改
      org.quartz.jobStore.class=org.springframework.scheduling.quartz.LocalDataSourceJobStore
      org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
      
      #quartz存储任务相关数据的表的前缀,无需修改
      org.quartz.jobStore.tablePrefix=QRTZ_
      #是否启用集群,启用,改为true,注意:启用集群后,必须配置下面的数据源,否则quartz调度器会初始化失败
      org.quartz.jobStore.isClustered=true
      #集群中服务器相互检测间隔,每台服务器都会按照下面配置的时间间隔往服务器中更新自己的状态,如果某台服务器超过以下时间没有checkin,调度器就会认为该台服务器已经down掉,不会再分配任务给该台服务器
      org.quartz.jobStore.clusterCheckinInterval=10000
      
      #Configure DataSources
      #连接数据库数据源名称,与下面配置中org.quartz.dataSource.myDS的myDS一致即可,可以无需修改
      org.quartz.jobStore.dataSource=myDS
      org.quartz.dataSource.myDS.driver=com.mysql.cj.jdbc.Driver
      org.quartz.dataSource.myDS.URL=jdbc:mysql://127.0.0.1:3306/ruoyi_local?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
      org.quartz.dataSource.myDS.user=root
      org.quartz.dataSource.myDS.password=root
      #配置连接数据库连接池大小,一般为上面配置的线程池的2倍
      org.quartz.dataSource.myDS.maxConnections=50
      #org.quartz.dataSource.myDS.validationQuery=select 1 from dual
      
        1.普通方法定时任务配置执行
        /**
         * 定时任务执行方法
         * @author moshangshang
         */
        public class QuartzTest {
        
            public void test(){
                System.out.println("该方法被调用" );
            }
        }
        

          配置触发

          /**
           * Quartz配置(方法测试任务配置)
           */
          @Configuration
          public class QuartzConfig  {
          
             /**
               * 要调用的工作类
               */
              @Bean
              public QuartzTest quartzDay(){
                  return new QuartzTest();
              }
          
          
              // targetMethod: 指定需要定时执行QuartzTest中的test()方法
              // concurrent:对于相同的JobDetail,当指定多个Trigger时, 很可能第一个job完成之前,
              // 第二个job就开始了。指定concurrent设为false,多个job不会并发运行,第二个job将不会在第一个job完成之前开始。
              // triggers:通过再添加其他的ref元素可在list中放置多个触发器。 
              @Bean(name = "daymailtask")
              public MethodInvokingJobDetailFactoryBean detailFactoryBean(@Qualifier("quartzDay") Object quartzDay) {
                  MethodInvokingJobDetailFactoryBean bean = new MethodInvokingJobDetailFactoryBean();
                  bean.setTargetObject(quartzDay);
                  //调用方法名
                  bean.setTargetMethod("test");
                  bean.setConcurrent(false);
                  return bean;
              }
          
          
              @Bean(name = "doDayMailTime")
              public CronTriggerFactoryBean cronTriggerBean(@Qualifier("daymailtask") MethodInvokingJobDetailFactoryBean daymailtask) {
                  CronTriggerFactoryBean trigger = new CronTriggerFactoryBean();
                  trigger.setJobDetail(daymailtask.getObject());
                  // 十秒一次
                  trigger.setCronExpression("0/10 * * * * ?");
                  return trigger;
              }
          
          
              @Bean
              @Lazy(false)
              public SchedulerFactoryBean schedulerFactory(Trigger[] cronTriggerBean) {
                  SchedulerFactoryBean bean = new SchedulerFactoryBean();
                  System.err.println(cronTriggerBean[0]);
                  bean.setTriggers(cronTriggerBean);
                  return bean;
              }
          
              @Bean
              public Scheduler scheduler(SchedulerFactoryBean schedulerFactoryBean) {
                  return schedulerFactoryBean.getScheduler();
              }
              
          }
          
            2.对于quartz定时任务job中不能注入bean对象处理

            由于Quartz的job类中无法直接注入spring容器的bean对象,会报空指针异常,所以自定义的quartz的工厂来将spring的bean进行注入,然后在调度器工厂的bean中进行设置,即可以在job类中对bean进行注入,但在操作时需要注入自己配置的工厂bean对象。

            @Component
            public class CustomJobFactory extends AdaptableJobFactory {
            
                @Autowired
                private AutowireCapableBeanFactory capableBeanFactory;
            
                @Override
                protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
                    //调用父类的方法
                    Object jobInstance = super.createJobInstance(bundle);
                    //进行注入
                    capableBeanFactory.autowireBean(jobInstance);
                    return jobInstance;
                }
            }
            
              @Configuration
              public class QuartzConfig {
              
                  @Autowired
                  private CustomJobFactory customJobFactory;
              
                  @SneakyThrows
                  @Bean("schedulerFactory")
                  public StdSchedulerFactory scheduler(){
                      StdSchedulerFactory schedulerFactory = new StdSchedulerFactory();
                      Scheduler scheduler = schedulerFactory.getScheduler();
                      // 自定义 JobFactory 使得在 Quartz Job 中可以使用 @Autowired
                      scheduler.setJobFactory(customJobFactory);
                      scheduler.start();
                      return schedulerFactory;
                  }
              
              }
              
                 @Autowired
                 private StdSchedulerFactory schedulerFactory ;
                
                  3.加载属性配置文件
                  @Slf4j
                  @Component
                  public class QuartzManager implements ApplicationListener<ContextRefreshedEvent> {
                  
                      @Autowired
                  	private StdSchedulerFactory schedulerFactory ;
                  
                      private static final String CONFIG_FILE = "quartz.properties";
                   
                      //操作方法,见操作工具类
                  
                      private void init() {
                          try {
                              Properties properties = new Properties();
                              properties.load(Thread.currentThread().getContextClassLoader().getResourceAsStream(CONFIG_FILE));
                              schedulerFactory.initialize(properties);
                              System.out.println(schedulerFactory.getScheduler());
                          } catch (Exception e) {
                              e.printStackTrace();
                          }
                      }
                  
                      @Override
                      public void onApplicationEvent(ContextRefreshedEvent event) {
                          if (null == event.getApplicationContext().getParent()) {
                              init();
                          }
                      }
                  
                  }
                  

                    使用参考springboot整合,使用schedulerFactory操作

                    2.纯yml配置方式

                    pom依赖

                           <dependency>
                                <groupId>org.springframework.boot</groupId>
                                <artifactId>spring-boot-starter-quartz</artifactId>
                            </dependency>
                    

                      推荐使用springboot的自动注入依赖及yml配置,该版本测试基于springboot2.7

                      yml配置文件
                      spring:
                         #定时任务
                        quartz:
                          # 将任务等保存化到数据库
                          job-store-type: jdbc
                          # 程序结束时会等待quartz相关的内容结束
                          wait-for-jobs-to-complete-on-shutdown: true
                          # QuartzScheduler启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录
                          overwrite-existing-jobs: true
                          # 延迟启动
                          startup-delay: 1
                          properties:
                            org:
                              quartz:
                                # scheduler相关
                                scheduler:
                                  # scheduler的实例名
                                  instanceName: scheduler
                                  rmi:
                                    export: false
                                    proxy: false
                                  #集群中每台服务器自己的id,AUTO表示自动生成,无需修改,由于配置集群时,只有一个调度器,必须保证每个服务器该值都相同,
                                  instanceId: AUTO
                                  wrapJobExecutionInUserTransaction: false
                                  threadsInheritContextClassLoaderOfInitializer: true
                                  skipUpdateCheck: true
                                  #一次性取出的任务数,默认值是1,适合负载均衡,但不适合大量的短时任务
                                  batchTriggerAcquisitionMaxCount: 25
                                # 持久化相关
                                jobStore:
                                  acquireTriggersWithinLock: true
                                  # 表示如果某个任务到达执行时间,而此时线程池中没有可用线程时,任务等待的最大时间,
                                  # 如果等待时间超过下面配置的值(毫秒),本次就不在执行,而等待下一次执行时间的到来,可根据任务量和负责程度来调整
                                  misfireThreshold: 60000
                                  # 实现集群时,任务的存储实现方式,org.quartz.impl.jdbcjobstore.JobStoreTX表示数据库存储
                                  # 升级spring boot > 2.5.6的版本后将不再支持JobStoreTX方式进行配置默认数据源
                                  class: org.springframework.scheduling.quartz.LocalDataSourceJobStore
                                  #oracle数据库为 org.quartz.impl.jdbcjobstore.oracle.OracleDelegate
                                  driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
                                  # quartz存储任务相关数据的表的前缀
                                  tablePrefix: qrtz_
                                  # 是否启用集群,启用,改为true,注意:启用集群后,必须配置下面的数据源,否则quartz调度器会初始化失败
                                  isClustered: true
                                  # 集群中服务器相互检测间隔,每台服务器都会按照下面配置的时间间隔往服务器中更新自己的状态,
                                  # 如果某台服务器超过以下时间没有checkin,调度器就会认为该台服务器已经down掉,不会再分配任务给该台服务器
                                  clusterCheckinInterval: 10000
                                  # 是否将JobDataMap中的属性转为字符串存储
                                  useProperties: false
                                # 线程池相关
                                threadPool:
                                  class: org.quartz.simpl.SimpleThreadPool
                                  # 线程池中线程数,可根据任务数量和负载度来调整
                                  threadCount: 25
                                  # 线程优先级
                                  threadPriority: 5
                                  threadsInheritContextClassLoaderOfInitializingThread: true
                                # 数据源配置
                                # 连接数据库数据源名称,与下面配置中dataSource.myDS的myDS一致
                                dataSource: myDS
                                dataSource.myDS.driver: com.mysql.cj.jdbc.Driver
                                dataSource.myDS.URL: jdbc:mysql://127.0.0.1:3306/ruoyi_local?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
                                dataSource.myDS.user: root
                                dataSource.myDS.password: root
                                # 配置连接数据库连接池大小,一般为上面配置的线程池的2倍
                                dataSource.myDS.maxConnections: 50
                                # dataSource.myDS.validationQuery: select 1 from dual
                      
                        编写任务核心处理方法

                        注意事项:

                        • Quartz的定时任务的job类中实现的StatefulJob接口已过时,在开发时尽量使用 实现Job接口或者继承QuartzJobBean类的方式进行执行类的实现。
                        /**
                         * 定时任务执行方法
                         * @author moshangshang
                         */
                        public class QuartzTestJob extends QuartzJobBean {
                        
                            @Override
                            protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
                                String userName = (String) context.getJobDetail().getJobDataMap().get("username");
                                System.out.println("userName:" + userName);
                            }
                        
                        }
                        

                          1.编写任务执行配置

                          /**
                           * 定时任务执行配置
                           * @author moshangshang
                           */
                          @Configuration
                          public class TaskConfig {
                          
                              @Bean("helloJob")
                              public JobDetail helloJobDetail() {
                                  return JobBuilder.newJob(QuartzTestJob.class)
                                          .withIdentity("DateTimeJob")
                                          .usingJobData("username", "Hello Quartz")
                                          .storeDurably()//即使没有Trigger关联时,也不需要删除该JobDetail
                                          .build();
                              }
                          
                              @Bean
                              public Trigger printTimeJobTrigger() {
                                  // 每秒执行一次
                                  CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule("0/1 * * * * ?");
                                  return TriggerBuilder.newTrigger()
                                          .forJob(helloJobDetail())
                                          .withIdentity("quartzTaskService")
                                          .withSchedule(cronScheduleBuilder)
                                          .build();
                              }
                              
                          }
                          

                            2.注入SchedulerFactoryBean动态使用

                            springboot使用yml配置时可直接给工具类注入SchedulerFactoryBean对象使用

                            在使用yml自动配置时,配置自定义的StdSchedulerFactory,获取的是默认的属性配置,yml不会生效,需使用SchedulerFactoryBean

                            @Autowired
                            private SchedulerFactoryBean schedulerFactory;
                            
                            • 1
                            • 2

                            3.操作工具类

                            @Slf4j
                            @Component
                            public class QuartzManager {
                            
                            
                                @Autowired
                                private Scheduler scheduler;
                            
                                /**
                                 * 添加一个定时任务
                                 *
                                 * @param jobName 任务名
                                 * @param jobGroupName 任务组名
                                 * @param triggerName 触发器名
                                 * @param triggerGroupName 触发器组名
                                 * @param jobClass 任务
                                 * @param cron 时间设置
                                 */
                                @SuppressWarnings({"unchecked", "rawtypes"})
                                public void addJob(String jobName, String jobGroupName, String triggerName, String triggerGroupName, Class jobClass,
                                                   String cron, String id) {
                                    try {
                                        // 任务名,任务组,任务执行类
                                        JobDetail jobDetail = JobBuilder.newJob(jobClass)
                                                .withIdentity(jobName, jobGroupName)
                                                .build();
                                        // 参数
                                        JobDataMap jobDataMap = jobDetail.getJobDataMap();
                                        jobDataMap.put("jkConfigId", id);
                                        // 创建Trigger对象
                                        Trigger trigger =  TriggerBuilder.newTrigger()
                                                // 触发器名,触发器组
                                                .withIdentity(triggerName, triggerGroupName).startNow()
                                                // 触发器时间设定
                                                .withSchedule(CronScheduleBuilder.cronSchedule(cron))
                                                .build();
                                        // 调度容器设置JobDetail和Trigger
                                        scheduler.scheduleJob(jobDetail, trigger);
                                        // 启动
                                        if (!scheduler.isShutdown()) {
                                            scheduler.start();
                                        }
                                    } catch (Exception e) {
                                        throw new RuntimeException(e);
                                    }
                                }
                            
                                /**
                                 * 添加一个定时任务
                                 */
                                @SuppressWarnings({"unchecked", "rawtypes"})
                                public void addJob(String jobName, String jobGroupName, String triggerName, String triggerGroupName, Class jobClass,
                                                   String cron, Map<String, Object> dataMap) {
                                    try {
                                        // 任务名,任务组,任务执行类
                                        JobDetail jobDetail = JobBuilder.newJob(jobClass)
                                                .withIdentity(jobName, jobGroupName)
                                                .build();
                                        // 参数
                                        JobDataMap jobDataMap = jobDetail.getJobDataMap();
                                        //传递的所有参数
                                        jobDataMap.putAll(dataMap);
                                        // 创建Trigger对象
                                        Trigger trigger =  TriggerBuilder.newTrigger()
                                                // 触发器名,触发器组
                                                .withIdentity(triggerName, triggerGroupName).startNow()
                                                // 触发器时间设定
                                                .withSchedule(CronScheduleBuilder.cronSchedule(cron))
                                                .build();
                                        // 调度容器设置JobDetail和Trigger
                                        scheduler.scheduleJob(jobDetail, trigger);
                                        // 启动
                                        if (!scheduler.isShutdown()) {
                                            scheduler.start();
                                        }
                                    } catch (Exception e) {
                                        throw new RuntimeException(e);
                                    }
                                }
                            
                                /**
                                 * 修改一个任务的触发时间
                                 *
                                 * @param triggerName 触发器名
                                 * @param triggerGroupName 触发器组名
                                 * @param cron 时间设置
                                 */
                                public void modifyJobTime(String triggerName, String triggerGroupName, String cron) {
                                    try {
                                        TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, triggerGroupName);
                                        CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
                                        if (trigger == null) {
                                            return;
                                        }
                                        String oldTime = trigger.getCronExpression();
                                        if (!oldTime.equalsIgnoreCase(cron)) {
                                            /** 方式一:修改一个任务的触发时间 */
                                            // 创建Trigger对象
                                            trigger = TriggerBuilder.newTrigger().startNow()
                                                    // 触发器时间设定
                                                    .withSchedule(CronScheduleBuilder.cronSchedule(cron)).build();
                                            scheduler.rescheduleJob(triggerKey, trigger);
                                            /** 方式二:先删除,然后再创建一个新的Job */
                                            // JobDetail jobDetail =
                                            // scheduler.getJobDetail(JobKey.jobKey(jobName,jobGroupName));
                                            // Class<? extends Job> jobClass = jobDetail.getJobClass();
                                            // removeJob(jobName,jobGroupName,triggerName,triggerGroupName);
                                            // addJob(jobName,jobGroupName,triggerName,triggerGroupName,jobClass,cron);
                                        }
                                    } catch (Exception e) {
                                        throw new RuntimeException(e);
                                    }
                                }
                            
                                /**
                                 * 触发状态为运行的任务
                                 * 立即执行一次任务
                                 */
                                public void triggerRunningJob(String jobName, String jobGroupName) {
                                    try {
                                        JobKey jobKey = JobKey.jobKey(jobName, jobGroupName);
                                        scheduler.triggerJob(jobKey);
                                    } catch (Exception e) {
                                        throw new RuntimeException(e);
                                    }
                                }
                            
                                /**
                                 * 移除一个任务
                                 */
                                public void removeJob(String jobName, String jobGroupName, String triggerName, String triggerGroupName) {
                                    try {
                                        TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, triggerGroupName);
                                        // 停止触发器
                                        scheduler.pauseTrigger(triggerKey);
                                        // 移除触发器
                                        scheduler.unscheduleJob(triggerKey);
                                        // 删除任务
                                        scheduler.deleteJob(JobKey.jobKey(jobName, jobGroupName));
                                    } catch (Exception e) {
                                        throw new RuntimeException(e);
                                    }
                                }
                            
                                /**
                                 * 启动所有定时任务
                                 */
                                public void startJobs() {
                                    try {
                                        scheduler.start();
                                    } catch (Exception e) {
                                        throw new RuntimeException(e);
                                    }
                                }
                            
                                /**
                                 * 关闭所有定时任务
                                 */
                                public void shutdownJobs() {
                                    try {
                                        if (!scheduler.isShutdown()) {
                                            scheduler.shutdown();
                                        }
                                    } catch (Exception e) {
                                        throw new RuntimeException(e);
                                    }
                                }
                            
                            
                                /**
                                 * 暂停所有定时任务
                                 */
                                public void standby() {
                                    try {
                                        scheduler.standby();
                                    } catch (Exception e) {
                                        throw new RuntimeException(e);
                                    }
                                }
                            
                            }
                            

                              注意:shutdown方法后不能再启动,会报错,可以使用暂停standby方法

                              The Scheduler cannot be restarted after shutdown() has been called.

                              测试

                              @RestController
                              @RequestMapping("/quartz")
                              public class QuartzController {
                              
                                  @Autowired
                                  private QuartzManager quartzManager;
                              
                              
                              
                                  @GetMapping("/start")
                                  public void test4() {
                                      quartzManager.startJobs();
                                  }
                              
                              
                                  @GetMapping("/standby")
                                  public void test5() {
                                      quartzManager.standby();
                                  }
                              
                              
                              
                                  @GetMapping("/add")
                                  public void test6() {
                                      quartzManager.addJob("DateTimeJob","aaa",
                                              "quartzTaskService","bbb", QuartzTestJob.class,"0/1 * * * * ?","11");
                                  }
                              
                              
                                  @GetMapping("/remove")
                                  public void test7() {
                                      quartzManager.removeJob("DateTimeJob",null,"quartzTaskService",null);
                                  }
                              
                              
                              }
                              
                                ### 集成 Quartz 实现定时任务调度 为了在 Spring Boot 项目中集成 Quartz 来实现定时任务调度,开发者可以通过引入相应的依赖来简化这一过程。每当向 Spring Boot 项目添加新的依赖项时,Spring Boot 自动配置会自动尝试根据该依赖项配置 bean[^4]。 #### 添加 Maven 或 Gradle 依赖 首先,在 `pom.xml` 文件中加入 Quartz 和其相关组件的支持: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-quartz</artifactId> </dependency> ``` 对于使用 Gradle 构建系统的项目,则应在 build.gradle 中添加如下内容: ```groovy implementation 'org.springframework.boot:spring-boot-starter-quartz' ``` #### 创建 Job 类 定义具体的作业类继承自 QuartzJobBean 并重写 executeInternal 方法完成业务逻辑处理工作: ```java import org.quartzDisallowConcurrentExecution; import org.quartz.PersistJobDataAfterExecution; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.springframework.scheduling.quartz.QuartzJobBean; @PersistJobDataAfterExecution @DisallowConcurrentExecution public class SampleJob extends QuartzJobBean { @Override protected void executeInternal(JobExecutionContext context) throws JobExecutionException { System.out.println("Executing job at " + new Date()); } } ``` 上述代码片段展示了创建一个简单的 Quartz 调度器实例的方法[^1]。 #### 定义 Scheduler Bean 通过 Java Config 方式注册 CronTriggerFactoryBean 及 MethodInvokingJobDetailFactoryBean 对象用于触发指定方法执行周期性的操作: ```java @Configuration @EnableScheduling public class QuartzConfig { @Autowired private ApplicationContext applicationContext; @Bean public JobDetail sampleJobDetail() { return JobBuilder.newJob(SampleJob.class).withIdentity("sampleJob").build(); } @Bean public Trigger sampleJobTrigger(@Qualifier("sampleJobDetail") JobDetail jobDetail) { return TriggerBuilder.newTrigger() .forJob(jobDetail) .withSchedule(CronScheduleBuilder.cronSchedule("0/5 * * * * ?")) .build(); } } ``` 这段配置说明了如何设置每五秒运行一次的任务计划表[^3]。
                                评论
                                添加红包

                                请填写红包祝福语或标题

                                红包个数最小为10个

                                红包金额最低5元

                                当前余额3.43前往充值 >
                                需支付:10.00
                                成就一亿技术人!
                                领取后你会自动成为博主和红包主的粉丝 规则
                                hope_wisdom
                                发出的红包
                                实付
                                使用余额支付
                                点击重新获取
                                扫码支付
                                钱包余额 0

                                抵扣说明:

                                1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
                                2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

                                余额充值