27

所以我在 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类型擦除之前的过程。我发现或提出的每个解决方案对我来说都很难看,或者只是莫名其妙地拒绝工作。解决这个问题的最佳方法是什么?

4

7 回答 7

26

如何添加一个构造函数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> {
}
于 2013-03-30T20:30:38.670 回答
4

这是一个最接近的解决方案。专门的 DAO 在业务层进行注释。正如 OP 提出的问题一样,最好的办法是在 EntityDAO 通用模板本身中添加一个带注释的 DAO。类型擦除似乎不允许将专用类型信息传递给 spring 工厂 [导致报告来自所有专用 DAO 的匹配 bean]

通用实体 DAO 模板

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.
}

一个示例专业实体 DAO

@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
}
于 2013-04-01T03:25:16.650 回答
4

您可以删除 @autowire 注释并使用 @PostConstruct 和 ServiceLocatorFactoryBean 执行延迟的“自动装配”。
您的 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>

这是一个不错的技巧,它将为您节省一些样板类。
顺便说一句,我不认为这个样板代码是一个大问题,我工作的项目使用 matsev 方法。

于 2013-04-02T11:20:30.440 回答
3

为什么需要通用服务?服务类适用于涉及多个实体的特定工作单元。您可以直接将存储库注入控制器。

这是带有构造函数参数的通用存储库的示例,您也可以将每个方法设为通用而不具有构造函数参数。但是每个方法调用都需要类作为参数:

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 定义示例 - 您可以拥有多个不同的 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;

这允许域存储库被子类化为特定的实体/方法,这将允许自动装配:

public class PersonRepository extends DomainRepository<Person> {
    public PersonRepository(){
        super(Person.class);
    }
    ...
于 2013-04-05T12:48:15.000 回答
1

对于这个问题,需要了解什么是自动接线。通俗地说,我们可以说通过自动装配,我们在部署 Web 应用程序时创建了一个对象实例/bean。因此,如果您要在多个具有相同名称的地方声明自动装配,那么现在解决这个问题。然后这个错误来了。自动装配可以通过多种方式完成,因此如果您使用多种类型的自动装配技术,那么也可能会出现此错误。

于 2013-04-05T12:38:38.503 回答
1

您应该在扩展这些泛型的类中使用自动装配

于 2013-04-06T20:12:06.627 回答
1

使用 Spring 4 的完整通用解决方案:


领域类

@Component
class Foo{
}

@Component
class Bar{
}

DAO 层

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
}
于 2015-05-20T20:12:18.537 回答