Diretório de artigos
A diferença entre os três métodos de injeção do @Autowired
Ao usar a anotação @Autowired para injeção de dependência no Spring, geralmente existem três métodos de injeção:
- Construtores autowired (injeção de construtor)
- Métodos Autowired (injeção de setter)
- Campos Autowired (injeção de propriedade)
@Autowired três métodos de injeção
1. Injeção de construtor
public class MovieRecommender {
private final CustomerPreferenceDao customerPreferenceDao;
@Autowired
public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
this.customerPreferenceDao = customerPreferenceDao;
}
// ...
}
Porque o ciclo de declaração do Bean tem aproximadamente os seguintes estágios: instanciação -> atribuição de atributos -> inicialização -> destruição . Ou seja, o método construtor precisa ser executado primeiro e depois a injeção de dependência de atributo é executada.Durante o processo de instanciação, se o objeto de instância correspondente ao parâmetro de construção não puder ser encontrado no contêiner ioc, a função construtora não será executada Isso garante que, enquanto um objeto for instanciado, todas as suas dependências não serão nulas (desde que todas as variáveis-membro sejam injetadas como parâmetros de construção ou tenham valores iniciais anexados a elas). Portanto, nesta forma de uso, podem ser encontrados problemas e lançadas exceções durante a fase de instanciação do objeto , que é diferente da injeção de atributos mencionada abaixo.
A partir do Spring Framework 4.3, se o bean de destino definir inicialmente apenas um construtor, não será necessário usar a anotação @Autowired nesse construtor. No entanto, se vários construtores estiverem disponíveis e não houver nenhum construtor padrão, pelo menos um dos construtores deverá ser anotado com @Autowired para instruir o contêiner sobre qual deles usar.
A anotação Lombok implementa injeção de construtor
Se você achar difícil escrever injeção de construtor, você pode usar a anotação @RequiredArgsConstructor do lombok para gerar automaticamente um construtor com @Autowired.
Nota: As dependências injetadas pelo construtor usando o lombok devem ser finais ou decoradas com @NonNull, caso contrário o lombok não irá injetar essas dependências.
@RequiredArgsConstructor(onConstructor=@_(@Autowired))
@Controller
public class BasicController {
private final UserService userService;
@RequestMapping("/hello")
@ResponseBody
public String hello(@RequestParam(name = "name", defaultValue = "unknown user") String name) {
return "Hello " + name;
}
// ...
}
2. Injeção de setter
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Autowired
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// ...
}
A injeção do setter será realizada após o processo de instanciação. Embora em teoria isso possa fazer com que a dependência seja nula, na verdade o Spring não atribuirá valores nulos à dependência. Se a dependência não existir no contêiner ioc, Spring lançará uma exceção. Portanto, a injeção de setter e a injeção de construtor têm o mesmo efeito em termos de uso e lançarão exceções no tempo. A diferença é que o tempo de injeção de dependência é diferente, um está na fase de instanciação e o outro está em fase de cessão de imóvel.
A injeção de setter não tem requisitos específicos quanto ao nome do método e ao número de parâmetros do método, como:
public class MovieRecommender {
private MovieCatalog movieCatalog;
private CustomerPreferenceDao customerPreferenceDao;
@Autowired
public void prepare(MovieCatalog movieCatalog,
CustomerPreferenceDao customerPreferenceDao) {
this.movieCatalog = movieCatalog;
this.customerPreferenceDao = customerPreferenceDao;
}
// ...
}
O exemplo acima ainda pertence a uma injeção setter.
3. Injeção de atributos
public class MovieRecommender {
private final CustomerPreferenceDao customerPreferenceDao;
@Autowired
private MovieCatalog movieCatalog;
@Autowired
public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
this.customerPreferenceDao = customerPreferenceDao;
}
// ...
}
A injeção de propriedade e a injeção de construtor podem ser usadas ao mesmo tempo. A injeção de atributos será lembrada pela ideia Field injection is not recommended
.
Tempo de execução @Autowired: A injeção de propriedade é essencialmente uma injeção direta no campo por meio de reflexão. Como o método de reflexão pode ser chamado para injeção somente após a criação do objeto, o tempo de execução é após a criação do objeto.
Pode haver alguns perigos ocultos nesta abordagem:
Pergunta um
@Autowired
private User user;
private String company;
public UserDaoImpl(){
this.company = user.getCompany();
}
Nenhum erro será relatado durante o processo de compilação, mas um NullPointerException será relatado após a execução.
Quando Java inicializa uma classe, ela segue a ordem de variáveis estáticas ou blocos de instruções estáticas -> variáveis de instância ou blocos de instruções de inicialização -> construtores -> @Autowired. Portanto, quando o método construtor desta classe é executado, o objeto do usuário ainda não foi injetado e seu valor ainda é nulo.
Questão 2
Quando conectamos automaticamente a injeção de atributos, se escrevermos o tipo de dependência errado, a exceção de ponteiro nulo só poderá ser encontrada ao usar essa dependência, em vez de reportá-la a tempo após a execução do projeto, o que nos impede de solucionar o problema a tempo.
Resumir
- Para dependências necessárias, é recomendado usar injeção de construtor, e geralmente é usado com final para garantir que a injeção de dependência seja imutável. O uso da injeção de construtor pode expressar as dependências de uma classe de forma muito clara, tornando as dependências de código mais óbvias e fáceis de manter. Além disso, esta abordagem melhora a testabilidade do seu código, uma vez que todas as dependências necessárias devem ser declaradas no construtor.
- Para dependências opcionais, a injeção de setter é recomendada. A injeção de setter pode evitar o acesso direto às propriedades da classe, conseguindo assim um melhor encapsulamento.
- A injeção de atributos pode reduzir o código de uma classe, tornando-o mais conciso e fácil de entender, mas a injeção de atributos pode ser injetada como nula. Esses erros só ocorrerão quando usados. Além disso, a injeção de atributos também apresenta alguns perigos ocultos.
- A injeção de construtor e setter é recomendada, enquanto a injeção de propriedade não é recomendada, a menos que a dependência seja muito simples e clara.
Use @Inject em vez de @Autowired
A partir do Spring 3.0, o Spring fornece suporte para anotações padrão JSR-330 (injeção de dependência). Essas anotações são verificadas da mesma maneira que as anotações do Spring. Para usá-los, introduza os pacotes jar relevantes no arquivo pom.
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
Assim como @Autowired, você pode usar @Inject em propriedades, métodos setter e parâmetros de construtor.
import javax.inject.Inject;
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Inject
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
public void listMovies() {
this.movieFinder.findMovies(...);
// ...
}
}
Além disso, você pode obter dependências através do carregamento lento através do método Provider.get() e utilizá-lo:
import javax.inject.Inject;
import javax.inject.Provider;
public class SimpleMovieLister {
private Provider<MovieFinder> movieFinder;
@Inject
public void setMovieFinder(Provider<MovieFinder> movieFinder) {
this.movieFinder = movieFinder;
}
public void listMovies() {
this.movieFinder.get().findMovies(...);
// ...
}
}
Você também pode usar @Named para especificar uma classe de implementação específica:
import javax.inject.Inject;
import javax.inject.Named;
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Inject
public void setMovieFinder(@Named("main") MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// ...
}
Prioridade de injeção:
- Corresponder por tipo
- Partida por Qualificatória
- Corresponder por nome
Como a anotação @Inject não possui atributos, um erro será relatado quando o carregamento do bean necessário falhar, o que é diferente de @Autowired.
referência
- https://docs.spring.io/spring-framework/docs/5.3.27/reference/html/core.html#beans-autowired-annotation
- https://juejin.cn/post/7023618746501562399
- https://blog.csdn.net/qq_39249094/article/details/121028234