無論是webservice還是http請求形式的服務,我們的程序的提供服務的時候,都是有一個入口,一個出口,通過一個請求獲取到一個響應,在這個請求和響應中間,程序處理業務邏輯,提供一定的功能(服務)。那麼在這個過程中程序會做什麼?
一般分爲四個階段,
一是參數的轉換,就是把請求轉換爲系統內部的請求實體。對webservice來說,可能是這樣,其他系統調用我們的系統是通過一個BizRequest,那麼在這個階段,我們把bizRequest轉換爲我們系統的內部實體類,bizRequestEntity。這樣做的好處就是隔離外部系統和系統內部的變化,我們的業務的變化,不需要影響到外圍系統。
二是參數的校驗,對參數格式和是否必需的校驗。一個比較好的建議是對一些必需的參數統一返回一個非法參數的錯誤碼。但是在系統內的日誌裏面需要打印具體那個參數的問題,方便查詢問題。、
三是主要業務邏輯的處理。主要邏輯建議使用邏輯處理框架(process framwork)。如果業務涉及數據庫事務,建議在處理框架中使用spring的事務模板TransactionTemplate.
四是對主要業務邏輯處理後返回結果的解析和處理。通過主要業務邏輯返回的錯誤碼構建返回給調用方的響應,或者做一些其他操作,比如通知第三方結果(比如下發短信通知用戶)。
在一個控制器中處理的這四個階段應該做到很好的封裝,在這個控制器中應該看到很少的代碼邏輯(邏輯幾乎都在第三階段),比較明顯的一個效果是,你會發現你記錄日誌的代碼跟這四個階段的代碼行數相差不多,甚至有超過。一個比較好的日誌記錄建議是,在一個單獨的日誌文件中,記錄整個流程的一個簡要日誌,即通過一個重要參數,比如用戶的ID,可以很容易的追蹤到用戶到達了那個階段,那個階段有什麼問題。在另外一個日誌中記錄一些比較重要的信息和參數。方便更加細緻的尋找問題。
對主要邏輯的處理,可以細化成三個階段,初始化,校驗,處理。
初始化主要是構建主要參數對象,比如一筆訂單構建一個Order對象,或者諮詢其他系統對某些參數賦值,針對某個需求,可以增加一個初始化處理類。校驗主要是對初始化後的各個參數做業務校驗,比如說用戶的狀態不能是已註銷。針對某個需求,可以很方便的增加校驗。處理,則是對其他系統的調用或者數據的落地。在這三個階段,可以把每一個需求或者每一個校驗都區分開,這樣每個原子性的校驗就可以被其他業務邏輯複用。從而實現程序的易擴展,高複用,易維護。
public abstract class AbstractPreBizProcessTemplate implements BizPreProcessTemplate {
private List<PreBizProcessInitializer> initList = new ArrayList<PreBizProcessInitializer>();
private List<PreBizProcessValidator> validatorList = new ArrayList<PreBizProcessValidator>();
private List<PreBizProcessorHandler> handlerList = new ArrayList<PreBizProcessorHandler>();
public Result process(PreBizContext preBizContext) {
Result result = new Result(false);
String methodName = "process";
try {
for (PreBizProcessInitializer initProcessor : initList) {
initProcessor.init(preBizContext);
}
for (PreBizProcessValidator validator : validatorList) {
validator.validate(preBizContext);
}
for (PreBizProcessorHandler handler : handlerList) {
handler.handler(preBizContext);
}
result.setSuccess(true);}
catch (PreBizProcessException e) {
ErrorEnum errorEnum = ((PreBizProcessException) e).getErrorEnum();
if (errorEnum == null) {
errorEnum = ErrorEnum.BIZ_PROC_ERROR;
}
result.setErrorEnum(errorEnum);
} catch (Exception e) {
result.setErrorEnum(ErrorEnum.SYSTEM_ERROR);
} finally {
}
return result;
}
可以使用XML配置或者標註:
<bean id="testTemplate" class="com.TestTemplate">
<property name="initList">
<list merge="true">
<ref local="testInitializer"/>
</list>
</property>
<property name="validatorList">
<list merge="true">
<ref local="testValidator"/>
</list>
</property>
<property name="handlerList">
<list merge="true">
<ref local="testHandler"/>
</list>
</property>
</bean>
標註:
@Target( { ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface TemplateMethod {
String[] value() default {};
String[] inits() default {};
String[] valis() default {};
String[] handlers() default {};
}
public class TestTemplate extends AbstractProcessTemplate implements InitializingBean,
BeanFactoryAware {
private DefaultListableBeanFactory factory;
@TemplateMethod(inits = { "inits1", "inits2" }, valis = { "vali1", "vali2" })
public void processBiz1() {
Context txt = new Context();
ChargeResult re = this.process(txt);
// System.out.println(re);
}
public void afterPropertiesSet() throws Exception {
System.out.println("InitializingBean-->" + this.getClass().getName() + "::"
+ this.getInits().size());
TemplateUtil.initProcessTemlates(factory);
}
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.factory = (DefaultListableBeanFactory) beanFactory;
}
}
public final class TemplateUtil {
public static void initProcessTemlates(ListableBeanFactory factory) {
System.out.println("initTemplate");
processInit(factory);
}
@SuppressWarnings("unchecked")
private static void processInit(ListableBeanFactory factory) {
Map<Object, Object> allTemplateClasses = BeanFactoryUtils.beansOfTypeIncludingAncestors(
factory, AbstractProcessTemplate.class);
Map<Object, Object> allInitClasses = BeanFactoryUtils.beansOfTypeIncludingAncestors(
factory, Initor.class);
Map<Object, Object> allValiClasses = BeanFactoryUtils.beansOfTypeIncludingAncestors(
factory, Validator.class);
Map<Object, Object> allHandlerClasses = BeanFactoryUtils.beansOfTypeIncludingAncestors(
factory, Handler.class);
for (Object key : allTemplateClasses.keySet()) {
Object value = allTemplateClasses.get(key);
if (AnnotationUtils.findAnnotation(value.getClass(), TemplateClass.class) != null) {
AbstractProcessTemplate templateClass = (AbstractProcessTemplate) value;
if (!templateClass.getInits().isEmpty() || !templateClass.getValis().isEmpty()
|| !templateClass.getHandlers().isEmpty()) {
return;
}
Method[] methods = templateClass.getClass().getDeclaredMethods();
for (Method method : methods) {
TemplateMethod temMethod = AnnotationUtils.findAnnotation(method,
TemplateMethod.class);
if (temMethod != null) {
String[] values = temMethod.value();
String[] inits = temMethod.inits();
String[] valis = temMethod.valis();
String[] handlers = temMethod.handlers();
if (values != null && values.length > 0) {
findBizBeans(allInitClasses, templateClass, values);
findBizBeans(allValiClasses, templateClass, values);
findBizBeans(allHandlerClasses, templateClass, values);
}
if (inits != null && inits.length > 0) {
findBizBeans(allInitClasses, templateClass, inits);
}
if (valis != null && valis.length > 0) {
findBizBeans(allValiClasses, templateClass, valis);
}
if (handlers != null && handlers.length > 0) {
findBizBeans(allHandlerClasses, templateClass, handlers);
}
}
}
System.out.println("註冊完成後:" + templateClass + ":" + templateClass.getInits().size()
+ ":" + templateClass.getValis().size() + ":"
+ templateClass.getHandlers().size());
}
}
}
private static void findBizBeans(Map<Object, Object> allClasses,
AbstractProcessTemplate templateClass,
String[] annotiedBeanNames) {
for (String beanName : annotiedBeanNames) {
Object bizBean = allClasses.get(beanName);
if (bizBean instanceof Initor) {
Initor realInitor = (Initor) bizBean;
templateClass.getInits().add(realInitor);
} else if (bizBean instanceof Validator) {
Validator realVali = (Validator) bizBean;
templateClass.getValis().add(realVali);
} else if (bizBean instanceof Handler) {
Handler realhander = (Handler) bizBean;
templateClass.getHandlers().add(realhander);
}
}
}
}
對新擴展的每一個業務,只需要新增一個模板類繼承抽象模板類即可。幾乎對每一個單獨的初始化器,驗證器和處理器都可以被很好的複用。
每一個業務都新增一個模板,每個模板實際上都沒有做什麼東西,是不是有點多餘?如果我使用一個標誌來區分不同的業務,那麼在一個模板裏面是不是就可以只用一個模板了?確實可以,但是那樣框架會變得相當重量級,這個框架後面會講到。
比較輕量級的解決方案可以是這樣:新增一個工廠類,並使用spring配置簡化bean的管理,將每個不用業務的模板注入到工廠類的map中,通過業務傳入key值尋找到對應的處理模板。
<bean id="testFactory" class="TestProcessorFactory">
<property name="processorTemplates">
<map>
<entry key="test">
<ref local="testProcessTemplate"/>
</entry>
</map>
</property>
</bean>
public class PayProcessorFactory {
private Map<String, PayProcessor> processors = new HashMap<String, PayProcessor>();
/**
* 根據業務場景獲取對應的處理器
* @param bizType 業務場景
* @return
*/
public PayProcessor getProcessor(String bizType) {
return processors.get(StringUtil.trim(bizType));
}
public void setProcessors(Map<String, PayProcessor> processors) {
this.processors = processors;
}
}
或者這麼做
public class TestTemplate extends AbstractProcessTemplate implements InitializingBean,
BeanFactoryAware {
private DefaultListableBeanFactory factory;
@TemplateMethod(inits = { "inits1", "inits2" }, valis = { "vali1", "vali2" })
public void processBiz1() {
}
public void afterPropertiesSet() throws Exception {
System.out.println("InitializingBean-->" + this.getClass().getName() + "::"
+ this.getInits().size());
TemplateUtil.initProcessTemlates(factory);
}
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.factory = (DefaultListableBeanFactory) beanFactory;
}
public String getSupportedBizType() {
return "test";
}
}
public class TemplateFactory {
private static Set<BizProcessTemplate> templates = new HashSet<BizProcessTemplate>();
public static BizProcessTemplate getPerperTemplate(String bizType) {
for (BizProcessTemplate template : templates) {
if (StringUtils.equals(bizType, template.getSupportedBizType())) {
return template;
}
}
return null;
}
}
如果你想使用多個模板,可以這麼做:
public interface BizProcessTemplate2 {
ChargeResult process(Context txt);
boolean canProcess(List<Class<BizProcessTemplate2>> supportedTemplateClasses);
}
public class TemplateProcessor {
private static Set<BizProcessTemplate2> templates = new HashSet<BizProcessTemplate2>();
public static void getProperTemplates(List<Class<BizProcessTemplate2>> supportedTemplates) {
for (BizProcessTemplate2 template : templates) {
if (template.canProcess(supportedTemplates)) {
template.process(new Context());
}
}
}
}
這個框架同時也是另外一種輕量級框架,結合你的業務,可以考慮下。
如果你的業務足夠簡單,懶的使用這麼繁瑣的框架,那麼可以嘗試下這種處理方式,以一個web請求爲例:
public class WebBizProcessHelper {
private static Logger LOGGER = LoggerFactory.getLogger(WebBizProcessHelper.class);
/**
* 處理業務邏輯,注意:<font color=red>WebBizCallBack的泛型類需要與第一個參數所表示的類一致,且能夠被實例化</font>
* <br>比如:<br>
* <code>
* new WebBizProcessHelper().process(<font color=red>TbSignatoryParameterBean.class</font>,
new WebBizCallBack<<font color=red>TbSignatoryParameterBean</font>>() {
//...
};
* </code>
* @param paramBeanRequestClass
* @param webBizCallBack
* @return
*/
@SuppressWarnings("unchecked")
public String process(Class<?> paramBeanRequestClass, WebBizCallBack webBizCallBack) {
Object request = null;
try {
request = BeanUtils.instantiateClass(paramBeanRequestClass);
processInitialize(webBizCallBack, request);
processValidate(webBizCallBack, request);
return processHande(webBizCallBack, request);
} catch (WebBizException e) {
try {
return webBizCallBack.processWebBizException(request, e);
} catch (Exception e1) {
LOGGER.error("WebBizProcessHelper#processWebBizException出現異常!phase:" + e.getPhase()
+ ",request:" + request + ",errorMsg:" + e1.getMessage());
}
} catch (Exception e) {
LOGGER.error("WebBizProcessHelper#process出現異常!" + ",request:" + request + ",errorMsg:"
+ e.getMessage());
}
return VelocityTemplateNameConstants.ERROR_VM;
}
/**
*
* @param webBizCallBack
* @param request
* @throws WebBizException
*/
@SuppressWarnings("unchecked")
private String processHande(WebBizCallBack webBizCallBack, Object request)
throws WebBizException {
try {
return webBizCallBack.handle(request);
} catch (Exception e) {
convetAllException2WebBizExceptoin(WebBizPhaseEnum.HANDLE_PHASE, e);
}
return VelocityTemplateNameConstants.ERROR_VM;
}
/**
*
* @param webBizCallBack
* @param request
* @throws WebBizException
*/
@SuppressWarnings("unchecked")
private void processValidate(WebBizCallBack webBizCallBack, Object request)
throws WebBizException {
try {
webBizCallBack.validate(request);
} catch (Exception e) {
convetAllException2WebBizExceptoin(WebBizPhaseEnum.VALIDATE_PHASE, e);
}
}
/**
*
* @param webBizCallBack
* @param request
* @throws WebBizException
*/
@SuppressWarnings("unchecked")
private void processInitialize(WebBizCallBack webBizCallBack, Object request)
throws WebBizException {
try {
webBizCallBack.initialize(request);
} catch (Exception e) {
convetAllException2WebBizExceptoin(WebBizPhaseEnum.INITIALIZE_PHASE, e);
}
}
/**
* 轉換後默認是系統異常
* @param e
* @throws WebBizException
* @throws Exception
*/
private void convetAllException2WebBizExceptoin(WebBizPhaseEnum phase, Exception e)
throws WebBizException {
if (e instanceof WebBizException) {
throw (WebBizException) e;
}
throw new WebBizException(phase, e, ErrorEnum.SYSTEM_ERROR.getErrorMessage());
}
/**
* 業務處理回調接口
* @author jiatao
* @version $Id: WebBizProcessHelper.java, v 0.1 2012-12-12 上午09:03:00 wb-jiatao Exp $
*/
public interface WebBizCallBack<K> {
/**
* 業務邏輯初始化
* @param paramBean
* @throws WebBizException
*/
void initialize(K paramBean) throws WebBizException;
/**
* 業務邏輯驗證
* @param paramBean
* @throws WebBizException
*/
void validate(K paramBean) throws WebBizException;
/**
* 業務邏輯處理
* @param paramBean
* @return
* @throws WebBizException
*/
String handle(K paramBean) throws WebBizException;
/**
* 處理各階段拋出的異常
* @param paramBean
* @param WebBizException
* @return
*/
String processWebBizException(K paramBean, WebBizException WebBizException);
}
/**
* 業務處理階段
* @author jiatao
* @version $Id: WebBizProcessHelper.java, v 0.1 2012-12-12 上午09:02:45 wb-jiatao Exp $
*/
public enum WebBizPhaseEnum {
INITIALIZE_PHASE, VALIDATE_PHASE, HANDLE_PHASE, PROCESS_EXCEPTION_PHASE;
}
/**
* 顯示錯誤頁面
* @param modelMap
* @param webBizexception
* @return
*/
public static String showErrorVM(final ModelMap modelMap, WebBizException webBizexception) {
return showErrorVM(modelMap, webBizexception.getMessage());
}
/**
* 顯示錯誤頁面
* @param modelMap
* @param webBizexception
* @return
*/
public static String showErrorVM(final ModelMap modelMap, String errorMsg) {
modelMap.addAttribute(ModelMapKeyConstants.ERROR_MESSAGE, errorMsg);
return VelocityTemplateNameConstants.ERROR_VM;
}
}
在你的Controller裏面,可以這麼做:
@RequestMapping(method = RequestMethod.GET)
public String testBizProcess(final ModelMap modelMap, final HttpServletRequest request) {
LogManageUtil.processLog(test, "enter", request.getParameter("s"),
"", "");
LogManageUtil.monitorLog(request.getParameter("s"), "test", "arrive",
"Y", request.getParameter("t"), "", "t", "t");
return new WebBizProcessHelper().process(ParameterBean.class,
new WebBizCallBack<ParameterBean>() {
public void initialize(ParameterBean paramBean) throws WebBizException {
initParameterBean(paramBean, request);
if (LOGGER.isInfoEnabled()) {
LOGGER.info("t:" + t);
}
}
public void validate(ParameterBean paramBean) throws WebBizException {
ValiResult valiResult = validateManagr.validate(paramBean);
if (LOGGER.isInfoEnabled()) {
LOGGER.info("能否簽約結果:" + signatoryPayResult
+ paramBean.getSign_alipay_user_id());
}
if (!valiResult.isSuccess()) {
throw new WebBizException(WebBizPhaseEnum.VALIDATE_PHASE,
"errorMsg");
}
}
public String handle(TbSignatoryParameterBean paramBean) throws WebBizException {
LogManageUtil.processLog(t, "success", paramBean
.getT(), "", "");
LogManageUtil.monitorLog(request.getParameter("t"), "t",
"finished", "Y", request.getParameter("t"), "", "t",
"t");
return VelocityTemplateNameConstants.T_VM;
}
public String processWebBizException(tParameterBean paramBean,
WebBizException WebBizException) {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("處理失敗:" + WebBizException.getPhase()
+ WebBizException.getMessage()
+ paramBean.getT());
}
LogManageUtil.failLog(T, "failture:webBizException", paramBean
.T(), "", WebBizException.getPhase()
+ WebBizException.getMessage());
return WebBizProcessHelper.showErrorVM(modelMap, WebBizException);
}
});
}