之前在项目中遇到这样一种情景:代码中有一个共用的任务处理模块,不同的数据会通过唯一键(任务名称和key fields)全部存入公共任务表里,然后在同步模块通过任务名称去获取相应的任务进行同步处理。
在存入公共任务表之前,不同的任务线都会进行各自的逻辑处理,而且基本上不存在共用的代码,所以在这里考虑建一个工厂类,由Spring注入一个Map<String, interface>,String对应的各个任务的名称,interface对应的是每个任务的处理接口。e.g.
<bean id="linkingDetectService" class="cn.vobile.service.linkingdetect.LinkingDetectServiceImpl">
<property name="taskPriority" ref="normal" />
<property name="commonTaskService" ref="commonTaskService" />
<property name="gson" ref="gson" />
</bean>
<bean id="reclaimMatchSubscriptionService" class="cn.vobile.service.matchsubscription.reclaim.ReclaimMatchSubscriptionServiceImpl">
<property name="taskPriority" ref="normal" />
<property name="matchSubscriptionService" ref="matchSubscriptionService"/>
<property name="gson" ref="gson" />
<property name="matchedVideoService" ref="matchedVideoCommonService" />
<property name="transactionTemplate" ref="masterTransactionTemplate" />
</bean>
<bean id="commonTaskFactory" class="cn.vobile.service.commontask.CommonTaskFactory">
<property name="serviceMap">
<map>
<entry key="linkingDetect"><ref bean="linkingDetectService"/></entry>
<entry key="reclaimMatchSubscription"><ref bean="reclaimMatchSubscriptionService"/></entry>
</map>
</property>
</bean>
工厂类CommonTaskFactory的代码如下:
public class CommonTaskFactory {
private Map<String, AbstractCommonService> serviceMap;
/**
* Create a task service by taskName.
*
* @param serviceName
* @return
*/
public AbstractCommonService create(String taskKey){
return serviceMap.get(taskKey);
}
/**
*
* @param serviceMap
*/
public void setServiceMap(Map<String, AbstractCommonService> serviceMap) {
this.serviceMap = serviceMap;
}
}
接口AbstractCommonService定义如下:
public interface AbstractCommonService {
/**
* save a task to workingCommonTask
*
* @param matchedVideo
* @param taskName
* @param matchType
* @param priority
*/
void addWorkingTask(MatchedVideo matchedVideo, CommonTaskName taskName, MatchType matchType);
}
工厂类方法调用代码示例如下:
AbstractCommonService linkingService = commonTaskFactory.create(CommonTaskKey.LINKINGDETECT.getCode());
linkingService.addWorkingTask(matchedVideo, CommonTaskName.SYNC_ADD_VIDEO_TO_TAISAN, null);
注:另外还有一种场景,各种任务存入公共任务表之前也许会存在很多的冗余代码(有很多的代码逻辑都是基本相同的),那么此时可以写一个抽象类(实现接口AbstractCommonService),把共用的代码写在该类中(每个任务线之间不同的部分可以使用抽象方法实现),然后各自不同的部分在各自的service类(继承抽象类)中实现父类中的抽象方法。代码如下:
<bean id="abstractCommonTaskService" class="cn.vobile.service.commontask.AbstractCommonTaskService" abstract="true">
<property name="commonTaskService" ref="commonTaskService" />
<property name="gson" ref="gson" />
</bean>
<bean id="linkingDetectService" class="cn.vobile.service.linkingdetect.LinkingDetectServiceImpl" parent="abstractCommonTaskService">
<property name="taskPriority" value="normal" />
</bean>
<bean id="reclaimMatchSubscriptionService" class="cn.vobile.service.matchsubscription.reclaim.ReclaimMatchSubscriptionServiceImpl" parent="abstractCommonTaskService">
<property name="taskPriority" value="normal" />
<property name="matchSubscriptionService" ref="matchSubscriptionService"/>
<property name="matchedVideoService" ref="matchedVideoCommonService" />
</bean>
<bean id="commonTaskFactory" class="cn.vobile.commontask.CommonTaskFactory">
<property name="serviceMap">
<map>
<entry key="linking_detect_taisan"><ref local="linkingDetectService"/></entry>
<entry key="match_subscription_reclaim"><ref local="reclaimMatchSubscriptionService"/></entry>
</map>
</property>
</bean>
抽象类AbstractCommonTaskService代码如下:
public abstract class AbstractCommonTaskService<T extends CommonTaskParams> implements AbstractCommonService<T> {
protected static final Logger logger = Logger.getLogger(AbstractCommonTaskService.class);
protected CommonTaskService commonTaskService;
protected TaskPriority taskPriority = TaskPriority.NORMAL;
protected Gson gson;
/**
* @see cn.vobile.service.commontask.AbstractCommonService#addWorkingTask(cn.vobile.service.commontask.CommonTaskParams, cn.vobile.enums.CommonTaskName)
*/
@Override
public void addWorkingTask(T params, CommonTaskName taskName) {
saveWorkingTask(params, taskName, taskPriority);
}
public void saveWorkingTask(T params, CommonTaskName taskName, TaskPriority priority) {
Assert.notNull(params, "CommonTaskParams must not be null");
Assert.notNull(taskName, "CommonTaskName must not be null");
Assert.notNull(priority, "TaskPriority must not be null");
if (isServiceToDeal(taskName)) {
String uniqueKey = new MD5().MD5(uniqueKey(params));
WorkingCommonTask oldTask = commonTaskService.getWorkingTaskByNameAndKey(taskName, uniqueKey);
if (isAddToWorkingTask(oldTask)) {
WorkingCommonTask task = new WorkingCommonTask();
task.setCompanyId(params.getCompanyId());
task.setTaskName(taskName.getCode());
task.setUniqueKey(uniqueKey);
task.setRetrievalField(retrievalField(params));
// task.setExtraInfo("");
task.setRetryCount(0);
task.setTaskPriority(priority.getCode());
task.setTaskStatus(CommonTaskStatus.CREATED.getCode());
task.setTimeout(0);
commonTaskService.saveWorkingTask(task, getBiz(params));
}
}
}
protected abstract String uniqueKey(T params);
protected boolean isAddToWorkingTask(WorkingCommonTask oldTask) {
return oldTask == null;
}
protected abstract String retrievalField(T params);
protected abstract String getBiz(T params);
protected abstract boolean isServiceToDeal(CommonTaskName taskName);
public void setCommonTaskService(CommonTaskService commonTaskService) {
this.commonTaskService = commonTaskService;
}
public void setTaskPriority(String taskPriority) {
this.taskPriority = TaskPriority.getByCode(taskPriority);
}
public void setGson(Gson gson) {
this.gson = gson;
}
}
实现类代码(e.g.ReclaimMatchSubscriptionServiceImpl)如下:
public class ReclaimMatchSubscriptionServiceImpl extends AbstractCommonTaskService<MatchedInfoParams> {
private static final Logger logger = Logger.getLogger(ReclaimMatchSubscriptionServiceImpl.class);
private MatchSubscriptionService matchSubscriptionService;
private MatchedVideoService matchedVideoService;
/**
* @see cn.vobile.service.commontask.AbstractCommonTaskService#addWorkingTask(cn.vobile.service.commontask.CommonTaskParams, cn.vobile.enums.CommonTaskName)
*/
@Override
public void addWorkingTask(MatchedInfoParams params, CommonTaskName taskName) {
MatchSubscriptionInfo matchSubscriptionInfo = matchSubscriptionService.getMatchSubscriptionInfoFromCache(params.getCompanyId(), params.getTrackingWebsiteId());
if (null == matchSubscriptionInfo) {
// logger.warn("Unsupported matchSubscription: companyId=" + companyId + ", websiteId=" + websiteId);
return;
}
super.addWorkingTask(params, taskName);
// sync history matches
if (!matchSubscriptionInfo.isSyncedHistoryMatches()) {
if (matchSubscriptionService.setSyncedHistoryMatchesFlag(matchSubscriptionInfo.getId())) {
matchSubscriptionInfo.setSyncedHistoryMatches(true);// change the syncedHistoryMatches flag to true in cache
List<Integer> matchIds = null;
if (MatchType.MATCHED_VIDEO == params.getMatchType()) {
matchIds = matchedVideoService.getMatchedVideoRightIds(params.getCompanyId(), params.getTrackingWebsiteId());
}
if (CollectionUtils.isNotEmpty(matchIds)) {
matchIds.remove(new Integer(params.getMatchId()));
saveHistoryMatches(params, CommonTaskName.SYNC_ADD_MATCH_TO_RECLAIM, matchIds);
}
}
}
}
private void saveHistoryMatches(final MatchedInfoParams params, final CommonTaskName taskName, final List<Integer> matchIds) {
new Thread() {
@Override
public void run() {
MatchedInfoParams clonedParams = params.clone();
for (final Integer matchId : matchIds) {
try {
clonedParams.setMatchId(matchId);
saveWorkingTask(clonedParams, taskName, TaskPriority.LOW);
} catch (Exception e) {
logger.error("Save history matches to working task failed: matchedId=" + matchId + ", companyId=" + params.getCompanyId() + ", taskName=" + taskName
+ ", matchType=" + params.getMatchType(), e);
}
}
}
}.start();
}
/**
* @see cn.vobile.service.commontask.AbstractCommonTaskService#uniqueKey(cn.vobile.service.commontask.CommonTaskParams)
*/
@Override
protected String uniqueKey(MatchedInfoParams params) {
return params.getMatchType().getCode() + "_" + params.getMatchId() + "_" + params.getCompanyId();
}
@Override
protected boolean isAddToWorkingTask(WorkingCommonTask oldTask) {
boolean ret = false;
CommonTaskName taskName = null;
if (null != oldTask) {
taskName = CommonTaskName.getByCode(oldTask.getTaskName());
}
if (null == oldTask
|| (taskName == CommonTaskName.SYNC_UPDATE_MATCH_TO_RECLAIM && CommonTaskStatus.STARTED == oldTask.getTaskStatusEnum() && !commonTaskService.updateWorkingTaskStatus(taskName,
oldTask.getUniqueKey(), CommonTaskStatus.STARTED, CommonTaskStatus.CREATED))) {
ret = true;
}
return ret;
}
/**
* @see cn.vobile.service.commontask.AbstractCommonTaskService#retrievalField(cn.vobile.service.commontask.CommonTaskParams)
*/
@Override
protected String retrievalField(MatchedInfoParams params) {
return params.getMatchType().getCode() + "_" + params.getMatchId();
}
/**
* @see cn.vobile.service.commontask.AbstractCommonTaskService#getBiz(cn.vobile.service.commontask.CommonTaskParams)
*/
@Override
protected String getBiz(MatchedInfoParams params) {
MatchSubscriptionBizData bizData = new MatchSubscriptionBizData();
bizData.setMatchType(params.getMatchType().getCode());
bizData.setMatchId(params.getMatchId());
return gson.toJson(bizData);
}
@Override
protected boolean isServiceToDeal(CommonTaskName taskName) {
return taskName.getServiceName() == ServiceName.MATCH_SUBSCRIPTION;
}
public void setMatchSubscriptionService(MatchSubscriptionService matchSubscriptionService) {
this.matchSubscriptionService = matchSubscriptionService;
}
public void setMatchedVideoService(MatchedVideoService matchedVideoService) {
this.matchedVideoService = matchedVideoService;
}
}
工厂类CommonTaskFactory代码如下:
public class CommonTaskFactory {
private Map<String, AbstractCommonService<CommonTaskParams>> serviceMap;
/**
* Create a task service by taskName && do the addWorkingTask method.
* @param paramList
*/
public void saveCommonTask(CommonTaskParams params, CommonTaskName[] taskNames) {
saveCommonTask(params, Arrays.asList(taskNames));
}
public void saveCommonTask(CommonTaskParams params, List<CommonTaskName> taskNames) {
Assert.notNull(params, "CommonTaskParams must not be null");
Assert.notEmpty(taskNames, "CommonTaskNames must not be empty");
for (CommonTaskName taskName : taskNames) {
saveCommonTask(params, taskName);
}
}
public void saveCommonTask(CommonTaskParams params, CommonTaskName taskName) {
Assert.notNull(params, "CommonTaskParams must not be null");
Assert.notNull(taskName, "CommonTaskName must not be null");
AbstractCommonService<CommonTaskParams> service = serviceMap.get(taskName.getServiceModule());
Assert.notNull(service, "can not find Processor!");
service.addWorkingTask(params, taskName);
}
/**
*
* @param serviceMap
*/
public void setServiceMap(Map<String, AbstractCommonService<CommonTaskParams>> serviceMap) {
this.serviceMap = serviceMap;
}
}
<strong><span style="color:#CC0000;">AbstractCommonService<CommonTaskParams> service = serviceMap.get(taskName.getServiceModule());</span></strong>
这种方式是以父类的引用来实例化子类对象,e.g. 父类A和子类B
A a=new B()
在这种方式中NEW的是B,内存中创建的空间其实是A+B,其中A+B中的B,指的是B中继承过来以外的部分,即B新增的属性和方法。因为声明的与内存中的不一致,所以视为多态!
接口AbstractCommonService代码如下:
public interface AbstractCommonService<T extends CommonTaskParams> {
/**
* save a task to workingCommonTask
*
* @param param
*/
void addWorkingTask(T params, CommonTaskName taskName);
}
方法的调用如下:
commonTaskFactory.saveCommonTask(new MatchedVideoParams(matchedVideo), new CommonTaskName[] { CommonTaskName.SYNC_ADD_MATCH_TO_RECLAIM, CommonTaskName.SYNC_ADD_VIDEO_TO_TAISAN });
将addWorkingTask方法中所需要用到T(MatchedVideoParams)中的参数,全部在T(MatchedVideoParams)初始化的时候定义好(在这使用接口),在这里不直接使用MatchedVideo(元Bean)作为T的原因是有一些参数是需要进行clone或者多个元字段进行拼接的, 代码如下:
public class MatchedVideoParams implements MatchedInfoParams {
private final MatchedVideo matchedVideo;
/**
* @param matchedVideo
*/
public MatchedVideoParams(MatchedVideo matchedVideo) {
Assert.notNull(matchedVideo, "MatchedVideo must not be null");
this.matchedVideo = matchedVideo;
}
/**
* @see cn.vobile.service.commontask.CommonTaskParams#getCompanyId()
*/
@Override
public int getCompanyId() {
return matchedVideo.getCompanyId();
}
/**
* @see cn.vobile.service.commontask.MatchedInfoParams#setMatchId(int)
*/
@Override
public void setMatchId(int matchId) {
matchedVideo.setId(matchId);
}
/**
* @see cn.vobile.service.commontask.MatchedInfoParams#getMatchId()
*/
@Override
public int getMatchId() {
return matchedVideo.getId();
}
/**
* @see cn.vobile.service.commontask.MatchedInfoParams#getTrackingWebsiteId()
*/
@Override
public int getTrackingWebsiteId() {
return matchedVideo.getTrackingWebsiteId();
}
/**
* @see cn.vobile.service.commontask.MatchedInfoParams#getKeyId()
*/
@Override
public String getKeyId() {
return matchedVideo.getKeyId();
}
/**
* @see cn.vobile.service.commontask.MatchedInfoParams#getMatchType()
*/
@Override
public MatchType getMatchType() {
return MatchType.MATCHED_VIDEO;
}
/**
* @see cn.vobile.service.commontask.MatchedInfoParams#clone(int)
*/
@Override
public MatchedVideoParams clone() {
MatchedVideo mv = new MatchedVideo();
BeanUtils.copyProperties(matchedVideo, mv);
return new MatchedVideoParams(mv);
}
}