Como injetar dependências em um objeto auto-instanciado no Spring?


128

Digamos que temos uma aula:

public class MyClass {
    @Autowired private AnotherBean anotherBean;
}

Em seguida, criamos um objeto dessa classe (ou alguma outra estrutura criou a instância dessa classe).

MyClass obj = new MyClass();

Ainda é possível injetar as dependências? Algo como:

applicationContext.injectDependencies(obj);

(Acho que o Google Guice tem algo assim)

Respostas:


194

Você pode fazer isso usando o autowireBean()método de AutowireCapableBeanFactory. Você o passa como um objeto arbitrário, e o Spring o tratará como algo que ele próprio criou e aplicará os vários bits e partes da fiação automática.

Para se apossar do AutowireCapableBeanFactory, basta autowire que:

private @Autowired AutowireCapableBeanFactory beanFactory;

public void doStuff() {
   MyBean obj = new MyBean();
   beanFactory.autowireBean(obj);
   // obj will now have its dependencies autowired.
}

Boa resposta (+1). Há também um segundo método, onde pode influenciar a forma como o autowiring acontece: static.springsource.org/spring/docs/3.0.x/javadoc-api/org/...
— Sean Patrick Floyd

Mas e se eu tiver dois objetos, e o primeiro autowires em segundo. Como a fábrica de feijão autowire lida com dependências no caso?
— Vadim Kirilchuk

3
Este é realmente um padrão ruim. Se é assim que você realmente usa o MyBean, por que não apenas ter um construtor com o AnotherBean como parâmetro. Algo como: codeprivate @Autowired AnotherBean bean; public void doStuff () {Objeto MyBean = novo MyBean (bean); } code. Parece ser como com todas essas anotações pessoas ficam realmente confuso e simplesmente não usam o padrão básico que estavam no SDK Java desde o dia 1. :(
— Denis

3
Eu concordo - a Primavera redefiniu o idioma inteiro. Agora usamos interfaces como classes concretas, métodos como instâncias de classe e todo tipo de métodos complicados e pesados ​​de código para fazer o que você costumava fazer com um design novo e decente.
— Rodney P. Barbati

@ Denis, se o MyBean tiver dependências que a classe real não precisa, você estará registrando dependências que na verdade não existem, apenas para instanciar uma classe, então não há realmente nenhuma diferença.
— 21716 Dalton

17

Você também pode marcar seu MyClass com a anotação @Configurable:

@Configurable
public class MyClass {
   @Autowired private AnotherClass instance
}

Então, no momento da criação, ele injeta automaticamente suas dependências. Você também deve ter <context:spring-configured/>em seu contexto de aplicativo xml.


2
Galz666, seu método parece muito mais limpo para o que eu quero fazer. No entanto, não consigo fazê-lo funcionar. Não tenho arquivo xml e estou usando inteiramente a configuração do Java. Existe um equivalente a <context:spring-configured/>?
— masstroy

1
Esta é uma solução limpa, mas requer um pouco mais de esforço: você deve usar a tecelagem em tempo de carregamento, como mostra o iimuhin acima, ou adicionar o compilador AspectJ ao projeto. O tempo de carregamento, como o nome sugere, causará custos adicionais no tempo de execução.
— precisa saber é o seguinte

4

Acabei de ter a mesma necessidade e, no meu caso, já era a lógica dentro da classe java não gerenciável do Spring que tinha acesso ApplicationContext. Inspirado por scaffman. Resolvido por:

AutowireCapableBeanFactory factory = applicationContext.getAutowireCapableBeanFactory();
factory.autowireBean(manuallyCreatedInstance);

3

Eu queria compartilhar minha solução que segue a @Configurableabordagem brieflymencionada na resposta @ glaz666 porque

  • A resposta de @skaffman tem quase 10 anos e isso não significa que não seja bom o suficiente ou que não funcione
  • A resposta de @ glaz666 é breve e realmente não me ajudou a resolver meu problema, mas me indicou a direção certa

Minha configuração

  1. Spring Boot 2.0.3 com Spring Neo4j & Aop starts(que é irrelevante de qualquer maneira)
  2. Instanciar um bean quando Spring Bootestiver pronto usando a @Configurableabordagem (usando ApplicationRunner)
  3. Gradle & Eclipse

Passos

Eu precisava seguir as etapas abaixo para fazê-lo funcionar

  1. O @Configurable(preConstruction = true, autowire = Autowire.BY_TYPE, dependencyCheck = false)a ser colocado em cima do seu Beanque deve ser instanciado manualmente. No meu caso, o Beanque deve ser instanciado manualmente possui @Autowiredserviços, portanto, os adereços para a anotação acima.
  2. Anotar principal da Primavera de inicialização XXXApplicaiton.java(ou o arquivo que é anotado com @SpringBootApplication) com o @EnableSpringConfigurede@EnableLoadTimeWeaving(aspectjWeaving=AspectJWeaving.ENABLED)
  3. Adicione as dependências no seu arquivo de construção (por exemplo, build.gradle ou pom.xml, dependendo de qual você usa) compile('org.springframework.boot:spring-boot-starter-aop')ecompile('org.springframework:spring-aspects:5.0.7.RELEASE')
  4. O novo + Beanque está anotado em @Configurablequalquer lugar e suas dependências devem ser conectadas automaticamente.

* No que diz respeito ao ponto 3 acima, estou ciente de que o org.springframework.boot:spring-boot-starter-aoppuxa transitivamente o spring-aop(como mostrado aqui a seguir ), mas, no meu caso, o Eclipse falhou ao resolver as @EnableSpringConfiguredanotações, por isso adicionei explicitamente a spring-aopdependência além do iniciador. Se você enfrentar o mesmo problema, apenas declare a dependência ou aventure-se a descobrir

  • Existe um conflito de versão
  • Por que o org.springframework.context.annotation.aspect.*não está disponível
  • O seu IDE está configurado corretamente
  • Etc etc.
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.