摘要:但,结果同样重要,加油。。而且可明确提出错误信息。业务异常的自定义封装类实现和一样,为了空间就不展示了。数据库异常在看两个类,验证信息异常。那就是字符串工具类。字符串处理及转换工具类,。
PayMap
PayMap是一个使用Java语言集成三方支付的小Demo,现已集成支付宝(国内、国际、移动端、PC端)、微信、银联(ACP、UPOP)、光大(网关、网页)、邮政支付,采用的技术栈为:SpringMVC+Spring+MyBatis+Shiro+RabbitMQ+Redis。
特性支持前面提到的各种**支付
支付请求调用支持HTTP和异步MQ
控制层统一异常处理
LogBack日志记录
Redis缓存机制
Shiro安全机制
MyBatis代码自动生成
HTTP请求日志记录
RESTful APIs
说明1、本文项目来自Martin404,自己只是临摹大佬的项目。
2、重要的是学习过程,而不是结果。但,结果同样重要,加油。gogogo。
3、框架搭建就略过了。配置文件太多。遇到的时候贴出来。也收藏起来,留着备用。
4、Gist、Insight.io for GitHub必备吧,划词翻译不懂的单词划一划。
5、在IDEA中我会注重代码规范,但是这里为了节约地方,能省的就省略了。还请谅解。
6、代码提交到这里了GitHub。根据提交记录找自己想要的类库。
7、重要的在后面,一切都只只是刚刚开始(希望不要被屏蔽)!!gogogo
1、核心包~common因为没有文档,只能根据自己之前的经验。先把必备的配置文件,包弄好。项目跑不起来,没关系。重要的是学习,先从核心包,common开始学习。配置文件就不贴了。需要的可以到GitHub去找。
我们先整体看一下结构
1、就先从异常开始吧,首先是定义,BaseException。为什么要这么定义呢?不知道大家有没有见过BaseDao、BaseAction。主要原因是为了方便扩展,父类不能实现的,在子类中加强。
回过头来我们在看下整体继承关系图,Throwable应该还有个Error错误。两者的区别在于后者是不可恢复的。这里的是运行时异常。 还有一种区分是受检查和非检查的。比如数据库连接关闭就是受检查异常,数组越界异常是非检查的。还有try{}catch{}finally{}这里不展开了。在Spring框架中可以受检查的包装成非受检查的。而且可明确提出错误信息。
public class BaseException extends RuntimeException { public BaseException(String message) { super(message,new Throwable(message)); } public BaseException(Throwable cause) { super(cause); } public BaseException(String message,Throwable cause) { super(message,cause); } }
2、接下来就是定义它的子类。
/** * Created by guo on 3/2/2018. * 系统类异常 */ public class SystemException extends BaseException { //实现和BaseException一样,构造方法名字换下。为了空间就不展示了。 } --------------------------------------------------------- /** * 业务异常的自定义封装类 */ public class BusinessException extends BaseException { //实现和BaseException一样,为了空间就不展示了。 } --------------------------------------------------------- /** * 数据库异常 */ public class DBException extends BaseException { }
3、在看两个类,验证信息异常。后者估计大家用得着。我不会告诉你们Gist了。什么?你不懂?快去GItHub看看。收藏代码的好地方。
/** * 验证异常,用于封装 */ public class ValidationError { private String objectName; private String fieldName; private String defaultMessage; // Constructor 、Setter 、Getter 、ToString 略。为了节约地方。 } ---------------------------------------------------------------- /** * Created by guo on 3/2/2018. * 异常返回码 */ public enum ResultCode { /** * 成功. ErrorCode : 0 */ SUCCESS("0", "成功"), /** * 未知异常. ErrorCode : 01 */ UnknownException("01", "未知异常"), /** * 系统异常. ErrorCode : 02 */ SystemException("02", "系统异常"), /** * 业务错误. ErrorCode : 03 */ BusinessException("03", "业务错误"), /** * 提示级错误. ErrorCode : 04 */ InfoException("04", "提示级错误"), /** * 数据库操作异常. ErrorCode : 020001 */ DBException("020001", "数据库操作异常"), /** * 参数验证错误. ErrorCode : 040001 */ ParamException("040001", "参数验证错误"), SystemMaintainException("11", "系统正在维护"); private String _code; private String _msg; // Constructor、Getter略 public static ResultCode getByCode(String code) { for (ResultCode ec : ResultCode.values()) { if (ec.getCode().equals(code)) { return ec; } } return null; } }
4、接下来看一些默认设置。
public class ActionConstants { /** * 默认值 - 执行时失败时ReturnContext的ReturnMsg */ public static final String DEFAULT_FAILED_RETURNMSG = "执行失败"; /** * 默认值key - 执行成功时ReturnContext的Returning */ public static final String DEFAULT_SUCCESS_RETURNMSG ="执行成功"; } ------------------------------------------------------------------------- /** * 从SpringApplicationContext中设置的系统参数 */ public class SystemConfig { //系统默认的游客用户名 private static String guestUsername = ""; private SystemConfig() {} //注意这里被私有化了。 public static void setGuestUsername(String guestUsername) { SystemConfig.guestUsername = guestUsername; } } ------------------- -------注意bean------------------------------------${shiro.guest.username}
5、这个也很重要,局部刷新。首先看下实现了Serializable,为什么呢?因为它要在网络中传输,所以需要序列成二进制格式的。还有当我们需要网络上的一个对象时,可以进行反序列化,在创建对象。或者想把内存中的对象保存在数据库中或者一个文件中。这里涉及ObjectOutputStream类的writeObject()方法、ObjectInputStream类的writeObject()方法。还有需要serialvUID,但不是必须的,最好加上。
/** * AJAX调用返回对象 */ public class AjaxResult implements Serializable { //请求结果是否为成功 private String ErrorCode = ResultCode.SUCCESS.getCode(); //请求返回信息 private String Message = ActionConstants.DEFAULT_SUCCESS_RETURNMSG; //请求结果 private Object Date = null; //Setter、Getter、toString.... /** * 获取正确结果模板 * //标准是这样写的 * @param message 请求返回信息 * @param obj 请求结果 * @return AjaxResult */ public static AjaxResult getOK(String message,Object obj) { AjaxResult result = new AjaxResult(); result.setMessage(message); result.setDate(obj); return result; } /** * 获取正确结果模板 */ public static AjaxResult getOK(Object obj) { AjaxResult result = new AjaxResult(); result.setMessage(ActionConstants.DEFAULT_SUCCESS_RETURNMSG); result.setDate(obj); return result; } /** * 获取正确结果模板 */ public static AjaxResult getOK() { return getOK(ActionConstants.DEFAULT_SUCCESS_RETURNMSG,null); } /** * 获取错误结果模板 */ public static AjaxResult getError(ResultCode errorCode,String message,Object obj) { AjaxResult result = new AjaxResult(); result.setErrorCode(errorCode.getCode()); result.setMessage(message); result.setDate(obj); return result; } /** * 获取错误结果模板 * * @return AjaxResult */ public static final AjaxResult getError(ResultCode resultCode) { AjaxResult result = new AjaxResult(); return getError(resultCode,resultCode.getMsg(),null); } }2、核心包~util
1、接下来我们看工具类,这个几个非常有用。自己也收集了一些。先看日期吧,会拿出部分作为介绍。
(1)、首先我们看到DateUtils继承了ProperyEditorSuppert。为什么要这么做?看名字叫属性编辑支持。在Spring中我们可以使用属性编辑器来将特定的字符串转为对象:String-->Object.在JDK中用于将XML文件中字符串转为特定的类型,同时提供了一个实现类,是他是他就是PropertEditorSuppert。在Spring注入式,遇到类型不一致,则回去调用相应的属性编辑器进行转换。如:setAsText(String str)、setValue().不然getValue()方法拿不到处理后的对象。
import org.joda.time.format.DateTimeFormat; //注意包名,大佬的付出 public class DateUtils extends PropertyEditorSupport { public static final DateTimeFormatter standard = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"); public static final DateTimeFormatter yy_MM_dd = DateTimeFormat.forPattern("yyyy-MM-dd"); public static final DateTimeFormatter date_sdf_wz = DateTimeFormat.forPattern("yyyy年MM月dd日"); //还有需要转换格式 ...................................常用的,其实有很多............................................. /** * 字符串转为日期 */ public static Date str2Date(String str,DateTimeFormatter sdf) { if (str == null || "".equals(str)) { return null; } DateTime date; try { date = sdf.parseDateTime(str); return date.toDate(); } catch (IllegalArgumentException e) { String errorMessage = "yyyy-MM-dd HH:mm:ss"; if (sdf.equals(yy_MM_dd)) { errorMessage = "yyyy-MM-dd"; } throw new BusinessException("输入的日期格式有误,因为"" + errorMessage + ""格式"); //异常直接给自定义的。 } } /** * 指定日期的默认显示,具体格式为:年-月-日 * @param date 指定的日期 * @return 指定日期按“年-月-日”显示 */ public static String formatDate(Date date) { return standard.print(date.getTime()); } /** * 返回UNIX时间戳-1970年至今的秒数(注意单位-不是毫秒) * @param str the str * @param format the format * @return the long */ public static long getUnixTimestamp(String str, DateTimeFormatter format) { DateTime dateTime = format.parseDateTime(str); return dateTime.getMillis() / 1000; } /** * 获取给定日期之间的日期 */ public static ListgetRangeDates(Long startTime, Long endTime) { List dateList = new ArrayList<>(); DateTime startDt = new DateTime(startTime * 1000); DateTime endDt = new DateTime(endTime * 1000); endDt = endDt.withTimeAtStartOfDay(); //因为查询结束时间不包含边界,特殊处理一下 if (endTime * 1000 == endDt.getMillis()) { endDt = endDt.minusDays(1); } dateList.add(getTime(startTime, date_sdf_wz)); while (endDt.isAfter(startDt)) { startDt = startDt.plusDays(1); dateList.add(getTime(startDt.getMillis() / 1000, date_sdf_wz)); } return dateList; } /** * 返回时间间隔天数 */ public static int getDateDiff(Long beginTime, Long endTime) { DateTime dateTime1 = new DateTime(beginTime * 1000); DateTime dateTime2 = new DateTime(endTime * 1000); return Days.daysBetween(dateTime1, dateTime2).getDays(); } }
(2)接下来我们看下ID生产策略,注意静态代码块,还有这里用到了重入锁ReentrantLock(),对资源进行加锁,同一时刻只会有一个线程能够占有锁.当前锁被线程占有时,其他线程会进入挂起状态,直到该锁被释放,其他挂起的线程会被唤醒并开始新的竞争.,AtomicInteger并发条件下原子自增运算。保证一致性。友情提示
/** * ID生成策略 */ public class IDGenerator { private static final DateFormat format = new SimpleDateFormat("yyyyMMddHHmmssSSS"); private static final Random r = new Random(); private static char[] A2Z = null; static { int j = 0; A2Z = new char[26]; for (int i = 65; i < 91; i++) { A2Z[j] = (char)i; j++; } } public static String getTargetId() { char[] temp = new char[5]; for (int i = 0; i < 5; i++) { temp[i] = A2Z[r.nextInt(26)]; } String string = new String(temp); Integer max = 999999; Integer min = 10000; int s = r.nextInt(max) % (max - min + 1) + min; return string + s; } public static String getTranSid() { Lock lock = new ReentrantLock(); lock.lock(); String temp = null; AtomicInteger atomicInteger = new AtomicInteger(); try { String currDate = format.format(new Date()); Integer max = 999; Integer min = 100; int s = r.nextInt(max) % (max - min + 1) + min; temp = currDate + String.valueOf(s); } finally { lock.unlock(); } return temp; } public static String getIcbcTimeStamp() { DateFormat dateFormatStamp = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Lock lock = new ReentrantLock(); lock.lock(); String temp = null; AtomicInteger atomicInteger = new AtomicInteger(); try { String currDate = dateFormatStamp.format(new Date()); Integer max = 999999; Integer min = 100000; int s = r.nextInt(max) % (max - min + 1) + min; temp = currDate + "." + String.valueOf(s); } finally { lock.unlock(); } return temp; } }
(3)、看一个加载配置文件的工具类
需要注意的是这里同样用到了静态代码块,在启动的时候就加载。还有就是反射机制,在这里用的是类的加载器完成的。其他可以用getClass(),Claass.forName();
还有一点就是Properties类,继承自Hashtable,实现Map接口,可以和IO对象组合使用,实现数据的持久存储。存储键值对,根据key找Value。
/** * Properties文件加载工具 */ public class PropertiedsUtil { public static Properties properties = new Properties(); public static ListconfigFile = Arrays.asList( "server_config.properties", "sys_config.properties"); static { try { for (String fileName : configFile) { InputStream in = PropertiedsUtil.class.getClassLoader().getResourceAsStream(fileName); properties.load(in); } }catch (IOException e) { throw new BusinessException("读取配置文件错误"); } } public static String getValue(String key) { return properties.getProperty(key, ""); } }
(4)、SqlSessionFactoryBean工具类
在 MyBatis 中,使用 SqlSessionFactoryBuilder创建SqlSessionFactory ,进而来创建 SqlSession。一旦你获得一个 session 之后,你可以使用它来执行映射语句,提交或回滚连接,最后,当不再需要它的时候, 你可以关闭 session。
/** * Created by guo on 3/2/2018. * SqlSession工厂bean */ public class SqlSessionFactoryBeanUtil extends SqlSessionFactoryBean { //这里使用了日志记录,方便查看。 private static Logger logger = LoggerFactory.getLogger(SqlSessionFactoryBeanUtil.class); @Override protected SqlSessionFactory buildSqlSessionFactory() throws IOException { try { return super.buildSqlSessionFactory(); //调用父类 }catch (NestedIOException e) { logger.error("ex:{}",e.getMessage()); throw new NestedIOException("Faild to parse mapping resource: " + e); }finally { ErrorContext.instance().reset(); } } }
(5)、最重要的一般压低出现。那就是字符串工具类。这个有些多,挑常用的罗列出来。
/** * Created by guo on 3/2/2018. * 字符串处理及转换工具类 */ public class StringUtil { private static Pattern numericPattern = Pattern.compile("^[0-9-]+$"); private static Pattern numericStringPattern = Pattern.compile("^[0-9--]+$"); private static Pattern floatNumericPattern = Pattern.compile("^[0-9-.]+$"); private static Pattern abcPattern = Pattern.compile("^[a-z|A-Z]+$"); public static final String splitStrPattern = ",|,|;|;|、|.|。|-|_|(|)|[|]|{|}||/| | |""; private static Logger logger = LoggerFactory.getLogger(StringUtil.class); /** * 判断是否数字表示 * @param src 源字符串 * @return 是否数字的标志 */ public static boolean isNumeric(String src) { boolean return_value = false; if (src != null && src.length() > 0) { Matcher m = numericPattern.matcher(src); if (m.find()) { return_value = true; } } return return_value; } /** * 判断是否纯字母组合 * @param src 源字符串 * @return 是否纯字母组合的标志 */ public static boolean isABC(String src) { boolean return_value = false; if (src != null && src.length() > 0) { Matcher m = abcPattern.matcher(src); if (m.find()) { return_value = true; } } return return_value; } /** * 判断是否浮点数字表示 */ public static boolean isFloatNumeric(String src) {} -------------------------------截取------------------------------------------------------------ /** * 把string array or list用给定的符号symbol连接成一个字符串 */ public static String joinString(List array, String symbol) { String result = ""; if (array != null) { for (int i = 0; i < array.size(); i++) { String temp = array.get(i).toString(); if (temp != null && temp.trim().length() > 0) result += (temp + symbol); } if (result.length() > 1) result = result.substring(0, result.length() - 1); } return result; } /** * 截取字符,不转码 */ public static String subStrNotEncode(String subject, int size) { if (subject.length() > size) { subject = subject.substring(0, size); } return subject; } /** * 取得字符串的实际长度(考虑了汉字的情况) */ public static int getStringLen(String SrcStr) { int return_value = 0; if (SrcStr != null) { char[] theChars = SrcStr.toCharArray(); for (int i = 0; i < theChars.length; i++) { return_value += (theChars[i] <= 255) ? 1 : 2; } } return return_value; } ---------------------------------分割、替换和转换------------------------------------------- /** * 根据指定的字符把源字符串分割成一个数组 */ public static ListparseString2ListByCustomerPattern(String pattern, String src) { if (src == null) return null; List list = new ArrayList (); String[] result = src.split(pattern); for (int i = 0; i < result.length; i++) { list.add(result[i]); } return list; } /** * 字符串替换 */ public static String stringReplace(String str, String sr, String sd) { String regEx = sr; Pattern p = Pattern.compile(regEx, Pattern.CASE_INSENSITIVE); Matcher m = p.matcher(str); str = m.replaceAll(sd); return str; } /** * 格式化一个float */ public static String formatFloat(float f, String format) { DecimalFormat df = new DecimalFormat(format); return df.format(f); } /** * 把 名=值 参数表转换成字符串 (a=1,b=2 =>a=1&b=2) */ public static String linkedHashMapToString(LinkedHashMap map) { if (map != null && map.size() > 0) { String result = ""; Iterator it = map.keySet().iterator(); while (it.hasNext()) { String name = (String) it.next(); String value = (String) map.get(name); result += (result.equals("")) ? "" : "&"; result += String.format("%s=%s", name, value); } return result; } return null; } /** * 转换编码 */ public static String changCoding(String s, String fencode, String bencode) { String str; try { if (StringUtil.isNotEmpty(s)) { str = new String(s.getBytes(fencode), bencode); } else { str = ""; } return str; } catch (UnsupportedEncodingException e) { return s; } }
下面的是转换,具体的用到了再说。
/** * 将字符串转换成十六进制编码 */ public static String toHexString(String str) throws UnsupportedEncodingException { // 根据默认编码获取字节数组 String hexString = "0123456789ABCDEF"; byte[] bytes = str.getBytes("GB2312"); StringBuilder sb = new StringBuilder(bytes.length * 2); // 将字节数组中每个字节拆解成2位16进制整数 for (byte b : bytes) { sb.append(Integer.toHexString(b + 0x800).substring(1)); } return sb.toString(); } /** * unicode 转字符串 */ public static String unicode2String(String unicode) { StringBuffer string = new StringBuffer(); String[] hex = unicode.split("u"); for (int i = 1; i < hex.length; i++) { // 转换出每一个代码点 int data = Integer.parseInt(hex[i], 16); // 追加成string string.append((char) data); } return string.toString(); }
gogogo...
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/68641.html
摘要:重要的是学习过程,而不是结果。但,结果同样重要,加油。。在这提一点,由于网络原因等异常情况支付平台可能出现多次发送支付结果的情况,通知回调接口商户要注意做好接口幂等,其余的不再多说。 7、sps.controller.base,front. 说明 如果有幸能看到,其实只为自己记录,回头复习用 1、本文项目来自Martin404,自己只是临摹大佬的项目。 2、重要的是学习过程,而不是结...
摘要:是一个使用语言集成三方支付的小,现已集成支付宝国内国际移动端端微信银联光大网关网页邮政支付,采用的技术栈为。系统初始化监听器在系统启动时运行进行一些初始化工作加载银联配置文件缓存初始化忽略过滤器我们先看关于日志的,真心看不懂,后面有一大堆。 PayMap PayMap是一个使用Java语言集成三方支付的小Demo,现已集成支付宝(国内、国际、移动端、PC端)、微信、银联(ACP、UPO...
摘要:是一个用开发的一个企业级中后台管理包含常用的业务,组件,及数据流转方案,前后端分离的开发方式,按业务划分的目录结构,可以大大提高我们的开发效率下面是整体的介绍,感兴趣的同学可以去官网详加了解。 dva-boot-admin 是一个用React开发的一个企业级中后台管理UI,包含常用的业务,组件,及数据流转方案,前后端分离的开发方式,按业务划分的目录结构,可以大大提高我们的开发效率 下面...
摘要:而调用后端服务就应用了的高级特分布式配置管理平台后端掘金轻量的分布式配置管理平台。关于网络深度解读后端掘金什么是网络呢总的来说,网络中的容器们可以相互通信,网络外的又访问不了这些容器。 在 Java 路上,我看过的一些书、源码和框架(持续更新) - 后端 - 掘金简书 占小狼转载请注明原创出处,谢谢!如果读完觉得有收获的话,欢迎点赞加关注 物有本末,事有终始,知所先后,则近道矣 ......
摘要:鉴于目前大多数服务器环境都是,提前接触能够相辅相成。正则也是必须要掌握的一个知识点。有多种创建多线程的方式,不过目前使用线程池的多一些。 原创:小姐姐味道(微信公众号ID:xjjdog),欢迎分享,转载请保留出处。 你可能有所感悟。零散的资料读了很多,但是很难有提升。到处是干货,但是并没什么用,简单来说就是缺乏系统化。另外,噪音太多,雷同的框架一大把,我不至于全都要去学了吧。 这里,我...
阅读 2555·2021-09-30 10:00
阅读 3491·2021-09-22 10:54
阅读 6212·2021-09-07 10:28
阅读 2943·2019-08-29 13:53
阅读 742·2019-08-29 12:42
阅读 958·2019-08-26 13:51
阅读 1258·2019-08-26 13:32
阅读 3021·2019-08-26 10:39