2010-10-04 12 views
9

Tôi biết điều này là rất thường được hỏi, nhưng tôi không thể tìm thấy một giải pháp làm việc:Mẫu DAO trừu tượng và sự cố "Proxy không thể được truyền tới ..." của Spring!

Đây là AbstractDAO tôi:

public interface AbstractDao<T> 
{ 
    public T get(Serializable id); 
    //other CRUD operations 
} 

Và đây là thực hiện JPA của tôi:

public abstract class AbstractDaoJpaImpl<T> implements AbstractDao<T> , Serializable 
{ 
    protected EntityManager em; 

    protected Class<T> clazz; 

    @SuppressWarnings("unchecked") 
    public AbstractDaoJpaImpl() 
    { 
    ParameterizedType genericSuperclass = (ParameterizedType) getClass().getGenericSuperclass(); 
    this.clazz = (Class<T>) genericSuperclass.getActualTypeArguments()[0]; 
    } 

    public abstract void setEntityManager(EntityManager em); 
    //implementations skipped 
} 

Và đây là dao của một thực thể:

public interface PersonDao extends AbstractDao<Person> 
{ 
    //empty 
} 

Đây là thực hiện nó:

@Repository 
public class PersonDaoImpl extends AbstractDaoJpaImpl<Person> implements PersonDao , OtherInterface 
{ 
    @PersistenceContext(unitName="company") 
    @Override 
    public void setEntityManager(EntityManager em) 
    { 
    this.em = em; 
    } 

    @Override // implements OtherInterface.additionalMethods() 
    public additionalMethods() 
    { 
    // implements... 
    } 
} 

Toàn bộ kiến ​​trúc rất đơn giản:

Interface AbstractDao xác định phương pháp CRUD đơn giản.

Giao diện PersonDao mở rộng AbstractDAO mà không có bất kỳ phương pháp bổ trợ nào.

lớp AbstractDaoJpaImpl xác định thực hiện JPA của AbstractDao

lớp PersonDaoImpl kéo dài AbstractDaoJpaImpl và thực hiện PersonDao VÀ OtherInterface, mà thêm aditionalMethods() ...

NẾU, PersonDaoImpl chỉ thực hiện PersonDao, mà không thực hiện OtherInterface.additionalMethods(), mọi thứ đều hoạt động tốt.

tôi có thể sử dụng

<tx:annotation-driven transaction-manager="transactionManager" /> 

trong tập tin XML mùa xuân của tôi.

NHƯNG, PersonDaoImpl thực hiện OtherInterface (s), khi kiểm tra/chạy, tôi phải cast DAO từ PersonDao để PersonDaoImpl hoặc OtherInterfaces, chẳng hạn như:

@RunWith(SpringJUnit4ClassRunner.class) 
@ContextConfiguration(locations={"classpath:app.xml"}) 
@TransactionConfiguration(transactionManager="transactionManager" , defaultRollback=false) 
public class PersonDaoTest 
{ 
    @Inject 
    PersonDao dao; 

    @Test 
    public void testAdditionalMethod() 
    { 
    PersonDaoImpl impl = (PersonDaoImpl) dao; 
    System.out.println(impl.additionalMethod(...)); 
    } 
} 

Vấn đề xảy ra khi (PersonDaoImpl) dao, mà throws "Proxy không thể được đúc để PersonDaoImpl" ngoại lệ:

java.lang.ClassCastException: $Proxy36 cannot be cast to foobar.PersonDaoImpl 
    at foobar.PersonDaoTest.testAdditionalMethod(PersonDaoTest.java:36) 

này thường hỏi khi googleing, mọi người đề nghị thêm proxy-target-class="true"-<tx:annotation-driven>:

<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" /> 

Điều này sẽ sử dụng CGLIB thay vì proxy động của JDK.

NHƯNG nó ném một ngoại lệ khi khởi tạo mùa xuân:

Caused by: java.lang.ClassCastException: java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType 

trong constructor AbstractDaoJpaImpl của:

ParameterizedType genericSuperclass = (ParameterizedType) getClass().getGenericSuperclass(); 

Mỗi câu hỏi dừng lại ở đây, tôi không thể tìm thấy bất kỳ làm việc giải pháp bây giờ.

Có ai cho tôi giải pháp làm việc không? Cảm ơn rất nhiều!

Môi trường: Mùa xuân-3.0.4, JavaEE-api-6.0, javax.inject, cglib-2.2, hibernate-JPA-2,0-api-1.0.0,

Trả lời

12

Bạn đang giải quyết vấn đề sai. Đậu nành không có nghĩa là được đúc thành các lớp gốc, cách này hay cách khác. Điều đó sẽ phá vỡ toàn bộ điểm tiêm phụ thuộc. Sau khi tất cả: khi bạn chỉ định một phụ thuộc như một giao diện bạn đang yêu cầu một bean thỏa mãn hợp đồng, nhưng không phải là các chi tiết thực hiện. Đúc nó vào lớp bean ban đầu sẽ phá vỡ khớp nối lỏng lẻo này.

Bạn đang nói phương thức bổ sung được sao lưu bằng giao diện bạn gọi OtherInterface, vậy tại sao không sử dụng phương thức đó thay thế? Sau khi tất cả, proxy sẽ thực hiện tất cả các giao diện của lớp đích, không chỉ các giao diện được tiêm.

@Test 
public void testAdditionalMethod() 
{ 
    OtherInterface oi = (OtherInterface) dao; 
    System.out.println(oi.additionalMethod(...)); 
} 

Về cơ bản bạn có các tùy chọn (sắp xếp từ sạch để bẩn):

  1. riêng mối quan tâm của bạn và sử dụng đậu khác nhau cho giao diện khác nhau
  2. Tạo một meta-giao diện kéo dài OtherInterfacePersonDao và để cho hạt của bạn triển khai thực hiện metainterface
  3. Đúc đậu tới giao diện bạn cần tại bất kỳ thời điểm nào.
+0

Cảm ơn, tôi nên @Inject OtherInterface otherInterfaceImpl; và thử nghiệm với otherInterfaceImpl. Nó hoạt động! – smallufo

0

Có mùa xuân luôn tạo ra các lớp proxy và cách nó thực sự phát hiện không xâm nhập dệt và aop bởi cấu hình xml ... thử googling cho rằng lỗi trong tài liệu mùa xuân có shud được quy tắc để làm theo và làm việc xung quanh.