要想获取使用指定注解的类信息,可借助工具:

org.reflections.Reflections

此工具将Java反射进行了高级封装,Reflections 通过扫描 classpath,索引元数据,允许在运行时查询这些元数据,也可以保存收集项目中多个模块的元数据信息。

使用 Reflections 可以查询以下元数据信息:

)获得某个类型的所有子类型
)获得标记了某个注解的所有类型/成员变量,支持注解参数匹配。
)使用正则表达式获得所有匹配的资源文件
)获得所有特定签名(包括参数,参数注解,返回值)的方法

Reflections 依赖 Google 的 Guava 库和 Javassist 库。

Maven引入方式:

<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>0.9.11</version>
</dependency>

sbt引入方式:

"org.reflections" % "reflections" % "0.9.11"

首先自定义注解:

package com.today.service.financetask.job
import java.lang.annotation.*;

/**
* 类功能描述:job 信息注解
*
* @author WangXueXing create at 19-5-4 上午9:14
* @version 1.0.0
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface JobInfo {
/**
* job id
* @return
*/
String jobId(); /**
* job name
* @return
*/
String jobName(); /**
* default cron
* @return
*/
String defaultCron();
}

将某些类添加注解:

package com.today.service.financetask.job
import java.util.Calendar

import com.today.api.checkaccount.scala.CheckAccountServiceClient
import com.today.api.checkaccount.scala.enums.FlatFormTypeEnum
import com.today.api.checkaccount.scala.request.ReconciliationRequest
import com.today.service.financetask.job.define.AbstractJob
import com.today.service.financetask.utils.JobInfo
import org.quartz.JobExecutionContext /**
* 自动对账
*/
@JobInfo(jobId="CHECK_ACCOUNT_PROCESS", jobName="对账系统自动对账定时任务", defaultCron="0 0 13 * * ?")
class CheckAccountJob extends AbstractJob{
/**
* start up the scheduled task
*
* @param context JobExecutionContext
*/
override def run(context: JobExecutionContext): Unit = {
val cal = Calendar.getInstance
cal.add(Calendar.DATE, -1)
new CheckAccountServiceClient().appReconciliation(new ReconciliationRequest(FlatFormTypeEnum.TODAY_APP,None))
}
}
import com.today.service.financetask.action.DailyStatementAction
import com.today.service.financetask.job.define.AbstractJob
import com.today.service.financetask.utils.JobInfo
import org.quartz.JobExecutionContext
import org.springframework.stereotype.Service /**
* 日次处理定时任务处理
*
* @author zhangc create at 2018/5/11 14:08
* @version 0.0.1
*/
@Service
@JobInfo(jobId="DAY_TIME_PROCESS", jobName="日次处理定时任务", defaultCron="0 30 2 * * ?")
class DayTimeProcessJob extends AbstractJob{
/**
* start up the scheduled task
*
* @param context JobExecutionContext
*/
override def run(context: JobExecutionContext): Unit = {
new DailyStatementAction().execute
}
}

通过Java反射及Reflections工具类实现被JobInfo注解的所有类信息:

import java.util

import com.today.service.financetask.utils.JobInfo
import org.quartz.Job
import org.reflections.Reflections import scala.collection.JavaConverters._
import scala.collection.mutable /**
* 通过注解获取所有通用Job信息
*
* @author BarryWang create at 2018/5/12 10:45
* @version 0.0.1
*/
object JobEnum {
/**
* 获取添加JobInfo注解的类信息
*/
val jobWithAnnotation: util.Set[Class[_]] = new Reflections("com.today.service.financetask.job").getTypesAnnotatedWith(classOf[JobInfo]) /**
* 获取所有job枚举值
* @return
*/
def values : mutable.Set[JobInfo] = jobWithAnnotation.asScala.map(getJobInfo(_)) /**
* 根据job class 获取job 信息
* @param jobClass
* @return
*/
def getJobInfo(jobClass : Class[_]): JobInfo = jobClass.getAnnotation(classOf[JobInfo]) /**
* jobId与jobName映射关系
* @return
*/
def jobIdNameMap : Map[String, String]={
jobWithAnnotation.asScala.map{sub =>
val jobInfo = getJobInfo(sub)
Map(jobInfo.jobId() -> jobInfo.jobName())
}.fold(Map())((i,j) => i++j)
} /**
* JobObject与JobEnum映射关系
* @return
*/
def jobClassInfoMap: Map[String, JobInfo] = {
jobWithAnnotation.asScala.map{sub =>
Map(sub.getName -> getJobInfo(sub))
}.fold(Map())((i,j) => i++j)
} /**
* jobId与JobEnum映射关系
* @return
*/
def jobIdInfoMap: Map[String, JobInfo] = {
jobWithAnnotation.asScala.map{sub =>
val jobInfo = getJobInfo(sub)
Map(jobInfo.jobId() -> jobInfo)
}.fold(Map())((i,j) => i++j)
} /**
* jobId与JobObject映射关系
* @return
*/
def jobIdClassMap: Map[String, Class[_ <: Job]] = {
jobWithAnnotation.asScala.map{sub =>
Map(getJobInfo(sub).jobId() -> sub.asInstanceOf[Class[_ <: Job]])
}.fold(Map[String, Class[_ <: Job]]())((i,j) => i++j)
} def main(args: Array[String]): Unit = {
println(jobIdClassMap)
}
}

至此,我们就可以获取所有被特定注解引用的类信息及注解信息,我们就可以全局管理特定类信息。

实现JobEnum后就可以统一对定时任务管理实现:

1.新添加定时任务完全可以制定一个Job子类,其他操作自动维护进去;

2.每个Job子类里面都需要实现 override def getJobAndApiInfo(context: JobExecutionContext): (String, String, JobEnum) 方法,这个也可以省掉,直接在父类统一实现;

3.统一修改定时任务开关及时间接口;

4.统一对定时任务启动时重启,就可以统一重启,不需要单独添加代码启动;

1上面代码片段“将某些类添加注解”

2父类代码如下:

import java.io.{PrintWriter, StringWriter}

import com.github.dapeng.core.helper.MasterHelper
import com.today.api.financetask.scala.enums.TScheduledTaskLogEnum
import com.today.service.financetask.action.sql.ScheduledTaskLogSql
import com.today.service.financetask.util.{AppContextHolder, Debug}
import com.today.service.financetask.utils.JobInfo
import org.quartz.{Job, JobExecutionContext}
import org.slf4j.LoggerFactory
import org.springframework.transaction.TransactionStatus
import org.springframework.transaction.support.TransactionTemplate import scala.util.{Failure, Success, Try} /**
* the abstract class for job
*/
trait AbstractJob extends Job{
/** 日志 */
val logger = LoggerFactory.getLogger(getClass) /**
* execute the job
* @param context
*/
override def execute(context: JobExecutionContext): Unit = {
getJobAndApiInfo(context) match {
case Some(x) => execute(context, x.jobId, x.jobName)
case None => logger.error("没有定义JobEnum及对应JobObject,请检查")
}
} /**
* execute the job
* @param context
* @param jobId
* @param jobName
*/
def execute(context: JobExecutionContext, jobId : String, jobName: String): Unit = {
//判断是否是选中的master节点,不是master节点不执行定时任务
if (!MasterHelper.isMaster("com.today.api.financetask.service.FinanceScheduledService", "1.0.0")) {
logger.info(s"Can't select master to run the job ${jobId}: ${jobName}")
return
} //记录开始日志
val logId = ScheduledTaskLogSql.insertScheduledTaskLog(jobId)
context.put("logId", logId)
logger.info(s"Starting the job ${jobId}: ${jobName} ...") //事物处理
val transactionTemplate: TransactionTemplate = AppContextHolder.getBean("transactionTemplate")
transactionTemplate.execute((status: TransactionStatus) =>{
Debug.reset()
//执行任务
Try(Debug.trace(s"${jobId}:${jobName}")(run(context))) match
{
case Success(x) => {
logger.info(s"Successfully execute the job ${jobId}: ${jobName}")
successLog(logId)
}
case Failure(e) => {
logger.error(s"Failure execute the job ${jobId}: ${jobName}", e)
failureLog(logId, status, e)
}
}
Debug.info()
})
} /**
* get the api information
* @return (interface name, interface version, JobEnum)
*/
def getJobAndApiInfo(context: JobExecutionContext) : Option[JobInfo] ={
JobEnum.jobClassInfoMap.get(this.getClass.getName)
} /**
* start up the scheduled task
* @param context JobExecutionContext
*/
def run(context: JobExecutionContext) /**
* 成功日志记录
* @param logId
*/
def successLog(logId: Long): Unit ={
ScheduledTaskLogSql.updateExportReportRecord(logId, TScheduledTaskLogEnum.SUCCESS, "Success")
} /**
* 失败日志记录
* @param logId
*/
def failureLog(logId: Long, status: TransactionStatus, e: Throwable): Unit ={
status.setRollbackOnly()
ScheduledTaskLogSql.updateExportReportRecord(logId, TScheduledTaskLogEnum.FAILURE, getExceptionStack(e))
} /**
*
* 功能说明:在日志文件中 ,打印异常堆栈
* @param e : Throwable
* @return : String
*/
def getExceptionStack(e: Throwable): String = {
val errorsWriter = new StringWriter
e.printStackTrace(new PrintWriter(errorsWriter))
errorsWriter.toString
}
}

3 统一修改定时任务开关及时间代码如下:

import com.today.api.financetask.scala.enums.{TScheduledTaskHasDeletedEnum, TScheduledTaskIsStartEnum}
import com.today.api.financetask.scala.request.StartOrStopByNameRequest
import com.today.api.financetask.scala.response.ServiceResponse
import com.today.service.commons.Action
import com.today.service.commons.Assert.assert
import com.today.service.commons.exception.CommonException.illegalArgumentException
import com.today.service.financetask.action.sql.ScheduledTaskActionSql
import com.today.service.financetask.dto.TScheduledTask
import com.today.service.financetask.job.define.JobEnum
import com.today.service.financetask.query.sql.ScheduledTaskQuerySql
import com.today.service.financetask.util.{CronConverter, QuartzManager} /**
* @description: 启停定时任务
* @author zhangc
* @date 2018\8\1 0001 15:02
* @version 1.0.0
*/
class StartOrStopByNameAction (request: StartOrStopByNameRequest) extends Action[ServiceResponse] {
override def preCheck: Unit = {
assert(!TScheduledTaskIsStartEnum.isUndefined(request.flag.id), illegalArgumentException("错误的启停标志!"))
} /**
* 根据传入的定时任务名称和启停标志,来判断启动或者停止定时任务
* 如果是1则更新数据库,删除定时任务,重新添加定时任务
* 如果是0则更新数据库,删除定时任务
* @return
*/
override def action: ServiceResponse = {
val scheduledTask = ScheduledTaskQuerySql.queryByJobId(request.processName)
val cron = CronConverter.convertHourMinuteToCron(request.processTime)
val jobInfo = JobEnum.jobIdInfoMap(request.processName)
//修改定时任务时间, 保存入库
if(scheduledTask.isEmpty){
ScheduledTaskActionSql.insertTScheduledTask(
TScheduledTask(jobInfo.jobName,
request.processName,
cron,
None,
request.flag.id,
None,
null,
null,
TScheduledTaskHasDeletedEnum.NO.id))
} else {
ScheduledTaskActionSql.updateTaskIsStart(request.flag.id,cron, request.processName)
}
request.flag match {
case TScheduledTaskIsStartEnum.YES => QuartzManager.modifyJobTime(request.processName, cron, JobEnum.jobIdClassMap(jobInfo.jobId()))
case _ => QuartzManager.removeJob(request.processName)
} ServiceResponse("200","success")
}
}

4下面就是统一对定时任务启动时重启,就可以统一重启,不需要单独添加代码启动:

import com.today.api.financetask.scala.enums.TScheduledTaskIsStartEnum
import com.today.api.financetask.scala.request.QueryAutoConfigRequest
import com.today.service.financetask.job._
import com.today.service.financetask.job.define.JobEnum
import com.today.service.financetask.query.sql.{AutoConfigQuerySql, ScheduledTaskQuerySql}
import com.today.service.financetask.util.QuartzManager
import com.today.service.financetask.utils.JobInfo
import org.slf4j.LoggerFactory
import org.springframework.context.ApplicationListener
import org.springframework.context.event.ContextRefreshedEvent
import org.springframework.stereotype.Service /**
* 类功能描述: 定时器监听器, 服务启动时启动定时器
*
* @author BarryWang create at 2018/5/11 12:04
* @version 0.0.1
*/
@Service
class ScheduleStartListener extends ApplicationListener[ContextRefreshedEvent] {
/** 日志 */
val logger = LoggerFactory.getLogger(getClass) /**
* 启动加载执行定时任务
*/
override def onApplicationEvent(event: ContextRefreshedEvent): Unit = {
logger.info("=======服务器重启定时任务启动start=======")
//启动服务时恢复常规定时任务
JobEnum.values.foreach(recoveryCommonJob(_))
logger.info("=======服务器重启定时任务启动end=======")
} /**
* 恢复通用定时任务
* @param jobInfo 定时任务枚举
*/
private def recoveryCommonJob(jobInfo: JobInfo)={
try {
val jobClass = JobEnum.jobIdClassMap(jobInfo.jobId)
ScheduledTaskQuerySql.queryByJobId(jobInfo.jobId) match {
case Some(x) => {
x.isStart match {
case TScheduledTaskIsStartEnum.YES.id => {
QuartzManager.addJobByCron(jobInfo.jobId, x.jobCron, jobClass)
logger.info(s"定时任务:'${jobInfo.jobName}'启动成功!")
}
case _ => logger.info(s"定时任务:'${jobInfo.jobName}'is_start标志为0,不启动")
}
}
case None => QuartzManager.addJobByCron(jobInfo.jobId, jobInfo.defaultCron(), jobClass)
}
} catch {
case e : Exception => logger.error(s"定时任务:'${jobInfo.jobName}'启动失败, 失败原因:", e)
}
}
}

本部分也是对Quartz实现可配置的分布式定时任务的优化重构,可详见:

Quartz实现分布式可动态配置的定时任务

Java Scala获取所有注解的类信息的更多相关文章

  1. java后台获取和js拼接展示信息

    java后台获取和js拼接展示信息: html页面代码: <div class="results-bd"> <table id="activityInf ...

  2. java反射获取和设置实体类的属性值 递归所有父类

    最近做一个通用数据操作接口,需要动态获取和设置实体类的属性值,为了通用实体做了多重继承,开始网上找到代码都不支持父类操作,只能自己搞一个工具类了,此工具类可以设置和获取所有父类属性,代码贴下面拿走不谢 ...

  3. Java——反射:运行时的类信息

    RTTI的使用 如果不知道某个对象的确切类型,RTTI会告诉我们,但是有一个限制:这个类型在编译时必须已知,这样才能使用RTTI识别它,并利用这些信息做一些有用的事情.  2.什么情况下需要反射 假设 ...

  4. Java:不得不知的Object类

    目录 一.equals 1.equals与==有啥区别? 2.equals方法的规范 3.instanceof 和getClass() 4.其他总结 二.hashCode 1.hashCode的规范 ...

  5. MFC中关于运行时类信息及动态创建对象的两个宏的意义(转)

    http://blog.csdn.net/ligand/article/details/49839507 MFC运行时类信息 用途: 程序在运行时,获取对象类的信息及类的继承关系 实现: 1.定义的类 ...

  6. Java反射获取类和对象信息全解析

    反射可以解决在编译时无法预知对象和类是属于那个类的,要根据程序运行时的信息才能知道该对象和类的信息的问题. 在两个人协作开发时,你只要知道对方的类名就可以进行初步的开发了. 获取类对象 Class.f ...

  7. JAVA基础知识之JVM-——通过反射查看类信息

    Class实例 当类被加载之后,JVM中就会生成一个Class实例,通过这个实例就可以访问JVM中的这个类.有三种方式可以获取Class对象 使用Class的静态方法forName(完整包名) 调用类 ...

  8. java 编程基础:注解(Annotation Processing Tool)注解处理器 利用注解解读类属性生成XML文件

    APT的介绍: APT(Annotation Processing Tool)是一种注解处理工具,它对源代码文件进行检测,并找出源文件所包含的注解信息,然后针对注解信息进行额外的处理. 使用APT工具 ...

  9. java 利用ManagementFactory获取jvm,os的一些信息--转

    原文地址:http://blog.csdn.net/dream_broken/article/details/49759043 想了解下某个Java项目的运行时jvm的情况,可以使用一些监控工具,比如 ...

随机推荐

  1. 升级 ASP.NET Core 3.0 设置 JSON 返回 PascalCase 格式与 SignalR 问题

    由于一些 JS 组件要求 JSON 格式是 PascalCase 格式,新版本 ASP.NET Core 3.0 中默认移除了 Newtonsoft.Json ,使用了微软自己实现的 System.T ...

  2. 基于opencv 识别、定位二维码 (c++版)

    前言 因工作需要,需要定位图片中的二维码:我遂查阅了相关资料,也学习了opencv开源库.通过一番努力,终于很好的实现了二维码定位.本文将讲解如何使用opencv定位二维码. 定位二维码不仅仅是为了识 ...

  3. 锁定文件失败,打不开磁盘“D:\biehl\centos\taotao\taotao-dubbo-register\CentOS-000001.vmdk”或它所依赖的某个快照磁盘。模块“Disk”启动失败。未能启动虚拟机

    1.锁定文件失败,打不开磁盘“D:\biehl\centos\taotao\taotao-dubbo-register\CentOS-000001.vmdk”或它所依赖的某个快照磁盘.模块“Disk” ...

  4. ConsoleColor原来有16种, enumData.GetValue(i)

    Console.WriteLine("Name:{0},Value:{0:D}", enumData.GetValue(i)); Name:Black,Value:0Name:Da ...

  5. MVC 表格按树状形式显示 jstree jqgrid

    1. 界面顯示 2.前端 jqgrid 代码 //加载表格 function GetGrid() { var selectedRowIndex = 0; var $gridTable = $('#gr ...

  6. background 设置文本框背景图

    background 属性的作用是给元素设置背景,它是一个复合属性,常用的子属性如下: background-color 指定元素的背景颜色. background-image 指定元素的背景图像. ...

  7. C 函数指针、回调函数

    参考链接:https://www.runoob.com/cprogramming/c-fun-pointer-callback.html 函数指针 函数指针就是执行函数的指针,他可以像正常函数一样去调 ...

  8. 如何将LNMP拆分为LNP+MySQL

    1.备份172.16.1.7上的数据库信息 [root@web01 ~]# mysqldump -uroot -p'oldxu.com' --all-databases > mysql-all. ...

  9. BUUCTF--checkin

    文件上传文件上传一般验证方式:1.本地js验证(客户端)2.MIME验证(服务端)3.拓展名验证(服务端)4.脚本内容(文件头)验证(服务端) 通常会用到exif_imagetype()函数,这个函数 ...

  10. 英文DIAMAUND钻石DIAMAUND词汇

    首先谈谈钻石和金刚石的名称.金刚石是一种天然矿物,是钻石的原石.习惯上人们常将加工过的金刚石称为钻石,而未加工过的称为金刚石(当然,有的金刚石不用加工便可应用).钻石是那些达到宝石级别的金刚石晶体切磨 ...