我想在我的服务器应用程序中使用Quartz Scheduler来使用HK2进行依赖注入。为了让Quartz作业能够访问DI,他们需要自己进行DI管理。结果,我写了一个超级简单的HK2感知工作工厂并将其注册到调度程序。
它可以很好地实现服务实例化,观察所请求的@Singleton
或@PerLookup
范围。但是,它们完成后无法destroy()
非单身服务(=作业)。
问题:如何让HK2正确管理工作,包括再次将其拆除?
我是否需要通过serviceLocator.getServiceHandle()
继续创建服务的路径,然后手动销毁服务,可能来自JobListener(但是如何获取ServiceHandle)?
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 '" + 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;
}
}
}
HelloWorldJob.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!");
}
}
答案 0 :(得分:2)
与@ jwells131313建议类似,我已经实现了一个JobListener destroy()
个适当的工作实例。为方便起见,我将ServiceHandle
传递给DataMap
{/ 1}}。
区别仅在于我对@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
注册。
答案 1 :(得分:1)
对于单身人士:
看起来像Singleton服务在作业完成时不会被销毁,因为它是一个Singleton,对吗?如果 期望在作业结束时销毁Singleton,那么服务似乎更像是一个&#34; JobScope&#34;而不是真正的Singleton范围。
JobScope:
如果&#34;乔布斯&#34;遵循一定的规则然后它可能是一个&#34;操作&#34;范围(请参阅Operation Example)。特别是工作可以在&#34;操作&#34;范围如果:
请注意,上述规则还意味着Jobs可以在同一个或不同时间存在于多个线程上。最重要的规则是,在一个线程上,一次只能激活一个Job。
如果这两条规则适用,那么我强烈建议您编写类似于&#34; JobScope&#34;的操作范围。
如果工作遵循上述规则,您可以定义JobScope:
@Scope
@Proxiable(proxyForSameScope = false)
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface JobScope {
}
这将是相应Context的整个实现:
@Singleton
public class JobScopeContext extends OperationContext<JobScope> {
public Class<? extends Annotation> getScope() {
return JobScope.class;
}
}
然后,当您知道乔布斯开始和停止时,您将使用OperationManager服务来启动和停止作业。
即使乔布斯不遵守&#34;操作&#34;你仍然可能想要使用&#34; JobScope&#34;当一个&#34; Job&#34;到了最后。
PerLookup:
因此,如果你的问题是关于PerLookup范围对象,你可能会遇到一些麻烦,因为你可能需要原始的ServiceHandle,这听起来你不会有。在这种情况下,如果您至少可以在PerLookup范围内找到原始服务WAS,则可以使用ServiceLocator.preDestroy来销毁该对象。