在spring-framework中实现20 @ RestController的最佳方法,无需重复(DRY)

时间:2015-05-23 02:16:02

标签: java spring spring-data

实施约20个休息控制器&服务,我发现自己很多代码都被重复了,所以我想出了这个装置。

CrudController.java     包app.controllers;

import org.springframework.data.repository.CrudRepository;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import app.models.Model;
import app.services.CrudService;

@RestController
public abstract class CrudController<M extends Model, S extends CrudService<M, ? extends CrudRepository<M,Long>>> {
    S service;

    public abstract void setService(S service);
    public abstract Boolean isAuthorized(Long entityId, S service);

    @RequestMapping(value="/create", method = RequestMethod.POST)
    public M create(M object) {
        if(isAuthorized(object.getId(), service)) {
            return service.save(object);
        }
        logUnauthorizedAccess();
        return null;
    }

    @RequestMapping(value="/update", method = RequestMethod.POST)
    public M update(M object) {
        if(isAuthorized(object.getId(), service)) {
            return service.update(object);
        }
        logUnauthorizedAccess();
        return null;
    }

    @RequestMapping(value="/delete", method = RequestMethod.POST)
    public Boolean delete(Long id) {
        if(isAuthorized(id, service)) {
            return service.delete(id);
        }
        logUnauthorizedAccess();
        return null;
    }

    @RequestMapping(value="/get", method = RequestMethod.GET)
    public @ResponseBody M get(Long id) {
        if(isAuthorized(id, service)) {
            return service.get(id);
        }
        logUnauthorizedAccess();
        return null;
    }


    @RequestMapping(value="/json", method = RequestMethod.GET)
    public @ResponseBody Iterable<M> json(ModelMap map) {
        return service.getAll();
    }

    private void logUnauthorizedAccess() {
        System.out.println("!!UN-AUTHORIZED ACCESS DETECTED!!");
    }
}

CrudService.java

package app.services;

import org.springframework.data.repository.CrudRepository;


public abstract class CrudService<M extends app.models.Model, R extends CrudRepository<M, Long>> {
    R repo;

    public abstract void setRepo(R repo);

    /**
     * Define the parameters that you want to save to the DB when calling the update() method
     * @param from source object
     * @param to DB object that gets saves, "return to" in this method
     * @return
     */
    public abstract M copy(M from, M to);

    public Iterable<M> getAll() {
        return this.repo.findAll();
    }

    /**
     * Mainly used to create a new entity
     * however, can also be used to save something without using the
     * update() method.
     * @param model
     * @return saved entity model
     */
    public M save(M model) {
        return this.repo.save(model);
    }

    public M get(Long id) {
        return this.repo.findOne(id);
    }

    public M update(M model) {
        M updated = this.repo.findOne(model.getId());
        updated = copy(model, updated);
        return this.repo.save(updated);
    }

    public Boolean delete(Long id) {
        this.repo.delete(id);
        return true;
    }
}

Model.java

package app.models;

import java.sql.Timestamp;
import java.util.Date;

import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
import javax.persistence.PrePersist;
import javax.persistence.Version;

@MappedSuperclass
public abstract class Model {
    @GeneratedValue
    @Id
    private Long id;

    private Date dateCreated;

    @Version
    private Timestamp dateModified;

    @PrePersist
    void createdAt() {
        this.setDateCreated(new Date());
    }

    public Long getId() {
        return id;
    }

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

    public Date getDateCreated() {
        return dateCreated;
    }

    public void setDateCreated(Date dateCreated) {
        this.dateCreated = dateCreated;
    }

    public Timestamp getDateModified() {
        return dateModified;
    }

    public void setDateModified(Timestamp dateModified) {
        this.dateModified = dateModified;
    }
}

现在我的所有控制器在给我的时候看起来像下面这样。 / create / get / update / delete / json

package app.controllers;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import app.models.Sample;
import app.services.SampleService;

@RestController
@RequestMapping("/sample")
public class SampleController extends CrudController<Sample, SampleService> {

    @Autowired
    @Override
    public void setService(SampleService service) {
        this.service = service;
    }

    @Override
    public Boolean isAuthorized(Long entityId, SampleService service) {
        return true;
    }
}

https://github.com/ddalcu/spring-starter/commit/27fb2a0719c4780d7cf648852d93b8fd3d8759c8

你们怎么想,好坏,更好的方法呢?

2 个答案:

答案 0 :(得分:4)

您应该查看Spring Data REST

答案 1 :(得分:1)

Spring能够处理泛型:

你真的不需要为每个CrudController创建Model的子类,只要内部的实现没有改变。

例如,您可以使用一个使用PathVariable的控制器。

然后,CrudServiceDao也有同样的可能性。

如果你现在看到对于某些实体你需要在这个控制器(或服务,dao)中进行特殊处理,你至少有两种方法我知道:

  • 使用controller,service和dao为此实体类型创建一个完整的新路由。
  • 不要简单地在crudcontroller中使用crudservice,而是自动装配列表(或地图)并查找正确的服务以继续路由。

第二层或第三层的分叉主要取决于你需要有特殊可能性的地方。

我已经看过两种方法,目前我在春季项目中使用了这种方法。 由于调度程序servlet根据您拥有的变量排序请求映射,因此只需使用专用控制器的pathvariable对代码进行硬编码即可轻松创建更具体的映射。