前段时间公司项目用到了 定时任务 所以写了一篇定时任务的文章 ,浏览量还不错 ,
所以就准备写第二篇, 如果你是一名Java工程师,你也可以会看到如下的页面 ,去添加定时任务
很显然他们只是披着不同的皮而已,本质上都是定时任务 , 也就是将所有的任务数据 交给了 Spring 进行管理 ,最后 将 任务Job信息 ,以及 参数传递的信息 对外进行暴露 cron 的输入值 ,然后交给数据库去进行 传参 这里我们来演示下 上面做到流程
这玩意 简单说 就是你要做什么事情 ,然后你要做这件事,你需要 用到定时任务, 所有 你就得 去实现人家第三方的库 ,这里我们用 quartz , 当然类似的还有很多很多,只是为了方便演示
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-quartzartifactId>
dependency>
在第一次使用之前首先我们需要生成一下quartz 自带默认的表 ,大概会生成11 张的样子
把yaml配置文件中的 initialize-schema: always 配置的 always 属性意思是:
每次初始化都会重新生成表(执行一次删除,执行一次创建),生成后,可以修改为 never
修改下列初始化结构的 yaml 属性 : initialize-schema:
所以你第一次可以用 always ,后面你就改成 never 就行了
spring:
## quartz定时任务,采用数据库方式
quartz:
job-store-type: jdbc
initialize-schema: embedded
#定时任务启动开关,true-开 false-关
auto-startup: true
#延迟1秒启动定时任务
startup-delay: 1s
#启动时更新己存在的Job
overwrite-existing-jobs: true
properties:
org:
quartz:
scheduler:
instanceName: MyScheduler
instanceId: AUTO
jobStore:
class: org.springframework.scheduling.quartz.LocalDataSourceJobStore
driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
tablePrefix: QRTZ_
isClustered: false
misfireThreshold: 12000
clusterCheckinInterval: 15000
threadPool:
class: org.quartz.simpl.SimpleThreadPool
threadCount: 1
threadPriority: 5
threadsInheritContextClassLoaderOfInitializingThread: true
我们在创建一张业务的表,方便待会弄增删改查
CREATE TABLE `sys_quartz_job` (
`id` varchar(32) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL,
`create_by` varchar(32) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT '创建人',
`create_at` datetime DEFAULT NULL COMMENT '创建时间',
`del_flag` int DEFAULT NULL COMMENT '删除状态',
`update_by` varchar(32) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT '修改人',
`update_at` datetime DEFAULT NULL COMMENT '修改时间',
`job_class_name` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT '任务类名',
`cron_expression` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT 'cron表达式',
`parameter` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT '参数',
`meeting_record_id` int DEFAULT NULL COMMENT '会议室记录id',
`description` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT '描述',
`status` int DEFAULT NULL COMMENT '状态 0正常 -1停止',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 ROW_FORMAT=DYNAMIC;
加上这张表,我们就有了12 张表, 其他都是 框架自带的,一张我们自己生成的
首先写一个类 ,你继承了人家quartz 的Job就可以用人家的功能,就这么简单,然后你已经有了一个任务了
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
@Component
public class QuartzDemoJob implements Job {
static int i = 0;
@Autowired
private QuartzDemoService quartzDemoService;
public QuartzDemoJob() {
}
@Autowired //这里不能直接注入,因为@Autowired注入是Spring的注入,要求注入对象与被注入对象都是在SpringIOC容器中存在,
public QuartzDemoJob(QuartzDemoService quartzDemoService) {
this.quartzDemoService = quartzDemoService;
}
@Transactional
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
// 编写 service 逻辑
}
public static String dateToStr(java.util.Date date, String strFormat) {
SimpleDateFormat sf = new SimpleDateFormat(strFormat);
return sf.format(date);
}
public static Date strToSqlDate(String strDate, String dateFormat) {
SimpleDateFormat sf = new SimpleDateFormat(dateFormat);
Date date = null;
try {
date = sf.parse(strDate);
} catch (ParseException e) {
e.printStackTrace();
}
return new Date(date != null ? date.getTime() : 0);
}
}
你有了这个任务之后 ,你现在需要 交给Spring 进行管理 ,所以你需要搞个配置 ,这你看了我上篇文章 就明白了 吧
首先我们简单点 ,Job就是任务 说人话就是你要做的事 ,你把你要做的事写了个类,然后给Spring 进行管理 配置一下, 所以我们现在把 Job弄到配置类里面去
@Bean
public JobDetailFactoryBean jobDetailFactoryBean(){
JobDetailFactoryBean factoryBean=new JobDetailFactoryBean();
//关联我们自己的Job类
factoryBean.setJobClass(QuartzDemoJob.class); //QuartzDemoJob的实例化并没有经过Spring的处理,
// Spring的注入是要求注入的对象和被注入的对象都要在Spring的IOC容器中
return factoryBean;
}
完整 代码 :
@Configuration
public class QuartzCoreConfig {
/**
* 1、创建Job对象
*/
@Bean
public JobDetailFactoryBean jobDetailFactoryBean(){
JobDetailFactoryBean factoryBean=new JobDetailFactoryBean();
//关联我们自己的Job类
factoryBean.setJobClass(QuartzDemoJob.class); //QuartzDemoJob的实例化并没有经过Spring的处理,
// Spring的注入是要求注入的对象和被注入的对象都要在Spring的IOC容器中
return factoryBean;
}
/**
* 2、创建Trigger对象
* Cron Trigger
*/
@Bean
public CronTriggerFactoryBean cronTriggerFactoryBean(JobDetailFactoryBean jobDetailFactoryBean){
CronTriggerFactoryBean factoryBean=new CronTriggerFactoryBean();
//关联JobDetail对象
factoryBean.setJobDetail(Objects.requireNonNull(jobDetailFactoryBean.getObject()));
//设置触发时间
factoryBean.setCronExpression("0/2 * * * * ?"); //每2秒触发一次, 分钟,小时,天,月,星期
// factoryBean.setCronExpression("0 0-59 0-22 * * ?"); //在每天0-22点期间的每1分钟触发
return factoryBean;
}
/**
* 3、创建Scheduler
*/
@Bean
public SchedulerFactoryBean schedulerFactoryBean(CronTriggerFactoryBean cronTriggerFactoryBean, MyadaptableJobFactory myadaptableJobFactory){
SchedulerFactoryBean factoryBean=new SchedulerFactoryBean();
//关联trigger
factoryBean.setTriggers(cronTriggerFactoryBean.getObject());
factoryBean.setJobFactory(myadaptableJobFactory); //调用myadaptableJobFactory把对象注入到SpringIOC容器中
return factoryBean;
}
}
SchedulerFactoryBean : 就是调度器的意思
CronTriggerFactoryBean : 就是 触发器的意思
JobDetailFactoryBean : 定时任务
在自己搞个工厂 将 quartz 手动创建一个实例
/**
* 2. 编写工厂模式 加载进Spring
*/
@Component("myadaptableJobFactory") //将该类实例化,使得可以直接用
public class MyadaptableJobFactory extends AdaptableJobFactory {
//AutowireCapableBeanFactory可以将一个对象添加到Spring IOC容器中,并且完成该对象注入
@Autowired
private AutowireCapableBeanFactory autowireCapableBeanFactory;
//该方法将实例化的任务对象手动的添加到SpringIOC容器中并且完成对象的注入
@Override
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
Object object = super.createJobInstance(bundle);
//将object对象添加到Spring IOC容器中并完成注入
this.autowireCapableBeanFactory.autowireBean(object);
return object;
}
}
上面我们做了三部
接下来完成三层 架构 MVC,搞个Controller ,只是提供思路,自己把他写完哈
@RestController
@RequestMapping("/sys/quartzJob")
@Slf4j
@Api(tags = "定时任务接口")
public class QuartzJobController {
@Autowired
private IQuartzJobService quartzJobService;
@Autowired
private Scheduler scheduler;
/**
* 分页列表查询
*
* @param quartzJob
* @param pageNo
* @param pageSize
* @param req
* @return
*/
@RequestMapping(value = "/list", method = RequestMethod.GET)
public Result<?> queryPageList(QuartzJob quartzJob, @RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
@RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize, HttpServletRequest req) {
QueryWrapper<QuartzJob> queryWrapper = new QueryWrapper<>();
queryWrapper.select("");
Page<QuartzJob> page = new Page<QuartzJob>(pageNo, pageSize);
IPage<QuartzJob> pageList = quartzJobService.page(page, queryWrapper);
return Result.ok(pageList);
}
/**
* 添加定时任务
*
* @param quartzJob
* @return
*/
//@RequiresRoles("admin")
@RequestMapping(value = "/add", method = RequestMethod.POST)
public Result<?> add(@RequestBody QuartzJob quartzJob) {
quartzJobService.saveAndScheduleJob(quartzJob);
return Result.ok("创建定时任务成功");
}
/**
* 更新定时任务
*
* @param quartzJob
* @return
*/
//@RequiresRoles("admin")
@RequestMapping(value = "/edit", method = {RequestMethod.PUT, RequestMethod.POST})
public Result<?> eidt(@RequestBody QuartzJob quartzJob) {
try {
quartzJobService.editAndScheduleJob(quartzJob);
} catch (SchedulerException e) {
log.error(e.getMessage(), e);
return Result.error("更新定时任务失败!");
}
return Result.ok("更新定时任务成功!");
}
/**
* 通过id删除
*
* @param id
* @return
*/
//@RequiresRoles("admin")
@RequestMapping(value = "/delete", method = RequestMethod.DELETE)
public Result<?> delete(@RequestParam(name = "id", required = true) String id) {
QuartzJob quartzJob = quartzJobService.getById(id);
if (quartzJob == null) {
return Result.error("未找到对应实体");
}
quartzJobService.deleteAndStopJob(id);
return Result.ok("删除成功!");
}
/**
* 批量删除
*
* @param ids
* @return
*/
//@RequiresRoles("admin")
@RequestMapping(value = "/deleteBatch", method = RequestMethod.DELETE)
public Result<?> deleteBatch(@RequestParam(name = "ids", required = true) String ids) {
if (ids == null || "".equals(ids.trim())) {
return Result.error("参数不识别!");
}
for (String id : Arrays.asList(ids.split(""))) {
QuartzJob job = quartzJobService.getById(id);
quartzJobService.deleteAndStopJob(id);
}
return Result.ok("删除定时任务成功!");
}
/**
* 暂停定时任务
*
* @param id
* @return
*/
//@RequiresRoles("admin")
@GetMapping(value = "/pause")
@ApiOperation(value = "停止定时任务")
public Result<Object> pauseJob(@RequestParam(name = "id") String id) {
QuartzJob job = quartzJobService.getById(id);
if (job == null) {
return Result.error("定时任务不存在!");
}
quartzJobService.pause(job);
return Result.ok("停止定时任务成功");
}
/**
* 启动定时任务
*
* @param id
* @return
*/
//@RequiresRoles("admin")
@GetMapping(value = "/resume")
@ApiOperation(value = "启动定时任务")
public Result<Object> resumeJob(@RequestParam(name = "id") String id) {
QuartzJob job = quartzJobService.getById(id);
if (job == null) {
return Result.error("定时任务不存在!");
}
quartzJobService.resumeJob(job);
//scheduler.resumeJob(JobKey.jobKey(job.getJobClassName().trim()));
return Result.ok("启动定时任务成功");
}
/**
* 通过id查询
*
* @param id
* @return
*/
@RequestMapping(value = "/queryById", method = RequestMethod.GET)
public Result<?> queryById(@RequestParam(name = "id", required = true) String id) {
QuartzJob quartzJob = quartzJobService.getById(id);
return Result.ok(quartzJob);
}
/**
* 导出excel
*
* @param request
* @param quartzJob
*/
@RequestMapping(value = "/exportXls")
public ModelAndView exportXls(HttpServletRequest request, QuartzJob quartzJob) {
// Step.1 组装查询条件
// QueryWrapper queryWrapper = QueryGenerator.initQueryWrapper(quartzJob, request.getParameterMap());
// // Step.2 AutoPoi 导出Excel
// ModelAndView mv = new ModelAndView(new JeecgEntityExcelView());
// List pageList = quartzJobService.list(queryWrapper);
// // 导出文件名称
// mv.addObject(NormalExcelConstants.FILE_NAME, "定时任务列表");
// mv.addObject(NormalExcelConstants.CLASS, QuartzJob.class);
// //获取当前登录用户
// //update-begin---author:wangshuai ---date:20211227 for:[JTC-116]导出人写死了------------
// LoginUser user = (LoginUser) SecurityUtils.getSubject().getPrincipal();
// mv.addObject(NormalExcelConstants.PARAMS, new ExportParams("定时任务列表数据", "导出人:" + user.getRealname(), "导出信息"));
// //update-end---author:wangshuai ---date:20211227 for:[JTC-116]导出人写死了------------
// mv.addObject(NormalExcelConstants.DATA_LIST, pageList);
return null;
}
/**
* 通过excel导入数据
*
* @param request
* @param response
* @return
*/
@RequestMapping(value = "/importExcel", method = RequestMethod.POST)
public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) throws IOException {
MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
Map<String, MultipartFile> fileMap = multipartRequest.getFileMap();
// 错误信息
List errorMessage = new ArrayList<>();
int successLines = 0, errorLines = 0;
for (Map.Entry entity : fileMap.entrySet()) {
// 获取上传文件对象
MultipartFile file = entity.getValue();
ImportParams params = new ImportParams();
params.setTitleRows(2);
params.setHeadRows(1);
params.setNeedSave(true);
try {
List listQuartzJobs = ExcelImportUtil.importExcel(file.getInputStream(), QuartzJob.class, params);
//add-begin-author:taoyan date:20210909 for:导入定时任务,并不会被启动和调度,需要手动点击启动,才会加入调度任务中 #2986
for (QuartzJob job : listQuartzJobs) {
job.setStatus(CommonConstant.STATUS_DISABLE);
}
List list = ImportExcelUtil.importDateSave(listQuartzJobs, IQuartzJobService.class, errorMessage, CommonConstant.SQL_INDEX_UNIQ_JOB_CLASS_NAME);
//add-end-author:taoyan date:20210909 for:导入定时任务,并不会被启动和调度,需要手动点击启动,才会加入调度任务中 #2986
errorLines += list.size();
successLines += (listQuartzJobs.size() - errorLines);
// } catch (Exception e) {
// log.error(e.getMessage(), e);
// return Result.error("文件导入失败!");
// } finally {
// try {
// file.getInputStream().close();
// } catch (IOException e) {
// e.printStackTrace();
// }
// }
// }
return Result.ok("");
}
/**
* 立即执行
*
* @param id
* @return
*/
//@RequiresRoles("admin")
@GetMapping("/execute")
public Result<?> execute(@RequestParam(name = "id", required = true) String id) {
QuartzJob quartzJob = quartzJobService.getById(id);
if (quartzJob == null) {
return Result.error("未找到对应实体");
}
try {
quartzJobService.execute(quartzJob);
} catch (Exception e) {
//e.printStackTrace();
log.info("定时任务 立即执行失败>>" + e.getMessage());
return Result.error("执行失败!");
}
return Result.ok("执行成功!");
}
}
@Data
@TableName("sys_quartz_job")
public class QuartzJob implements Serializable {
private static final long serialVersionUID = 1L;
/**
* id
*/
@TableId(type = IdType.ASSIGN_ID)
private java.lang.String id;
/**
* 创建人
*/
private java.lang.String createBy;
/**
* 创建时间
*/
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private java.util.Date createTime;
/**
* 删除状态
*/
private java.lang.Integer delFlag;
/**
* 修改人
*/
private java.lang.String updateBy;
/**
* 修改时间
*/
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private java.util.Date updateTime;
/**
* 任务类名
*/
@Excel(name = "任务类名", width = 40)
private java.lang.String jobClassName;
/**
* cron表达式
*/
@Excel(name = "cron表达式", width = 30)
private java.lang.String cronExpression;
/**
* 参数
*/
@Excel(name = "参数", width = 15)
private java.lang.String parameter;
/**
* 描述
*/
@Excel(name = "描述", width = 40)
private java.lang.String description;
/**
* 状态 0正常 -1停止
*/
@Excel(name = "状态", width = 15)
private java.lang.Integer status;
}
```
Mapper
```java
@Mapper
public interface QuartzJobMapper extends BaseMapper<QuartzJob> {
/**
* 根据jobClassName查询
* @param jobClassName 任务类名
* @return
*/
public List<QuartzJob> findByJobClassName(@Param("jobClassName") String jobClassName);
}
```
+ xml
```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.schduletest.mapper.QuartzJobMapper">
<!-- 根据jobClassName查询 -->
<select id="findByJobClassName" resultType="com.example.schduletest.entity.QuartzJob">
select * from sys_quartz_job where job_class_name = #{jobClassName}
</select>
</mapper>
```
+ Service
```java
package com.example.schduletest.service;
import com.example.schduletest.entity.QuartzJob;
import org.quartz.SchedulerException;
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.List;
/**
* @Description: 定时任务
*/
public interface IQuartzJobService extends IService<QuartzJob> {
/**
* 通过类名寻找定时任务
* @param jobClassName 类名
* @return List
*/
List<QuartzJob> findByJobClassName(String jobClassName);
/**
* 保存定时任务
* @param quartzJob
* @return boolean
*/
boolean saveAndScheduleJob(QuartzJob quartzJob);
/**
* 编辑定时任务
* @param quartzJob
* @return boolean
* @throws SchedulerException
*/
boolean editAndScheduleJob(QuartzJob quartzJob) throws SchedulerException;
/**
* 删除定时任务
* @param id
* @return boolean
*/
boolean deleteAndStopJob(String id);
/**
* 恢复定时任务
* @param quartzJob
* @return
*/
boolean resumeJob(QuartzJob quartzJob);
/**
* 执行定时任务
* @param quartzJob
* @throws Exception
*/
void execute(QuartzJob quartzJob) throws Exception;
/**
* 暂停任务
* @param quartzJob
* @throws SchedulerException
*/
void pause(QuartzJob quartzJob);
}
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.7.5version>
<relativePath/>
parent>
<groupId>com.examplegroupId>
<artifactId>Schdule-TestartifactId>
<version>0.0.1-SNAPSHOTversion>
<name>Schdule-Testname>
<description>Demo project for Spring Bootdescription>
<properties>
<java.version>8java.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-txartifactId>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<scope>runtimescope>
dependency>
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
<version>2.1.1version>
dependency>
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-boot-starterartifactId>
<version>3.4.3.1version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-quartzartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>cn.afterturngroupId>
<artifactId>easypoi-annotationartifactId>
<version>4.4.0version>
dependency>
<dependency>
<groupId>me.zhengjiegroupId>
<artifactId>eladmin-systemartifactId>
<version>2.6version>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
project>
server:
port: 8080
spring:
datasource:
name: mydb
url: jdbc:mysql://localhost:3306/test?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: password
driver-class-name: com.mysql.cj.jdbc.Driver
quartz:
job-store-type: jdbc
#定时任务启动开关,true-开 false-关
auto-startup: true
#延迟1秒启动定时任务
startup-delay: 1s
#启动时更新己存在的Job
overwrite-existing-jobs: true
properties:
org:
quartz:
scheduler:
instanceName: MyScheduler
instanceId: AUTO
jobStore:
class: org.springframework.scheduling.quartz.LocalDataSourceJobStore
driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
tablePrefix: QRTZ_
isClustered: false
misfireThreshold: 12000
clusterCheckinInterval: 15000
threadPool:
class: org.quartz.simpl.SimpleThreadPool
threadCount: 1
threadPriority: 5
threadsInheritContextClassLoaderOfInitializingThread: true
jdbc:
initialize-schema: never
mybatis:
mapper-locations: classpath:Mapper/*.xml #注意:一定要对应mapper映射xml文件的所在路径
type-aliases-package: com.example.schduletest.entity # 注意:对应实体类的路径
这是 本篇文章项目 结构 ,感谢大家的学习
终归本质来说,第三方框架, 最后集成在业务层面也大致只不过是个增删改查, 只不过 在业务层面不同而已,例如我们只是在新增的时候通过调度器创建了一个 Job
public boolean saveAndScheduleJob(QuartzJob quartzJob) {
// DB设置修改
quartzJob.setDelFlag(1);
boolean success = this.save(quartzJob);
if (success) {
if ("NORMAL".equals(quartzJob.getStatus())) {
// 定时器添加
this.schedulerAdd(quartzJob.getId(), quartzJob.getJobClassName().trim(), quartzJob.getCronExpression().trim(), quartzJob.getParameter());
}
}
return success;
}
博客项目演示层面 ,只是做了一个Demo : 更多 具体详情使用细节 ,请关注 源码实现细节