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

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. 搭建rsyslog日志服务器

    环境配置 centos7系统 client1:192.168.91.17 centos7系统 master:192.168.91.18 rsyslog客户端配置 1.rsyslog安装 yum ins ...

  2. mvc后台传到前台的值带html标签css(解决方法)

    mvc后台传到前台的值带html标签css MVC中要用:@Html.Raw(后台数据库取的值); 或者MvcHtmlString.create();

  3. Python 摘要算法hashlib 与hmac

    参考链接:https://www.liaoxuefeng.com/wiki/1016959663602400/1017686752491744 摘要算法(也成为哈希算法)是用来防篡改的,因为我们的即使 ...

  4. centos下非yum方式安装docker环境

    1.下载docker源码包 docker官网地址: https://download.docker.com/linux/static/stable/ 选择.tgz的包下载,例如:https://dow ...

  5. maven 学习---部署基于Maven的war文件到Tomcat

    在本教程中,我们将学习如何使用Maven的Tomcat插件打包并部署一个WAR文件到Tomcat(Tomcat的6和7. 要用到工具: Maven 3 Tomcat 6.0.37 Tomcat 7.0 ...

  6. 带你理解Xcode Derived Data

    什么是Xcode Derived Data?为什么它很重要呢? “Clean derived data”,当你遇到一些极其奇怪的构建问题时,你也许经常听到这句话. Derived Data是一个文件夹 ...

  7. <Android Studio> 1.如何APP配置权限

    Android Studio必须手动配置权限: 如添加权限 : android.permission.RECEIVE_BOOT_COMPLETED 1.android.permission.WRITE ...

  8. 【web后端开发】笔试题收集

    4399Web后端开发笔试题 题目来源:牛客网 1.linux中,用mkdir命令创建新的目录时,如果需要在其父目录不存在时先创建父目录的选项是   D A  -h B -d C  -f D -p [ ...

  9. python基础-面向对象编程之反射

    面向对象编程之反射 反射 定义:通过字符串对对象的属性和方法进行操作. 反射有4个方法,都是python内置的,分别是: hasattr(obj,name:str) 通过"字符串" ...

  10. HDU 1072 Nightmare 题解

    Nightmare Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total S ...