@Autowired和静态方法


100

我有@Autowired必须从静态方法中使用的服务。我知道这是错误的,但是我无法更改当前的设计,因为这需要大量的工作,因此我需要一些简单的技巧。我不能更改randomMethod()为非静态,而需要使用此自动装配的bean。有什么线索怎么做?

@Service
public class Foo {
    public int doStuff() {
        return 1;
    }
}

public class Boo {
    @Autowired
    Foo foo;

    public static void randomMethod() {
         foo.doStuff();
    }
}

4
静态方法不能引用非静态/实例字段。
Sotirios Delimanolis

18
这就是为什么我创建此线程的原因,有没有一种方法可以从静态方法中访问自动
装配

为什么在静态方法中使用@Autowired错误?
user59290

Answers:


151

您可以通过执行以下解决方案之一来做到这一点:

使用构造函数@Autowired

这种方法将构造需要一些bean作为构造函数参数的bean。在构造函数代码中,您可以将静态字段的值设置为构造函数执行的参数。样品:

@Component
public class Boo {

    private static Foo foo;

    @Autowired
    public Boo(Foo foo) {
        Boo.foo = foo;
    }

    public static void randomMethod() {
         foo.doStuff();
    }
}

使用@PostConstruct将值移交给静态字段

这里的想法是在spring配置bean之后将bean移交给静态字段。

@Component
public class Boo {

    private static Foo foo;
    @Autowired
    private Foo tFoo;

    @PostConstruct
    public void init() {
        Boo.foo = tFoo;
    }

    public static void randomMethod() {
         foo.doStuff();
    }
}

3
这是安全的解决方案吗?
Taks

2
我使用了第一个解决方案,它的工作原理很吸引人,谢谢!
victorleduc '16

1
第一个解决方案不支持使用@Qualifier。如果使用多个存储库仍然存在问题。
user1767316 '16

15
什么可以保证在访问静态方法之前调用构造函数?
David Dombrowsky

2
初始化方法将导致SonarQube错误,因为非静态方法会修改静态字段。
jDub9

45

您必须通过静态应用程序上下文访问器方法解决此问题:

@Component
public class StaticContextAccessor {

    private static StaticContextAccessor instance;

    @Autowired
    private ApplicationContext applicationContext;

    @PostConstruct
    public void registerInstance() {
        instance = this;
    }

    public static <T> T getBean(Class<T> clazz) {
        return instance.applicationContext.getBean(clazz);
    }

}

然后,您可以以静态方式访问bean实例。

public class Boo {

    public static void randomMethod() {
         StaticContextAccessor.getBean(Foo.class).doStuff();
    }

}

尽管我不太了解它,但我实际上还是喜欢此解决方案。.我只是想着弹簧,我需要快速重构一些代码..这是将静态与自动装配混合在一起的问题。.该解决方案的安全性如何?
Taks

2
如果您可以控制静态调用,那将是相当安全的。最明显的负面影响是,您可能会getBean在初始化上下文(NPE)之前或在销毁带有其bean的上下文之后调用它。这种方法的好处是“丑陋”的静态上下文访问被封装在一个方法/类中。
Pavel Horal

1
这救了我一命。它比其他方法非常有用。
凤凰城

6

您可以做的是@Autowired一个setter方法,并让它设置一个新的静态字段。

public class Boo {
    @Autowired
    Foo foo;

    static Foo staticFoo;   

    @Autowired
    public void setStaticFoo(Foo foo) {
        Boo.staticFoo = foo;
    }

    public static void randomMethod() {
         staticFoo.doStuff();
    }
}

当处理完bean时,Spring会将一个Foo实现实例注入到instance字段中foo。然后它将同样的Foo实例注入到setStaticFoo()参数列表中,这将用于设置静态字段。

这是一个糟糕的解决方法,如果您尝试randomMethod()在Spring处理的实例之前使用,则会失败Boo


将使用@PostConstruct帮助吗?
Taks

@Taks当然,也可以。在setStaticFoo()那是,没有Foo参数。
Sotirios Delimanolis

问题是它会使它更安全.. :)我以为Spring会在允许我们执行任何方法之前先处理所有内容
。– Taks

1
@Taks显示方式无效(除非您显示伪代码)。有什么线索怎么做?您得到的多个答案是变通办法,但是它们都有相同的问题,即在Spring处理您的类(实际上是处理一个有副作用的实例)之前,您不能使用static字段。从这个意义上讲,这是不安全的。
Sotirios Delimanolis

3

很烂,但是您可以通过使用ApplicationContextAware接口来获取bean 。就像是 :

public class Boo implements ApplicationContextAware {

    private static ApplicationContext appContext;

    @Autowired
    Foo foo;

    public static void randomMethod() {
         Foo fooInstance = appContext.getBean(Foo.class);
         fooInstance.doStuff();
    }

    @Override
    public void setApplicationContext(ApplicationContext appContext) {
        Boo.appContext = appContext;
    }
}

0

这建立在@Pavel的answer的基础上,解决了从静态getBean方法访问时Spring上下文未初始化的可能性:

@Component
public class Spring {
  private static final Logger LOG = LoggerFactory.getLogger (Spring.class);

  private static Spring spring;

  @Autowired
  private ApplicationContext context;

  @PostConstruct
  public void registerInstance () {
    spring = this;
  }

  private Spring (ApplicationContext context) {
    this.context = context;
  }

  private static synchronized void initContext () {
    if (spring == null) {
      LOG.info ("Initializing Spring Context...");
      ApplicationContext context = new AnnotationConfigApplicationContext (io.zeniq.spring.BaseConfig.class);
      spring = new Spring (context);
    }
  }

  public static <T> T getBean(String name, Class<T> className) throws BeansException {
    initContext();
    return spring.context.getBean(name, className);
  }

  public static <T> T getBean(Class<T> className) throws BeansException {
    initContext();
    return spring.context.getBean(className);
  }

  public static AutowireCapableBeanFactory getBeanFactory() throws IllegalStateException {
    initContext();
    return spring.context.getAutowireCapableBeanFactory ();
  }
}

这里重要的是initContext方法。它确保上下文将始终被初始化。但是,请注意,initContext由于同步,这将是代码中的争用点。如果您的应用程序高度并行化(例如:高流量站点的后端),那么这可能不是您的理想解决方案。


-2

使用AppContext。确保在上下文文件中创建一个bean。

private final static Foo foo = AppContext.getApplicationContext().getBean(Foo.class);

public static void randomMethod() {
     foo.doStuff();
}

这是什么??@Autowired和getBean有什么区别
madhairsilence 2016年

通常,当您不能将类转换为常规的spring @Component时,使用遗留代码会发生很多情况。
carpinchosaurio
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.