したがって、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
型消去の前に処理します。私が見つけた、または思いついたすべての解決策は、私には醜いように見えるか、または単に不可解に機能することを拒否します。この問題を回避する最善の方法は何ですか?
コンストラクターを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;
}
次のように、複数の異なるSpringBeanを作成します。
@Service
class FooService extends GenericService<Foo, FooDao> {
}
@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アプローチを使用しています。
なぜ一般的なサービスが必要なのですか?サービスクラスは、複数のエンティティが関与する特定の作業単位を対象としています。リポジトリをコントローラに直接注入するだけです。
コンストラクター引数を持つジェネリックリポジトリの例を次に示します。代わりに各メソッドをジェネリックにし、コンストラクター引数を持たないようにすることもできます。ただし、各メソッド呼び出しには、パラメーターとしてクラスが必要です。
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;
そしてこれにより、Domainreposirotyを特定のエンティティ/メソッドにサブクラス化することができ、自動配線ができなくなります。
public class PersonRepository extends DomainRepository<Person> {
public PersonRepository(){
super(Person.class);
}
...
これが最も近い解決策です。特殊なDAOは、ビジネス層で注釈が付けられます。 OPからの質問のように、最善の努力は、EntityDAO汎用テンプレート自体に注釈付きのDAOを含めることです。型消去では、特殊な型情報が春の工場に渡されないようです[すべての特殊な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
}
この質問では、オートワイヤーとは何かを理解する必要があります。一般的に言えば、autowireを使用して、Webアプリのデプロイ時にオブジェクトインスタンス/ Beanを作成すると言えます。したがって、同じ名前の複数の場所で自動配線を宣言するかどうかという質問に進みます。次に、このエラーが発生します。自動配線は複数の方法で実行できるため、複数のタイプの自動配線手法を使用している場合は、このエラーが発生する可能性もあります。
これらのジェネリックスを拡張するクラスでは自動配線を使用する必要があります
完全なジェネリックソリューション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
}