Java後端支付大雜燴之core.dao,service,web(重點是接口的設計)(二)

PayMap

PayMap是一個使用Java語言集成三方支付的小Demo,現已集成支付寶(國內、國際、移動端、PC端)、微信、銀聯(ACP、UPOP)、光大(網關、網頁)、郵政支付,採用的技術棧爲:SpringMVC+Spring+MyBatis+Shiro+RabbitMQ+Redis。java

特性

  • 支持前面提到的各類**支付
  • 支付請求調用支持HTTP和異步MQ
  • 控制層統一異常處理
  • LogBack日誌記錄
  • Redis緩存機制
  • Shiro安全機制
  • MyBatis代碼自動生成
  • HTTP請求日誌記錄
  • RESTful APIs

說明

  • 一、本文項目來自Martin404,本身只是臨摹大佬的項目。
  • 二、重要的是學習過程,而不是結果。但,結果一樣重要,加油。gogogo。
  • 三、框架搭建就略過了。配置文件太多。遇到的時候貼出來。也收藏起來,留着備用。
  • 四、Gist、Insight.io for GitHub必備吧,劃詞翻譯不懂的單詞劃一劃。
  • 五、在IDEA中我會注重代碼規範,可是這裏爲了節約地方,能省的就省略了。還請諒解。
  • 六、代碼提交到這裏了GitHub。根據提交記錄找本身想要的類庫。
  • 七、重要的在後面,一切都只只是剛剛開始(但願不要被屏蔽)!!gogogo

二、核心包~common.dao,service,web,mq.

(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() {}
  • 三、泛型接口,JDK,Spring中大量運用泛型。

總結來講泛型能夠提升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

  • @SelectProvider註解用於生成查詢用的sql語句,有別於@Select註解,@SelectProvide指定一個Class及其方法,而且經過調用Class上的這個方法來得到sql語句。
  • @ResultMap註解用於從查詢結果集RecordSet中取數據而後拼裝實體bean。
  • @SelectProvide方法,若是參數使用了@Param註解,那麼參數在Map中以@Param的值爲key

在這裏使用了註解的形式,可是也能夠在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 正式進入業務邏輯部分。

相關文章
相關標籤/搜索