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();
}
}
Vous pouvez le faire en suivant l'une des solutions:
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();
}
}
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();
}
}
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();
}
}
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
.
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;
}
}
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.