在非单例bean上修复Spring代理上的BeanNotOfRequiredTypeException?

我在从应用程序上下文中提取Spring bean时遇到问题。

当我尝试;

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(您可以在下面详细介绍)。

InnerThread bean被初始化为非单例,因为我需要多个实例。 InnerThread类还扩展了Thread。 有趣的是,这个错误出现在OuterThread中,它的设置方式与InnerThread完全相同。

我试图在下面包含所有相关的代码清单/堆栈跟踪。 有更多Spring体验的人可以告诉我这里发生了什么吗?


代码/配置清单

OuterThread.java片段:

 public class OuterThread extends Thread { private Queue createInnerThreads() { Queue 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中:

        

堆栈跟踪

 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=,eetop=0,single_step=false,daemon=false,stillborn=false,target=,group=java.lang.ThreadGroup[name=main,maxpri=10],contextClassLoader=sun.misc.Launcher$AppClassLoader@11b86e7,inheritedAccessControlContext=java.security.AccessControlContext@1524d43,threadLocals=,inheritableThreadLocals=java.lang.ThreadLocal$ThreadLocalMap@2cbc86,stackSize=0,nativeParkEventPointer=0,tid=9,threadStatus=0,parkBlocker=,blocker=,blockerLock=java.lang.Object@a68fd8,stopBeforeStart=false,throwableFromStop=,uncaughtExceptionHandler=] 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.(InnerThread.java:50) - Constructing InnerThread object, com.generic.InnerThread@1080876[em=,coolScheme=,coolUrl=,date=,error=,millisecondsTaken=0,thresholdMet=false,reconId=0,result=-2,searchId=0,username=,uuid=,name={T,h,r,e,a,d,-,2},priority=5,threadQ=,eetop=0,single_step=false,daemon=false,stillborn=false,target=,group=java.lang.ThreadGroup[name=main,maxpri=10],contextClassLoader=sun.misc.Launcher$AppClassLoader@11b86e7,inheritedAccessControlContext=java.security.AccessControlContext@1524d43,threadLocals=,inheritableThreadLocals=java.lang.ThreadLocal$ThreadLocalMap@3aef16,stackSize=0,nativeParkEventPointer=0,tid=10,threadStatus=0,parkBlocker=,blocker=,blockerLock=java.lang.Object@126c6ea,stopBeforeStart=false,throwableFromStop=,uncaughtExceptionHandler=] 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) 

类似的问题没有回答我的问题

  • 自动浇铸弹簧豆
  • 转换为同一个类时出现ClassCastException。

用@注释你的@Configuration

 @EnableAspectJAutoProxy(proxyTargetClass = true) 

您还必须添加以下依赖项:

  org.aspectj aspectjweaver 1.7.2  

再次,在花了几个小时试图调试之后,我在StackOverflow上发布后立即找到答案。

我从问题中遗漏的一个关键点是InnerThread有一个事务方法(抱歉认为这是无关紧要的)。 这是OuterThread和InnerThread之间的重要区别。

从Spring文档 :

注意

多个部分在运行时折叠为单个统一自动代理创建器,它应用指定的任何部分(通常来自不同的XML bean定义文件)的最强代理设置。 这也适用于和元素。

要明确: 使用’proxy-target-class =“true”’ ,或者元素将强制使用CGLIB代理来处理所有这三个代理 。

将上面的内容添加到我的配置中(基于persistance-context.xml,你可以看到上面加载的)行似乎解决了这个问题。 但是,我认为这可能是一个快速解决方法而不是真正的解决方案。

我想我在这里遇到了一些更深层次的问题,第一个问题是我发现Spring像删除咒语一样令人困惑。 其次,我应该使用Spring的TaskExecutor来启动我的线程。 我的第三个线程应该实现Runnable而不是扩展Thread(参见下面的SO问题)。

也可以看看

  • 使用ServiceLocatorFactoryBean和@Transactional的BeanNotOfRequiredTypeException (没有什么比在搜索之后找到一个线程的更好的了,因为它的响应是“已经回答了一百万次。”)
  • Spring Docs中的6.6节。
  • Java:“实现Runnable”与“扩展线程”

这只是一个猜测,但尝试创建一个接口InnerThreadInterface,然后让InnerThread类扩展它。

之后你应该能够做到:

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

即使我引用了CGLIB并使用了proxy-target-class =“true”设置,我也遇到了这个问题。 我确定了ehcache:注释标签是责备…删除以下配置为我解决了这个问题。 幸运的是,我能够使用hibernate 2级缓存,而不必使用ehcache声明性缓存。

     

我有同样的问题:

这只是一个猜测,但尝试创建一个接口InnerThreadInterface,然后让InnerThread类扩展它。 之后你应该能够做到:

我使用这种方式而不是上面发布的方式,它工作正常。 不需要将proxy-target-class设置为true,这需要更多不在我的类路径中的库。

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

解决此问题的一种方法是扩展可运行的界面并创建自己的界面:

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

然后你应该实现你的接口而不是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(){ ..... } ... } 

这样可以正常工作:

 ... MyInterface mynterface = SpringApplicationContext.getBean("myInterface", MyInterface.class); myInterface.doSomething(); ExecutorService executor = Executors.newSingleThreadExecutor(); executor.submit(myInterface); ... 

处理这个问题的另一种方法是实现一个接口并从spring请求接口,代理将完全实现接口,并且转换应该没有问题。

创建一个DAO接口并实现到SERVICE类的DAO接口,并在applicationContext.xml文件中提供SERVICE类的配置详细信息,即spring配置文件,并使用bean id访问bean并引用如此实例化的代理的实例反对DAO界面…它将完美地工作….