Как работает электропроводка весной?
Я немного запутался, как инверсия управления (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
}
Когда он видит @Autowired
Spring будет искать класс, соответствующий свойству в 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
Помечает конструктор, поле, метод сеттера или метод конфигурации как автоматически подключаемые средствами 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 аннотации