A diferença entre os três métodos de injeção de @Autowired e o uso básico da anotação @Inject

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:

  1. Construtores autowired (injeção de construtor)
  2. Métodos Autowired (injeção de setter)
  3. 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

  1. 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.
  2. 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.
  3. 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.
  4. 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:

  1. Corresponder por tipo
  2. Partida por Qualificatória
  3. 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

Acho que você gosta

Origin blog.csdn.net/m0_63323097/article/details/130463814
Recomendado
Clasificación