编写程序的时候,随着需求和业务的增加,代码的维护会越来越困难,如何做到可扩展,易维护呢?一个比较好的方案就是提高代码的复用率,抽取易变的部分统一维护,以降耦。
代码框架一般可以分为两类,一类是业务逻辑的抽象,主要用于提高业务逻辑代码的复用率,比如不同业务对某个规则的验证。另外一类是处理流程的抽象,主要用于降耦,即对修改关闭,对扩展开放。新增的业务不影响原来的业务。当然,更多的是将两者的合理糅合。
先从第一类开始。
无论是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);
}
});
}