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

510

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

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

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

И по моему Controllers, как бы яinstantiateinstance этой услуги?

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

UserService userService = new UserServiceImpl();
Бланкмэн
источник

Ответы:

703

Во-первых, и это самое главное - все бины 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 - либо с <bean id=".." class="..">использованием, либо с использованием @Serviceаннотации. Поскольку он будет единственным разработчиком UserService, он будет введен.
  • Помимо @Autowiredаннотации, Spring может использовать XML-конфигурируемую автоматическую разводку. В этом случае все поля, которые имеют имя или тип, совпадающий с существующим компонентом, автоматически вводятся в компонент. Фактически, это было первоначальной идеей автопроводки - вводить поля с зависимостями без какой-либо настройки. Другие аннотации, такие как @Inject, @Resourceтакже могут быть использованы.
Bozho
источник
7
да, UserServiceImpl помечен Service, а UserService является интерфейсом
Божо
17
область по умолчанию - singleton, поэтому у вас будет только один экземпляр компонента, который вводится в нескольких местах. Если вы явно определите область действия как «прототип», то будут существовать несколько экземпляров, возможно, ленивых (в зависимости от конфигурации)
Божо
2
Большое спасибо за ваш пост, это действительно прояснило для меня. Что касается «Поскольку это будет единственный разработчик или UserService, он будет введен». Что делать, если есть несколько классов, которые реализуют Userservice? Как Spring узнает, какую реализацию он должен использовать?
Шишигами
7
если есть один, обозначенный как «основной», он использует его. В противном случае это вызывает исключение
Божо
3
нет, userService создается только один раз, он находится в одноэтапной области
Божо
64

Зависит от того, хотите ли вы использовать маршрут аннотаций или маршрут определения 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
}

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

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

UserService service = new UserServiceImpl();

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

Бен Дж
источник
2
Так какая польза от определения bean idв applicationContext.xml. Нам нужно определить userServiceпеременную с UserServiceтипом. Так зачем делать запись в xmlфайле.
гадюка
20

@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(););
    }
}
Мохит Бансал
источник
10
Это не скомпилируется и, как правило, неверно. @Autowiredне означает, что «вы можете использовать все функции (методы) и переменные в Bклассе из класса A». То, что он делает, приводит экземпляр Aв экземпляры B, так что вы можете сделать a.getId()из B.
Дмитрий Минковский
@dimadima Итак, если он сделает System.out.println («Значение идентификатора формы A class» + a.getId ()); и не так, как он это сделал на самом деле, это будет более правильным. Пожалуйста, ответьте, так как это интуитивно понятно для меня и согласно моему нынешнему уровню понимания объясняет Autowiring.
Джон Доу
Весенняя аннотация в проводном режиме представлена ​​весной 2.5 docs.spring.io/spring-framework/docs/2.5.x/api/org/…
SpringLearner
1
Для лучшего понимания, как я новичок в этом, будет ли @autowired создавать экземпляр класса A с помощью конструктора по умолчанию? Если нет, то как создаются значения в компоненте или сервисе, если мы используем autowired. Я предполагаю, что если он вызывает конструктор по умолчанию, зачем использовать autowiring, просто сделайте A a = new A (). Просьба уточнить?
Самер
@Sameer С помощью зависимостей Autowiring вы можете сохранить много шаблонного кода в своих модульных тестах, а также в классах Controller, Service и Dao, поскольку создание экземпляров полей сопровождается им автоматически. Не нужно вызывать конструктор.
килтек
10

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

Пример:

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 работает внутри?

Джит Сингх Пармар
источник
6

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

@Service("userService")

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

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

@Autowired
UserService userService;
Джитендер Чахар
источник
3

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

UserService userService = new UserServiceImpl();

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

@Autowired
private UserService userService;

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

public class AccController {

    @Autowired
    private UserService userService;
} 

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

Абдуссалама
источник
1

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

@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аннотацию на конструкторе, но она также может быть использована на установщике или поле.

Michu93
источник
0

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

k13i
источник
0

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

И тогда вы можете автоматически подключить свой сервис, используя метод полевого ввода.

public class YourController{

 @Autowired
 private UserService userService; 

}

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

Дэвид Фам
источник
0

Есть 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Аннотации можно использовать на сеттерах. В приведенном ниже примере, когда аннотация используется в методе setter, метод setter вызывается с экземпляром 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;
    }
}
Mak
источник
0

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

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

Ответ: Мы определяем это как byType, byName, конструктор.

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

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

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

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

Пратик Гаурав
источник