Spring不调用原型Bean中的@Autowired setter [英] Spring not calling @Autowired setters in Prototype Beans

查看:88
本文介绍了Spring不调用原型Bean中的@Autowired setter的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

问题出现在运行spring-boot,java 8的k8s pod中(下面有更多详细信息)

The problem appears in a k8s pod running spring-boot, java 8 (more details below)

使用ObjectProvider<>并调用*provider.getObject(.....)*时, 在Spring Configuration中定义的原型bean,时不时地(从不 找到使它定期发生的方法)未调用二传手注入方法.

When using ObjectProvider<> and calling *provider.getObject(.....)*, on a prototype bean defined in Spring Configuration, every now and then (never find a way to make it happen regularly) setter injection methods are not called.

更新10月2日:参见春季问题#25840

在大多数情况下,它可以很好地工作,但是有时,它会构造一个新对象 但没有调用@Autowired setter方法(已检查日志信息).

Most of the time this works perfectly well, but sometimes, it constructs a new object but misses to call the @Autowired setter method (checked with log information).

我们还发现90%的时间发生在应用程序启动后,但并非总是如此.

We also discover that 90% of the time it happens after application startup, but not always.

9月21日更新:删除(重新启动)de pod解决了该问题.

UPDATE sept, 21: Deleting (Restarting) de pod solves the problem.

更新日期为9月22日:一旦发生,它将一直发生,我的意思是不是一次失败,而下次它运行正常时,它将始终无法调用设置方法.

UPDATE sept, 22: Once it happens it will happen all the time, I mean is not that it fails once, and the next time it works ok, it will always fail to call setters.

9月23日更新:与真实应用程序中的并发问题有关的新证据直到现在都没有出现,似乎只有一个线程 产生问题. (请看下面的课程,以更好地理解 此说明)

UPDATE sept, 23: New evidence related to concurrency problem in the real application, didn't appear until now, a single thread alone seemed to generate the problem. (take a look at classes below to understand better this description)

ToipParentClass (这是一个策略实现)已将@Autowired设置为

ToipParentClass (this is a Strategy Implementation) has setter @Autowired for

  • VlocityService
  • OrderManagemenService

InternetParentClass (这是一项策略实施)已将@Autowired设置为

InternetParentClass (this is a Strategy Implementation) has setter @Autowired for

  • VlocityService
  • OrderManagemenService

日志(注释)

[-nio-80-exec-10] GuidTaskController   : Build Strategy using XOM_TOIP_OFFER .                    
[p-nio-80-exec-2] GuidTaskController   : Build Strategy using XOM_INTERNET_OFFER .                    
[-nio-80-exec-10] ToipParentClass      : @Constructing ToipParentClass                                             
[p-nio-80-exec-2] InternetParentClass  : @Constructing InternetParentClass                                         
[-nio-80-exec-10] ToipParentClass      : @Autowiring VlocityServices@7951cd46                                      
[p-nio-80-exec-2] InternetParentClass  : @Autowiring VlocityServices@7951cd46                                      
[-nio-80-exec-10] ToipParentClass      : @Autowiring OrderManagementService@3385326a     
-------------------------------------------------------------
ERROR !!! Missing @Autowiring log  
[p-nio-80-exec-2] InternetParentClass  : @Autowiring OrderManagementService@7951cd46                      
-------------------------------------------------------------
[p-nio-80-exec-2] Controller           : Unexpected Error
2020-09-22T18:56:45.544525916Z
-------------------------------------------------------------
ERROR: NullPointer when using not set OrderManagementService
-------------------------------------------------------------
2020-09-22T18:56:45.544530395Z java.lang.NullPointerException: null
2020-09-22T18:56:45.544534074Z  at InternetParentClass.generateIds(InternetParentClass.java:50) ~[classes!/:BUG001_PrototypeBeanAutowired-8]                                       
2020-09-22T18:56:45.544538568Z  at GuidTaskController.setGUID(GuidTaskController.java:180) ~[classes!/:BUG001_PrototypeBeanAutowired-8]

我在 https://github.com/toniocus/strategy-calculator以单独的,不同的jdk8版本运行它,并在pod中使用的同一docker映像(项目中的所有内容)中运行,但未能使其失败.

I made a simple test in https://github.com/toniocus/strategy-calculator run it standalone, different jdk8 versions, also in the same docker image used in the pod (all things in the project), and failed to make it FAIL.

关于在哪里寻找问题的任何想法,关于尝试什么的建议,甚至 一个解决方案:-),将非常欢迎,在此先感谢

Any ideas on where to look for a problem, suggestions on what to try, or even a solution :-), will be greatly welcome, thanks in advance

在产品版本,类别下面.

Below the product versions, classes.

k8s:

v1.14.9-eks-658790

spring-boot:

2.1.4.RELEASE

JDK:

  openjdk version "1.8.0_212"
  OpenJDK Runtime Environment (IcedTea 3.12.0) (Alpine 8.212.04-r0)
  OpenJDK 64-Bit Server VM (build 25.212-b04, mixed mode)

课程

所涉及的类的代码段,没有真实的代码,不用担心语法错误. (可以在 https://github.com/toniocus/strategy-calculator 中找到真实代码)

The Classes

A snippet on the classes involved, no real code, do not worry about syntax errors. (real code can be found in https://github.com/toniocus/strategy-calculator)

// ============================================================
public interface Strategy {
   void work(...);
}

// ============================================================
@Service
public class Service {
    public void doSomething() {
       ......
       ......
    }
}

// ============================================================
public class MobileStrategy implements Strategy {
   private Service service;

   @Autowired
   public void setService(Service s) {
       this.service = s;   // setter not called every now and then
   }



   public void work(...) {
       this.service.doSomething();  // throws NullPointerException every now an then
   }
}

// ============================================================
public enum StrategyEnum {

    MOBILE("mobileKey", MobileStrategy.class),
    TV("tvKey", TvStrategy.class),
    .......

    public Class<Strategy> getImplementationClass() {
       ......
    }

    public StrategyEnum findByKey(String key) {
       .....
    }
}

// ============================================================
@Configuration
public class Configuration {

    @Bean
    @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public Strategy getStrategy(final StrategyEnum selectorEnum) {

        try {

            Constructor<? extends Strategy> constructor =
                    selectorEnum.getImplementationClass().getConstructor();

            return constructor.newInstance();
        }
        catch (Exception ex) {

            throw new Exception("Not able to instantiate interface implementation"
                    + " for enum: " + selectorEnum
                    , ex);
        }

    }
}


// ============================================================
@RestController
public class MathOperRestController {

    @Autowired
    ObjectProvider<Strategy> provider;

    @GetMapping("/{operation}/{x}/{y}")
    public BigDecimal add(
            @PathVariable("operation") final String operation
            , @PathVariable("x") final BigDecimal x
            , @PathVariable("y") final BigDecimal y
            ) {

        Strategy strategy = this.provider.getObject(StrategyEnum.findByKey(operation));
        strategy.doWork(x, y);

        return BigDecimal.ZERO;
    }

9月21日更新将Service类添加到示例

UPDATE sept,21 added Service class to samples

推荐答案

我发现的问题

更新10月1日:请阅读问题中的@Denium评论!!!! (为此).

The problem I found

Update oct, 1: Please read @Denium comment in the question !!!! (thanks for it).

在今天(9月23日)更新之后,显然该问题是并发问题,我能够轻松地重现它(请参阅

After today's (Sept, 23) update, seems clearly the problem is a concurrency problem, I was able to reproduce it easily (See SimpleSpringAppTest.java) in a Simple Spring app, no spring-boot.

使所有Strategy实施都具有相同的@Autowired设置器集 而且错误仍然存​​在.

Made all Strategy implementations have the same set of @Autowired setters and the error is still there.

似乎已经完成了一些缓存,并且@Autowired设置器是从缓存中获取的,而不是从新构造的对象中获取的,尽管我试图在这么短的时间内挖掘难以理解的弹簧源.

Seems there is some caching done, and @Autowired setters are taken from the cache, and not from the newly constructed object, although I try to dig into spring sources difficult to understand in so short time.

问题已解决,避免了并发(下面的更改),所以现在是我的问题:

The problem was solved, avoiding concurrency (changes below), so now my question:

这是预期的行为还是错误?

我找不到任何有关此问题的文档或在任何地方描述此行为,因此仍然是我的问题.

I was not able to find any documentation regarding this problem or describing this behaviour anywhere, so still a question for me.

我在spring-boot版本1.5.22、2.1.4(我们当前使用的版本)和2.3.4中进行了测试,并且在所有情况下都出现了相同的问题,仅在一个简单的spring应用程序中,不需要RestController或如此.

I tested in spring-boot versions 1.5.22, 2.1.4 (what we are currently using), and 2.3.4 and in all cases the same problem appears, just in a simple spring application, no need of RestController or so.

添加一个中间工厂,以确保同步"创建策略bean.

Add an intermediate Factory to ensure Strategy beans are created 'synchronously'.

更新十月1:从@Deinum注释开始,据我了解,Spring将每次(或几乎每次)扫描类以查找批注,因此我猜想变通方法2可能更好解决方案.

Update oct, 1: After @Deinum comments, from what I understood, Spring will be scanning classes every time (or almost every time) for annotations, so I guess Workaround 2 is probably a better solution.

此解决方案更适合我当前的环境.

This solution is more suitable for my current environment.

请注意getStrategy(...)方法是同步的,我想这个解决方案 将对性能产生一些影响,但仍然无法衡量.

Note the getStrategy(...) method is synchronized, I guess this solution will have some performance impact, but still not able to measure it.

@Component
public class StrategyFactory {

    @Autowired
    ObjectProvider<Strategy> provider;

    public synchronized Strategy getStrategy(final MathOperEnum operation) {
        return this.provider.getObject(operation);
    }

}

RestController中的更改

现在使用StrategyFactory代替ObjectProvider

Changes in RestController

Now using the StrategyFactory instead of the ObjectProvider

@RestController
public class MathOperRestController {

    @Autowired
    StrategyFactory factory;

    @GetMapping("/{operation}/{x}/{y}")
    public BigDecimal add(
            @PathVariable("operation") final String operation
            , @PathVariable("x") final BigDecimal x
            , @PathVariable("y") final BigDecimal y
            ) {

        Strategy strategy = this.factory
             .getStrategy(StrategyEnum.findByKey(operation));

        strategy.doWork(x, y);

        return BigDecimal.ZERO;
    }
}

解决方法2

  • 使StrategyFactory ApplicationContextAware
  • 在每个策略实施中添加@ Componente/@ Scope批注
  • 删除@Configuration类
  • @Component
    public class StrategyFactory implements ApplicationContextAware {
    
        private ApplicationContext ctx;
    
    
        public Strategy getStrategy(final StrategyEnum operation) {
            return this.ctx.getBean(
                   operation.getImplementationClass()
                   );
        }
    
    
        @Override
        public void setApplicationContext(
             final ApplicationContext applicationContext) 
        throws BeansException {
    
            this.ctx = applicationContext;
    
        }
    
    }
    

    这篇关于Spring不调用原型Bean中的@Autowired setter的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆