Fiação na primavera: @Autowired, @Resource e @Inject
1. Visão geral
Este artigo do Spring Framework irá demonstrar o uso de anotações relacionadas à injeção de dependência, ou seja, as anotações@Resource,@Injecte@Autowired. Essas anotações fornecem às classes uma maneira declarativa de resolver dependências. Por exemplo:
@Autowired
ArbitraryClass arbObject;
em vez de instancia-los diretamente (o caminho imperativo), por exemplo:
ArbitraryClass arbObject = new ArbitraryClass();
Duas das três anotações pertencem ao pacote de extensão Java:javax.annotation.Resourceejavax.inject.Inject. A anotação@Autowired pertence ao pacoteorg.springframework.beans.factory.annotation.
Cada uma dessas anotações pode resolver dependências por injeção de campo ou por injeção de setter. Um exemplo simplificado, mas prático, será usado para demonstrar a distinção entre as três anotações, com base nos caminhos de execução adotados por cada anotação.
Os exemplos focarão em como usar as três anotações de injeção durante o teste de integração. A dependência exigida pelo teste pode ser um arquivo arbitrário ou uma classe arbitrária.
2. A anotação@Resource
A anotação@Resource faz parte da coleção de anotaçõesJSR-250 e é fornecida com Java EE. Esta anotação possui os seguintes caminhos de execução, listados por precedência:
-
Corresponder por Nome
-
Corresponder por tipo
-
Jogo por Qualificador
Esses caminhos de execução são aplicáveis ao setter e à injeção de campo.
2.1. Injeção em Campo
Resolver dependências por injeção de campo é alcançado anotando uma variável de instância com a anotação@Resource.
2.1.1. Corresponder por Nome
O teste de integração usado para demonstrar a injeção de campo por nome é listado da seguinte maneira:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
loader=AnnotationConfigContextLoader.class,
classes=ApplicationContextTestResourceNameType.class)
public class FieldResourceInjectionIntegrationTest {
@Resource(name="namedFile")
private File defaultFile;
@Test
public void givenResourceAnnotation_WhenOnField_ThenDependencyValid(){
assertNotNull(defaultFile);
assertEquals("namedFile.txt", defaultFile.getName());
}
}
Vamos examinar o código. No teste de integraçãoFieldResourceInjectionTest, na linha 7, a resolução da dependência por nome é alcançada passando o nome do bean como um valor de atributo para a anotação@Resource:
@Resource(name="namedFile")
private File defaultFile;
Essa configuração resolverá dependências usando o caminho de execução de correspondência por nome. O beannamedFile deve ser definido no contexto do aplicativoApplicationContextTestResourceNameType.
Observe que o ID do bean e o valor do atributo de referência correspondente devem corresponder:
@Configuration
public class ApplicationContextTestResourceNameType {
@Bean(name="namedFile")
public File namedFile() {
File namedFile = new File("namedFile.txt");
return namedFile;
}
}
A falha em definir o bean no contexto do aplicativo resultará emorg.springframework.beans.factory.NoSuchBeanDefinitionException sendo lançado. Isso pode ser demonstrado alterando o valor do atributo passado na anotação@Bean, no contexto do aplicativoApplicationContextTestResourceNameType; ou alterando o valor do atributo passado na anotação@Resource, no teste de integraçãoFieldResourceInjectionTest.
2.1.2. Corresponder por tipo
Para demonstrar o caminho de execução de correspondência por tipo, apenas remova o valor do atributo na linha 7 do teste de integraçãoFieldResourceInjectionTest para que tenha a seguinte aparência:
@Resource
private File defaultFile;
e execute o teste novamente.
O teste ainda passará porque se a anotação@Resource não receber um nome de bean como um valor de atributo, o Spring Framework irá prosseguir com o próximo nível de precedência, correspondência por tipo, a fim de tentar resolver a dependência .
2.1.3. Jogo por Qualificador
Para demonstrar o caminho de execução match-by-qualifier, o cenário de teste de integração será modificado para que haja dois beans definidos no contexto do aplicativoApplicationContextTestResourceQualifier:
@Configuration
public class ApplicationContextTestResourceQualifier {
@Bean(name="defaultFile")
public File defaultFile() {
File defaultFile = new File("defaultFile.txt");
return defaultFile;
}
@Bean(name="namedFile")
public File namedFile() {
File namedFile = new File("namedFile.txt");
return namedFile;
}
}
O teste de integraçãoQualifierResourceInjectionTest será usado para demonstrar a resolução de dependência de correspondência por qualificador. Nesse cenário, uma dependência específica do bean precisa ser injetada em cada variável de referência:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
loader=AnnotationConfigContextLoader.class,
classes=ApplicationContextTestResourceQualifier.class)
public class QualifierResourceInjectionIntegrationTest {
@Resource
private File dependency1;
@Resource
private File dependency2;
@Test
public void givenResourceAnnotation_WhenField_ThenDependency1Valid(){
assertNotNull(dependency1);
assertEquals("defaultFile.txt", dependency1.getName());
}
@Test
public void givenResourceQualifier_WhenField_ThenDependency2Valid(){
assertNotNull(dependency2);
assertEquals("namedFile.txt", dependency2.getName());
}
}
Execute o teste de integração e umorg.springframework.beans.factory.NoUniqueBeanDefinitionException é lançado. Essa exceção é lançada porque o contexto do aplicativo encontrou duas definições de bean do tipoFile, e está confuso quanto a qual bean deve resolver a dependência.
Para resolver esse problema, consulte as linhas 7 a 10 do teste de integraçãoQualifierResourceInjectionTest:
@Resource
private File dependency1;
@Resource
private File dependency2;
e adicione as seguintes linhas de código:
@Qualifier("defaultFile")
@Qualifier("namedFile")
para que o bloco de código tenha a seguinte aparência:
@Resource
@Qualifier("defaultFile")
private File dependency1;
@Resource
@Qualifier("namedFile")
private File dependency2;
Execute o teste de integração novamente, desta vez ele deve passar. O objetivo deste teste foi demonstrar que mesmo se houver vários beans definidos em um contexto de aplicativo, a anotação@Qualifier elimina qualquer confusão, permitindo que dependências específicas sejam injetadas em uma classe.
2.2. Injeção de Setter
Os caminhos de execução adotados ao injetar dependências em um campo são aplicáveis à injeção baseada em setter.
2.2.1. Corresponder por Nome
A única diferença é que o teste de integraçãoMethodResourceInjectionTest tem um método setter:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
loader=AnnotationConfigContextLoader.class,
classes=ApplicationContextTestResourceNameType.class)
public class MethodResourceInjectionIntegrationTest {
private File defaultFile;
@Resource(name="namedFile")
protected void setDefaultFile(File defaultFile) {
this.defaultFile = defaultFile;
}
@Test
public void givenResourceAnnotation_WhenSetter_ThenDependencyValid(){
assertNotNull(defaultFile);
assertEquals("namedFile.txt", defaultFile.getName());
}
}
Resolver dependências por injeção de setter é feito anotando o método de setter correspondente de uma variável de referência. Passe o nome da dependência do bean como um valor de atributo para a anotação@Resource:
private File defaultFile;
@Resource(name="namedFile")
protected void setDefaultFile(File defaultFile) {
this.defaultFile = defaultFile;
}
A dependência do beannamedFile será reutilizada neste exemplo. O nome do bean e o valor do atributo correspondente devem corresponder.
Execute o teste de integração como está e ele passará.
Para ver se a dependência foi realmente resolvida pelo caminho de execução match-by-name, altere o valor do atributo passado para a anotação@Resource para um valor de sua escolha e execute o teste novamente. Desta vez, o teste falhará comNoSuchBeanDefinitionException.
2.2.2. Corresponder por tipo
Para demonstrar a execução baseada em setter, correspondência por tipo, usaremos o teste de integraçãoMethodByTypeResourceTest:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
loader=AnnotationConfigContextLoader.class,
classes=ApplicationContextTestResourceNameType.class)
public class MethodByTypeResourceIntegrationTest {
private File defaultFile;
@Resource
protected void setDefaultFile(File defaultFile) {
this.defaultFile = defaultFile;
}
@Test
public void givenResourceAnnotation_WhenSetter_ThenValidDependency(){
assertNotNull(defaultFile);
assertEquals("namedFile.txt", defaultFile.getName());
}
}
Execute este teste como está e ele passará.
Para verificar se a dependênciaFile foi realmente resolvida pelo caminho de execução de correspondência por tipo, altere o tipo de classe da variáveldefaultFile para outro tipo de classe comoString. Execute o teste de integraçãoMethodByTypeResourceTest novamente e, desta vez, umNoSuchBeanDefinitionException será lançado.
A exceção verifica se a correspondência por tipo foi realmente usada para resolver a dependência deFile. ONoSuchBeanDefinitionException confirma que o nome da variável de referência não precisa corresponder ao nome do bean. Em vez disso, a resolução de dependência depende do tipo de classe do bean correspondendo ao tipo de classe da variável de referência.
2.2.3. Jogo por Qualificador
O teste de integraçãoMethodByQualifierResourceTest será usado para demonstrar o caminho de execução match-by-qualifier:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
loader=AnnotationConfigContextLoader.class,
classes=ApplicationContextTestResourceQualifier.class)
public class MethodByQualifierResourceIntegrationTest {
private File arbDependency;
private File anotherArbDependency;
@Test
public void givenResourceQualifier_WhenSetter_ThenValidDependencies(){
assertNotNull(arbDependency);
assertEquals("namedFile.txt", arbDependency.getName());
assertNotNull(anotherArbDependency);
assertEquals("defaultFile.txt", anotherArbDependency.getName());
}
@Resource
@Qualifier("namedFile")
public void setArbDependency(File arbDependency) {
this.arbDependency = arbDependency;
}
@Resource
@Qualifier("defaultFile")
public void setAnotherArbDependency(File anotherArbDependency) {
this.anotherArbDependency = anotherArbDependency;
}
}
O objetivo deste teste é demonstrar que mesmo se várias implementações de bean de um tipo específico forem definidas em um contexto de aplicativo, uma anotação@Qualifier pode ser usada junto com a anotação@Resource para resolver uma dependência.
Semelhante à injeção de dependência baseada em campo, se houver vários beans definidos em um contexto de aplicativo, umNoUniqueBeanDefinitionException é lançado se nenhuma anotação@Qualifier for usada para especificar qual bean deve ser usado para resolver dependências.
3. A anotação@Inject
A anotação@Inject pertence à coleção de anotaçõesJSR-330. Esta anotação possui os seguintes caminhos de execução, listados por precedência:
-
Corresponder por tipo
-
Jogo por Qualificador
-
Corresponder por Nome
Esses caminhos de execução são aplicáveis ao setter e à injeção de campo. Para acessar a anotação@Inject, a bibliotecajavax.inject deve ser declarada como uma dependência Gradle ou Maven.
Para Gradle:
testCompile group: 'javax.inject', name: 'javax.inject', version: '1'
Para Maven:
javax.inject
javax.inject
1
3.1. Injeção em Campo
3.1.1. Corresponder por tipo
O exemplo de teste de integração será modificado para usar outro tipo de dependência, ou seja, a classeArbitraryDependency. A dependência de classeArbitraryDependency serve meramente como uma dependência simples e não tem mais significado. Está listado da seguinte forma:
@Component
public class ArbitraryDependency {
private final String label = "Arbitrary Dependency";
public String toString() {
return label;
}
}
O teste de integraçãoFieldInjectTest em questão está listado da seguinte forma:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
loader=AnnotationConfigContextLoader.class,
classes=ApplicationContextTestInjectType.class)
public class FieldInjectIntegrationTest {
@Inject
private ArbitraryDependency fieldInjectDependency;
@Test
public void givenInjectAnnotation_WhenOnField_ThenValidDependency(){
assertNotNull(fieldInjectDependency);
assertEquals("Arbitrary Dependency",
fieldInjectDependency.toString());
}
}
Ao contrário da anotação@Resource, que resolve dependências por nome primeiro; o comportamento padrão da anotação@Inject resolve dependências por tipo.
Isso significa que, mesmo que o nome da variável de referência de classe seja diferente do nome do bean, a dependência ainda será resolvida, desde que o bean seja definido no contexto do aplicativo. Observe como o nome da variável de referência no seguinte teste:
@Inject
private ArbitraryDependency fieldInjectDependency;
difere do nome do bean configurado no contexto do aplicativo:
@Bean
public ArbitraryDependency injectDependency() {
ArbitraryDependency injectDependency = new ArbitraryDependency();
return injectDependency;
}
e quando o teste é executado, ele pode resolver a dependência.
3.1.2. Jogo por Qualificador
Mas e se houver várias implementações de um tipo de classe específico e uma certa classe exigir um bean específico? Vamos modificar o exemplo de teste de integração para que outra dependência seja necessária.
Neste exemplo, criamos uma subclasse da classeArbitraryDependency, usada no exemplo de correspondência por tipo, para criar a classeAnotherArbitraryDependency:
public class AnotherArbitraryDependency extends ArbitraryDependency {
private final String label = "Another Arbitrary Dependency";
public String toString() {
return label;
}
}
O objetivo de cada caso de teste é garantir que cada dependência seja injetada corretamente em cada variável de referência:
@Inject
private ArbitraryDependency defaultDependency;
@Inject
private ArbitraryDependency namedDependency;
O teste de integraçãoFieldQualifierInjectTest usado para demonstrar a correspondência por qualificador é listado como segue:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
loader=AnnotationConfigContextLoader.class,
classes=ApplicationContextTestInjectQualifier.class)
public class FieldQualifierInjectIntegrationTest {
@Inject
private ArbitraryDependency defaultDependency;
@Inject
private ArbitraryDependency namedDependency;
@Test
public void givenInjectQualifier_WhenOnField_ThenDefaultFileValid(){
assertNotNull(defaultDependency);
assertEquals("Arbitrary Dependency",
defaultDependency.toString());
}
@Test
public void givenInjectQualifier_WhenOnField_ThenNamedFileValid(){
assertNotNull(defaultDependency);
assertEquals("Another Arbitrary Dependency",
namedDependency.toString());
}
}
Se houver várias implementações de uma classe específica em um contexto de aplicativo e o teste de integraçãoFieldQualifierInjectTest tenta injetar as dependências da maneira listada abaixo:
@Inject
private ArbitraryDependency defaultDependency;
@Inject
private ArbitraryDependency namedDependency;
aNoUniqueBeanDefinitionException será lançado.
Lançar essa exceção é a maneira do Spring Framework de apontar que existem várias implementações de uma determinada classe e não se sabe qual delas usar. Para elucidar a confusão, vá para as linhas 7 e 10 do teste de integraçãoFieldQualifierInjectTest:
@Inject
private ArbitraryDependency defaultDependency;
@Inject
private ArbitraryDependency namedDependency;
passe o nome do bean necessário para a anotação@Qualifier, que é usada junto com a anotação@Inject. O bloco de código agora terá a seguinte aparência:
@Inject
@Qualifier("defaultFile")
private ArbitraryDependency defaultDependency;
@Inject
@Qualifier("namedFile")
private ArbitraryDependency namedDependency;
A anotação@Qualifier espera uma correspondência estrita ao receber um nome de bean. Certifique-se de que o nome do bean seja passado paraQualifier corretamente, caso contrário, umNoUniqueBeanDefinitionException será lançado. Execute o teste novamente e, desta vez, deve passar.
3.1.3. Corresponder por Nome
O teste de integraçãoFieldByNameInjectTest usado para demonstrar correspondência por nome é semelhante ao caminho de execução de correspondência por tipo. A única diferença agora é que um bean específico é necessário, em oposição a um tipo específico. Neste exemplo, subclassificamos a classeArbitraryDependency novamente para produzir a classeYetAnotherArbitraryDependency:
public class YetAnotherArbitraryDependency extends ArbitraryDependency {
private final String label = "Yet Another Arbitrary Dependency";
public String toString() {
return label;
}
}
Para demonstrar o caminho de execução da correspondência por nome, usaremos o seguinte teste de integração:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
loader=AnnotationConfigContextLoader.class,
classes=ApplicationContextTestInjectName.class)
public class FieldByNameInjectIntegrationTest {
@Inject
@Named("yetAnotherFieldInjectDependency")
private ArbitraryDependency yetAnotherFieldInjectDependency;
@Test
public void givenInjectQualifier_WhenSetOnField_ThenDependencyValid(){
assertNotNull(yetAnotherFieldInjectDependency);
assertEquals("Yet Another Arbitrary Dependency",
yetAnotherFieldInjectDependency.toString());
}
}
O contexto do aplicativo é listado da seguinte maneira:
@Configuration
public class ApplicationContextTestInjectName {
@Bean
public ArbitraryDependency yetAnotherFieldInjectDependency() {
ArbitraryDependency yetAnotherFieldInjectDependency =
new YetAnotherArbitraryDependency();
return yetAnotherFieldInjectDependency;
}
}
Execute o teste de integração como está e ele passará.
Para verificar se a dependência foi realmente injetada pelo caminho de execução match-by-name, altere o valoryetAnotherFieldInjectDependency, que foi passado para a anotação@Named para outro nome de sua escolha. Execute o teste novamente - desta vez, umNoSuchBeanDefinitionException é lançado.
3.2. Injeção de Setter
A injeção baseada em setter para a anotação@Inject é semelhante à abordagem usada para injeção baseada em setter@Resource. Em vez de anotar a variável de referência, o método setter correspondente é anotado. Os caminhos de execução seguidos pela injeção de dependência baseada em campo também se aplicam à injeção baseada em setter.
4. A anotação@Autowired
O comportamento da anotação@Autowired é semelhante ao da anotação@Inject. A única diferença é que a anotação@Autowired faz parte do framework Spring. Esta anotação tem os mesmos caminhos de execução que a anotação@Inject, listados em ordem de precedência:
-
Corresponder por tipo
-
Jogo por Qualificador
-
Corresponder por Nome
Esses caminhos de execução são aplicáveis ao setter e à injeção de campo.
4.1. Injeção em Campo
4.1.1. Corresponder por tipo
O exemplo de teste de integração usado para demonstrar o caminho de execução de correspondência por tipo de@Autowired será semelhante ao teste usado para demonstrar o caminho de execução de correspondência por tipo de@Inject. O teste de integraçãoFieldAutowiredTest usado para demonstrar correspondência por tipo usando a anotação@Autowired é listado como segue:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
loader=AnnotationConfigContextLoader.class,
classes=ApplicationContextTestAutowiredType.class)
public class FieldAutowiredIntegrationTest {
@Autowired
private ArbitraryDependency fieldDependency;
@Test
public void givenAutowired_WhenSetOnField_ThenDependencyResolved() {
assertNotNull(fieldDependency);
assertEquals("Arbitrary Dependency", fieldDependency.toString());
}
}
O contexto do aplicativo para este teste de integração está listado da seguinte maneira:
@Configuration
public class ApplicationContextTestAutowiredType {
@Bean
public ArbitraryDependency autowiredFieldDependency() {
ArbitraryDependency autowiredFieldDependency =
new ArbitraryDependency();
return autowiredFieldDependency;
}
}
O objetivo do teste de integração é demonstrar que a correspondência por tipo tem precedência sobre os outros caminhos de execução. Observe na linha 8 do teste de integraçãoFieldAutowiredTest como o nome da variável de referência:
@Autowired
private ArbitraryDependency fieldDependency;
é diferente do nome do bean no contexto do aplicativo:
@Bean
public ArbitraryDependency autowiredFieldDependency() {
ArbitraryDependency autowiredFieldDependency =
new ArbitraryDependency();
return autowiredFieldDependency;
}
Quando o teste é executado, ele passa.
Para confirmar que a dependência foi realmente resolvida usando o caminho de execução match-by-type, altere o tipo da variável de referênciafieldDependency e execute o teste de integração novamente. Desta vez, o teste de integraçãoFieldAutowiredTest deve falhar, com umNoSuchBeanDefinitionException sendo lançado. Isso verifica se a correspondência por tipo foi usada para resolver a dependência.
4.1.2. Jogo por Qualificador
E se confrontado com uma situação em que várias implementações de bean foram definidas no contexto do aplicativo, como a listada abaixo:
@Configuration
public class ApplicationContextTestAutowiredQualifier {
@Bean
public ArbitraryDependency autowiredFieldDependency() {
ArbitraryDependency autowiredFieldDependency =
new ArbitraryDependency();
return autowiredFieldDependency;
}
@Bean
public ArbitraryDependency anotherAutowiredFieldDependency() {
ArbitraryDependency anotherAutowiredFieldDependency =
new AnotherArbitraryDependency();
return anotherAutowiredFieldDependency;
}
}
Se o teste de integraçãoFieldQualifierAutowiredTest, listado abaixo, for executado:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
loader=AnnotationConfigContextLoader.class,
classes=ApplicationContextTestAutowiredQualifier.class)
public class FieldQualifierAutowiredIntegrationTest {
@Autowired
private ArbitraryDependency fieldDependency1;
@Autowired
private ArbitraryDependency fieldDependency2;
@Test
public void givenAutowiredQualifier_WhenOnField_ThenDep1Valid(){
assertNotNull(fieldDependency1);
assertEquals("Arbitrary Dependency", fieldDependency1.toString());
}
@Test
public void givenAutowiredQualifier_WhenOnField_ThenDep2Valid(){
assertNotNull(fieldDependency2);
assertEquals("Another Arbitrary Dependency",
fieldDependency2.toString());
}
}
aNoUniqueBeanDefinitionException será lançado.
A exceção é devido à ambiguidade causada pelos dois beans definidos no contexto do aplicativo. O Spring Framework não sabe qual dependência de bean deve ser conectada automaticamente a qual variável de referência. Resolva esse problema adicionando a anotação@Qualifier às linhas 7 e 10 do teste de integraçãoFieldQualifierAutowiredTest:
@Autowired
private FieldDependency fieldDependency1;
@Autowired
private FieldDependency fieldDependency2;
para que o bloco de código tenha a seguinte aparência:
@Autowired
@Qualifier("autowiredFieldDependency")
private FieldDependency fieldDependency1;
@Autowired
@Qualifier("anotherAutowiredFieldDependency")
private FieldDependency fieldDependency2;
Execute o teste novamente e desta vez ele será aprovado.
4.1.3. Corresponder por Nome
O mesmo cenário de teste de integração será usado para demonstrar o caminho de execução de correspondência por nome ao usar a anotação@Autowired para injetar uma dependência de campo. Ao autowiring dependências por nome, a anotação@ComponentScan deve ser usada com o contexto do aplicativo,ApplicationContextTestAutowiredName:
@Configuration
@ComponentScan(basePackages={"com.example.dependency"})
public class ApplicationContextTestAutowiredName {
}
A anotação@ComponentScan pesquisará pacotes de classes Java que foram anotadas com a anotação@Component. Por exemplo, no contexto do aplicativo, o pacotecom.example.dependency será verificado em busca de classes que foram anotadas com a anotação@Component. Nesse cenário, o Spring Framework deve detectar a classeArbitraryDependency, que tem a anotação@Component:
@Component(value="autowiredFieldDependency")
public class ArbitraryDependency {
private final String label = "Arbitrary Dependency";
public String toString() {
return label;
}
}
O valor do atributo,autowiredFieldDependency, passado para a anotação@Component, informa ao Spring Framework que a classeArbitraryDependency é um componente denominadoautowiredFieldDependency. Para que a anotação@Autowired resolva dependências por nome, o nome do componente deve corresponder ao nome do campo definido no teste de integraçãoFieldAutowiredNameTest; consulte a linha 8:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
loader=AnnotationConfigContextLoader.class,
classes=ApplicationContextTestAutowiredName.class)
public class FieldAutowiredNameIntegrationTest {
@Autowired
private ArbitraryDependency autowiredFieldDependency;
@Test
public void givenAutowiredAnnotation_WhenOnField_ThenDepValid(){
assertNotNull(autowiredFieldDependency);
assertEquals("Arbitrary Dependency",
autowiredFieldDependency.toString());
}
}
Quando o teste de integraçãoFieldAutowiredNameTest for executado como está, ele será aprovado.
Mas como sabemos que a anotação@Autowired realmente invocou o caminho de execução match-by-name? Altere o nome da variável de referênciaautowiredFieldDependency para outro nome de sua escolha e execute o teste novamente.
Desta vez, o teste falhará e umNoUniqueBeanDefinitionException é lançado. Uma verificação semelhante seria alterar o valor do atributo@Component,autowiredFieldDependency, para outro valor de sua escolha e executar o teste novamente. UmNoUniqueBeanDefinitionException também será lançado.
Essa exceção prova que, se um nome de bean incorreto for usado, nenhum bean válido será encontrado. Portanto, o caminho de execução da correspondência por nome foi chamado.
4.2. Injeção de Setter
A injeção baseada em setter para a anotação@Autowired é semelhante à abordagem demonstrada para a injeção baseada em setter@Resource. Em vez de anotar a variável de referência com a anotação@Inject, o setter correspondente é anotado. Os caminhos de execução seguidos pela injeção de dependência baseada em campo também se aplicam à injeção baseada em setter.
5. Aplicando Estas Anotações
Isso levanta a questão: qual anotação deve ser usada e em que circunstâncias? A resposta para essas perguntas depende do cenário de design enfrentado pelo aplicativo em questão e de como o desenvolvedor deseja alavancar o polimorfismo com base nos caminhos de execução padrão de cada anotação.
5.1. Uso amplo de aplicativos de singletons por meio de polimorfismo
Se o design for tal que os comportamentos do aplicativo sejam baseados em implementações de uma interface ou classe abstrata, e esses comportamentos forem usados em todo o aplicativo, use a anotação@Inject ou@Autowired.
O benefício dessa abordagem é que, quando o aplicativo é atualizado, ou um patch precisa ser aplicado para corrigir um erro; as classes podem ser trocadas com um impacto negativo mínimo no comportamento geral do aplicativo. Nesse cenário, o caminho de execução padrão primário é correspondido por tipo.
5.2. Configuração refinada do comportamento do aplicativo por meio do polimorfismo
Se o design for tal que o aplicativo tenha um comportamento complexo, cada comportamento será baseado em diferentes interfaces / classes abstratas, e o uso de cada uma dessas implementações varia no aplicativo, então use a anotação@Resource. Nesse cenário, o caminho de execução padrão primário é a correspondência por nome.
5.3. A injeção de dependência deve ser tratada exclusivamente pela plataforma Java EE
Se houver um mandato de design para todas as dependências serem injetadas pela plataforma Java EE e não Spring, então a escolha é entre a anotação@Resource e a anotação@Inject. Você deve restringir a decisão final entre as duas anotações, com base no caminho de execução padrão necessário.
5.4. A injeção de dependência deve ser tratada exclusivamente pelo Spring Framework
Se o mandato for para que todas as dependências sejam tratadas pelo Spring Framework, a única escolha é a anotação@Autowired.
5.5. Resumo da discussão
A tabela abaixo resume a discussão.
Cenário |
@Recurso |
@Injetar |
@Autowired |
Uso de singletons em todo o aplicativo por meio de polimorfismo |
✗ |
✔ |
✔ |
Configuração refinada do comportamento do aplicativo por meio de polimorfismo |
✔ |
✗ |
✗ |
A injeção de dependência deve ser tratada exclusivamente pela plataforma Java EE |
✔ |
✔ |
✗ |
A injeção de dependência deve ser tratada exclusivamente pelo Spring Framework |
✗ |
✗ |
✔ |
6. Conclusão
O artigo teve como objetivo fornecer uma visão mais profunda do comportamento de cada anotação. A compreensão de como cada anotação se comporta contribuirá para um melhor design e manutenção geral do aplicativo.
O código usado durante a discussão pode ser encontrado emGitHub.