Como funciona a fiação automática na primavera?

510

Estou um pouco confuso sobre como funciona a inversão de control ( IoC) Spring.

Digamos que eu tenha uma classe de serviço chamada UserServiceImplque implementa a UserServiceinterface.

Como seria isso @Autowired?

E na minha Controllers, como eu instantiateum instancedeste serviço?

Eu apenas faria o seguinte?

UserService userService = new UserServiceImpl();
Blankman
fonte

Respostas:

703

Primeiro, e mais importante - todos os beans Spring são gerenciados - eles "vivem" dentro de um contêiner, chamado "contexto do aplicativo".

Segundo, cada aplicativo tem um ponto de entrada para esse contexto. Os aplicativos da Web têm um Servlet, o JSF usa um el-resolvedor, etc. Além disso, há um local em que o contexto do aplicativo é autoinicializado e todos os beans - com conexão automática. Em aplicativos da web, isso pode ser um ouvinte de inicialização.

A fiação automática ocorre colocando uma instância de um bean no campo desejado em uma instância de outro bean. Ambas as classes devem ser beans, isto é, devem ser definidas para viver no contexto do aplicativo.

O que é "viver" no contexto do aplicativo? Isso significa que o contexto instancia os objetos, não você. Ou seja, você nunca faznew UserServiceImpl() - o contêiner encontra cada ponto de injeção e define uma instância lá.

Nos seus controladores, você apenas tem o seguinte:

@Controller // Defines that this class is a spring bean
@RequestMapping("/users")
public class SomeController {

    // Tells the application context to inject an instance of UserService here
    @Autowired
    private UserService userService;

    @RequestMapping("/login")
    public void login(@RequestParam("username") String username,
           @RequestParam("password") String password) {

        // The UserServiceImpl is already injected and you can use it
        userService.login(username, password);

    }
}

Algumas notas:

  • Em sua applicationContext.xmlvocê deve habilitar o <context:component-scan>modo que as aulas são digitalizados para o @Controller, @Service, etc. anotações.
  • O ponto de entrada para um aplicativo Spring-MVC é o DispatcherServlet, mas está oculto para você e, portanto, a interação direta e a inicialização do contexto do aplicativo acontecem nos bastidores.
  • UserServiceImpltambém deve ser definido como bean - usando <bean id=".." class="..">ou usando a @Serviceanotação. Uma vez que será o único implementador de UserService, será injetado.
  • Além da @Autowiredanotação, o Spring pode usar a fiação automática configurável em XML. Nesse caso, todos os campos que possuem um nome ou tipo que corresponde a um bean existente são automaticamente injetados. De fato, essa foi a ideia inicial da fiação automática - ter campos injetados com dependências sem nenhuma configuração. Outras anotações como @Inject, @Resourcetambém podem ser usadas.
Bozho
fonte
7
sim, UserServiceImpl é anotado com Service, e UserService é a interface
Bozho
16
o escopo padrão é singleton, portanto, você terá apenas uma instância do bean, que é injetada em vários locais. Se você definir explicitamente o escopo a ser "protótipo", então vai existir várias instâncias, possivelmente preguiçoso (dependendo da configuração)
Bozho
2
Muito obrigado pela sua postagem, realmente esclareceu tudo para mim. Em relação a 'Como ele será o único implementador ou UserService, ele será injetado.' - e se houver várias classes que implementam o serviço de usuário? Como o Spring sabe qual implementação deve usar?
Shishigami
7
se houver um designado como "primário", ele será usado. Caso contrário, ele lança uma exceção
Bozho
3
não, userService é criado apenas uma vez, está no escopo
único
64

Depende se você deseja a rota de anotações ou a rota de definição XML do bean.

Digamos que você tenha os beans definidos no seu applicationContext.xml:

<beans ...>

    <bean id="userService" class="com.foo.UserServiceImpl"/>

    <bean id="fooController" class="com.foo.FooController"/>

</beans>

A fiação automática acontece quando o aplicativo é iniciado. Portanto, em fooController, que por razões de interesse deseja usar a UserServiceImplclasse, você deve anotá-la da seguinte maneira:

public class FooController {

    // You could also annotate the setUserService method instead of this
    @Autowired
    private UserService userService;

    // rest of class goes here
}

Quando vir @Autowired, o Spring procurará uma classe que corresponda à propriedade no applicationContexte injetará automaticamente. Se você tiver mais de umUserService bean, terá que qualificar qual deles deve usar.

Se você fizer o seguinte:

UserService service = new UserServiceImpl();

Ele não atenderá a @Autowiredmenos que você o configure.

Ben J
fonte
2
Então, o que é o uso de definir bean idem applicationContext.xml. Teremos que definir a userServicevariável com o UserServicetipo Então, por que fazer entrada no xmlarquivo.
viper
20

@Autowired é uma anotação introduzida na Primavera 2.5 e é usada apenas para injeção.

Por exemplo:

class A {

    private int id;

    // With setter and getter method
}

class B {

    private String name;

    @Autowired // Here we are injecting instance of Class A into class B so that you can use 'a' for accessing A's instance variables and methods.
    A a;

    // With setter and getter method

    public void showDetail() {
        System.out.println("Value of id form A class" + a.getId(););
    }
}
mohit bansal
fonte
10
Isso não será compilado e geralmente está incorreto. @Autowirednão significa que "você pode usar toda a função (método) e variável na Bclasse da classe A". O que ele faz é trazer uma instância de Apara instâncias de B, para que você possa fazer a a.getId()partir B.
Dmitry Minkovsky
@dimadima Então, se ele fizer System.out.println ("Valor da classe ID do formulário A + + a.getId ());; e não como ele realmente fez, será mais correto. Responda, pois este é intuitivamente claro para mim e, de acordo com o meu nível atual de entendimento, está explicando a fiação automática.
31915 John Doe
anotação autowired é introduzido na primavera 2,5 docs.spring.io/spring-framework/docs/2.5.x/api/org/...
SpringLearner
1
Para entender melhor como eu sou novo nisso, o @autowired instancia a Classe A usando o construtor padrão? Se não, como os valores serão instanciados em um bean ou serviço se usarmos o cabeamento automático. Eu acho que se ele chama construtor padrão, por que usar a fiação automática em primeiro lugar, basta fazer A a = new A (). Por favor, esclareça?
Sameer 04/10
As dependências do @Sameer By Autowiring podem salvar muitos códigos padrão nos testes de unidade e também nas classes Controller, Service e Dao, porque a instanciação dos campos é fornecida automaticamente. Não há necessidade de chamar o construtor.
26417 kiltek
9

Como @Autowiredfunciona internamente?

Exemplo:

class EnglishGreeting {
   private Greeting greeting;
   //setter and getter
}

class Greeting {
   private String message;
   //setter and getter
}

arquivo .xml será semelhante se não estiver usando @Autowired:

<bean id="englishGreeting" class="com.bean.EnglishGreeting">
   <property name="greeting" ref="greeting"/>
</bean>

<bean id="greeting" class="com.bean.Greeting">
   <property name="message" value="Hello World"/>
</bean>

Se você estiver usando, @Autowiredentão:

class EnglishGreeting {
   @Autowired //so automatically based on the name it will identify the bean and inject.
   private Greeting greeting;
   //setter and getter
}

arquivo .xml será semelhante se não estiver usando @Autowired:

<bean id="englishGreeting" class="com.bean.EnglishGreeting"></bean>

<bean id="greeting" class="com.bean.Greeting">
   <property name="message" value="Hello World"/>
</bean>

Se ainda tiver alguma dúvida, veja abaixo a demonstração ao vivo

Como o @Autowired funciona internamente?

jeet singh parmar
fonte
6

Você só precisa anotar sua classe de serviço UserServiceImplcom a anotação:

@Service("userService")

O contêiner de mola cuidará do ciclo de vida desta classe quando se registrar como serviço.

Em seu controlador, você pode conectá-lo automaticamente (instanciar) e usar sua funcionalidade:

@Autowired
UserService userService;
Jitender Chahar
fonte
3

O injetor de dependência do Spring ajuda você a remover o acoplamento de suas classes. Em vez de criar objetos como este:

UserService userService = new UserServiceImpl();

Você o usará depois de introduzir o DI:

@Autowired
private UserService userService;

Para conseguir isso, você precisa criar um bean do seu serviço em seu ServiceConfigurationarquivo. Depois disso, você precisa importar esseServiceConfiguration classe para sua WebApplicationConfigurationclasse, para poder conectar automaticamente esse bean ao seu Controller da seguinte maneira:

public class AccController {

    @Autowired
    private UserService userService;
} 

Você pode encontrar um POC baseado em configuração java aqui exemplo .

AbdusSalam
fonte
1

Maneira padrão:

@RestController
public class Main {
    UserService userService;

    public Main(){
        userService = new UserServiceImpl();
    }

    @GetMapping("/")
    public String index(){
        return userService.print("Example test");
    }
}

Interface de serviço do usuário:

public interface UserService {
    String print(String text);
}

Classe UserServiceImpl:

public class UserServiceImpl implements UserService {
    @Override
    public String print(String text) {
        return text + " UserServiceImpl";
    }
}

Resultado: Example test UserServiceImpl

Esse é um ótimo exemplo de classes acopladas estreitas, exemplo de design incorreto e haverá problemas com os testes (o PowerMockito também é ruim).

Agora, vamos dar uma olhada na injeção de dependência do SpringBoot, um bom exemplo de acoplamento flexível:

A interface permanece a mesma,

Classe principal:

@RestController
public class Main {
    UserService userService;

    @Autowired
    public Main(UserService userService){
        this.userService = userService;
    }

    @GetMapping("/")
    public String index(){
        return userService.print("Example test");
    }
}

Classe ServiceUserImpl:

@Component
public class UserServiceImpl implements UserService {
    @Override
    public String print(String text) {
        return text + " UserServiceImpl";
    }
}

Resultado: Example test UserServiceImpl

e agora é fácil escrever teste:

@RunWith(MockitoJUnitRunner.class)
public class MainTest {
    @Mock
    UserService userService;

    @Test
    public void indexTest() {
        when(userService.print("Example test")).thenReturn("Example test UserServiceImpl");

        String result = new Main(userService).index();

        assertEquals(result, "Example test UserServiceImpl");
    }
}

Eu mostrei @Autowiredanotação no construtor, mas também pode ser usada no setter ou no campo.

Michu93
fonte
0

Todo o conceito de inversão de controle significa que você está livre de uma tarefa para instanciar objetos manualmente e fornecer todas as dependências necessárias. Quando você anota uma classe com a anotação apropriada (por exemplo @Service), o Spring instancia automaticamente o objeto para você. Se você não estiver familiarizado com anotações, também poderá usar o arquivo XML. No entanto, não é uma má idéia instanciar classes manualmente (com a newpalavra - chave) em testes de unidade quando você não deseja carregar todo o contexto da primavera.

k13i
fonte
0

Lembre-se de que você deve ativar a @Autowiredanotação adicionando elemento <context:annotation-config/>ao arquivo de configuração da mola. Isso registrará o AutowiredAnnotationBeanPostProcessorque cuida do processamento da anotação.

E então você pode instalar automaticamente seu serviço usando o método de injeção em campo.

public class YourController{

 @Autowired
 private UserService userService; 

}

Encontrei isso no post da anotação Spring @autowired

David Pham
fonte
0

Existem três maneiras de criar uma instância usando @Autowired.

1. @Autowiredem Propriedades

A anotação pode ser usada diretamente nas propriedades, eliminando, portanto, a necessidade de getters e setters:

    @Component("userService")
    public class UserService {

        public String getName() {
            return "service name";
        }
    }

    @Component
    public class UserController {

        @Autowired
        UserService userService

    }

No exemplo acima, o Spring procura e injeta userServicequandoUserController é criado.

2. @Autowiredem setters

A @Autowiredanotação pode ser usada nos métodos do setter. No exemplo abaixo, quando a anotação é usada no método setter, o método setter é chamado com a instância de userServicequando UserControlleré criado:

public class UserController {

    private UserService userService;

    @Autowired
    public void setUserService(UserService userService) {
            this.userService = userService;
    }
}

3. @Autowiredem construtores

A @Autowiredanotação também pode ser usada em construtores. No exemplo abaixo, quando a anotação é usada em um construtor, uma instância de userServiceé injetada como argumento ao construtor quando UserControlleré criada:

public class UserController {

    private UserService userService;

    @Autowired
    public UserController(UserService userService) {
        this.userService= userService;
    }
}
Mak
fonte
0

Em palavras simples, a fiação automática, a fiação dos links automaticamente, agora vem a pergunta sobre quem faz isso e que tipo de fiação. A resposta é: O contêiner faz isso e o tipo de fiação secundária é suportado; as primitivas precisam ser feitas manualmente.

Pergunta: Como o contêiner sabe que tipo de fiação?

Resposta: Definimos como byType, byName, construtor.

Pergunta: Existe alguma maneira de não definirmos o tipo de fiação automática?

Resposta: Sim, está lá fazendo uma anotação, @Autowired.

Pergunta: Mas como o sistema sabe, eu preciso escolher esse tipo de dados secundários?

Resposta: Você fornecerá esses dados no arquivo spring.xml ou usando anotações de estereótipo para sua classe, para que o contêiner possa criar os objetos para você.

Pratik Gaurav
fonte