@Autowired ve statik yöntem


104

@AutowiredStatik bir yöntem içinden kullanılması gereken hizmetim var . Bunun yanlış olduğunu biliyorum, ancak çok fazla çalışma gerektireceği için mevcut tasarımı değiştiremiyorum, bu yüzden bunun için basit bir kesmeye ihtiyacım var. randomMethod()Statik olmayacak şekilde değiştiremem ve bu otomatik kablolu fasulyeyi kullanmam gerekiyor. Bunun nasıl yapılacağına dair bir ipucu var mı?

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

public class Boo {
    @Autowired
    Foo foo;

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

4
Statik bir yöntem, statik olmayan / örnek bir alana başvuruda bulunamaz.
Sotirios Delimanolis

19
ben bu konuyu yarattı yüzden, Autowired örneği statik yöntemle içinden erişilebilir olabilir bir yolu var ...
Taks

@Autowired'ı statik yöntemde kullanmak neden yanlış?
user59290

Yanıtlar:


154

Çözümlerden birini takip ederek bunu yapabilirsiniz:

Oluşturucu @Autowired kullanma

Bu yaklaşım, yapıcı parametreler olarak bazı fasulyeleri gerektiren fasulyeyi oluşturacaktır. Yapıcı kodu içinde, yapıcı yürütmesi için parametre olarak alınan değerle statik alanı ayarlarsınız. Örneklem:

@Component
public class Boo {

    private static Foo foo;

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

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

Değeri statik alana aktarmak için @PostConstruct kullanma

Buradaki fikir, fasulye yay ile yapılandırıldıktan sonra bir fasulyeyi statik bir alana teslim etmektir.

@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
bu güvenli bir çözüm mü?
Taks

2
İlk çözümü kullandım ve harika çalıştı, teşekkürler!
victorleduc

1
İlk çözüm @Qualifier'ın kullanımını desteklemiyor. Birkaç Depo kullanılıyorsa sorun olmaya devam eder.
user1767316

16
Statik yönteme erişilmeden önce kurucunun çağrılmasını ne garanti eder?
David Dombrowsky

2
init yöntemi, statik alanı değiştiren statik olmayan yöntem nedeniyle SonarQube hatasına neden olur.
jDub9

45

Statik uygulama bağlam erişimcisi yaklaşımıyla bu sorunu çözmeniz gerekir:

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

}

Daha sonra bean örneklerine statik bir şekilde erişebilirsiniz.

public class Boo {

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

}

Aslında tam olarak anlamadığım halde bu çözümü beğendim .. Sadece kafamı ilkbahardan alıyorum ve bir kod parçasını hızlı bir şekilde yeniden düzenlemem gerekiyor .. ve bu statik ve otomatik kablolu karıştırma meselesi .. bu çözüm ne kadar güvenli?
Taks

2
Statik aramaların kontrolünüz altında olması oldukça güvenlidir. En belirgin olumsuz yönü, getBeanbağlam başlatılmadan (NPE) önce veya çekirdekleriyle bağlam yok edildikten sonra arayacağınız olabilir . Bu yaklaşımın yararı , "çirkin" statik bağlam erişiminin tek bir yöntem / sınıf içine alınmasıdır.
Pavel Horal

1
Bu hayatımı kurtardı. Diğer yaklaşıma göre çok kullanışlıdır.
phoenix

6

Yapabileceğiniz şey @Autowiredbir ayarlayıcı yöntemdir ve yeni bir statik alan oluşturmasını sağlamaktır.

public class Boo {
    @Autowired
    Foo foo;

    static Foo staticFoo;   

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

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

Fasulye işlendiğinde, Spring Fooörnek alanına bir uygulama örneği enjekte edecektir foo. Daha sonra aynı Fooörneği setStaticFoo(), statik alanı ayarlamak için kullanılacak olan bağımsız değişken listesine de enjekte edecektir.

Bu korkunç bir çözümdür ve randomMethod()Spring'in bir örneğini işlemeden önce kullanmaya çalışırsanız başarısız olur Boo.


@PostConstruct yardımını kullanır mısınız?
Taks

@Taks Elbette, bu da işe yarıyor. Açık setStaticFoo()olduğu olmadan, Fooparametre.
Sotirios Delimanolis

Soru, daha güvenli kılacak olan .. :) ben .. bize herhangi yöntemleri çalıştırma izin vermeden önce her şeyi işlemek olacağını bahar düşünce
Taks

1
@Taks Gösterme şekliniz işe yaramıyor (sözde kod göstermediğiniz sürece). Bunun nasıl yapılacağına dair bir ipucu var mı? Aldığınız çoklu yanıtlar geçici çözümlerdir, ancak hepsi Spring sınıfınızı işleyene kadar statik alanı kullanamayacağınızla aynı soruna sahiptir (aslında bir yan etkisi olan bir örneği işliyor). Bu anlamda güvenli değil.
Sotirios Delimanolis

3

Berbat ama ApplicationContextAwarearayüzü kullanarak fasulyeyi elde edebilirsiniz . Gibi bir şey :

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

Bu , statik getBean yönteminden erişilirken Spring bağlamının başlatılmama olasılığını çözmek için @ Pavel'in cevabına dayanır :

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

Buradaki önemli parça initContextyöntemdir. Bağlamın her zaman başlatılmasını sağlar. Ancak, initContextkodunuz senkronize edilirken bunun bir çekişme noktası olacağını unutmayın . Uygulamanız büyük ölçüde paralelleştirilmişse (örneğin, yüksek trafikli bir sitenin arka ucu), bu sizin için iyi bir çözüm olmayabilir.


-2

AppContext'i kullanın. Bağlam dosyanızda bir fasulye oluşturduğunuzdan emin olun.

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

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

Bu nedir?? @Autowired ve
getBean

Sınıfı düzenli bir @Component yayına dönüştüremediğinizde olağandır, eski kodda çok olur.
carpinchosaurio
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.