摘要:是一个使用语言集成三方支付的小,现已集成支付宝国内国际移动端端微信银联光大网关网页邮政支付,采用的技术栈为。系统初始化监听器在系统启动时运行进行一些初始化工作加载银联配置文件缓存初始化忽略过滤器我们先看关于日志的,真心看不懂,后面有一大堆。
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
2、核心包~common.dao,service,web,mq.
(1)、我们先从dao开始吧,这里也可以是web。熟悉的来了,IBaseMapper
图片+代码可以说明一切问题。反射+泛型。很重。要是还不懂,再用文字描述
1、泛型类,是在实例化类的时候指明泛型的具体类型;支持创建可以按类型进行参数化的类
2、泛型方法,是在调用方法的时候指明具体的类型。public List
3、泛型接口,JDK,Spring中大量运用泛型。
总结来说泛型可以提高Java程序的类型安全,所有的类型转换都是自动和隐式的。为优化,性能带来收益。
IBaseDao接口:
/** * 持久层通用接口 */ public interface IBaseDao{ void save(T entity); void delete(T entity); void update(T entity); T findById(Serializable id); List findAll(); }
BaseDaoImpl
/** * 持久层通用实现 */ public class BaseDaoImplextends HibernateDaoSupport implements IBaseDao { //代表的是某个实体的类型 private Class entityClass; @Resource//根据类型注入spring工厂中的会话工厂对象sessionFactory public void setMySessionFactory(SessionFactory sessionFactory){ super.setSessionFactory(sessionFactory); } //在父类(BaseDaoImpl)的构造方法中动态获得entityClass public BaseDaoImpl() { ParameterizedType superclass = (ParameterizedType) this.getClass().getGenericSuperclass(); //获得父类上声明的泛型数组 Type[] actualTypeArguments = superclass.getActualTypeArguments(); entityClass = (Class ) actualTypeArguments[0]; } public void save(T entity) { this.getHibernateTemplate().save(entity); } public T findById(Serializable id) { return this.getHibernateTemplate().get(entityClass, id); } public List findAll() { String hql = "FROM " + entityClass.getSimpleName(); return (List ) this.getHibernateTemplate().find(hql); } }
(2)、接下来,再看本项目中定义IBaseMapper
@SelectProvider注解用于生成查询用的sql语句,有别于@Select注解,@SelectProvide指定一个Class及其方法,并且通过调用Class上的这个方法来获得sql语句。
@ResultMap注解用于从查询结果集RecordSet中取数据然后拼装实体bean。
@SelectProvide方法,如果参数使用了@Param注解,那么参数在Map中以@Param的值为key
在这里使用了注解的形式,但是也可以在XMl方法配置。
public interface IBaseMapper{ @SelectProvider(type = MapperProvider.class, method = "dynamicSQL") T selectOne(T record); @SelectProvider(type = MapperProvider.class, method = "dynamicSQL") T selectByPrimaryKey(Object key); @InsertProvider(type = MapperProvider.class, method = "dynamicSQL") int insert(T record); ...........................忽略了几个....................... @DeleteProvider(type = MapperProvider.class, method = "dynamicSQL") int delete(T record); @DeleteProvider(type = MapperProvider.class, method = "dynamicSQL") int deleteByPrimaryKey(Object key); @DeleteProvider(type = MapperProvider.class, method = "dynamicSQL") int deleteByExample(Object example); @UpdateProvider(type = MapperProvider.class, method = "dynamicSQL") int updateByExample(@Param("record") T record, @Param("example") Object example); List getAllByPage(RowBounds rowBounds); //这个是用于分页的。 }
(3)、中间插一个RabbitMQ MSG序列化JSON转换器,主要作用是客户端和服务端需要传输Json格式的数据包,所以需要进行转换。这个工具包也是必备的之一。友情提示,安装MQ时,一定要以系统管理员运行CMD。
RabbitMQ已经实现了Jackson的消息转换(Jackson2JsonMessageConverter),由于考虑到效率,如下使用Gson实现消息转换。
如下消息的转换类的接口MessageConverter,Jackson2JsonMessageConverter的父类AbstractJsonMessageConverter针对json转换的基类。
我们实现Gson2JsonMessageConverter转换类也继承AbstractJsonMessageConverter。
为了节约地方,代码放Gist了,需要的时候直接去找。
/** * MQ MSG序列化JSON转换器 */ public class Gson2JsonMessageConverter extends AbstractJsonMessageConverter { private static Logger logger = LoggerFactory.getLogger(Gson2JsonMessageConverter.class); private static ClassMapper classMapper = new DefaultClassMapper(); private static Gson gson = new Gson(); @Override protected Message createMessage(Object object, MessageProperties messageProperties) { byte[] bytes = null; try { String jsonString = gson.toJson(object); jsonString.getBytes(getDefaultCharset()); } catch (IOException e) { new MessageConversionException("Failed to convert Mesage context",e); } messageProperties.setContentType(MessageProperties.CONTENT_TYPE_JSON); messageProperties.setContentEncoding(getDefaultCharset()); if (bytes != null) { messageProperties.setContentLength(bytes.length); } classMapper.fromClass(object.getClass(),messageProperties); return new Message(bytes,messageProperties); } @Override public ClassMapper getClassMapper() { return new DefaultClassMapper(); } }
(4)、接下来就是定义基础IBaseService以及实现类
public interface IBaseService{ /** * 根据主键查询指定实体 */ T getId(Object id) ; List getByEntiry(T entity); PageInfo getByPage(RowBounds rowBounds); int save(T entity); int update(T entity); int delete(Object id); int saveSelective(T entity) throws DBException; int updateSelective(T entity); }
IBaseService:全部代码在这Gist
/** * Created by guo on 3/2/2018. */ public abstract class BaseServiceimplements IBaseService { private static Logger logger = LoggerFactory.getLogger(BaseService.class); @Resource protected RabbitTemplate amqpTemplate; @Autowired protected RedisTemplate redisTemplate; public abstract IBaseMapper getBaseMapper(); /** * 根据主键查询指定实体 * @param id * @return */ @Override public T getId(Object id) { return this.getBaseMapper().selectByPrimaryKey(id); } /** * 获取分页数据 */ @Override public PageInfo getByPage(RowBounds rowBounds) { List list = this.getBaseMapper().getAllByPage(rowBounds); return new PageInfo (list); } /** * 保存对象,保存所有属性 */ @Override public int save(T entity) { return this.getBaseMapper().insert(entity); } /** * 删除指定数据 */ @Override public int delete(Object id) { return this.getBaseMapper().deleteByPrimaryKey(id); } /** * 更新对象,值更新对象中不为Null的属性,主键不能为NULL */ @Override public int updateSelective(T entity) { return this.getBaseMapper().updateByPrimaryKeySelective(entity); } }
(5)、接下来就是web包中的内容,涉及监听器和过滤器。
/** * 系统初始化监听器,在系统启动时运行,进行一些初始化工作 */ public class InitListener implements javax.servlet.ServletContextListener { private static Logger logger = LoggerFactory.getLogger(InitListener.class); public static ApplicationContext context; public void contextDestroyed(ServletContextEvent arg0) { } public void contextInitialized(ServletContextEvent servletContextEvent) { context = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContextEvent.getServletContext()); //加载银联upop配置文件 SDKConfig.getConfig().loadPropertiesFromSrc(); String proPath = servletContextEvent.getServletContext().getRealPath("/"); SDKConfig config = SDKConfig.getConfig(); config.setSignCertDir(proPath + config.getSignCertDir()); config.setSignCertPath(proPath + config.getSignCertPath()); config.setValidateCertDir(proPath + config.getValidateCertDir()); //缓存初始化忽略 } }
过滤器:
我们先看关于日志的,真心看不懂,后面有一大堆。代码地址
/** * request response log记录过滤器 */ public class LoggingFilter extends OncePerRequestFilter { protected static final Logger logger = LoggerFactory.getLogger(LoggingFilter.class); private static final String REQUEST_PREFIX = "Request: "; private static final String RESPONSE_PREFIX = "Response: "; private AtomicLong id = new AtomicLong(1); @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, final FilterChain filterChain) throws ServletException, IOException { if (logger.isDebugEnabled()) { long requestId = id.incrementAndGet(); request = new RequestWrapper(requestId, request); } try { filterChain.doFilter(request, response); } finally { if (logger.isDebugEnabled()) { logRequest(request); } } } //... }
还有一个请求包装类,和响应类。。代码地址
public class RequestWrapper extends HttpServletRequestWrapper { private final ByteArrayOutputStream bos = new ByteArrayOutputStream(); private long id; public RequestWrapper(Long requestId, HttpServletRequest request) { super(request); this.id = requestId; } @Override public ServletInputStream getInputStream() throws IOException { return new ServletInputStream() { private TeeInputStream tee = new TeeInputStream(RequestWrapper.super.getInputStream(), bos); @Override public int read() throws IOException { return tee.read(); } }; } public byte[] toByteArray() { return bos.toByteArray(); } } --------------------------------------------------------------------------------- public class ResponseWrapper extends HttpServletResponseWrapper { private final ByteArrayOutputStream bos = new ByteArrayOutputStream(); private PrintWriter writer = new PrintWriter(bos); private long id; public ResponseWrapper(Long requestId, HttpServletResponse response) { super(response); this.id = requestId; } @Override public ServletOutputStream getOutputStream() throws IOException { return new ServletOutputStream() { private TeeOutputStream tee = new TeeOutputStream(ResponseWrapper.super.getOutputStream(), bos); @Override public void write(int b) throws IOException { tee.write(b); } }; } @Override public PrintWriter getWriter() throws IOException { return new TeePrintWriter(super.getWriter(), writer); } public byte[] toByteArray() { return bos.toByteArray(); } }
这一块只是得补补,用到的时候再看,还有一个TeePrintWriter
核心包的东东算是完了,重点是在IBaseMapper、IBaseService的设计。这里用到了泛型,还有Mybatis3.X新特性,基于注解的。其实完全可以用XML配置文件。
gogogo 正式进入业务逻辑部分。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/68640.html
摘要:重要的是学习过程,而不是结果。但,结果同样重要,加油。。在这提一点,由于网络原因等异常情况支付平台可能出现多次发送支付结果的情况,通知回调接口商户要注意做好接口幂等,其余的不再多说。 7、sps.controller.base,front. 说明 如果有幸能看到,其实只为自己记录,回头复习用 1、本文项目来自Martin404,自己只是临摹大佬的项目。 2、重要的是学习过程,而不是结...
摘要:但,结果同样重要,加油。。而且可明确提出错误信息。业务异常的自定义封装类实现和一样,为了空间就不展示了。数据库异常在看两个类,验证信息异常。那就是字符串工具类。字符串处理及转换工具类,。 PayMap PayMap是一个使用Java语言集成三方支付的小Demo,现已集成支付宝(国内、国际、移动端、PC端)、微信、银联(ACP、UPOP)、光大(网关、网页)、邮政支付,采用的技术栈为:S...
摘要:而调用后端服务就应用了的高级特分布式配置管理平台后端掘金轻量的分布式配置管理平台。关于网络深度解读后端掘金什么是网络呢总的来说,网络中的容器们可以相互通信,网络外的又访问不了这些容器。 在 Java 路上,我看过的一些书、源码和框架(持续更新) - 后端 - 掘金简书 占小狼转载请注明原创出处,谢谢!如果读完觉得有收获的话,欢迎点赞加关注 物有本末,事有终始,知所先后,则近道矣 ......
摘要:应用的研发上线运维运营形成闭环,顺利完成从对内服务到公共平台的升级。从功能角度,只能支持静态方式设置反向代理,然后,而平台有服务对应的后端服务和端口是有动态调整需求。架构上是基础组件需要进行升级,数据访问层日志监控系统等。 介绍 MaxLeap早期是一家研发、运营移动应用和手机游戏公司,发展过程中积累了很多通用组件。这些组件很大程度帮公司在移动研发过程中节省了时间和成本,...
阅读 3488·2023-04-25 14:57
阅读 2540·2021-11-22 14:56
阅读 2042·2021-09-29 09:45
阅读 1746·2021-09-22 15:53
阅读 3296·2021-08-25 09:41
阅读 870·2019-08-29 15:22
阅读 3267·2019-08-29 13:22
阅读 3095·2019-08-29 13:08