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.