【问题标题】:Spring 3.2 Autowire generic typesSpring 3.2 Autowire 泛型类型
【发布时间】:2013-01-16 21:38:23
【问题描述】:

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

【问题讨论】:

  • 您是否尝试将 @Autowired 注释移动到 setter?
  • 您是否尝试过使用带有注释的@Qualifier 来指定您想要的实现版本?
  • 你遇到type erasure。这些问题可能有用:(1)(2)
  • 请注意:泛型只是编译糖。它们在运行时不存在。所以当你需要构造你的对象时,你没有任何关于这个的信息......

标签: java generics spring-mvc inversion-of-control autowired


【解决方案1】:

如何在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> {
}

【讨论】:

【解决方案2】:

这是最接近的解决方案。专门的 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
}

【讨论】:

    【解决方案3】:

    您可以删除 @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);           
     }    
    

    您需要将以下 sn-p 添加到您的 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 方法。

    【讨论】:

      【解决方案4】:

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

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

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

      【讨论】:

        【解决方案5】:

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

        【讨论】:

          【解决方案6】:

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

          【讨论】:

            【解决方案7】:

            完整的通用解决方案使用 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
            }
            

            【讨论】:

              猜你喜欢
              • 2017-05-21
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2019-05-30
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多