【问题标题】:Spring JPA threads: IllegalArgumentException ExceptionSpring JPA 线程:IllegalArgumentException 异常
【发布时间】:2015-09-26 16:04:30
【问题描述】:

我已将一个 Thread 类注释如下:

@Transactional(propagation = Propagation.REQUIRED)
@Component
@Scope("prototype")
public class ThreadRetrieveStockInfoEuronext implements Runnable {
   ...
}

线程被连接到一个控制器类中,并通过 Global.poolMultiple.execute(threadRetrieveStockInfoEuronext); 调用

public class RetrievelController {

    @Autowired
    private ThreadRetrieveStockInfoEuronext threadRetrieveStockInfoEuronext;

    @RequestMapping(value = "/.retrieveStockInfo.htm", method = RequestMethod.POST)
    public ModelAndView submitRetrieveStockInfo(@ModelAttribute("retrieveStockInfoCommand") RetrieveStockInfoCommand command, BindingResult result, HttpServletRequest request) throws Exception {

        Global.poolMultiple.execute(threadRetrieveStockInfoEuronext);
        return new ModelAndView(".retrieveStockInfo", "retrieveStockInfoCommand", command);
    }

全局类:

@SuppressWarnings("unchecked")
@Component
public class Global {

    // create ExecutorService to manage threads
    public static ExecutorService poolMultiple = Executors.newFixedThreadPool(10);
    public static ExecutorService poolSingle = Executors.newFixedThreadPool(1);
...
}

运行应用时,出现如下异常:

java.lang.IllegalArgumentException:无法设置 com.chartinvest.admin.thread.ThreadRetrieveStockInfoEuronext 字段

com.chartinvest.controller.RetrieveController.threadRetrieveStockInfoEuronext 到 com.sun.proxy.$Proxy52

【问题讨论】:

    标签: java spring hibernate jpa transactional


    【解决方案1】:

    这是因为您的 Runnable 实现是一个 Spring 组件,使用 @Transactional 注释,并且在 Spring 中,通过将类的实际实例包装在处理事务的基于接口的 JDK 代理中来实现声明性事务。因此,自动装配的实际对象不是 ThreadRetrieveStockInfoEuronext 的实例,而是其接口 Runnable 的实例,它委托给 ThreadRetrieveStockInfoEuronext 的实例。

    解决这个问题的通常方法是自动装配接口而不是自动装配具体类型。但是在这种情况下,这个类首先不应该是 Spring 组件,也不应该是事务性的。顺便说一句,将其设为原型会给您一种错觉,即每次调用 submitRetrieveStockInfo() 时都会创建一个新实例,但这是不正确的:创建了一个实例并自动连接到 RetrieveController 单例中,因此对所有实例都使用相同的可运行对象对控制器的请求。

    只需将 ThreadRetrieveStockInfoEuronext 设为一个简单的类,使用 new 实例化它,将 Spring bean 作为参数传递,并使其 run() 方法调用该 Spring bean 的事务方法:

    @Transactional
    @Component
    public class ThreadRetrieveStockInfoEuronextImpl implements ThreadRetrieveStockInfoEuronext {
        @Override 
        void doSomething() { ... }
    }
    
    public class RetrievelController {
    
        @Autowired
        private ThreadRetrieveStockInfoEuronext threadRetrieveStockInfoEuronext;
    
        @RequestMapping(value = "/.retrieveStockInfo.htm", method = RequestMethod.POST)
        public ModelAndView submitRetrieveStockInfo(@ModelAttribute("retrieveStockInfoCommand") RetrieveStockInfoCommand command, BindingResult result, HttpServletRequest request) throws Exception {
    
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    threadRetrieveStockInfoEuronext.doSomething();
                }
            };
    
            Global.poolMultiple.execute(runnable);
            return new ModelAndView(".retrieveStockInfo", "retrieveStockInfoCommand", command);
        }
    

    【讨论】:

    • 非常感谢 JB 的建议,解决方案完美!
    猜你喜欢
    • 1970-01-01
    • 2016-12-26
    • 2011-08-07
    • 2019-02-21
    • 2018-10-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多