我想自己大概是发春期又到了,确实在夜深人静的时候想找人倾诉! 倾诉自己的担忧,分享自己的喜与乐。不知道这是因为什么,也许是看了教父之后,让我对人生有了一些重新的思考。 再坚强的人也有脆弱的时候,所有的坚强都是为了要保护的人,包括保护自己。 抑或许,只是自己年龄到了,荷尔蒙的作用使自己希望寻找到一个伴侣。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 automannn@163.com
* @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 automannn@163.com
* @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 automannn@163.com
* @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 automannn@163.com
* @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 automannn@163.com
* @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 automannn@163.com
* @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;
}
}
}
控制层的代码类似,为了完成控制层的代理,需要将返回的数据类型也统一。 最终,当整个后台系统的重复逻辑越多,代码量越大,那么代理所起到的效果就越大。 如果要完成类似于框架的效果,可以通过配置+策略模式的方式进行扩展。 封装不变,扩展可变。