代码的提升

支付渠道策略模式实践

在项目中可能会用到这样的一种需求,比如说支付账单,通过不同的支付渠道有不同的优惠价格。

 

上图是一个思维导图,一共有四种方式可以实现该需求。

具体的实现步骤:

第一简单完成银行的CRUD。

第二定义支付接口

/**
 * 
 * 功能:定义支付接口<br>
 * 作者:张<br>
 * 时间:2018年12月7日<br>
 * 版本:1.0<br>
 *
 */
public interface Strategy {
	
	BigDecimal pay(String channelId);

}

第三、某个银行实现支付接口

public class ICBCBank implements Strategy{
	
	@Resource
	private BjPayMethodServiceImpl bjPayMethodServiceImpl;
	
	@Override
	public BigDecimal pay(String channelId) {
		BigDecimal price = new BigDecimal("5000");
		BjPayMethod payMethod = bjPayMethodServiceImpl.getById(channelId);
		if (payMethod == null)
			return null;
		return price.multiply(new BigDecimal(payMethod.getDiscount()));
	}

}

第四、定义上下文

public class Context {
	// 根据上文(渠道Id),得到下文的某个实现类。
	public BigDecimal pay(String channelId) throws Exception{
		
		// 我们需要一个类帮我们创建具体的实现类(通过工厂模式),条件就是channelId
		StrategyFactory strategyFactory = StrategyFactory.getInstance();
		Strategy strategy = strategyFactory.create(channelId);
		return strategy.pay(channelId);
	}
}

第五、创建注解并添加到银行的支付接口实现类上通过map对注解进行维护

@Target(ElementType.TYPE)// @Target:小时注解可以写在什么地方, TYPE:Class、FIELD:变量、METHOD:方法
@Retention(RetentionPolicy.RUNTIME)// @Retention注解生命周期 , SOURCE:编译器直接忽略,CLASS:JVM忽略
public @interface Pay {
	
	String channelId();
}

第六、通过工厂创建Strategy
 

public class StrategyFactory {
	
	private static StrategyFactory strategyFactory = new StrategyFactory();
	// 通过map 维护注解(自定义注解)
	public static Map<String, String> source_map = new HashMap<>();
	
	static {
		// 通过包路径获取其下所有的类
		Reflections reflections = new Reflections("com.baojian.zhang.api.service.impl");
		// 获取有 @Pay 注解的类
		Set<Class<?>> classSet = reflections.getTypesAnnotatedWith(Pay.class);
		for (Class<?> c : classSet) {
			// 获取类上注解的信息
			Pay pay = (Pay)c.getAnnotation(Pay.class);
			// 维护source_map。 key:channelId,value:类名
			source_map.put(pay.channelId(), c.getCanonicalName());
		}
	}
	
	private StrategyFactory(){}
	
	// 获取的 StrategyFactory (单例模式;饿汉式)
	public static StrategyFactory getInstance() {
		return strategyFactory;
	}
	
	// 通过map 获得具体的创建某个银行的实现类的方法。
	public Strategy create(String channelId) throws Exception{
		String className = source_map.get(channelId);
		Class<?> name = Class.forName(className);
		return (Strategy) name.newInstance();
	}
	
}

最后进行测试

@RestController
@RequestMapping("/api/pay")
@Api(value="/api/pay", description="支付API模块")
public class PayController {
	
	@RequestMapping(value = "/testPay", method = RequestMethod.GET)
	@ApiOperation(value = "测试支付", notes = "测试支付", response = BaseVO.class)
	public BaseVO<BigDecimal> testPay(
			@RequestParam(value="channelId",required=true)@ApiParam(name="channelId", value="支付渠道",required=true)String channelId) {
		
		Context context = new Context();
		BigDecimal price = null;
		try {
			price = context.pay(channelId);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return new BaseVO<BigDecimal>(1, "支付成功", price);
		
	}
}

返回结果是空……

提示我一开始写好的银行CRUD实现类通过注解不能获取到。

经查找原因是:当我们在通过工厂创建支付接口的实现类时,已经脱离了spring的管理,所以注入不能生效,获取不到实例。

这时我们可也去 实现 ApplicationContextAware 接口,获取到 applicationContext,这样就能通过applicationContext获取到所有的Bean,

@Component
public class BeanUtils implements ApplicationContextAware{
	
	private static ApplicationContext applicationContext;

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		this.applicationContext = applicationContext;
	}
	
	public BeanUtils() {
		if (applicationContext == null)
			return;
		try {
			// 获取集成该类的类,扫描成员变量
			Reflections reflections = new Reflections(this.getClass(), new FieldAnnotationsScanner());
			// 获取所有含有 @Resource 注解的成员变量
			Set<Field> fields = reflections.getFieldsAnnotatedWith(javax.annotation.Resource.class);
			// 循环成员变量
			for (Field field : fields) {
				// 循环目的就是把不能被spring 管理的注入对象,手动让applicationContext set 进去
				String simpleName = field.getType().getSimpleName();
				// 因为我们spring 里面管理的 bean 的name 都是首字母小写,所以需要转换
				String beanName = toLowerCaseFirstOne(simpleName);
				// 通过 beanName 让 applicationContext 去获取beanName 对象
				Object bean = applicationContext.getBean(beanName);
				if (bean == null)
					return;
				// 如果变量是 private修饰的,要打破封装
				field.setAccessible(true);
				field.set(this, bean);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	//首字母转小写
	public static String toLowerCaseFirstOne(String s){
	  if(Character.isLowerCase(s.charAt(0)))
	    return s;
	  else
	    return (new StringBuilder()).append(Character.toLowerCase(s.charAt(0))).append(s.substring(1)).toString();
	}
}

测试成功。

当我们在添加一个支付渠道的时候,只要再次重复第三步并添加相应 channelId 注解即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值