So I have a number of generics in Spring 3.2 and ideally my architecture would look something like this.
所以我在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>{}
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?
遗憾的是,对于泛型的多个实现,自动装配会引发有关多个匹配bean定义的错误。我认为这是因为@Autowired进程在类型擦除之前。我找到或想出的每一个解决方案看起来都很难看,或者只是莫名其妙地拒绝工作。解决这个问题的最佳方法是什么?
7 个解决方案
#1
25
How about adding a constructor to the GenericService
and move the autowiring to the extending class, e.g.
如何将构造函数添加到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);
}
}
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
从Spring 4.0 RC1开始,可以基于泛型类型进行自动装配,这意味着您可以编写类似的通用服务
class GenericService<T, T_DAO extends GenericDao<T>> {
@Autowired
private T_DAO tDao;
}
and create multiple different Spring beans of it like:
并创建多个不同的Spring bean,如:
@Service
class FooService extends GenericService<Foo, FooDao> {
}
#2
4
You can remove the @autowire annotation and perform delayed “autowire” using @PostConstruct and ServiceLocatorFactoryBean.
Your GenericService will look similar to this
您可以使用@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 does the magic for you.
In order to use it you need to add an interface similar to the one below:
daoLocatorFactoryBean为您带来了魔力。要使用它,您需要添加类似下面的界面:
public interface DaoLocator {
public GenericDao<?> lookup(String serviceName);
}
You need to add the following snippet to your applicationContext.xml
您需要将以下代码段添加到applicationContext.xml
<bean id="daoLocatorFactoryBean"
class="org.springframework.beans.factory.config.ServiceLocatorFactoryBean">
<property name="serviceLocatorInterface"
value="org.haim.springframwork.*.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.
这是一个很好的技巧,它将为您节省很少的样板类。 B.T.W我不认为这个样板代码是一个大问题,我工作的项目使用matsev方法。
#3
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]
这是最接近的解决方案。专业DAO在业务层注释。与OP的问题一样,最好的努力是在EntityDAO通用模板本身中添加带注释的DAO。类型擦除似乎不允许将专用类型信息传递到spring工厂[导致报告来自所有专用DAO的匹配bean]
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
3
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:
下面是一个带有构造函数参数的通用存储库的示例,您也可以使每个方法都是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);
}
Example of bean definition for the generic repository - you could have multple different beans, using different contstructor args.
通用存储库的bean定义示例 - 您可以使用不同的contstructor args来拥有多个不同的bean。
<bean id="tagRepository" class="com.yourcompnay.data.DomainRepository">
<constructor-arg value="com.yourcompnay.domain.Tag"/>
</bean>
Depdncy injection of bean using resource annotation
使用资源注释对bean进行Depdncy注入
@Resource(name = "tagRepository")
private DomainRepository<Tag> tagRepository;
And this allows the Domainreposiroty to be subclassed for specific entities/methods, which woul dallow autowiring :
这样就可以将Domainreposiroty子类化为特定的实体/方法,这些实体/方法不会自动装配:
public class PersonRepository extends DomainRepository<Person> {
public PersonRepository(){
super(Person.class);
}
...
#5
1
You should use autowiring in classes which extends these generics
您应该在扩展这些泛型的类中使用自动装配
#6
1
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.
对于这个问题,人们需要了解autowire是什么。通常我们可以说,通过autowire,我们在部署Web应用程序时创建了一个对象实例/ bean。所以现在回答一下这个问题,如果你在同一个地方宣布自动装配。然后出现这个错误。自动装配可以通过多种方式完成,因此如果您使用多种类型的自动装配技术,那么也可能会出现此错误。
#7
1
Complete Generic Solution using Spring 4:
使用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
}
#1
25
How about adding a constructor to the GenericService
and move the autowiring to the extending class, e.g.
如何将构造函数添加到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);
}
}
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
从Spring 4.0 RC1开始,可以基于泛型类型进行自动装配,这意味着您可以编写类似的通用服务
class GenericService<T, T_DAO extends GenericDao<T>> {
@Autowired
private T_DAO tDao;
}
and create multiple different Spring beans of it like:
并创建多个不同的Spring bean,如:
@Service
class FooService extends GenericService<Foo, FooDao> {
}
#2
4
You can remove the @autowire annotation and perform delayed “autowire” using @PostConstruct and ServiceLocatorFactoryBean.
Your GenericService will look similar to this
您可以使用@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 does the magic for you.
In order to use it you need to add an interface similar to the one below:
daoLocatorFactoryBean为您带来了魔力。要使用它,您需要添加类似下面的界面:
public interface DaoLocator {
public GenericDao<?> lookup(String serviceName);
}
You need to add the following snippet to your applicationContext.xml
您需要将以下代码段添加到applicationContext.xml
<bean id="daoLocatorFactoryBean"
class="org.springframework.beans.factory.config.ServiceLocatorFactoryBean">
<property name="serviceLocatorInterface"
value="org.haim.springframwork.*.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.
这是一个很好的技巧,它将为您节省很少的样板类。 B.T.W我不认为这个样板代码是一个大问题,我工作的项目使用matsev方法。
#3
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]
这是最接近的解决方案。专业DAO在业务层注释。与OP的问题一样,最好的努力是在EntityDAO通用模板本身中添加带注释的DAO。类型擦除似乎不允许将专用类型信息传递到spring工厂[导致报告来自所有专用DAO的匹配bean]
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
3
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:
下面是一个带有构造函数参数的通用存储库的示例,您也可以使每个方法都是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);
}
Example of bean definition for the generic repository - you could have multple different beans, using different contstructor args.
通用存储库的bean定义示例 - 您可以使用不同的contstructor args来拥有多个不同的bean。
<bean id="tagRepository" class="com.yourcompnay.data.DomainRepository">
<constructor-arg value="com.yourcompnay.domain.Tag"/>
</bean>
Depdncy injection of bean using resource annotation
使用资源注释对bean进行Depdncy注入
@Resource(name = "tagRepository")
private DomainRepository<Tag> tagRepository;
And this allows the Domainreposiroty to be subclassed for specific entities/methods, which woul dallow autowiring :
这样就可以将Domainreposiroty子类化为特定的实体/方法,这些实体/方法不会自动装配:
public class PersonRepository extends DomainRepository<Person> {
public PersonRepository(){
super(Person.class);
}
...
#5
1
You should use autowiring in classes which extends these generics
您应该在扩展这些泛型的类中使用自动装配
#6
1
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.
对于这个问题,人们需要了解autowire是什么。通常我们可以说,通过autowire,我们在部署Web应用程序时创建了一个对象实例/ bean。所以现在回答一下这个问题,如果你在同一个地方宣布自动装配。然后出现这个错误。自动装配可以通过多种方式完成,因此如果您使用多种类型的自动装配技术,那么也可能会出现此错误。
#7
1
Complete Generic Solution using Spring 4:
使用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
}