2009-05-05 37 views
39

Tôi có 2 dự án Java khác nhau, một dự án có 2 lớp: dynamicbeans.DynamicBean2dynamic.Validator.ClassCastException khi truyền tới cùng một lớp

Về dự án khác, tôi nạp cả hai lớp học năng động và lưu trữ chúng trên một Object

class Form { 
    Class beanClass; 
    Class validatorClass; 
    Validator validator; 
} 

sau đó tôi đi trước và tạo ra một đối tượng Validator sử dụng validatorClass.newInstance() và lưu trữ nó trên validator sau đó tôi có thể tạo một bean đối tượng cũng sử dụng beanClass.newInstance() và thêm nó vào phiên.

portletRequest.setAttribute("DynamicBean2", bean); 

Trong vòng đời của dự án Form, tôi gọi validator.validate() mà tải các đối tượng đậu tạo ra trước đó trong phiên (Tôi đang chạy Websphere Portal Server). Khi tôi cố gắng để đưa đối tượng này trở lại vào một DynamicBean2 nó không thành công với một ClassCastException.

Khi tôi kéo các đối tượng lại ra khỏi phiên sử dụng

faces.getApplication().createValueBinding("#{DynamicBean2}").getValue(faces); 

và kiểm tra các lớp của nó bằng cách sử .getClass() tôi nhận được dynamicbeans.DynamicBean2. Đây là lớp tôi muốn truyền nó đến tuy nhiên khi tôi cố gắng lấy ClassCastException.

Bất kỳ lý do nào khiến tôi nhận được thông báo này?

Trả lời

47

Tôi không hoàn toàn theo mô tả của bạn về luồng chương trình, nhưng thường khi bạn nhận được ClassCastExceptions, bạn không thể giải thích bạn đã tải lớp đó bằng một trình nạp lớp rồi thử đưa nó vào cùng một lớp được nạp bởi trình nạp lớp khác. Điều này sẽ không hoạt động - chúng được đại diện bởi hai đối tượng Class khác nhau bên trong JVM và việc cast sẽ thất bại.

Có một article about classloading in WebSphere. Tôi không thể nói nó áp dụng như thế nào với ứng dụng của bạn, nhưng có một số giải pháp khả thi. Tôi có thể nghĩ đến ít nhất là:

  1. Thay đổi bối cảnh lớp nạp bằng tay. Yêu cầu bạn thực sự có thể có được một tham chiếu đến một trình nạp lớp thích hợp, điều này có thể không thực hiện được trong trường hợp của bạn.

    Thread.currentThread().setContextClassloader(...); 
    
  2. Đảm bảo lớp được tải bởi trình nạp lớp cao hơn trong cấu trúc phân cấp.

  3. Tuần tự hóa và deserialize đối tượng. (Yuck!)

Có thể có cách phù hợp hơn cho trường hợp cụ thể của bạn.

+3

Đoán của tôi (Tôi không có kinh nghiệm với máy chủ cổng) là bạn đang nạp lớp vào các portlet khác nhau, mỗi một trình nạp lớp của riêng nó. Sau đó bạn đặt thể hiện vào một đối tượng phiên làm việc và khi bạn lấy nó từ một portlet sai (từ đó nó được tạo ra), bạn có ngoại lệ của mình. Tôi đoán (dựa trên kinh nghiệm của WAS) rằng bạn cần đặt jar với lớp của bạn ở đâu đó cao hơn trong phân cấp tải lớp để nó được nạp bởi cha mẹ của cả hai trình nạp lớp portlet. – Robin

+2

tức là Số 2, với chi tiết. – Robin

+1

@waxwing Điều này đã được khá nhiều thời gian. Nhưng, liệu bạn có phiền không khi xây dựng giải pháp đầu tiên có thể bạn đã nói? Tôi cảm thấy rằng một vấn đề tôi đang gặp có thể được giải quyết bằng cách này. Cảm ơn trước! –

10

Các đối tượng lớp được nạp trong các trình nạp lớp khác nhau, do đó các cá thể được tạo từ trong mỗi lớp được xem là 'không tương thích'. Đây là một vấn đề phổ biến trong một môi trường có nhiều trình nạp lớp khác nhau đang được sử dụng và các đối tượng đang được truyền xung quanh. Những vấn đề này có thể dễ dàng phát sinh trong môi trường cổng thông tin Java EE và cổng thông tin.

Việc tạo một thể hiện của một lớp yêu cầu rằng Lớp được liên kết với đối tượng đang được đúc giống với lớp được tải bởi trình nạp lớp ngữ cảnh chủ đề hiện tại.

+0

Có cách nào tôi nên tải các lớp học để ngăn chặn điều này xảy ra không? –

+0

Phần bí ẩn là lý do tại sao hai bộ nạp lớp nên tải cùng một lớp. Trong bất kỳ ứng dụng Java, bất kỳ lớp nào cũng luôn được nạp bởi chỉ một trình nạp lớp. Bất cứ khi nào bạn nhận được hành vi này một cái gì đó thực sự kỳ lạ đang xảy ra với bộ nạp lớp của bạn. –

+2

Điều đó phụ thuộc vào môi trường bạn đang hoạt động. Nếu bạn sử dụng thông tin bộ nhớ đệm, và bạn đưa dữ liệu vào bộ nhớ cache này từ hai ứng dụng web khác nhau trong cùng một EAR, chúng có thể có trình tạo lớp của riêng chúng. một mục được lấy từ bộ nhớ cache này từ webapp khác, nó sẽ dẫn đến một ClassCastException. Việc chia sẻ dữ liệu bộ nhớ có thể rất phức tạp trong các môi trường này, vì vậy bạn phải biết các lớp đang được nạp từ đâu để nó hoạt động. – Robin

0

Tôi gặp sự cố A2AClassCastException khi cố gắng tạo Danh sách đối tượng từ XML bằng cách sử dụng Apache Commons Digester.

List<MyTemplate> templates = new ArrayList<MyTemplate>(); 
Digester digester = new Digester(); 
digester.addObjectCreate("/path/to/template", MyTemplate.class); 
digester.addSetNext("/path/to/template", "add"); 
// Set more rules... 
digester.parse(f); // f is a pre-defined File 

for(MyTemplate t : templates) { // ClassCastException: Cannot cast mypackage.MyTemplate to mypackage.MyTemplate 
    // Do stuff 
} 

Như đã nêu ở trên, nguyên nhân là người đào không sử dụng cùng một ClassLoader như phần còn lại của chương trình. Tôi chạy nó trong JBoss, và hóa ra commons-digester.jar không nằm trong thư mục lib của JBoss, mà là trong thư mục lib của webapp. Sao chép các jar vào mywebapp/WEB-INF/lib cũng giải quyết được vấn đề. Một giải pháp khác là casll.setClassLoader (MyTemplate.class.getClassLoader()), nhưng cảm giác đó là một giải pháp khá xấu trong ngữ cảnh này.

0

Tôi đã gặp vấn đề tương tự khi sử dụng một số phiên bản JBoss trên các máy khác nhau. Để xấu tôi đã không vấp ngã trên bài đăng này trước đó.
Đã có các tạo tác được triển khai trên các máy khác nhau, hai trong số chúng đã khai báo trình nạp lớp với tên giống hệt nhau. Tôi đã thay đổi một trong các tên của trình nạp lớp và mọi thứ hoạt động tốt => Cẩn thận với Copy & Dán!

Tại sao ClassCastException không đề cập đến trình tải lớp có liên quan? - Tôi nghĩ đó sẽ là thông tin rất hữu ích.
Có ai biết nếu có bất kỳ thứ gì như thế này trong tương lai không? Cần phải kiểm tra các bộ tải lớp 20-30 hiện vật không phải là dễ chịu. Hoặc có điều gì đó tôi đã bỏ lỡ trong văn bản ngoại lệ?

EDIT: Tôi đã chỉnh sửa tệp META-INF/jboss-app.xml và thay đổi tên của trình tải, ý tưởng là phải có một tên duy nhất. Tại nơi làm việc, chúng tôi sử dụng id tạo tác (duy nhất) kết hợp với phiên bản được chèn bởi maven ({$ version}) trong khi xây dựng.
Sử dụng các trường động chỉ là tùy chọn nhưng sẽ giúp nếu bạn muốn triển khai các phiên bản khác nhau của cùng một ứng dụng.

<jboss-app> 
    <loader-repository> 
    com.example:archive=unique-archive-name-{$version} 
    </loader-repository> 
</jboss-app> 

Bạn có thể tìm thấy một số thông tin ở đây: https://community.jboss.org/wiki/ClassLoadingConfiguration

+0

Bạn có thể cho biết cách bạn đã thực hiện điều đó, một số mã không? – Rachel

+0

Tôi đã cập nhật câu trả lời của mình, xin lỗi vì đã đến trễ. – Andrei

0

tôi đã cùng một vấn đề, và cuối cùng tôi tìm thấy một workaround trên java.net:

Sao chép tất cả các file org.eclipse.persistence jarglassfish4/glassfish/modules-WEB-INF/lib. Sau đó, truy cập vào glassfish-web.xml và đặt class-delegate thành false.

Làm việc cho tôi!

0

Tôi gặp vấn đề tương tự trên EJB wildfly, EJB đã trả về danh sách các đối tượng và có điều khiển từ xa và giao diện cục bộ. Tôi đã sử dụng giao diện Local do nhầm lẫn những gì đã làm việc tốt cho đến khi bạn cố gắng để cast các đối tượng trong danh sách.

giao diện Local/Remote:

public interface DocumentStoreService { 

    @javax.ejb.Remote 
    interface Remote extends DocumentStoreService { 
    } 

    @javax.ejb.Local 
    interface Local extends DocumentStoreService { 
    } 

bean EJB:

@Stateless 
public class DocumentStoreServiceImpl implements DocumentStoreService.Local, DocumentStoreService.Remote { 

Các đúng wrapper mùa xuân quanh EJB:

<bean id="documentStoreService" class="org.springframework.ejb.access.LocalStatelessSessionProxyFactoryBean"> 
    <property name="jndiName" value="java:global/dpc/dpc-ejb/DocumentStoreServiceImpl!santam.apps.dpc.service.DocumentStoreService$Remote"/> 
    <property name="businessInterface" value="santam.apps.dpc.service.DocumentStoreService$Remote"/> 
    <property name="resourceRef" value="true" /> 
</bean> 

Lưu ý $ từ xa, Bạn có thể thay đổi này đến $ Local và nó sẽ tìm thấy giao diện Local chỉ tốt, và cũng thực hiện các phương thức mà không có bất kỳ vấn đề nào (từ ứng dụng eparate trên cùng một vùng chứa), nhưng các đối tượng mô hình không được sắp xếp lại và từ một trình nạp lớp khác nếu bạn sử dụng giao diện cục bộ do nhầm lẫn.

0

Một tùy chọn khác:

xảy ra với tôi trong WebLogic, nhưng tôi đoán nó có thể xảy ra trong các máy chủ khác nữa - nếu bạn (chỉ) "Xuất bản" và do một số lớp học của bạn được tái nạp. Thay vào đó hãy làm "Clean" để tất cả các lớp sẽ được nạp lại với nhau.

1

Tôi đã gặp sự cố tương tự với tra cứu EJB từ một EJB khác. Tôi đã giải quyết việc thêm @Remote (MyInterface.class) vào cấu hình lớp EJB

Các vấn đề liên quan