Как работает электропроводка весной?

Я немного запутался, как инверсия управления (IoC) работает в Spring,

Скажем, у меня есть класс обслуживания под названием UserServiceImpl который реализует UserService интерфейс.

Как бы это было @Autowired?

И по моему Controllers как бы я instantiate instance этого сервиса?

Буду ли я просто сделать следующее?

UserService userService = new UserServiceImpl();

8 ответов

Решение

Во-первых, и это самое главное - все бины Spring управляются - они "живут" внутри контейнера, называемого "контекстом приложения".

Во-вторых, каждое приложение имеет точку входа в этот контекст. Веб-приложения имеют сервлет, JSF использует el-resolver и т. Д. Кроме того, есть место, где контекст приложения загружается, а все компоненты - автоматически подключаются. В веб-приложениях это может быть слушатель запуска.

Автопроводка происходит путем помещения экземпляра одного компонента в нужное поле в экземпляре другого компонента. Оба класса должны быть bean-компонентами, то есть они должны быть определены для жизни в контексте приложения.

Что такое "жизнь" в контексте приложения? Это означает, что контекст создает объекты, а не вы. Т.е. - ты никогда не сделаешь new UserServiceImpl() - контейнер находит каждую точку впрыска и устанавливает там экземпляр.

В ваших контроллерах у вас просто есть следующее:

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

    }
}

Несколько заметок:

  • В вашем applicationContext.xml Вы должны включить <context:component-scan> так что классы сканируются на @Controller, @Serviceи др. аннотации.
  • Точкой входа для приложения Spring-MVC является DispatcherServlet, но он скрыт от вас, и, следовательно, прямое взаимодействие и начальная загрузка контекста приложения происходят за сценой.
  • UserServiceImpl также должен быть определен как бин - либо с использованием <bean id=".." class=".."> или используя @Service аннотаций. Поскольку он будет единственным разработчиком UserServiceбудет введено.
  • Отдельно от @Autowired Аннотация, Spring может использовать XML-конфигурируемую автоматическую разводку. В этом случае все поля с именем или типом, совпадающим с существующим компонентом, автоматически вводятся в компонент. Фактически, это была первоначальная идея автопроводки - вводить поля с зависимостями без какой-либо настройки. Другие аннотации, такие как @Inject, @Resource также может быть использован.

Зависит от того, прошли ли вы маршрут аннотаций или маршрут определения XML-компонента.

Скажем, у вас есть бобы, определенные в вашем applicationContext.xml:

<beans ...>

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

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

</beans>

Автопроводка происходит при запуске приложения. Итак, в fooController, который ради аргументов хочет использовать UserServiceImpl класс, вы бы аннотировали его следующим образом:

public class FooController {

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

    // rest of class goes here
}

Когда он видит @AutowiredSpring будет искать класс, соответствующий свойству в applicationContext, и автоматически вставлять его. Если у вас более одного компонента UserService, вам нужно будет определить, какой из них следует использовать.

Если вы делаете следующее:

UserService service = new UserServiceImpl();

Он не поднимет @Autowired, если вы не установите его самостоятельно.

@Autowired это аннотация, представленная в Spring 2.5, и она используется только для инъекций.

Например:

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(););
    }
}

Как @Autowired работает внутри?

Ex -

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

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

XML-файл будет выглядеть одинаково, если не использовать @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>

Если вы используете @Autowired, то

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

XML-файл будет выглядеть одинаково, если не использовать @Autowired

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

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

Если все еще есть какие-то сомнения, пройдите демонстрацию ниже

Как @Autowired работает внутри?

@Autowired

  • Помечает конструктор, поле, метод сеттера или метод конфигурации как автоматически подключаемые средствами Spring для внедрения зависимостей.

  • Только один конструктор (не более) из любого заданного класса EJB-компонента может нести эту аннотацию, указывая, что конструктор автоматически подключается при использовании в качестве Spring-компонента. Такой конструктор не должен быть публичным.

  • Поля вводятся сразу после создания компонента, до того, как будут вызваны какие-либо методы конфигурации. Такое поле конфигурации не должно быть общедоступным.

  • Методы Config могут иметь произвольное имя и любое количество аргументов; каждый из этих аргументов будет автоматически связан с соответствующим компонентом в контейнере Spring. Методы установки свойств бина фактически являются частным случаем такого общего метода конфигурации. Такие методы конфигурации не должны быть публичными.

  • В случае методов с несколькими аргументами параметр 'required' применим для всех аргументов.

  • В случае типа зависимости Collection или Map контейнер автоматически соединит все bean-компоненты, соответствующие объявленному типу значения. В случае Map ключи должны быть объявлены как тип String и будут преобразованы в соответствующие имена бинов.

Вам просто нужно аннотировать свой класс обслуживания UserServiceImpl с аннотацией

@Service("userService")

Контейнер Spring позаботится о жизненном цикле этого класса, поскольку он регистрируется как сервис.

Затем в вашем контроллере вы можете автоматически подключить (создать экземпляр) его и использовать его функциональность.

@Autowired
UserService userService;

Внедрение зависимостей Spring поможет вам удалить связь из ваших классов. Вместо создания такого объекта

UserService userService = new UserServiceImpl();

Вы будете использовать это после введения DI

@Autowired
private UserService userService;

Для достижения этого вам нужно создать компонент вашего сервиса в вашем файле ServiceConfiguration. После этого вам нужно импортировать этот класс ServiceConfiguration в ваш класс WebApplicationConfiguration, чтобы вы могли автоматически связывать этот bean-компонент с вашим контроллером следующим образом.

public class AccController {

    @Autowired
    private UserService userService;
} 

Вы можете найти POC на основе конфигурации Java здесь пример

Есть 3 способа создать экземпляр, используя @Autowired.

1. @Autowired на свойствах

Аннотации можно использовать непосредственно в свойствах, что устраняет необходимость в геттерах и сеттерах:

    @Component("userService")
    public class UserService {

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

    @Component
    public class UserController {

        @Autowired
        UserService userService

    }

В приведенном выше примере Spring ищет и вводит userService когда UserController создано.

2. @Autowired по сеттерам

В @Autowiredаннотацию можно использовать для методов установки. В приведенном ниже примере, когда аннотация используется в методе установки, метод установки вызывается с экземпляромuserService когда UserController создано:

public class UserController {

    private UserService userService;

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

3. @Autowired на конструкторах

В @Autowiredаннотация также может использоваться для конструкторов. В приведенном ниже примере, когда аннотация используется в конструкторе, экземплярuserService вводится в качестве аргумента конструктору, когда UserController создано:

public class UserController {

    private UserService userService;

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

Стандартный способ:

@RestController
public class Main {
    UserService userService;

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

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

Интерфейс пользовательского сервиса:

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

UserServiceImpl класс:

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

Выход: Example test UserServiceImpl

Это отличный пример тесно связанных классов, пример плохого дизайна и проблемы с тестированием (PowerMockito тоже плох).

Теперь давайте посмотрим на внедрение зависимостей SpringBoot, хороший пример слабой связи:

Интерфейс остался прежним,

Основной класс:

@RestController
public class Main {
    UserService userService;

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

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

ServiceUserImpl класс:

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

Выход: Example test UserServiceImpl

и теперь легко написать тест:

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

Я показал @Autowired аннотация для конструктора, но ее также можно использовать для установщика или поля.

Проще говоря, Autowiring, проводка ссылок автоматически, теперь возникает вопрос, кто это делает и какой вид проводки. Ответ: Контейнер делает это, и поддерживается вторичный тип проводки, примитивы нужно делать вручную.

Вопрос: Как контейнер узнать, какой тип проводки?

Ответ: Мы определяем его как byType,byName,constructor.

Вопрос: Есть ли способ не определять тип автомонтирования?

Ответ: Да, с помощью одной аннотации @Autowired.

Вопрос: Но как система узнает, что мне нужно выбрать этот тип вторичных данных?

Ответ: вы предоставите эти данные в файле spring.xml или с помощью аннотаций стеротипов к вашему классу, чтобы контейнер сам мог создавать объекты для вас.

Вся концепция инверсии управления означает, что вы свободны от рутинной работы по созданию экземпляров объектов вручную и предоставлению всех необходимых зависимостей. Когда вы аннотируете класс с соответствующей аннотацией (например, @Service) Spring автоматически создаст объект для вас. Если вы не знакомы с аннотациями, вы также можете использовать XML-файл. Тем не менее, создание экземпляров классов вручную (с new ключевое слово) в модульных тестах, когда вы не хотите загружать весь весенний контекст.

Имейте в виду, что вы должны включить аннотацию @Autowired, добавив элемент <context:annotation-config/> в конфигурационный файл пружины. Это зарегистрирует AutowiredAnnotationBeanPostProcessor, который заботится об обработке аннотации.

И тогда вы можете автоматически подключить свой сервис с помощью метода Field Injection.

public class YourController{

 @Autowired
 private UserService userService; 

}

Я нашел это из поста Spring @autowired аннотации

Другие вопросы по тегам