web-dev-qa-db-fra.com

@ Méthode automatique et statique

J'ai le service @Autowired Qui doit être utilisé depuis une méthode statique. Je sais que c'est faux, mais je ne peux pas changer le design actuel, car cela demanderait beaucoup de travail. J'ai donc besoin d'un simple piratage pour cela. Je ne peux pas changer randomMethod() pour qu'il soit non statique et je dois utiliser ce bean autowired. Des indices comment faire ça?

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

public class Boo {
    @Autowired
    Foo foo;

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

Vous pouvez le faire en suivant l'une des solutions:

Utilisation du constructeur @Autowired

Cette approche construira le haricot nécessitant des haricots en tant que paramètres de constructeur. Dans le code constructeur, vous définissez le champ statique avec la valeur obtenue en tant que paramètre pour l'exécution du constructeur. Échantillon:

@Component
public class Boo {

    private static Foo foo;

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

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

Utiliser @PostConstruct pour transférer la valeur sur un champ statique

L'idée ici est de remettre un haricot à un champ statique après que le haricot ait été configuré par le printemps.

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

Vous devez contourner ce problème via une approche d'accesseur de contexte d'application statique:

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

}

Ensuite, vous pouvez accéder aux instances de bean de manière statique.

public class Boo {

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

}
40
Pavel Horal

Ce que vous pouvez faire, c'est @Autowired Une méthode de définition et la définir avec un nouveau champ statique.

public class Boo {
    @Autowired
    Foo foo;

    static Foo staticFoo;   

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

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

Lorsque le bean est traité, Spring injecte une instance de mise en œuvre Foo dans le champ d’instance foo. Il injectera également la même instance Foo dans la liste d'arguments setStaticFoo(), qui sera utilisée pour définir le champ statique.

Cette solution de contournement est terrible et échouera si vous essayez d'utiliser randomMethod() avant que Spring n'ait traité une instance de Boo.

6

C'est nul, mais vous pouvez obtenir le haricot en utilisant l'interface ApplicationContextAware. Quelque chose comme :

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;
    }
}
3
Jean-Philippe Bond

Ceci s'appuie sur @ Pavel's answer , pour résoudre la possibilité que le contexte Spring ne soit pas initialisé lors de l'accès à partir de la méthode statique 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 ();
  }
}

La pièce importante ici est la méthode initContext. Cela garantit que le contexte sera toujours initialisé. Cependant, notez que initContext sera un point de discorde dans votre code lorsqu’il sera synchronisé. Si votre application est fortement parallélisée (par exemple: le backend d'un site à fort trafic), cela pourrait ne pas être une bonne solution pour vous.

0
Hashken