Spring 适配模式应用

需求:

完成多通道支付系统,例如支持 微信支付,支付宝支付等,后续会持续集成其他支付通道。

 

条件:

为下游商户提供一个统一的下单接口,根据参数、通道标识码 来调用对就的通道完成下单。

 

思路:

遇到这种情景,首先想到的是使用 适配模式来完成。项目框架是spring 体系,所以要在pring下来做适配模式。解决各种 if  else ,使不同通道代码解耦,并易扩展其他支付通道

 

1.定义一个下单的service接口,每一个支付通道就是一个独立的实现类。因为不同通道中具有相同的业务逻辑,所以上层在抽象一个父类。

 

2.创建一个集中管理和路由的上下文,来管理和路由各个通道实现类。

 

3.因为不同的支付传递的参数也不同,所以可根据接收参数的实体来进行路由。

 

 

代码:

注:按照代码执行顺序列出。

 

package com.zzkx.pay.wechat.controller.test;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.StringUtils;
import org.json.JSONException;
import org.json.JSONObject;
import org.reflections.Reflections;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

import com.google.gson.Gson;
import com.zzkx.pay.common.enums.ExpCodeEnum;
import com.zzkx.pay.common.exception.CommonBizException;

/**
 * @author lyy
 */
public class OrderRequestParamResolver implements HandlerMethodArgumentResolver {
	
	private final Logger LOG = LoggerFactory.getLogger(OrderRequestParamResolver.class);
	
	private final Map> map = new HashMap<>();
	
	private final Reflections reflection = new Reflections("com.zzkx");
	
	private final Gson gson = new Gson();
	
	public  OrderRequestParamResolver() {
		
		Set> clazzs = reflection.getTypesAnnotatedWith(PayAnnotation.class);
		for(Class clazz : clazzs) {
			PayAnnotation annotation = AnnotationUtils.getAnnotation(clazz, PayAnnotation.class);
			String code = annotation.value();
			map.put(code, clazz);
		}
	}
	

	@Override
	public boolean supportsParameter(MethodParameter parameter) {
		return parameter.hasMethodAnnotation(PayAnnotation.class);
	}

	@Override
	public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
		HttpServletRequest httpServletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
		String value = parameter.getMethodAnnotation(PayAnnotation.class).value();
		Object object = requestToBean(httpServletRequest,value);
		return object;
	}
	
	private Object requestToBean(HttpServletRequest request,String name) throws UnsupportedEncodingException, IOException {

		try {
			BufferedReader streamReader = new BufferedReader( new InputStreamReader(request.getInputStream(), "UTF-8"));
			StringBuilder responseStrBuilder = new StringBuilder();
			String inputStr;
			while ((inputStr = streamReader.readLine()) != null)
				responseStrBuilder.append(inputStr);
			
			JSONObject jsonObject = new JSONObject(responseStrBuilder.toString());
			String code = jsonObject.getString(name);
			if(!StringUtils.isNotBlank(code))throw new CommonBizException(ExpCodeEnum.PARAM_INVALID);
			Class clazz = map.get(code);
			if(clazz == null)throw new CommonBizException(ExpCodeEnum.CHANNEL_WITHOUT);
			
			Object object = gson.fromJson(jsonObject.toString(), clazz);
			return object;
		} catch (UnsupportedEncodingException e) {
			LOG.error(e.getMessage(),e);
			new CommonBizException();
		} catch(IOException e) {
			LOG.error(e.getMessage(),e);
			new CommonBizException();
		} catch (JSONException e) {
			throw new CommonBizException(ExpCodeEnum.JSON_INVALID);
		}

		return null;

	}
	

}
package com.zzkx.pay.wechat.controller.test;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author lyy
 */
@Target({ElementType.METHOD,ElementType.TYPE})  
@Retention(RetentionPolicy.RUNTIME)  
public @interface PayAnnotation {

	String value();
}
@RestController
public class TestController {
	@Autowired
	private TestService testService;
	
	
	@Autowired
	private PayService obj;
	


	@RequestMapping("/pay")
	@PayAnnotation("type")
	public Object pay1(EntityBase base) throws IOException {
		obj.createOrder(base);
		return null;
	}

 
  
package com.zzkx.pay.wechat.controller.test;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.stereotype.Component;

/**
 * @author lyy
 */
@Component
public class PayContext implements BeanFactoryPostProcessor {

	private static ConfigurableListableBeanFactory beanFactory;
	
	private Map m = new HashMap<>();
	
	
	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
		PayContext.beanFactory = beanFactory;
		init();

	}
	
	private void init() {
		Class clazz = PayService.class;
		
		
		Object instance = Proxy.newProxyInstance(clazz.getClassLoader(), 
				new Class[]{clazz}, 
				new InvocationHandler() {

					@Override
					public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
						String name = args[0].getClass().getSimpleName();
						CommonPayService service = m.get(name);
						return method.invoke(service, args);
					}
					
				});
		
		beanFactory.registerSingleton(clazz.getSimpleName(), instance);
		
		Map beansOfType = beanFactory.getBeansOfType(CommonPayService.class);
		System.out.println(beansOfType);
		for(Entry entry : beansOfType.entrySet()) {
			ParameterizedType type = (ParameterizedType) entry.getValue().getClass().getGenericSuperclass();
			Type[] arguments = type.getActualTypeArguments();
			Class claz = (Class) arguments[0];
			m.put(claz.getSimpleName(), entry.getValue());
		}
	}

}
public interface PayService {

	public Object createOrder(T t);
}






public interface CommonPayService extends PayService{

}




public abstract class AbstractPayServiceImpl implements CommonPayService {
	//公共部分代码
}

 

@Service
public class AlipayServiceImpl extends AbstractPayServiceImpl {

	@Override
	public Object createOrder(AlipayEntity t) {
		System.out.println(t.toString());
		return null;
	}

}






@Service
public class WechatServiceImpl extends AbstractPayServiceImpl {

	@Override
	public Object createOrder(WechatPayEntity t) {
		System.out.println(t.toString());
		return null;
	}

}

 

 

@PayAnnotation("alipay")
public class AlipayEntity implements EntityBase{

	private String orderId;
	
	private String goodsName;

	public String getOrderId() {
		return orderId;
	}

	public void setOrderId(String orderId) {
		this.orderId = orderId;
	}

	public String getGoodsName() {
		return goodsName;
	}

	public void setGoodsName(String goodsName) {
		this.goodsName = goodsName;
	}

	@Override
	public String toString() {
		return "AlipayEntity [orderId=" + orderId + ", goodsName=" + goodsName + "]";
	}

	

	
}




@PayAnnotation("wx")
public class WechatPayEntity implements EntityBase {

	private String orderId;
	
	private String mchId;

	public String getOrderId() {
		return orderId;
	}

	public void setOrderId(String orderId) {
		this.orderId = orderId;
	}

	public String getMchId() {
		return mchId;
	}

	public void setMchId(String mchId) {
		this.mchId = mchId;
	}

	@Override
	public String toString() {
		return "WechatPayEntity [orderId=" + orderId + ", mchId=" + mchId + "]";
	}



	
	
}

/**
 * @author lyy
 */
@Configuration
public class WebMvcConfig implements WebMvcConfigurer  {
	


	@Override
	public void addArgumentResolvers(List resolvers) {
		resolvers.add(new OrderRequestParamResolver());
	}
	
	

}

 

 

测试:

 

Spring 适配模式应用_第1张图片

返回结果打印:

 WechatPayEntity [orderId=1, mchId=null]

 

type换成 alipay 打印:

 

AlipayEntity [orderId=1, goodsName=nan]

你可能感兴趣的:(Spring)