@ วิธีการทำงานอัตโนมัติและแบบคงที่


104

ฉันมี@Autowiredบริการที่ต้องใช้จากวิธีการคงที่ ฉันรู้ว่านี่ผิด แต่ฉันไม่สามารถเปลี่ยนการออกแบบปัจจุบันได้เนื่องจากต้องใช้งานมากดังนั้นฉันจึงต้องการแฮ็คง่ายๆสำหรับสิ่งนั้น ฉันไม่สามารถเปลี่ยนrandomMethod()เป็นไม่คงที่และฉันจำเป็นต้องใช้ถั่วอัตโนมัตินี้ มีเบาะแสวิธีการทำอย่างไร?

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

public class Boo {
    @Autowired
    Foo foo;

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

4
วิธีการแบบคงที่ไม่สามารถอ้างอิงฟิลด์ที่ไม่คงที่ / อินสแตนซ์
Sotirios Delimanolis

19
นั่นคือเหตุผลที่ฉันสร้างเธรดนี้มีวิธีที่สามารถเข้าถึงอินสแตนซ์แบบอัตโนมัติได้จากภายในวิธีการแบบคงที่ ...
ตาก

เหตุใดการใช้ @Autowired ในวิธีการคงที่ผิด
user59290

คำตอบ:


154

คุณสามารถทำได้โดยทำตามวิธีแก้ปัญหาข้อใดข้อหนึ่ง:

ใช้ตัวสร้าง @Autowired

วิธีนี้จะสร้างถั่วที่ต้องใช้ถั่วเป็นพารามิเตอร์ตัวสร้าง ภายในโค้ดคอนสตรัคเตอร์คุณตั้งค่าฟิลด์สแตติกด้วยค่า got เป็นพารามิเตอร์สำหรับการเรียกใช้คอนสตรัคเตอร์ ตัวอย่าง:

@Component
public class Boo {

    private static Foo foo;

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

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

ใช้ @PostConstruct เพื่อส่งค่าไปยังฟิลด์คงที่

แนวคิดต่อไปนี้คือการส่งมอบ bean ให้กับฟิลด์แบบคงที่หลังจากที่กำหนดค่า bean โดย spring

@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
นี่เป็นวิธีที่ปลอดภัยหรือไม่?
ตาก

2
ฉันใช้วิธีแก้ปัญหาแรกและได้ผลอย่างมีเสน่ห์ขอบคุณ!
victorleduc

1
โซลูชันแรกไม่รองรับการใช้งาน @Qualifier ยังคงเป็นปัญหาหากใช้หลาย Repository
user1767316

16
อะไรจะรับประกันว่าตัวสร้างถูกเรียกก่อนที่จะเข้าถึงวิธีการแบบคงที่
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();
    }

}

ฉันชอบวิธีแก้ปัญหานี้จริง ๆ แม้ว่าฉันจะไม่เข้าใจอย่างถ่องแท้ก็ตาม .. ฉันเพิ่งจะหัวสปริงและฉันจำเป็นต้อง refactor โค้ดบางส่วนอย่างรวดเร็ว .. และนี่คือปัญหาของการผสมแบบคงที่กับระบบอัตโนมัติ .. วิธีนี้ปลอดภัยแค่ไหน?
ตาก

2
ค่อนข้างปลอดภัยหากการโทรแบบคงที่อยู่ภายใต้การควบคุมของคุณ ด้านลบที่ชัดเจนที่สุดคืออาจเกิดขึ้นได้ที่คุณจะโทรgetBeanก่อนบริบทจะเริ่มต้น (NPE) หรือหลังจากบริบทที่มีถั่วถูกทำลาย วิธีนี้มีประโยชน์ตรงที่การเข้าถึงบริบทคงที่ "น่าเกลียด"ถูกรวมไว้ในเมธอด / คลาสเดียว
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();
    }
}

เมื่อถั่วที่ได้รับการประมวลผล, ฤดูใบไม้ผลิจะฉีดตัวอย่างเช่นการดำเนินการเข้าไปในเขตอินสแตนซ์Foo fooจากนั้นจะฉีดFooอินสแตนซ์เดียวกันลงในไฟล์setStaticFoo()รายการอาร์กิวเมนต์ซึ่งจะใช้ในการตั้งค่าฟิลด์คงที่

นี่เป็นวิธีแก้ปัญหาที่แย่มากและจะล้มเหลวหากคุณพยายามใช้randomMethod()ก่อนที่ Spring จะประมวลผลอินสแตนซ์ของBooไฟล์.


จะใช้ @PostConstruct ช่วยไหม
ตาก

@Taks Sure ทำงานเกินไป เมื่อวันsetStaticFoo()ที่เป็นโดยไม่ต้องFooพารามิเตอร์
Sotirios Delimanolis

คำถามคือมันจะทำให้ปลอดภัยขึ้นไหม .. :) ฉันคิดว่าฤดูใบไม้ผลิจะดำเนินการทุกอย่างก่อนที่จะอนุญาตให้เราดำเนินการวิธีการใด ๆ ..
ตาก

1
@Taks วิธีที่คุณแสดงมันไม่ได้ผล (เว้นแต่คุณจะแสดงรหัสหลอก) มีเบาะแสวิธีการทำอย่างไร? คำตอบหลายคำตอบที่คุณได้รับเป็นวิธีแก้ปัญหา แต่ทั้งหมดมีปัญหาเดียวกันคือคุณไม่สามารถใช้ฟิลด์คงที่จนกว่า Spring จะประมวลผลชั้นเรียนของคุณ (จริง ๆ แล้วการประมวลผลหนึ่งอินสแตนซ์ที่มีผลข้างเคียง) ในแง่นั้นมันไม่ปลอดภัย
Sotirios Delimanolis

3

มันห่วย แต่คุณสามารถรับถั่วได้โดยใช้ApplicationContextAwareอินเทอร์เฟซ สิ่งที่ต้องการ :

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เพื่อแก้ปัญหาความเป็นไปได้ที่บริบท Spring จะไม่ถูกเตรียมใช้งานเมื่อเข้าถึงจากเมธอด getBean แบบคงที่:

@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

เป็นเรื่องปกติเมื่อคุณไม่สามารถเปลี่ยนคลาสให้เป็น Spring @Component ปกติได้มันจะเกิดขึ้นมากมายกับรหัสเดิม
carpinchosaurio
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.