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