¿Cómo funciona el autowiring en Spring?

510

Estoy un poco confundido sobre cómo funciona la inversión de control ( IoC) Spring.

Digamos que tengo una clase de servicio llamada UserServiceImplque implementa la UserServiceinterfaz.

¿Cómo sería esto @Autowired?

Y en mi Controllers, ¿cómo podría instantiateuno instancede este servicio?

¿Haría lo siguiente?

UserService userService = new UserServiceImpl();
Blankman
fuente

Respuestas:

703

Primero, y lo más importante, todos los beans de Spring se administran, "viven" dentro de un contenedor, llamado "contexto de aplicación".

En segundo lugar, cada aplicación tiene un punto de entrada a ese contexto. Las aplicaciones web tienen un Servlet, JSF usa un el-resolver, etc. Además, hay un lugar donde el contexto de la aplicación está arrancado y todos los beans tienen conexión automática. En aplicaciones web, esto puede ser un escucha de inicio.

El cableado automático ocurre colocando una instancia de un bean en el campo deseado en una instancia de otro bean. Ambas clases deben ser beans, es decir, deben definirse para vivir en el contexto de la aplicación.

¿Qué es "vivir" en el contexto de la aplicación? Esto significa que el contexto crea instancias de los objetos, no de usted. Es decir, nunca se hace new UserServiceImpl(), el contenedor encuentra cada punto de inyección y establece una instancia allí.

En sus controladores, solo tiene lo siguiente:

@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);

    }
}

Algunas notas

  • En su applicationContext.xmldebe activar el <context:component-scan>modo que las clases se analizan en busca de la @Controller, @Service, etc. anotaciones.
  • El punto de entrada para una aplicación Spring-MVC es el DispatcherServlet, pero está oculto para usted y, por lo tanto, la interacción directa y el arranque del contexto de la aplicación ocurre detrás de escena.
  • UserServiceImpltambién debe definirse como bean, ya sea usando <bean id=".." class="..">o usando la @Serviceanotación. Como será el único implementador de UserService, se inyectará.
  • Además de la @Autowiredanotación, Spring puede usar cableado automático configurable por XML. En ese caso, todos los campos que tengan un nombre o tipo que coincida con un bean existente se inyectarán automáticamente. De hecho, esa fue la idea inicial del cableado automático: inyectar campos con dependencias sin ninguna configuración. Otras anotaciones como @Inject, @Resourcetambién se pueden utilizar.
Bozho
fuente
77
sí, UserServiceImpl está anotado con Service, y UserService es la interfaz
Bozho
17
el alcance predeterminado es singleton, por lo que solo tendrá una instancia del bean, que se inyecta en varios lugares. Si define explícitamente el alcance como "prototipo", existirán varias instancias, posiblemente perezosas (dependiendo de la configuración)
Bozho
2
Muchas gracias por tu publicación, realmente me aclaró las cosas. En cuanto a "Dado que será el único implementador o UserService, se inyectará". - ¿Qué pasa si hay varias clases que implementan el servicio de usuario? ¿Cómo sabe Spring qué implementación debe usar?
Shishigami el
77
si hay uno designado como "primario", lo usa. De lo contrario, arroja una excepción
Bozho,
3
no, userService se crea solo una vez, está en alcance
único
64

Depende de si desea la ruta de anotaciones o la ruta de definición XML del bean.

Digamos que tenía los frijoles definidos en su applicationContext.xml:

<beans ...>

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

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

</beans>

El cableado automático ocurre cuando se inicia la aplicación. Entonces, en fooController, por razones de argumentos, quiere usar la UserServiceImplclase, la anotaría de la siguiente manera:

public class FooController {

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

    // rest of class goes here
}

Cuando vea @Autowired, Spring buscará una clase que coincida con la propiedad en el applicationContext, y la inyectará automáticamente. Si tiene más de un UserServicebean, entonces deberá calificar cuál debe usar.

Si haces lo siguiente:

UserService service = new UserServiceImpl();

No recogerá el a @Autowiredmenos que lo configure usted mismo.

Ben J
fuente
2
Entonces, ¿de qué sirve definir bean iden applicationContext.xml. Tendremos que definir la userServicevariable con UserServicetipo. Entonces, ¿por qué hacer una entrada en el xmlarchivo?
víbora
20

@Autowired es una anotación introducida en Spring 2.5, y se usa solo para inyección.

Por ejemplo:

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
fuente
10
Esto no se compilará y generalmente es incorrecto. @Autowiredno significa que "puede usar todas las funciones (método) y variables en Bclase de clase A". Lo que hace es traer una instancia de Aen instancias de B, por lo que puede hacer a.getId()desde B.
Dmitry Minkovsky
@dimadima Entonces, si lo hace System.out.println ("Valor de id forma A class" + a.getId ()); y no como lo ha hecho, será más correcto. Responda, ya que este es intuitivamente claro para mí y según mi nivel actual de comprensión está explicando Autowiring.
John Doe
la anotación automática se introduce en la primavera 2.5 docs.spring.io/spring-framework/docs/2.5.x/api/org/…
SpringLearner
1
Para una mejor comprensión, ya que soy nuevo en esto, ¿@autowired instanciará la Clase A usando el constructor predeterminado? Si no, cómo se instancian los valores en un bean o servicio si usamos autowired. Supongo que si llama al constructor predeterminado, ¿por qué usar autowiring en primer lugar, simplemente haga A a = new A (). Por favor aclarar?
Sameer
@Sameer Mediante las dependencias de Autowiring, puede guardar una gran cantidad de código repetitivo en sus Pruebas unitarias y también en las Clases de controlador, servicio y Dao, porque la creación de instancias de los campos viene con él automáticamente. No es necesario llamar al constructor.
kiltek
10

¿Cómo @Autowiredfunciona internamente?

Ejemplo:

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

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

archivo .xml se verá igual si no usa @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>

Si está usando @Autowiredentonces:

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

archivo .xml se verá igual si no usa @Autowired:

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

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

Si todavía tiene alguna duda, vaya a la demostración en vivo a continuación

¿Cómo funciona internamente @Autowired?

jeet singh parmar
fuente
6

Solo necesita anotar su clase de servicio UserServiceImplcon anotaciones:

@Service("userService")

Spring container se encargará del ciclo de vida de esta clase a medida que se registre como servicio.

Luego, en su controlador, puede cablearlo automáticamente (instanciarlo) y usar su funcionalidad:

@Autowired
UserService userService;
Jitender Chahar
fuente
3

La inyección de dependencia de Spring te ayuda a eliminar el acoplamiento de tus clases. En lugar de crear objetos como este:

UserService userService = new UserServiceImpl();

Lo usará después de introducir DI:

@Autowired
private UserService userService;

Para lograr esto, necesita crear un bean de su servicio en su ServiceConfigurationarchivo. Después de eso, debe importar esa ServiceConfigurationclase a su WebApplicationConfigurationclase para poder conectar automáticamente ese bean a su Controlador de la siguiente manera:

public class AccController {

    @Autowired
    private UserService userService;
} 

Puede encontrar un POC basado en la configuración de Java aquí, por ejemplo .

AbdusSalam
fuente
1

Forma estándar:

@RestController
public class Main {
    UserService userService;

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

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

Interfaz de servicio de usuario:

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

Clase UserServiceImpl:

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

Salida: Example test UserServiceImpl

Ese es un gran ejemplo de clases estrechamente acopladas, mal ejemplo de diseño y habrá problemas con las pruebas (PowerMockito también es malo).

Ahora echemos un vistazo a la inyección de dependencia SpringBoot, un buen ejemplo de acoplamiento flojo:

La interfaz sigue siendo la misma.

Clase principal:

@RestController
public class Main {
    UserService userService;

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

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

Clase ServiceUserImpl:

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

Salida: Example test UserServiceImpl

y ahora es fácil escribir prueba:

@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");
    }
}

Mostré @Autowiredanotaciones en el constructor pero también se puede usar en setter o campo.

Michu93
fuente
0

Todo el concepto de inversión de control significa que está libre de una tarea para instanciar objetos manualmente y proporcionar todas las dependencias necesarias. Cuando anota una clase con la anotación adecuada (por ejemplo @Service) Spring creará automáticamente una instancia de objeto para usted. Si no está familiarizado con las anotaciones, también puede usar el archivo XML. Sin embargo, no es una mala idea crear instancias de clases manualmente (con la newpalabra clave) en pruebas unitarias cuando no desea cargar todo el contexto de primavera.

k13i
fuente
0

Tenga en cuenta que debe habilitar la @Autowiredanotación agregando elementos <context:annotation-config/>al archivo de configuración de Spring. Esto registrará el AutowiredAnnotationBeanPostProcessorque se encarga del procesamiento de la anotación.

Y luego puede conectar automáticamente su servicio utilizando el método de inyección de campo.

public class YourController{

 @Autowired
 private UserService userService; 

}

Encontré esto en la publicación Spring @autowired annotation

David Pham
fuente
0

Hay 3 formas de crear una instancia usando @Autowired.

1. @Autowireden Propiedades

La anotación se puede usar directamente en las propiedades, eliminando así la necesidad de getters y setters:

    @Component("userService")
    public class UserService {

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

    @Component
    public class UserController {

        @Autowired
        UserService userService

    }

En el ejemplo anterior, Spring busca e inyecta userServicecuando UserControllerse crea.

2. @Autowireden setters

La @Autowiredanotación se puede utilizar en métodos de establecimiento. En el ejemplo a continuación, cuando la anotación se utiliza en el método setter, se llama al método setter con la instancia de userServicecuándo UserControllerse crea:

public class UserController {

    private UserService userService;

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

3. @Autowiredsobre constructores

La @Autowiredanotación también se puede usar en constructores. En el ejemplo a continuación, cuando la anotación se usa en un constructor, una instancia de userServicese inyecta como un argumento para el constructor cuando UserControllerse crea:

public class UserController {

    private UserService userService;

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

En palabras simples, Autowiring, el cableado de enlaces automáticamente, ahora viene la pregunta de quién hace esto y qué tipo de cableado. La respuesta es: el contenedor hace esto y el tipo de cableado secundario es compatible, las primitivas deben hacerse manualmente.

Pregunta: ¿Cómo sabe el contenedor qué tipo de cableado?

Respuesta: Lo definimos como byType, byName, constructor.

Pregunta: ¿Hay alguna forma en que no definamos el tipo de cableado automático?

Respuesta: Sí, está ahí haciendo una anotación, @Autowired.

Pregunta: ¿Pero cómo sabe el sistema que necesito elegir este tipo de datos secundarios?

Respuesta: Proporcionará esos datos en su archivo spring.xml o mediante anotaciones de esterotipo a su clase para que el contenedor pueda crear los objetos por usted mismo.

Pratik Gaurav
fuente