泛型 Dao、服务层和多个匹配的 bean
Posted
技术标签:
【中文标题】泛型 Dao、服务层和多个匹配的 bean【英文标题】:Generic Dao, service layer and multiple matching beans 【发布时间】:2013-11-15 16:58:32 【问题描述】:我正在编写一个 Spring MVC 3.2.3 和 Hibernate 4.2.1 应用程序,刚刚开始它的架构。我决定使用 Generic Dao 模式,因为我将使用很多常见的 CRUD 操作。我知道 JPA 已经存在了一段时间,但我真的很想使用通用 dao 模式来实现这一点。 对于问题本身。使用一个实体进行设置和测试时一切正常。但是当我添加第二个时,我开始看到错误:
“没有定义 [com.segurosweb.daos.GenericDao] 类型的合格 bean:预期的单个匹配 bean,但找到了 2:cobradorDaoImpl,productorDaoImpl”。
我知道这是因为Spring在启动时无法判断要注入哪个组件,但我不知道如何解决这个问题。
我使用的通用 DAO 实现是:
GenericDao.java
package com.segurosweb.daos;
import java.util.List;
public interface GenericDao<T>
public void saveOrUpdate(T dom);
public List<T> getAll();
public T get(long id);
GenericDaoImpl.java
package com.segurosweb.daos;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
@SuppressWarnings("unchecked")
@Repository
public abstract class GenericDaoImpl<T> implements GenericDao<T>
private Class<T> type;
@Autowired
private SessionFactory sessionFactory;
@SuppressWarnings("rawtypes")
public GenericDaoImpl()
Type t = getClass().getGenericSuperclass();
ParameterizedType pt = (ParameterizedType) t;
type = (Class) pt.getActualTypeArguments()[0];
public Session getCurrentSession()
return sessionFactory.getCurrentSession();
... all other implementation...
CobradorDao.java
package com.segurosweb.daos;
import com.segurosweb.entities.Cobrador;
public interface CobradorDao extends GenericDao<Cobrador>
CobradorDaoImpl.java
package com.segurosweb.daos;
import org.springframework.stereotype.Repository;
import com.segurosweb.entities.Cobrador;
@Repository
public class CobradorDaoImpl extends GenericDaoImpl<Cobrador> implements CobradorDao
GenericService.java
package com.segurosweb.service;
import java.util.List;
public interface GenericService<T>
public void saveOrUpdate(T dom);
public List<T> getAll();
public T get(long id);
GenericServiceImpl.java
package com.segurosweb.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import com.segurosweb.daos.GenericDao;
public class GenericServiceImpl<T> implements GenericService<T>
@Autowired
private GenericDao<T> tDao;
@Override
public void saveOrUpdate(T dom)
tDao.saveOrUpdate(dom);
@Override
public List<T> getAll()
return tDao.getAll();
@Override
public T get(long id)
return tDao.get(id);
CobradorService.java
package com.segurosweb.service;
import com.segurosweb.entities.Cobrador;
public interface CobradorService extends GenericService<Cobrador>
CobradorServiceImpl.java
package com.segurosweb.service;
import org.springframework.stereotype.Service;
import com.segurosweb.entities.Cobrador;
@Service
public class CobradorServiceImpl extends GenericServiceImpl<Cobrador> implements CobradorService
还有我的控制器,它有一个非常简单的 url 映射来测试一切是否正常工作(或不工作!)
package com.segurosweb.controllers;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
import com.segurosweb.entities.Cobrador;
import com.segurosweb.service.CobradorService;
@Controller
@RequestMapping("/cobradores/**")
public class CobradorController
static final Logger log = LogManager.getLogger(CobradorController.class.getSimpleName());
@Autowired
private CobradorService cobServ;
@RequestMapping(value="view.html",method = RequestMethod.GET)
public ModelAndView setUpForm(ModelMap model)
log.info("cobradores/view.html hitted.");
cobServ.saveOrUpdate(new Cobrador("emi","lio","hola","321"));
return new ModelAndView("/secure/Productores");
Cobrador.java 是一个非常简单的 POJO 类,使用 @Entity 注解。
我还有另一个实体的匹配接口和实现,称为 Productor:ProductorDao、ProductorDaoImpl、ProductorService 和 ProductorServiceImpl。
我得到的确切错误是:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'cobradorController': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.segurosweb.service.CobradorService com.segurosweb.controllers.CobradorController.cobServ; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'cobradorServiceImpl': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.segurosweb.daos.GenericDao com.segurosweb.service.GenericServiceImpl.tDao; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [com.segurosweb.daos.GenericDao] is defined: expected single matching bean but found 2: cobradorDaoImpl,productorDaoImpl
那么,我在这里遗漏了一些重要的东西吗?我现在想坚持这种通用 DAO 模式,除非你们告诉我这是非常糟糕的主意。
提前感谢你们能给我的任何帮助!
【问题讨论】:
【参考方案1】:对于初学者,我会放弃通用 DAO 并切换到 Spring Data JPA,不要尝试重新发明***,这样你就不用编写另一种内部框架了。
关于通用服务,或者更好的服务方法,通常一对一地调用你的 dao 上的相应方法,恕我直言,这是一种设计味道。您的服务应提供要解决的业务案例/用例。像saveOrUpdate
这样的方法并不是真正具有商业意义的东西。在这些情况下,我可能会直接调用存储库(仅仅为了该层而添加另一层是不明智的恕我直言)。
有一个问题/博客基本上得出了相同的结论(我只是找不到它了,这是 Spring Data JPA 的维护者 Oliver Gierke 的评论。
问题是Spring 4.0之前的版本存在依赖注入和泛型接口的问题,这个在Spring 4.0中一直是solved
链接:
Spring Data Jpa 春天 4.0 release post【讨论】:
我阅读了更多关于 Spring Data JPA 的内容,你说得对,通用 DAO “嵌入”了。我还检查了 Spring 4.0,事实上,它对注入泛型接口的方式进行了更正。所以这个事实将我的设计问题减少到 2 个选项,移动到 Spring Data JPA,或者等待 Spring 4.0 发布。非常感谢。【参考方案2】:在 Spring 3x 版本中,即使您使用了泛型类型,识别适当的 bean 也是一个问题。 Spring 4 已经解决了这个错误。所以根据我的说法,你必须使用 @Qualifier 注释来告诉 spring 容器你要注入哪个具有相同父类型的 bean。
@Repository("cobradorDao")
@Repository("productorDao")
@Autowire
@Qualifier("cobradorDao")
CobradorDao cobradorDao;
@Autowire
@Qualifier("productorDao")
ProductorDao productorDao;
希望对你有帮助。
【讨论】:
【参考方案3】:你应该被这个守则修复。
GenericServiceImpl.java
package com.segurosweb.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import com.segurosweb.daos.GenericDao;
public class GenericServiceImpl<T> implements GenericService<T>
private GenericDao<T> tDao;
public GenericServiceImpl(GenericDao<T> tDao)
this.tDao= tDao;
@Override
public void saveOrUpdate(T dom)
tDao.saveOrUpdate(dom);
@Override
public List<T> getAll()
return tDao.getAll();
@Override
public T get(long id)
return tDao.get(id);
CobradorServiceImpl.java
package com.segurosweb.service;
import org.springframework.stereotype.Service;
import com.segurosweb.entities.Cobrador;
@Service
public class CobradorServiceImpl extends GenericServiceImpl<Cobrador> implements CobradorService
@Autowired
public CobradorServiceImpl(CobradorDao tDao)
super(tDao);
【讨论】:
以上是关于泛型 Dao、服务层和多个匹配的 bean的主要内容,如果未能解决你的问题,请参考以下文章