1. 程式人生 > >java後臺設計的思考:使用反射代理所有的後臺操作

java後臺設計的思考:使用反射代理所有的後臺操作

  我想自己大概是發春期又到了,確實在夜深人靜的時候想找人傾訴!  傾訴自己的擔憂,分享自己的喜與樂。不知道這是因為什麼,也許是看了教父之後,讓我對人生有了一些重新的思考。 再堅強的人也有脆弱的時候,所有的堅強都是為了要保護的人,包括保護自己。  抑或許,只是自己年齡到了,荷爾蒙的作用使自己希望尋找到一個伴侶。anyway, 此時此刻,我還是決定繼續學習總結,這讓自己多少有一些安全感。

   如題,這是自己曾經的一次練習實踐。目的是為了減少冗餘程式碼,最後希望達到的效果是,我們只需要寫上自己的伺服器路徑以及定義好自己的資料來源標準,便可以實現一個後臺服務的效果。 

包結構:(每個包的名字正是它所擔任的邏輯角色)。 

通常我們做開發時,需要完成四個垂直邏輯分層: 實體,持久層,業務層,控制層。

   實體的主要作用是對客觀事物的抽象,也是整個過程的根本。 它還有一個任務就是與資料庫中的持久化實體進行對映。 為了完成這個資料庫對映任務,hibernate和mybatis提供了很好的支援。 我才用的是基於hibernate註解的實現方式。 因為hibernate是data-jpa的標準實現。

    它的程式碼構成是這樣的:

package com.automannn.mainBottomItem.entity;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class MainBottomItem{
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    private String iconUrl;
    private String text;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public MainBottomItem() {
    }

    public String getIconUrl() {
        return iconUrl;
    }

    public void setIconUrl(String iconUrl) {
        this.iconUrl = iconUrl;
    }

    public String getText() {
        return text;
    }

    public void setText(String text) {
        this.text = text;
    }
}

  之所以很便捷,是因為它對很多的屬性都具有預設的配置,而這些預設配置在我們對資料屬性要求不是特別嚴格時,通常都是適用的。

  hibernate作為jpa標準實現的一個重大特點是,一種叫做jpql的查詢實現。 這使得我們可以簡化很多的程式碼操作。 當我使用了datajpa之後,整個持久層的構成程式碼如下:

package com.automannn.mainBottomItem.dao;

import com.automannn.mainBottomItem.entity.MainBottomItem;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.JpaRepository;

/**
 * @author 
[email protected]
* @time 2018/11/3 17:30 */ public interface BottomItemDao extends JpaRepository<MainBottomItem,Integer> { }

   實體和持久層採用jpa標準實現,在整個四層之中,已經有兩層的程式碼被自動完成了。  但是仍然有兩層的程式碼任然需要我們處理。 並且通常業務層的程式碼是最多,也最複雜。 但是儘管如此,任然存在大量相似的邏輯。  這幾個層之間的通訊也會因為實體標準的不同而各自不同。  因此首先要做的是統一標準。

   控制層與業務層的互動通過統一的標準: dto物件進行通訊。

package com.automannn.mainBottomItem.dto;


import java.util.List;

/**
 * @author [email protected]
 * @time 2018/10/21 20:33
 */
public abstract class BaseDto<T> {
   protected T data;

   protected List<T> dataList;

   protected DtoInfo dtoInfo;

   public BaseDto(DtoInfo dtoInfo) {
        this.dtoInfo = dtoInfo;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

    public List<T> getDataList() {
        return dataList;
    }

    public void setDataList(List<T> dataList) {
        this.dataList = dataList;
    }

    public DtoInfo getDtoInfo() {
        return dtoInfo;
    }

    public void setDtoInfo(DtoInfo dtoInfo) {
        this.dtoInfo = dtoInfo;
    }
}
package com.automannn.mainBottomItem.dto;

/**
 * @author [email protected]
 * @time 2018/10/22 14:12
 */
public enum DtoInfo {

    SUCCESS(200,"成功"),
    ERROR(1000,"失敗");

    private int state;

    private String message;

    DtoInfo(int state, String message){
        this.state=state;
        this.message=message;
    }

    public int getState() {
        return state;
    }

    public String getMessage() {
        return message;
    }
}

  這就使得有了一個比較固定的邏輯抽象: DTO,DAO,ENTITY。   業務層操作dao,返回dto,entity在整個層間作為dto的內部物件。

  於是可以將業務層的程式碼通過aop攔截實現,它的實現是這樣的:

package com.automannn.meimeijiong.going.service;

import com.automannn.meimeijiong.going.dto.BaseDto;
import org.springframework.data.domain.Pageable;

/**
 * @author [email protected]
 * @time 2018/10/24 16:02
 */
public interface IBaseService<T,S extends BaseDto<T>> {
    S add(T t,S s);
    S update(T t,S s);
    S delete(T t,S s);
    S queryOne(T t,S s);
    S queryList(T t, S s);
}
package com.automannn.meimeijiong.going.service.impl;

import com.automannn.meimeijiong.going.dao.LvpaiDao;
import com.automannn.meimeijiong.going.dao.LvpaiRoleDao;
import com.automannn.meimeijiong.going.dto.LvpaiDto;
import com.automannn.meimeijiong.going.entity.Lvpai;
import com.automannn.meimeijiong.going.service.ILvpaiService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * @author [email protected]
 * @time 2018/10/25 0:33
 */
@Service
public class LvpaiServiceImpl implements ILvpaiService {

    @Autowired
    LvpaiDao dao;
    @Override
    public LvpaiDto add(Lvpai lvpai, LvpaiDto lvpaiDto) { return null; }

    @Override
    public LvpaiDto update(Lvpai lvpai, LvpaiDto lvpaiDto) {
        return null;
    }

    @Override
    public LvpaiDto delete(Lvpai lvpai, LvpaiDto lvpaiDto) {
        return null;
    }

    @Override
    public LvpaiDto queryOne(Lvpai lvpai, LvpaiDto lvpaiDto) {
        return null;
    }

    @Override
    public LvpaiDto queryList(Lvpai lvpai, LvpaiDto lvpaiDto) {
        return null;
    }
}

它的攔截:

package com.automannn.meimeijiong.going.templateImplProxy;

import com.automannn.meimeijiong.going.dto.BaseDto;
import com.automannn.meimeijiong.going.dto.DtoInfo;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.data.domain.Example;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Optional;

/**
 * @author [email protected]
 * @time 2018/10/24 17:42
 */
@Component
@Aspect
public class AspectServiceImplInterceptor {

    private static String METHOD_NAME=null;
    private static BaseDto DTO =null;
    private static Object ENEITY=null;
    private static JpaRepository DAO=null;

    @Around("execution(* com.automannn.meimeijiong.going.service.impl.*.add*(..))||" +
            "execution(* com.automannn.meimeijiong.going.service.impl.*.update*(..))||" +
            "execution(* com.automannn.meimeijiong.going.service.impl.*.delete*(..))||" +
            "execution(* com.automannn.meimeijiong.going.service.impl.*.query*(..))")
    public Object doImpl(ProceedingJoinPoint pjp) throws NoSuchFieldException, IllegalAccessException {
        Signature signature = pjp.getSignature();
        METHOD_NAME= signature.getName();
        Object[] args= pjp.getArgs();
        ENEITY=args[0];
        DTO = (BaseDto) args[1];

        Class clazz = pjp.getTarget().getClass();

       Field field= clazz.getDeclaredField("dao");
       field.setAccessible(true);
        DAO= (JpaRepository) field.get(pjp.getTarget());

        Object target =null;
        if ("add".equals(METHOD_NAME)){
           target= doAddLogic(ENEITY, DTO,DAO);
        }else if ("update".equals(METHOD_NAME)){
            target= doUpdateLogic(ENEITY, DTO,DAO);
        }else if ("delete".equals(METHOD_NAME)){
            target= doDeleteLogic(ENEITY, DTO,DAO);
        }else if ("queryOne".equals(METHOD_NAME)){
            target=doQueryOneLogic(ENEITY, DTO,DAO);
        }else {
            target=doQueryListLogic(ENEITY, DTO,DAO);
        }
        return target;

    }

    private Object doAddLogic(Object entity,BaseDto returnValue,JpaRepository dao){
        Object saveCallbackBean= dao.save(entity);
        if (!isIdExisted(saveCallbackBean)){
            returnValue.setDtoInfo(DtoInfo.ERROR);
        }else {
            returnValue.setDtoInfo(DtoInfo.SUCCESS);
            returnValue.setData(saveCallbackBean);
        }

        return returnValue;
    }



    private Object doUpdateLogic(Object entity,BaseDto returnValue,JpaRepository dao){
        if (!isIdExisted(entity)){
            returnValue.setDtoInfo(DtoInfo.ERROR);
            return returnValue;
        }
        Object saveCallbackBean=dao.save(entity);
        returnValue.setDtoInfo(DtoInfo.SUCCESS);
        returnValue.setData(saveCallbackBean);

        return returnValue;
    }

    private Object doDeleteLogic(Object entity,BaseDto returnValue,JpaRepository dao){
        if (!isIdExisted(entity)){
            returnValue.setDtoInfo(DtoInfo.ERROR);
            return returnValue;
        }
        dao.delete(entity);
        returnValue.setDtoInfo(DtoInfo.SUCCESS);
        return returnValue;

    }

    private Object doQueryOneLogic(Object entity,BaseDto returnValue,JpaRepository dao){
        Optional optional = dao.findOne(Example.of(entity));
        try {
            Object result = optional.get();
            returnValue.setDtoInfo(DtoInfo.SUCCESS);
            returnValue.setData(result);
        }catch (NoSuchElementException e){
            returnValue.setDtoInfo(DtoInfo.ERROR);
        }
        return returnValue;
    }

    private Object doQueryListLogic(Object entity,BaseDto returnValue,JpaRepository dao){
        List<Object> resultList = dao.findAll(Example.of(entity));
        if (resultList==null){
            returnValue.setDtoInfo(DtoInfo.ERROR);
        }else {
            returnValue.setDtoInfo(DtoInfo.SUCCESS);
            returnValue.setDataList(resultList);
        }

        return returnValue;
    }

    private boolean isIdExisted(Object saveCallbackBean) {
        try {
            Field field = saveCallbackBean.getClass().getDeclaredField("id");
            field.setAccessible(true);
            int id = (int) field.get(saveCallbackBean);
            if (id>0){
                return true;
            }else {
                return false;
            }
        } catch (Exception e) {
            return false;
        }
    }

}

   控制層的程式碼類似,為了完成控制層的代理,需要將返回的資料型別也統一。  最終,當整個後臺系統的重複邏輯越多,程式碼量越大,那麼代理所起到的效果就越大。  如果要完成類似於框架的效果,可以通過配置+策略模式的方式進行擴充套件。  封裝不變,擴充套件可變。