所以我在Spring 3.2中有很多泛型,理想情况下我的架构看起来像这样。
class GenericDao<T>{}
class GenericService<T, T_DAO extends GenericDao<T>>
{
// FAILS
@Autowired
T_DAO;
}
@Component
class Foo{}
@Repository
class FooDao extends GenericDao<Foo>{}
@Service
FooService extends GenericService<Foo, FooDao>{}
不幸的是,对于泛型的多个实现,自动装配会引发有关多个匹配bean定义的错误。我认为这是因为@Autowired
在类型擦除之前处理。我找到或想出的每一个解决方案看起来都很难看,或者只是莫名其妙地拒绝工作。解决这个问题的最佳方法是什么?
答案 0 :(得分:25)
如何将构造函数添加到GenericService
并将自动装配移动到扩展类,例如
class GenericService<T, T_DAO extends GenericDao<T>> {
private final T_DAO tDao;
GenericService(T_DAO tDao) {
this.tDao = tDao;
}
}
@Service
FooService extends GenericService<Foo, FooDao> {
@Autowired
FooService(FooDao fooDao) {
super(fooDao);
}
}
<强>更新强>
从Spring 4.0 RC1开始,可以基于泛型类型进行自动装配,这意味着您可以编写通用服务,例如
class GenericService<T, T_DAO extends GenericDao<T>> {
@Autowired
private T_DAO tDao;
}
并创建多个不同的Spring bean,如:
@Service
class FooService extends GenericService<Foo, FooDao> {
}
答案 1 :(得分:4)
您可以使用@PostConstruct和ServiceLocatorFactoryBean删除@autowire注释并执行延迟的“autowire”。
您的GenericService看起来与此类似
public class GenericService<T, T_DAO extends GenericDao<T>>{
@Autowired
private DaoLocator daoLocatorFactoryBean;
//No need to autowried, autowireDao() will do this for you
T_DAO dao;
@SuppressWarnings("unchecked")
@PostConstruct
protected void autowireDao(){
//Read the actual class at run time
final Type type;
type = ((ParameterizedType) getClass().getGenericSuperclass())
.getActualTypeArguments()[1];
//figure out the class of the fully qualified class name
//this way you can know the bean name to look for
final String typeClass = type.toString();
String daoName = typeClass.substring(typeClass.lastIndexOf('.')+1
,typeClass.length());
daoName = Character.toLowerCase(daoName.charAt(0)) + daoName.substring(1);
this.dao = (T_DAO) daoLocatorFactoryBean.lookup(daoName);
}
daoLocatorFactoryBean为你带来了魔力 为了使用它,您需要添加类似于下面的界面:
public interface DaoLocator {
public GenericDao<?> lookup(String serviceName);
}
您需要将以下代码段添加到applicationContext.xml
<bean id="daoLocatorFactoryBean"
class="org.springframework.beans.factory.config.ServiceLocatorFactoryBean">
<property name="serviceLocatorInterface"
value="org.haim.springframwork.stackoverflow.DaoLocator" />
</bean>
这是一个很好的技巧,它可以节省很少的样板类 B.T.W我不认为这个样板代码是一个大问题,我工作的项目使用matsev方法。
答案 2 :(得分:3)
这是最接近的解决方案。专业DAO在业务层注释。与OP的问题一样,最好的努力是在EntityDAO通用模板本身中使用带注释的DAO。类型擦除似乎不允许将专用类型信息传递到spring工厂[导致报告来自所有专用DAO的匹配bean]
public class EntityDAO<T>
{
@Autowired
SessionFactory factory;
public Session getCurrentSession()
{
return factory.getCurrentSession();
}
public void create(T record)
{
getCurrentSession().save(record);
}
public void update(T record)
{
getCurrentSession().update(record);
}
public void delete(T record)
{
getCurrentSession().delete(record);
}
public void persist(T record)
{
getCurrentSession().saveOrUpdate(record);
}
public T get(Class<T> clazz, Integer id)
{
return (T) getCurrentSession().get(clazz, id);
}
}
public abstract class EntityBusinessService<T>
implements Serializable
{
public abstract EntityDAO<T> getDAO();
//Rest of code.
}
@Transactional
@Repository
public class UserDAO
extends EntityDAO<User>
{
}
@Transactional
@Service
@Scope("prototype")
public class UserBusinessService
extends EntityBusinessService<User>
{
@Autowired
UserDAO dao;
@Override
public EntityDAO<User> getDAO()
{
return dao;
}
//Rest of code
}
答案 3 :(得分:3)
为什么要通用服务?服务类适用于涉及多个实体的特定工作单元。您可以直接将存储库注入控制器。
以下是带有构造函数参数的通用存储库的示例,您也可以将每个方法设置为Generic而不具有构造函数参数。但是每个方法调用都需要class作为参数:
public class DomainRepository<T> {
@Resource(name = "sessionFactory")
protected SessionFactory sessionFactory;
public DomainRepository(Class genericType) {
this.genericType = genericType;
}
@Transactional(readOnly = true)
public T get(final long id) {
return (T) sessionFactory.getCurrentSession().get(genericType, id);
}
通用存储库的bean定义示例 - 您可以使用不同的contstructor args来拥有多个不同的bean。
<bean id="tagRepository" class="com.yourcompnay.data.DomainRepository">
<constructor-arg value="com.yourcompnay.domain.Tag"/>
</bean>
使用资源注释对bean进行深度注入
@Resource(name = "tagRepository")
private DomainRepository<Tag> tagRepository;
这样就可以将Domainreposiroty子类化为特定的实体/方法,这些实体/方法可以自动装配:
public class PersonRepository extends DomainRepository<Person> {
public PersonRepository(){
super(Person.class);
}
...
答案 4 :(得分:1)
对于这个问题,我们需要了解autowire是什么。通常我们可以说,通过autowire,我们在部署Web应用程序时创建了一个对象实例/ bean。因此,如果您在同一个名称的多个地方声明自动装配,那么现在回答这个问题。然后出现这个错误。自动装配可以通过多种方式完成,因此如果您使用多种类型的自动装配技术,那么也可能会出现此错误。
答案 5 :(得分:1)
您应该在扩展这些泛型的类中使用自动装配
答案 6 :(得分:1)
完整的通用解决方案使用Spring 4 :
@Component
class Foo{
}
@Component
class Bar{
}
interface GenericDao<T>{
//list of methods
}
class GenericDaoImpl<T> implements GenericDao<T>{
@Autowired
SessionFactory factory;
private Class<T> domainClass; // Get Class Type of <T>
public Session getCurrentSession(){
return factory.getCurrentSession();
}
public DaoImpl() {
this.domainClass = (Class<T>) GenericTypeResolver.resolveTypeArgument(getClass(), DaoImpl.class);
}
//implementation of methods
}
interface FooDao extends GenericDao<Foo>{
//Define extra methods if required
}
interface BarDao extends GenericDao<Bar>{
//Define extra methods if required
}
@Repository
class FooDao extends GenericDaoImpl<Foo> implements FooDao{
//implementation of extra methods
}
@Repository
class BarDao extends GenericDaoImpl<Bar> implements BarDao{
//implementation of extra methods
}
interface GenericService<T>{
//List of methods
}
class GenericServiceImpl<T> implements GenericService<T>{
@Autowire
protected GenericDao<T> dao; //used to access DAO layer
}
class FooService extends GenericService<Foo>{
//Add extra methods of required
}
class BarService extends GenericService<Bar>{
//Add extra methods of required
}
@Service
class FooServiceImpl extends GenericServiceImpl<Foo> implements GenericService<Foo>{
//implementation of extra methods
}
@Service
class BarServiceImpl extends GenericServiceImpl<Bar> implements GenericService<Bar>{
//implementation of extra methods
}