摘要:是一個(gè)使用語(yǔ)言集成三方支付的小,現(xiàn)已集成支付寶國(guó)內(nèi)國(guó)際移動(dòng)端端微信銀聯(lián)光大網(wǎng)關(guān)網(wǎng)頁(yè)郵政支付,采用的技術(shù)棧為。系統(tǒng)初始化監(jiān)聽(tīng)器在系統(tǒng)啟動(dòng)時(shí)運(yùn)行進(jìn)行一些初始化工作加載銀聯(lián)配置文件緩存初始化忽略過(guò)濾器我們先看關(guān)于日志的,真心看不懂,后面有一大堆。
PayMap
PayMap是一個(gè)使用Java語(yǔ)言集成三方支付的小Demo,現(xiàn)已集成支付寶(國(guó)內(nèi)、國(guó)際、移動(dòng)端、PC端)、微信、銀聯(lián)(ACP、UPOP)、光大(網(wǎng)關(guān)、網(wǎng)頁(yè))、郵政支付,采用的技術(shù)棧為:SpringMVC+Spring+MyBatis+Shiro+RabbitMQ+Redis。
特性支持前面提到的各種**支付
支付請(qǐng)求調(diào)用支持HTTP和異步MQ
控制層統(tǒng)一異常處理
LogBack日志記錄
Redis緩存機(jī)制
Shiro安全機(jī)制
MyBatis代碼自動(dòng)生成
HTTP請(qǐng)求日志記錄
RESTful APIs
說(shuō)明1、本文項(xiàng)目來(lái)自Martin404,自己只是臨摹大佬的項(xiàng)目。
2、重要的是學(xué)習(xí)過(guò)程,而不是結(jié)果。但,結(jié)果同樣重要,加油。gogogo。
3、框架搭建就略過(guò)了。配置文件太多。遇到的時(shí)候貼出來(lái)。也收藏起來(lái),留著備用。
4、Gist、Insight.io for GitHub必備吧,劃詞翻譯不懂的單詞劃一劃。
5、在IDEA中我會(huì)注重代碼規(guī)范,但是這里為了節(jié)約地方,能省的就省略了。還請(qǐng)諒解。
6、代碼提交到這里了GitHub。根據(jù)提交記錄找自己想要的類(lèi)庫(kù)。
7、重要的在后面,一切都只只是剛剛開(kāi)始(希望不要被屏蔽)??!gogogo
2、核心包~common.dao,service,web,mq.
(1)、我們先從dao開(kāi)始吧,這里也可以是web。熟悉的來(lái)了,IBaseMapper
圖片+代碼可以說(shuō)明一切問(wèn)題。反射+泛型。很重。要是還不懂,再用文字描述
1、泛型類(lèi),是在實(shí)例化類(lèi)的時(shí)候指明泛型的具體類(lèi)型;支持創(chuàng)建可以按類(lèi)型進(jìn)行參數(shù)化的類(lèi)
2、泛型方法,是在調(diào)用方法的時(shí)候指明具體的類(lèi)型。public List
3、泛型接口,JDK,Spring中大量運(yùn)用泛型。
總結(jié)來(lái)說(shuō)泛型可以提高Java程序的類(lèi)型安全,所有的類(lèi)型轉(zhuǎn)換都是自動(dòng)和隱式的。為優(yōu)化,性能帶來(lái)收益。
IBaseDao接口:
/** * 持久層通用接口 */ public interface IBaseDao{ void save(T entity); void delete(T entity); void update(T entity); T findById(Serializable id); List findAll(); }
BaseDaoImpl
/** * 持久層通用實(shí)現(xiàn) */ public class BaseDaoImplextends HibernateDaoSupport implements IBaseDao { //代表的是某個(gè)實(shí)體的類(lèi)型 private Class entityClass; @Resource//根據(jù)類(lèi)型注入spring工廠中的會(huì)話工廠對(duì)象sessionFactory public void setMySessionFactory(SessionFactory sessionFactory){ super.setSessionFactory(sessionFactory); } //在父類(lèi)(BaseDaoImpl)的構(gòu)造方法中動(dòng)態(tài)獲得entityClass public BaseDaoImpl() { ParameterizedType superclass = (ParameterizedType) this.getClass().getGenericSuperclass(); //獲得父類(lèi)上聲明的泛型數(shù)組 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)、接下來(lái),再看本項(xiàng)目中定義IBaseMapper
@SelectProvider注解用于生成查詢(xún)用的sql語(yǔ)句,有別于@Select注解,@SelectProvide指定一個(gè)Class及其方法,并且通過(guò)調(diào)用Class上的這個(gè)方法來(lái)獲得sql語(yǔ)句。
@ResultMap注解用于從查詢(xún)結(jié)果集RecordSet中取數(shù)據(jù)然后拼裝實(shí)體bean。
@SelectProvide方法,如果參數(shù)使用了@Param注解,那么參數(shù)在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); ...........................忽略了幾個(gè)....................... @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); //這個(gè)是用于分頁(yè)的。 }
(3)、中間插一個(gè)RabbitMQ MSG序列化JSON轉(zhuǎn)換器,主要作用是客戶端和服務(wù)端需要傳輸Json格式的數(shù)據(jù)包,所以需要進(jìn)行轉(zhuǎn)換。這個(gè)工具包也是必備的之一。友情提示,安裝MQ時(shí),一定要以系統(tǒng)管理員運(yùn)行CMD。
RabbitMQ已經(jīng)實(shí)現(xiàn)了Jackson的消息轉(zhuǎn)換(Jackson2JsonMessageConverter),由于考慮到效率,如下使用Gson實(shí)現(xiàn)消息轉(zhuǎn)換。
如下消息的轉(zhuǎn)換類(lèi)的接口MessageConverter,Jackson2JsonMessageConverter的父類(lèi)AbstractJsonMessageConverter針對(duì)json轉(zhuǎn)換的基類(lèi)。
我們實(shí)現(xiàn)Gson2JsonMessageConverter轉(zhuǎn)換類(lèi)也繼承AbstractJsonMessageConverter。
為了節(jié)約地方,代碼放Gist了,需要的時(shí)候直接去找。
/** * MQ MSG序列化JSON轉(zhuǎn)換器 */ 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)、接下來(lái)就是定義基礎(chǔ)IBaseService以及實(shí)現(xiàn)類(lèi)
public interface IBaseService{ /** * 根據(jù)主鍵查詢(xún)指定實(shí)體 */ 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(); /** * 根據(jù)主鍵查詢(xún)指定實(shí)體 * @param id * @return */ @Override public T getId(Object id) { return this.getBaseMapper().selectByPrimaryKey(id); } /** * 獲取分頁(yè)數(shù)據(jù) */ @Override public PageInfo getByPage(RowBounds rowBounds) { List list = this.getBaseMapper().getAllByPage(rowBounds); return new PageInfo (list); } /** * 保存對(duì)象,保存所有屬性 */ @Override public int save(T entity) { return this.getBaseMapper().insert(entity); } /** * 刪除指定數(shù)據(jù) */ @Override public int delete(Object id) { return this.getBaseMapper().deleteByPrimaryKey(id); } /** * 更新對(duì)象,值更新對(duì)象中不為Null的屬性,主鍵不能為NULL */ @Override public int updateSelective(T entity) { return this.getBaseMapper().updateByPrimaryKeySelective(entity); } }
(5)、接下來(lái)就是web包中的內(nèi)容,涉及監(jiān)聽(tīng)器和過(guò)濾器。
/** * 系統(tǒng)初始化監(jiān)聽(tīng)器,在系統(tǒng)啟動(dòng)時(shí)運(yùn)行,進(jìn)行一些初始化工作 */ 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()); //加載銀聯(lián)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()); //緩存初始化忽略 } }
過(guò)濾器:
我們先看關(guān)于日志的,真心看不懂,后面有一大堆。代碼地址
/** * request response log記錄過(guò)濾器 */ 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); } } } //... }
還有一個(gè)請(qǐng)求包裝類(lèi),和響應(yīng)類(lèi)。。代碼地址
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(); } }
這一塊只是得補(bǔ)補(bǔ),用到的時(shí)候再看,還有一個(gè)TeePrintWriter
核心包的東東算是完了,重點(diǎn)是在IBaseMapper、IBaseService的設(shè)計(jì)。這里用到了泛型,還有Mybatis3.X新特性,基于注解的。其實(shí)完全可以用XML配置文件。
gogogo 正式進(jìn)入業(yè)務(wù)邏輯部分。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/68640.html
摘要:重要的是學(xué)習(xí)過(guò)程,而不是結(jié)果。但,結(jié)果同樣重要,加油。。在這提一點(diǎn),由于網(wǎng)絡(luò)原因等異常情況支付平臺(tái)可能出現(xiàn)多次發(fā)送支付結(jié)果的情況,通知回調(diào)接口商戶要注意做好接口冪等,其余的不再多說(shuō)。 7、sps.controller.base,front. 說(shuō)明 如果有幸能看到,其實(shí)只為自己記錄,回頭復(fù)習(xí)用 1、本文項(xiàng)目來(lái)自Martin404,自己只是臨摹大佬的項(xiàng)目。 2、重要的是學(xué)習(xí)過(guò)程,而不是結(jié)...
摘要:但,結(jié)果同樣重要,加油。。而且可明確提出錯(cuò)誤信息。業(yè)務(wù)異常的自定義封裝類(lèi)實(shí)現(xiàn)和一樣,為了空間就不展示了。數(shù)據(jù)庫(kù)異常在看兩個(gè)類(lèi),驗(yàn)證信息異常。那就是字符串工具類(lèi)。字符串處理及轉(zhuǎn)換工具類(lèi),。 PayMap PayMap是一個(gè)使用Java語(yǔ)言集成三方支付的小Demo,現(xiàn)已集成支付寶(國(guó)內(nèi)、國(guó)際、移動(dòng)端、PC端)、微信、銀聯(lián)(ACP、UPOP)、光大(網(wǎng)關(guān)、網(wǎng)頁(yè))、郵政支付,采用的技術(shù)棧為:S...
摘要:而調(diào)用后端服務(wù)就應(yīng)用了的高級(jí)特分布式配置管理平臺(tái)后端掘金輕量的分布式配置管理平臺(tái)。關(guān)于網(wǎng)絡(luò)深度解讀后端掘金什么是網(wǎng)絡(luò)呢總的來(lái)說(shuō),網(wǎng)絡(luò)中的容器們可以相互通信,網(wǎng)絡(luò)外的又訪問(wèn)不了這些容器。 在 Java 路上,我看過(guò)的一些書(shū)、源碼和框架(持續(xù)更新) - 后端 - 掘金簡(jiǎn)書(shū) 占小狼轉(zhuǎn)載請(qǐng)注明原創(chuàng)出處,謝謝!如果讀完覺(jué)得有收獲的話,歡迎點(diǎn)贊加關(guān)注 物有本末,事有終始,知所先后,則近道矣 ......
摘要:應(yīng)用的研發(fā)上線運(yùn)維運(yùn)營(yíng)形成閉環(huán),順利完成從對(duì)內(nèi)服務(wù)到公共平臺(tái)的升級(jí)。從功能角度,只能支持靜態(tài)方式設(shè)置反向代理,然后,而平臺(tái)有服務(wù)對(duì)應(yīng)的后端服務(wù)和端口是有動(dòng)態(tài)調(diào)整需求。架構(gòu)上是基礎(chǔ)組件需要進(jìn)行升級(jí),數(shù)據(jù)訪問(wèn)層日志監(jiān)控系統(tǒng)等。 介紹 ? ? ? ?MaxLeap早期是一家研發(fā)、運(yùn)營(yíng)移動(dòng)應(yīng)用和手機(jī)游戲公司,發(fā)展過(guò)程中積累了很多通用組件。這些組件很大程度幫公司在移動(dòng)研發(fā)過(guò)程中節(jié)省了時(shí)間和成本,...
摘要:另外小程序云應(yīng)用有一套高可用架構(gòu),提供監(jiān)控預(yù)警能力。自主可控小程序云應(yīng)用提供服務(wù)器,開(kāi)發(fā)者可以擁有登錄或重啟,也可以修改密碼。也就是說(shuō),服務(wù)器是由小程序云應(yīng)用提供,但使用權(quán)歸開(kāi)發(fā)者。 前不久有一個(gè)朋友問(wèn)我,到底是做什么端的小程序比較好? 我只問(wèn)了一句,你的產(chǎn)品里是否涉及錢(qián)和服務(wù),如果涉及這兩者,建議你選擇支付寶小程序。你可以通過(guò)其他小程序玩裂變,但如果你想做服務(wù)和商業(yè),一定要考慮支付寶...
閱讀 3530·2023-04-25 14:57
閱讀 2577·2021-11-22 14:56
閱讀 2100·2021-09-29 09:45
閱讀 1781·2021-09-22 15:53
閱讀 3334·2021-08-25 09:41
閱讀 912·2019-08-29 15:22
閱讀 3310·2019-08-29 13:22
閱讀 3136·2019-08-29 13:08