Исправление BeanNotOfRequiredTypeException на прокси-сервере Spring, не являющемся одноэлементным компонентом?

У меня проблема с извлечением bean-компонента Spring из контекста приложения.

Когда я пытаюсь;

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

Я получил;

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

Без указанного класса в вызове getBean () я получаю ClassCastException (подробности которого вы можете увидеть ниже).

Bean-компонент InnerThread инициализируется как не-синглтон, потому что мне нужно несколько экземпляров. Класс InnerThread также является расширением Thread. Интересно то, что эта ошибка появляется в OuterThread, который настроен точно так же, как InnerThread.

Я попытался включить все соответствующие листинги кода / трассировки стека ниже. Может ли кто-нибудь с большим опытом Spring сказать мне, что здесь происходит?


Листинг кода / конфигурации

Фрагмент 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>

Трассировки стека

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)

Подобные вопросы, которые не отвечают на мой вопрос


person James McMahon    schedule 08.05.2009    source источник


Ответы (9)


Добавьте аннотации к вашему @Configuration классу с помощью

@EnableAspectJAutoProxy(proxyTargetClass = true) 

Вам также необходимо добавить следующую зависимость:

<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.7.2</version>
</dependency> 
person thSoft    schedule 09.09.2013
comment
Это действительно должно быть отмечено как принятый ответ. - person Andrew Landsverk; 18.06.2015
comment
На самом деле, для правильного сопоставления типов не обязательно использовать прокси CGLIB. - person antonu17; 01.06.2016
comment
Это сработало для меня очень хорошо. Это должен быть принятый ответ. - person gonella; 07.09.2016
comment
Это мне очень помогло. Должен быть отмечен как принятый ответ. - person DTnapaT; 12.01.2017
comment
Оно работает. Но я согласен с antonu17. Это перебор. Решить это намного проще. Проблема заключается в том, что ответил Том. - person mahieus; 26.06.2019

Еще раз, потратив часы на отладку, я нахожу ответ сразу после публикации в StackOverflow.

Ключевым моментом, который я не упомянул в своем вопросе, является то, что InnerThread имеет транзакционный метод (извините, что это не имеет значения). Это важное различие между OuterThread и InnerThread.

Из документации Spring:

Примечание

Во время выполнения несколько разделов сворачиваются в единую унифицированную программу создания автопрокси, которая применяет самые строгие настройки прокси, указанные в любом из разделов (обычно из разных файлов определения компонентов XML). Это также относится к элементам и.

Чтобы было ясно: использование 'proxy-target-class = "true"' on, иначе элементы заставят использовать прокси CGLIB для всех трех из них.

Добавление приведенной выше строки в мою конфигурацию (на основе persistance-context.xml, которую вы можете видеть загруженной выше), похоже, решает проблему. Однако я думаю, что это может быть быстрое решение, а не реальное решение.

Я думаю, у меня есть несколько более глубоких проблем, во-первых, то, что я считаю Spring столь же запутанной, как и удаление ругательств. Во-вторых, мне, вероятно, следует использовать TaskExecutor Spring, чтобы начать мои обсуждения. В-третьих, мои потоки должны реализовывать Runnable вместо расширения Thread (см. Вопрос SO ниже).

См. также

person James McMahon    schedule 08.05.2009
comment
Проверьте ответ Тома (первым классом должен быть интерфейс, это исправлено, но ожидается экспертная оценка). Я считаю, что это верное направление, чтобы решить эту проблему должным образом. Пожалуйста, подумайте еще раз над правильным ответом, так как это может заставить людей добавлять дополнительные зависимости и конфигурации, которые им на самом деле не нужны (/ хотят). - person mahieus; 26.06.2019

Один из способов решить эту проблему - расширить исполняемый интерфейс и создать свой собственный:

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

Затем вы должны реализовать свой интерфейс вместо исполняемого:

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

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


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

             ...
        }

Это будет нормально работать:

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

    myInterface.doSomething();
  ExecutorService executor = Executors.newSingleThreadExecutor();
    executor.submit(myInterface);
...
person tom    schedule 06.03.2012

Это всего лишь предположение, но попробуйте создать интерфейс InnerThreadInterface, а затем позвольте классу InnerThread расширить его.

После этого вы сможете:

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

person egaga    schedule 08.05.2009
comment
Я попробовал, но, к сожалению, на этом нет игральных костей. - person James McMahon; 09.05.2009
comment
Это действительно помогло в моем случае, но пример немного неверен. Должно быть: InnerThreadInterface inner = ctx.getBean("innerThread", InnerThreadInterface.class); - person xor_eq; 23.09.2015

У меня была эта проблема, хотя я ссылался на CGLIB и использовал настройку proxy-target-class = "true". Я решил, что виноват тег ehcache: annotation ... Удаление следующей конфигурации решило эту проблему. К счастью, я смог использовать кеширование уровня гибернации 2 вместо декларативного кеширования ehcache.

<ehcache:annotations>
    <ehcache:caching id="myCacheModel" cacheName="myCache"/>
    <ehcache:flushing id="myFlushModel" cacheNames="myCache" when="after"/>
</ehcache:annotations> 
person Community    schedule 23.06.2009

Другой способ справиться с этой проблемой - реализовать интерфейс и запросить интерфейс из spring, прокси-сервер полностью реализует интерфейс, и при приведении не должно возникнуть проблем.

person Nathan Feger    schedule 05.08.2010
comment
Не всегда практичное решение. - person Snekse; 09.11.2010

У меня такая же проблема:

Это всего лишь предположение, но попробуйте создать интерфейс InnerThreadInterface, а затем позвольте классу InnerThread расширить его. После этого вы сможете:

Я использую этот способ вместо того, что опубликовано выше, и он работал нормально. Не было необходимости устанавливать для proxy-target-class значение true, для этого требовалось больше библиотек, которых не было в моем пути к классам.

InnerThreadInterface inner = (InnerThreadInterface)ctx.getBean("innerThread");
person Boechi    schedule 21.12.2010

создать интерфейс DAO и реализовать интерфейс DAO для класса SERVICE и предоставить подробные сведения о конфигурации класса SERVICE в файле applicationContext.xml, то есть в файле конфигурации spring, и получить доступ к компоненту с использованием идентификатора компонента и ссылаться на экземпляр созданного таким образом прокси объект интерфейса DAO ... он будет работать отлично ....

person czarabid    schedule 04.12.2013

Эта проблема была до тех пор, пока я просто не перезапустил Idea и не пересобирал проект Maven.

person Roman    schedule 14.11.2020