119

我有必须@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

7 回答 7

175

您可以通过以下解决方案之一执行此操作:

使用构造函数@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();
    }
}
于 2013-07-15T17:48:19.843 回答
50

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

@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();
    }

}
于 2013-07-15T17:26:37.730 回答
8

您可以做的是@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.

于 2013-07-15T17:25:37.623 回答
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;
    }
}
于 2013-07-15T17:25:51.070 回答
2

创建静态上下文的最简单方法自然是在应用程序启动时。这将防止需要使用附加类进行不自然的实现。

@SpringBootApplication
public class MyApplication {

    private static ApplicationContext appContext;


    public static void main(String[] args) {
        appContext = SpringApplication.run(MyApplication.class, args);
    }

    public static ApplicationContext getAppContext() {
        return appContext;
    }
}

然后,在任何需要静态访问 bean 的地方,都可以使用 ApplicationContext 来获取类的实例。

public class Boo {
    public static void randomMethod() {
         MyApplication.getAppContext()
                            .getBean(Foo.class).doStuff();
    }
}

问候..

于 2021-07-08T02:41:22.963 回答
0

这建立在@Pavel 的答案之上,以解决从静态 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将是您的代码中的一个争论点,因为它是同步的。如果您的应用程序高度并行化(例如:高流量站点的后端),这对您来说可能不是一个好的解决方案。

于 2019-03-01T03:51:59.870 回答
-2

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

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

public static void randomMethod() {
     foo.doStuff();
}
于 2014-06-03T12:11:21.913 回答