Spring 3.2 Autowire generic types

匿名 (未验证) 提交于 2019-12-03 02:45:02

问题:

So I have a number of generics in Spring 3.2 and ideally my architecture would look something like this.

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

Unfortunately with multiple implementations of the generics the autowiring throws an error about multiple matching bean definitions. I assume this is because @Autowired processes before type erasure. Every solution I've found or come up with looks ugly to me or just inexplicably refuses to work. What is the best way around this problem?

回答1:

How about adding a constructor to the GenericService and move the autowiring to the extending class, e.g.

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

Update:

As of Spring 4.0 RC1, it is possible to autowire based on generic type, which means that you can write a generic service like

class GenericService<T, T_DAO extends GenericDao<T>> {      @Autowired     private T_DAO tDao; } 

and create multiple different Spring beans of it like:

@Service class FooService extends GenericService<Foo, FooDao> { } 


回答2:

You can remove the @autowire annotation and perform delayed “autowire” using @PostConstruct and ServiceLocatorFactoryBean.
Your GenericService will look similar to this

    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 does the magic for you.
In order to use it you need to add an interface similar to the one below:

 public interface DaoLocator {         public GenericDao<?> lookup(String serviceName);             }     

You need to add the following snippet to your applicationContext.xml

  <bean id="daoLocatorFactoryBean"        class="org.springframework.beans.factory.config.ServiceLocatorFactoryBean">       <property name="serviceLocatorInterface"               value="org.haim.springframwork.stackoverflow.DaoLocator" />     </bean> 

This is a nice trick and it will save you little boilerplate classes.
B.T.W I do not see this boilerplate code as a big issue and the project I working for uses matsev approach.



回答3:

Here is a closest solution. The specialized DAOs are annotated at the business layer. As in the question from OP, the best effort would be having an annotated DAO in the EntityDAO generic template itself. Type erasure seems to be not allowing the specialized type information to get passed onto the spring factories [resulting in reporting matching beans from all the specialized DAOs]

The Generic Entity DAO template

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

The Generic Entity Based Business Layer Template

public abstract class EntityBusinessService<T> implements Serializable {     public abstract EntityDAO<T> getDAO();      //Rest of code. } 

An Example Specialized Entity DAO

@Transactional @Repository public class UserDAO extends EntityDAO<User> { } 

An Example Specialized Entity Business Class

@Transactional @Service @Scope("prototype") public class UserBusinessService extends EntityBusinessService<User> {     @Autowired     UserDAO dao;      @Override     public EntityDAO<User> getDAO()      {         return dao;     }      //Rest of code } 


回答4:

Why do you want a generic service ? Service classes are meant for specific units of work involving multple entities. You can just inject a repository straight into a controller.

Here is an example of generic repository with constructor argument, you could also make each method Generic instead and have no constructor argument. But each method call would require class as parameter:

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

Example of bean definition for the generic repository - you could have multple different beans, using different contstructor args.

<bean id="tagRepository" class="com.yourcompnay.data.DomainRepository">         <constructor-arg value="com.yourcompnay.domain.Tag"/> </bean> 

Depdncy injection of bean using resource annotation

@Resource(name = "tagRepository") private DomainRepository<Tag> tagRepository; 

And this allows the Domainreposiroty to be subclassed for specific entities/methods, which woul dallow autowiring :

public class PersonRepository extends DomainRepository<Person> {     public PersonRepository(){         super(Person.class);     }     ... 


回答5:

You should use autowiring in classes which extends these generics



回答6:

For this question one needs to understand about what autowire is. In common terms we can say that through autowire we create a object instance/bean at the time of deployment of the web app. So now going with the question if you are declaring autowiring in multiple places with the same name. Then this error comes. Autowiring can be done in multiple ways so if you are using multiple type of autowiring technique, then also one could get this error.



回答7:

Complete Generic Solution using Spring 4:


Domain Class

@Component class Foo{ }  @Component class Bar{ } 

DAO Layer

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 } 

Service Layer

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 } 


标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!