PayMap是一個使用Java語言集成三方支付的小Demo,現已集成支付寶(國內、國際、移動端、PC端)、微信、銀聯(ACP、UPOP)、光大(網關、網頁)、郵政支付,採用的技術棧爲:SpringMVC+Spring+MyBatis+Shiro+RabbitMQ+Redis。java
(1)、咱們先從dao開始吧,這裏也能夠是web。熟悉的來了,IBaseMapper<T> 仍是定義基礎接口,但與中不一樣的是這個用泛型<T>修飾,爲何呢?先看一張圖。git
<img src="https://user-gold-cdn.xitu.io...;h=570&f=jpeg&s=28410" width = "300" height = "200"/>github
圖片+代碼能夠說明一切問題。反射+泛型。很重。要是還不懂,再用文字描述web
public List<T> findAll() {}
總結來講泛型能夠提升Java程序的類型安全,全部的類型轉換都是自動和隱式的。爲優化,性能帶來收益。redis
IBaseDao接口:spring
/** * 持久層通用接口 */ public interface IBaseDao<T> { void save(T entity); void delete(T entity); void update(T entity); T findById(Serializable id); List<T> findAll(); }
BaseDaoImpl<T>實現類:sql
/** * 持久層通用實現 */ public class BaseDaoImpl<T> extends HibernateDaoSupport implements IBaseDao<T> { //表明的是某個實體的類型 private Class<T> 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<T>) actualTypeArguments[0]; } public void save(T entity) { this.getHibernateTemplate().save(entity); } public T findById(Serializable id) { return this.getHibernateTemplate().get(entityClass, id); } public List<T> findAll() { String hql = "FROM " + entityClass.getSimpleName(); return (List<T>) this.getHibernateTemplate().find(hql); } }
(2)、接下來,再看本項目中定義IBaseMapper<T>chrome
在這裏使用了註解的形式,可是也能夠在XMl方法配置。json
public interface IBaseMapper<T> { @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<T> 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> { /** * 根據主鍵查詢指定實體 */ T getId(Object id) ; List<T> getByEntiry(T entity); PageInfo<T> 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 BaseService<T> implements IBaseService<T> { private static Logger logger = LoggerFactory.getLogger(BaseService.class); @Resource protected RabbitTemplate amqpTemplate; @Autowired protected RedisTemplate redisTemplate; public abstract IBaseMapper<T> getBaseMapper(); /** * 根據主鍵查詢指定實體 * @param id * @return */ @Override public T getId(Object id) { return this.getBaseMapper().selectByPrimaryKey(id); } /** * 獲取分頁數據 */ @Override public PageInfo<T> getByPage(RowBounds rowBounds) { List<T> list = this.getBaseMapper().getAllByPage(rowBounds); return new PageInfo<T>(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 正式進入業務邏輯部分。