HK2工厂用于石英作业,执行后不破坏服务



我想在我的服务器应用程序中使用 Quartz Scheduler,该应用程序使用 HK2 进行依赖注入。为了使 Quartz 作业能够访问 DI,它们需要自己进行 DI 管理。结果,我编写了一个超级简单的 HK2 感知作业工厂,并将其注册到调度程序。

它可以很好地实例化服务,观察请求的@Singleton@PerLookup范围。但是,它无法在非单一实例服务(= 作业)完成后destroy()它们。

问:我如何让HK2正确管理工作,包括再次拆除工作?

我是否需要通过serviceLocator.getServiceHandle()创建服务,然后手动销毁服务,也许是从 JobListener(但如何获取 ServiceHandle 到它)?

香港2工作工厂.java

@Service
public class Hk2JobFactory implements JobFactory {
private final Logger log = LoggerFactory.getLogger(getClass());
@Inject
ServiceLocator serviceLocator;
@Override
public Job newJob(TriggerFiredBundle bundle, Scheduler scheduler) throws SchedulerException {
JobDetail jobDetail = bundle.getJobDetail();
Class<? extends Job> jobClass = jobDetail.getJobClass();
try {
log.debug("Producing instance of Job '" + jobDetail.getKey() + "', class=" + jobClass.getName());
Job job = serviceLocator.getService(jobClass);
if (job == null) {
log.debug("Unable to instantiate job via ServiceLocator, returning unmanaged instance.");
return jobClass.newInstance();
}
return job;
} catch (Exception e) {
SchedulerException se = new SchedulerException(
"Problem instantiating class '"
+ jobDetail.getJobClass().getName() + "'", e);
throw se;
}
}
}

你好世界工作.java

@Service
@PerLookup
public class HelloWorldJob implements Job {
private final Logger log = LoggerFactory.getLogger(this.getClass());
@PostConstruct
public void setup() {
log.info("I'm born!");
}
@PreDestroy
public void shutdown() {
// it's never called... :-(
log.info("And I'm dead again");
}
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
log.info("Hello, world!");
}
}

与@jwells131313建议类似,我已经实现了一个 JobListener,可以在适当的情况下destroy()作业实例。为了方便这一点,我在工作DataMap中传递了ServiceHandle

区别仅在于我对@PerLookup范围非常满意。

Hk2JobFactory.java:

@Service
public class Hk2JobFactory implements JobFactory {
private final Logger log = LoggerFactory.getLogger(getClass());
@Inject
ServiceLocator serviceLocator;
@Override
public Job newJob(TriggerFiredBundle bundle, Scheduler scheduler) throws SchedulerException {
JobDetail jobDetail = bundle.getJobDetail();
Class<? extends Job> jobClass = jobDetail.getJobClass();
try {
log.debug("Producing instance of job {} (class {})", jobDetail.getKey(), jobClass.getName());
ServiceHandle sh = serviceLocator.getServiceHandle(jobClass);
if (sh != null) {
Class scopeAnnotation = sh.getActiveDescriptor().getScopeAnnotation();
if (log.isTraceEnabled()) log.trace("Service scope is {}", scopeAnnotation.getName());
if (scopeAnnotation == PerLookup.class) {
// @PerLookup scope means: needs to be destroyed after execution
jobDetail.getJobDataMap().put(SERVICE_HANDLE_KEY, sh);
}
return jobClass.cast(sh.getService());
}
log.debug("Unable to instantiate job via ServiceLocator, returning unmanaged instance");
return jobClass.newInstance();
} catch (Exception e) {
SchedulerException se = new SchedulerException(
"Problem instantiating class '"
+ jobDetail.getJobClass().getName() + "'", e);
throw se;
}
}
}

Hk2CleanupJobListener.java:

public class Hk2CleanupJobListener extends JobListenerSupport {
public static final String SERVICE_HANDLE_KEY = "hk2_serviceHandle";
private final Map<String, String> mdcCopy = MDC.getCopyOfContextMap();
@Override
public String getName() {
return getClass().getSimpleName();
}
@Override
public void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException) {
JobDetail jobDetail = context.getJobDetail();
ServiceHandle sh = (ServiceHandle) jobDetail.getJobDataMap().get(SERVICE_HANDLE_KEY);
if (sh == null) {
if (getLog().isTraceEnabled()) getLog().trace("No serviceHandle found");
return;
}
Class scopeAnnotation = sh.getActiveDescriptor().getScopeAnnotation();
if (scopeAnnotation == PerLookup.class) {
if (getLog().isTraceEnabled()) getLog().trace("Destroying job {} after it was executed (Class {})", 
jobDetail.getKey(), 
jobDetail.getJobClass().getName()
);
sh.destroy();
}
}
}

两者都在Scheduler注册。

对于单身人士:

似乎在作业完成后不会销毁单例服务,因为它是单例,对吧? 如果您期望在作业结束时销毁单例,那么该服务似乎更像是"JobScope",而不是真正的单例范围。

工作范围:

如果"作业"遵循某些规则,那么它可能是"操作"范围的良好候选项(请参阅操作示例)。 在以下情况下,作业可以位于"操作"范围内:

  1. 可以同时进行许多并行作业
  2. 一个线程上一次只能有一个作业处于活动状态

请注意,上述规则还意味着作业可以同时或在不同时间存在于多个线程上。 最重要的规则是,在单个线程上,一次只能有一个作业处于活动状态。

如果这两个规则适用,那么我强烈建议编写一个类似于"JobScope"的操作范围。

如果作业遵循上述规则,您可以通过这种方式定义 JobScope:

@Scope
@Proxiable(proxyForSameScope = false)
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface JobScope {
}

这将是相应上下文的整个实现:

@Singleton
public class JobScopeContext extends OperationContext<JobScope> {
public Class<? extends Annotation> getScope() {
return JobScope.class;
}
}

然后,您将使用 OperationManager 服务在作业启动和停止时启动和停止作业。

即使作业不遵循"操作"的规则,您仍然可能希望使用"JobScope"作用域,该作用域知道在"作业"结束时销毁其服务。

PerLookup:

因此,如果你的问题是关于PerLookup范围对象,你可能会遇到一些麻烦,因为你可能需要原始的ServiceHandle,这听起来像是你不会有的。 在这种情况下,如果您至少可以发现原始服务实际上在PerLookup范围内,则可以使用ServiceLocator.preDestroy来销毁对象。

最新更新