web-dev-qa-db-fra.com

Correction de l’exception BeanNotOfRequiredTypeException sur un proxy Spring sur un bean non singleton?

Je ne parviens pas à extraire un bean Spring d'un contexte d'application.

Quand j'essaye

InnerThread instance = (InnerThread) SpringContextFactory.getApplicationContext().getBean("innerThread", InnerThread.class);

Je reçois;

org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'innerThread' must be of type [com.generic.InnerThread], but was actually of type [$Proxy26]

Sans la classe spécifiée dans l'appel getBean (), j'obtiens une exception ClassCastException (que vous pouvez voir en détail ci-dessous).

Le bean InnerThread est en cours d'initialisation en tant que non-singleton, car j'ai besoin de plusieurs instances. La classe InnerThread étend également Thread. La chose intéressante est que cette erreur apparaît dans OuterThread, qui est configuré exactement de la même manière que InnerThread.

J'ai essayé d'inclure toutes les listes de codes/traces de pile pertinentes ci-dessous. Est-ce que quelqu'un avec plus d'expérience du printemps pourrait me dire ce qui se passe ici?


Liste de code/configuration

Extrait de OuterThread.Java:

public class OuterThread extends Thread {
    private Queue<InnerThread> createInnerThreads() {
        Queue<InnerThread> threads = new ArrayBlockingQueue();

        ApplicationContext ctx = SpringContextFactory.getApplicationContext();
        int i = 0;
        for (SearchRule search : searches) {
            logger.debug("Number of times looped " + i++);
            //Seprated lines to get a better sense of what is going on
            Object proxy = ctx.getBean("innerThread", InnerThread.class);
            logger.debug(ReflectionToStringBuilder.toString(proxy));
            logger.debug("proxy.getClass(): " + proxy.getClass());
            logger.debug("proxy.getClass().getClassLoader(): " + proxy.getClass().getClassLoader());
            logger.debug("proxy.getClass().getDeclaringClass(): " + proxy.getClass().getDeclaringClass());
            logger.debug("InnerThread.class.getClassLoader(): " + InnerThread.class.getClassLoader());

            //---Exception here---
            InnerThread cst = (InnerThread) proxy;

            threads.add(cst);
        }
        return threads;
    }

    public static void main(String[] args) throws Exception {
        try {
            OuterThread instance = (OuterThread) SpringContextFactory.getApplicationContext().getBean("outerThread", OuterThread.class);
            instance.run();
        } catch (Exception ex) {
            logger.error("Fatal exception.", ex);
            throw ex;
        }
    }
}

SpringContextFactory.Java:

public class SpringContextFactory {
    static final Logger logger = LoggerFactory.getLogger(SpringContextFactory.class);
    private static ApplicationContext ctx;
    private static final String DEFAULT_PATH = "META-INF/app-context.xml";

    public static ApplicationContext getApplicationContext() {
        return getApplicationContext(DEFAULT_PATH);
    }

    public static synchronized ApplicationContext getApplicationContext(String path) {
        if (ctx == null) return createApplicationContext(path);
        else return ctx;
    }

    private static ApplicationContext createApplicationContext(String path) {
        if (logger.isDebugEnabled()) logger.debug("Loading Spring Context...");
        ctx = new ClassPathXmlApplicationContext(path);
        if (logger.isDebugEnabled()) logger.debug("Spring Context Loaded");
        return ctx;
    }
}

app-context.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

    <!-- persistence context from separate jar -->
    <import resource="persistence-context.xml"/>

    <bean id="outerThread" class="com.generic.OuterThread" scope="prototype"/>
    <bean id="innerThread" class="com.generic.InnerThread" scope="prototype"/>

</beans>

Trace de la pile

2009-05-08 14:34:37,341 [main] DEBUG com.generic.OuterThread.init(OuterThread.Java:59) - Initializing OuterThread object, com.generic.OuterThread@1c8fb4b[em=org.hibernate.ejb.EntityManagerImpl@e2892b,currentTime=Java.util.GregorianCalendar[time=1241634874841,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=Sun.util.calendar.ZoneInfo[id="America/New_York",offset=-18000000,dstSavings=3600000,useDaylight=true,transitions=235,lastRule=Java.util.SimpleTimeZone[id=America/New_York,offset=-18000000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=3,startMonth=2,startDay=8,startDayOfWeek=1,startTime=7200000,startTimeMode=0,endMode=3,endMonth=10,endDay=1,endDayOfWeek=1,endTime=7200000,endTimeMode=0]],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2009,MONTH=4,WEEK_OF_YEAR=19,WEEK_OF_MONTH=2,DAY_OF_MONTH=6,DAY_OF_YEAR=126,DAY_OF_WEEK=4,DAY_OF_WEEK_IN_MONTH=1,AM_PM=1,HOUR=2,HOUR_OF_DAY=14,MINUTE=34,SECOND=34,MILLISECOND=841,ZONE_OFFSET=-18000000,DST_OFFSET=3600000],maxConcurrentThreads=5,reconId=3,reportUsername=TEST,useOffset=false,username=removed,uuid=bf61494d-ff96-431c-a41f-1e292d0c9fbe,name={T,h,r,e,a,d,-,1},priority=5,threadQ=<null>,eetop=0,single_step=false,daemon=false,stillborn=false,target=<null>,group=Java.lang.ThreadGroup[name=main,maxpri=10],contextClassLoader=Sun.misc.Launcher$AppClassLoader@11b86e7,inheritedAccessControlContext=Java.security.AccessControlContext@1524d43,threadLocals=<null>,inheritableThreadLocals=Java.lang.ThreadLocal$ThreadLocalMap@2cbc86,stackSize=0,nativeParkEventPointer=0,tid=9,threadStatus=0,parkBlocker=<null>,blocker=<null>,blockerLock=Java.lang.Object@a68fd8,stopBeforeStart=false,throwableFromStop=<null>,uncaughtExceptionHandler=<null>]
2009-05-08 14:34:37,341 [main] DEBUG org.springframework.orm.jpa.ExtendedEntityManagerCreator$ExtendedEntityManagerInvocationHandler.doJoinTransaction(ExtendedEntityManagerCreator.Java:385) - No local transaction to join
2009-05-08 14:34:37,529 [main] DEBUG com.generic.OuterThread.createInnerThreads(OuterThread.Java:139) - Number of times looped 0
2009-05-08 14:34:37,529 [main] DEBUG org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory$1.run(AbstractAutowireCapableBeanFactory.Java:458) - Creating instance of bean 'searchThread' with merged definition [Root bean: class [com.generic.InnerThread]; scope=prototype; abstract=false; lazyInit=false; autowireCandidate=true; autowireMode=0; dependencyCheck=0; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in class path resource [META-INF/app-context.xml]]
2009-05-08 14:34:37,545 [main] DEBUG com.generic.InnerThread.<init>(InnerThread.Java:50) - Constructing InnerThread object, com.generic.InnerThread@1080876[em=<null>,coolScheme=<null>,coolUrl=<null>,date=<null>,error=<null>,millisecondsTaken=0,thresholdMet=false,reconId=0,result=-2,searchId=0,username=<null>,uuid=<null>,name={T,h,r,e,a,d,-,2},priority=5,threadQ=<null>,eetop=0,single_step=false,daemon=false,stillborn=false,target=<null>,group=Java.lang.ThreadGroup[name=main,maxpri=10],contextClassLoader=Sun.misc.Launcher$AppClassLoader@11b86e7,inheritedAccessControlContext=Java.security.AccessControlContext@1524d43,threadLocals=<null>,inheritableThreadLocals=Java.lang.ThreadLocal$ThreadLocalMap@3aef16,stackSize=0,nativeParkEventPointer=0,tid=10,threadStatus=0,parkBlocker=<null>,blocker=<null>,blockerLock=Java.lang.Object@126c6ea,stopBeforeStart=false,throwableFromStop=<null>,uncaughtExceptionHandler=<null>]
2009-05-08 14:34:37,545 [main] DEBUG org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.Java:203) - Returning cached instance of singleton bean 'entityManagerFactory'
2009-05-08 14:34:37,545 [main] DEBUG org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.Java:203) - Returning cached instance of singleton bean 'org.springframework.transaction.config.internalTransactionAdvisor'
2009-05-08 14:34:37,560 [main] DEBUG org.springframework.transaction.interceptor.AbstractFallbackTransactionAttributeSource.getTransactionAttribute(AbstractFallbackTransactionAttributeSource.Java:108) - Adding transactional method [report] with attribute [PROPAGATION_REQUIRED,ISOLATION_DEFAULT]
2009-05-08 14:34:37,560 [main] DEBUG org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.buildAdvisors(AbstractAutoProxyCreator.Java:494) - Creating implicit proxy for bean 'searchThread' with 0 common interceptors and 1 specific interceptors
2009-05-08 14:34:37,560 [main] DEBUG org.springframework.aop.framework.JdkDynamicAopProxy.getProxy(JdkDynamicAopProxy.Java:113) - Creating JDK dynamic proxy: target source is SingletonTargetSource for target object [com.generic.InnerThread@1080876]
2009-05-08 14:34:37,591 [main] DEBUG com.generic.OuterThread.createInnerThreads(OuterThread.Java:141) - $Proxy26@1594a88[h=org.springframework.aop.framework.JdkDynamicAopProxy@1f0cf51]
2009-05-08 14:34:37,591 [main] DEBUG com.generic.OuterThread.createInnerThreads(OuterThread.Java:142) - proxy.getClass(): class $Proxy26
2009-05-08 14:34:37,591 [main] DEBUG com.generic.OuterThread.createInnerThreads(OuterThread.Java:143) - proxy.getClass().getClassLoader(): Sun.misc.Launcher$AppClassLoader@11b86e7
2009-05-08 14:34:37,591 [main] DEBUG com.generic.OuterThread.createInnerThreads(OuterThread.Java:144) - proxy.getClass().getDeclaringClass(): null
2009-05-08 14:34:37,591 [main] DEBUG com.generic.OuterThread.createInnerThreads(OuterThread.Java:145) - InnerThread.class.getClassLoader(): Sun.misc.Launcher$AppClassLoader@11b86e7
2009-05-08 14:34:37,591 [main] ERROR com.generic.OuterThread.run(OuterThread.Java:101) - Exception in OuterThread, ending reconciliation.
Java.lang.ClassCastException: $Proxy26 cannot be cast to com.generic.InnerThread
    at com.generic.OuterThread.createInnerThreads(OuterThread.Java:148)
    at com.generic.OuterThread.run(OuterThread.Java:65)
    at com.generic.OuterThread.main(OuterThread.Java:170)

Des questions similaires qui ne répondent pas à ma question

25
James McMahon

Une fois encore, après avoir passé des heures à essayer de résoudre ce problème, je trouve la réponse juste après avoir posté sur StackOverflow.

Un point clé que j'ai omis de ma question est qu'InnerThread a une méthode transactionnelle (désolée, cela n'était pas pertinent). C'est la différence importante entre OuterThread et InnerThread.

De la Documentation Spring :

Remarque

Plusieurs sections sont réduites en un seul créateur unifié de proxy automatique unifié au moment de l'exécution, qui applique les paramètres de proxy les plus robustes spécifiés par l'une des sections (généralement à partir de fichiers de définition de bean XML différents). Ceci s'applique également aux éléments et.

Pour être clair: utiliser 'proxy-target-class = "true"' on, ou des éléments forceront l’utilisation de proxy CGLIB pour les trois.

L'ajout de ce qui précède à ma configuration (basée sur persistance-context.xml, que vous pouvez voir chargée ci-dessus) semble résoudre le problème. Cependant, je pense que cela peut être une solution de contournement rapide par opposition à une solution réelle.

Je pense que j'ai quelques problèmes plus profonds ici, le premier étant que je trouve le printemps aussi déroutant que l'explétif supprimé. Deuxièmement, je devrais probablement utiliser TaskExecutor de Spring -/pour lancer mes discussions. Troisièmement, mes threads doivent implémenter Runnable au lieu d’étendre Thread (voir la question SO ci-dessous).

Voir aussi

24
James McMahon

Annotez votre classe @Configuration avec

@EnableAspectJAutoProxy(proxyTargetClass = true) 

Vous devez également ajouter la dépendance suivante:

<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.7.2</version>
</dependency> 
31
thSoft

Ceci est juste une hypothèse, mais essayez de créer une interface InnerThreadInterface, puis laissez la classe InnerThread l'étendre.

Après cela, vous devriez pouvoir faire:

InnerThreadInterface inner = ctx.getBean ("innerThread", InnerThread.class);

2
egaga

Une façon de résoudre ce problème consiste à étendre l'interface exécutable et à créer la vôtre:

public class MyInterface extends Runnable {
  // your own method declarations here
  void doSomething();
  ...
}

Ensuite, vous devez implémenter votre interface à la place du runnable: 

@Component("myInterface")
@Scope("prototype")
     public class MyInterfaceImpl extends MyInterface {

          // your own method declarations here
          public void doSomething(){
          ...
          }


          // implement run from Runnable Interface
          @Transactional
          public void run(){
                .....
          }

             ...
        }

Cela fonctionnera bien:

...
    MyInterface mynterface = SpringApplicationContext.getBean("myInterface", MyInterface.class);

    myInterface.doSomething();
  ExecutorService executor = Executors.newSingleThreadExecutor();
    executor.submit(myInterface);
...
1
tom

J'ai eu le même problème:

Ceci est juste une hypothèse, mais essayez de faire de Une interface InnerThreadInterface, , Puis laissez la classe InnerThread la développer. Après cela, vous devriez pouvoir faire:

J'utilise cette façon au lieu de ce qui est affiché ci-dessus et cela a bien fonctionné. Il n'était pas nécessaire de définir la classe proxy-target sur true, cela nécessitait davantage de bibliothèques qui n'étaient pas sur mon chemin de classe.

InnerThreadInterface inner = (InnerThreadInterface)ctx.getBean("innerThread");
1
Boechi

J'ai eu ce problème même si j'ai référencé CGLIB et utilisé le paramètre proxy-target-class = "true". J'ai déterminé ehcache: la balise d'annotation était à blâmer ... La suppression de la configuration suivante a résolu ce problème. Heureusement, j'ai pu utiliser la mise en cache Hibernate niveau 2 au lieu de devoir utiliser la mise en cache déclarative ehcache.

<ehcache:annotations>
    <ehcache:caching id="myCacheModel" cacheName="myCache"/>
    <ehcache:flushing id="myFlushModel" cacheNames="myCache" when="after"/>
</ehcache:annotations> 
1
Shawn

Une autre façon de gérer ce problème serait d'implémenter une interface et de demander l'interface à spring, le proxy implémenterait entièrement l'interface et la distribution ne devrait pas avoir de problème.

0
Nathan Feger