동일한 클래스로 캐스팅 할 때 ClassCastException
-
05-07-2019 - |
문제
2 개의 다른 Java 프로젝트가 있으며 하나는 2 개의 클래스가 있습니다. dynamicbeans.DynamicBean2
그리고 dynamic.Validator
.
다른 프로젝트에서, 나는이 두 수업을 동적으로로드하고 Object
class Form {
Class beanClass;
Class validatorClass;
Validator validator;
}
그런 다음 계속해서 a Validator
객체 사용 validatorClass.newInstance()
그리고 그것을 저장하십시오 validator
그런 다음 Bean 객체를 만들고 사용합니다 beanClass.newInstance()
세션에 추가하십시오.
portletRequest.setAttribute("DynamicBean2", bean);
수명주기 동안 Form
프로젝트, 전화 validator.validate()
세션에서 이전에 생성 된 Bean 객체를로드합니다 (WebSphere Portal Server를 실행 중입니다). 이 개체를 다시 캐스팅하려고 할 때 DynamicBean2
ClassCastException으로 실패합니다.
내가
faces.getApplication().createValueBinding("#{DynamicBean2}").getValue(faces);
그리고 그것을 사용하는 클래스를 확인하십시오 .getClass()
나는 얻다 dynamicbeans.DynamicBean2
. 이것은 내가 캐스팅하고 싶은 수업이지만, 시도 할 때 ClassCastException을 얻습니다.
내가 이것을 얻는 이유는 무엇입니까?
해결책
프로그램 흐름에 대한 설명을 따르지는 않지만 일반적으로 ClassCastExceptions를 얻을 때 한 클래스 로더로 클래스를로드 한 다음 다른 클래스 로더로로드 된 동일한 클래스로 캐스팅하려고 시도 할 수 없습니다. 이것은 작동하지 않습니다. JVM 내부의 두 개의 다른 클래스 객체로 표시되며 캐스트가 실패합니다.
있습니다 WebSphere의 클래스로드에 관한 기사. 응용 프로그램에 어떻게 적용되는지 말할 수는 없지만 여러 가지 가능한 솔루션이 있습니다. 나는 적어도 생각할 수있다 :
컨텍스트 클래스 로더를 수동으로 변경하십시오. 실제로 적절한 클래스 로더에 대한 참조를 얻을 수 있어야합니다.이 경우에는 불가능할 수 있습니다.
Thread.currentThread().setContextClassLoader(...);
클래스가 계층 구조에서 더 높은 클래스 로더에 의해로드되어 있는지 확인하십시오.
객체를 직렬화하고 사로화하십시오. (왝!)
그래도 특정 상황에 더 적절한 방법이있을 수 있습니다.
다른 팁
클래스 객체는 다른 클래스 로더에로드되었으므로 각 클래스에서 생성 된 인스턴스는 '호환되지 않는'것으로 간주됩니다. 이것은 다양한 클래스 로더가 사용되고 객체가 통과되는 환경에서 일반적인 문제입니다. 이러한 문제는 Java EE 및 포털 환경에서 쉽게 발생할 수 있습니다.
클래스의 인스턴스를 캐스팅하려면 주조되는 객체에 링크 된 클래스가 현재 스레드 컨텍스트 클래스 로더에 의해로드 된 것과 동일해야합니다.
Apache Commons Digester를 사용하여 XML에서 객체 목록을 작성하려고 할 때 A2AclassCastException 문제가 발생했습니다.
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
}
위에서 언급 한 바와 같이, 원인은 소화조가 프로그램의 나머지 부분과 동일한 클래스 로더를 사용하지 않기 때문입니다. 나는 이것을 Jboss에서 실행했고, Commons-Digester.jar는 JBoss의 LIB 디렉토리가 아니라 WebApp의 LIB 디렉토리에 있다는 것이 밝혀졌습니다. JAR을 MyWebApp/Web-Inf/Lib에 복사하면 문제가 해결되었습니다. 또 다른 솔루션은 Casll Digester.setClassLoader (MyTemplate.class.getClassLoader ())이지만이 맥락에서 상당히 추악한 솔루션처럼 느껴집니다.
다른 기계에서 여러 JBOSS 인스턴스를 사용하는 동안 같은 문제가있었습니다. 나쁘게 나는이 게시물을 일찍 우연히 발견하지 못했습니다.
다른 기계에 아티팩트가 배포되었고, 그 중 2 개는 동일한 이름을 가진 클래스 로더를 선언했습니다. 클래스 로더 이름 중 하나를 변경했으며 모든 것이 잘 작동했습니다 => 복사 및 붙여 넣기를 조심하십시오!
ClassScastException이 관련 클래스 로더를 언급하지 않는 이유는 무엇입니까? - 나는 그것이 매우 유용한 정보라고 생각합니다.
미래에 이와 같은 것이 있을지 아는 사람이 있습니까? 20-30 아티팩트의 클래스 로더를 확인 해야하는 것은 그리 즐겁지 않습니다. 아니면 예외 텍스트에서 놓친 것이 있습니까?
편집하다: 메타 -inf/jboss-app.xml 파일을 편집하고 로더의 이름을 변경했습니다. 아이디어는 고유 한 이름을 갖는 것입니다. 직장에서는 빌드 중에 Maven ({$ version})이 삽입 한 버전과 결합 된 아티팩트 ID (고유)를 사용합니다.
다이나믹 필드를 사용하는 것은 선택 사항 일뿐이지만 동일한 응용 프로그램의 다른 버전을 배포하려는 경우 도움이됩니다.
<jboss-app>
<loader-repository>
com.example:archive=unique-archive-name-{$version}
</loader-repository>
</jboss-app>
여기에서 몇 가지 정보를 찾을 수 있습니다. https://community.jboss.org/wiki/classloadingconfiguration
같은 문제가 있었고 마침내 java.net에서 해결 방법을 찾았습니다.
모두 복사하십시오 org.eclipse.persistence jar
파일 glassfish4/glassfish/modules
에게 WEB-INF/lib
. 그런 다음 당신의 입장 glassfish-web.xml
, 그리고 설정 class-delegate
에게 false
.
나를 위해 일했습니다!
JAXB와 JBOSS와 비슷한 문제가 7.1로 비슷했습니다. 문제와 솔루션은 다음과 같습니다. javax.xml.bind.jaxbexception : 클래스 *** 또는 그 슈퍼 클래스 가이 맥락에 알려져 있습니다.. 주어진 예외는 org.foo.bar.valueset이 org.foo.bar.valueset에 캐스트 할 수 없습니다.
나는 Wildfly EJB에서 같은 문제를 겪었고, EJB는 객체 목록을 반환하고 있으며 리모컨 및 로컬 인터페이스가 있습니다. 나는 목록에 객체를 캐스팅하려고 할 때까지 잘 작동하는 것을 실수로 로컬 인터페이스를 사용했습니다.
로컬/원격 인터페이스 :
public interface DocumentStoreService {
@javax.ejb.Remote
interface Remote extends DocumentStoreService {
}
@javax.ejb.Local
interface Local extends DocumentStoreService {
}
ejb bean :
@Stateless
public class DocumentStoreServiceImpl implements DocumentStoreService.Local, DocumentStoreService.Remote {
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>
$ remote를 참고하면이를 $ 로컬로 변경할 수 있으며 로컬 인터페이스를 잘 찾을 수 있으며 문제없이 메소드를 실행할 수 있지만 (동일한 컨테이너의 별도 응용 프로그램에서) 모델 객체는 마샬이 아니며 있습니다. 실수로 로컬 인터페이스를 사용하는 경우 다른 클래스 로더에서.
다른 옵션 :
Weblogic에서 나에게 일어 났지만 다른 서버에서도 일어날 수 있다고 생각합니다. "게시"하는 경우 일부 클래스가 다시로드됩니다. 대신 모든 클래스가 함께로드되도록 "청소"하십시오.
다른 EJB의 EJB 조회에도 같은 문제가있었습니다. EJB 클래스 구성에 @remote (myinterface.class)를 추가했습니다.
동일했다 my.package.MyClass cannot be cast to my.package.MyClass
Wildfly 10.1에서, 내가 이해했듯이, 나는 그의 대답에 @emil lundberg가 묘사 한 것과 반대를했다.
나는 그것을 추가했다 기준 치수 (포함하는 my.package.MyClass
) 에게 my.war/WEB-INF/jboss-deployment-structure.xml
의존성으로
<dependencies>
...
<module name="my.package"/>
</dependencies>
해당 대응을 제거했습니다 항아리 ~에서 my.war/WEB-INF/lib
, 전쟁을 재개 한 다음 코드는 예상대로 작동했습니다.
따라서 우리는 그것이 문제를 해결했는지 확인했습니다. 이제 업데이트 된 버전이 전쟁 조립 및 배포됩니다.
이것을 위해, 그 근원에서 전쟁, 추가해야합니다 <scope>provided</scope>
그것들을 위해 항아리 안에 pom.xml
, 언제 my.war
다음에 수정/강화 코드가 주입 된 상태에서 다시 조립됩니다. 항아리 ~ 안으로 my.war/WEB-INF/lib
.
의존성을 추가 한 후이 문제가 발생했습니다. spring-boot-devtools
내 스프링 부츠 프로젝트에서. 의존성을 제거하고 문제가 사라졌습니다. 이 시점에서 가장 좋은 추측은 그 것입니다 spring-boot-devtools
새로운 클래스 로더를 가져오고 일부 스레드에서 새 클래스 로더를 사용하지 않는 특정 경우 다른 클래스 로더간에 클래스 캐스팅 문제 문제가 발생합니다.