- PVSM.RU - https://www.pvsm.ru -

Вы можете использовать это руководство для различных целей:
Примечание: Статья ~ 9000 слов, вероятно, не стоит читать ее на мобильном устройстве. Добавьте ее в закладки и вернитесь позже. И даже на компьютере ешь читай этого слона по одному кусочку за раз :-)
Примечание переводчика:
Текст статьи весьма большой и в переводе могут остаться неточности несмотря на многократную вычитку текста. Буду благодарен всем за конструктивные замечания.
Многие компании используют Spring, однако когда вы захотите узнать об этом фреймворке и перейдете на сайт spring.io [1], то увидите, что вселенная Spring на самом деле состоит из 21 различных активных проектов. Ой!
Кроме того, если вы начали программировать с помощью Spring в последние пару лет, очень велика вероятность того, что вы перешли непосредственно к Spring Boot [2] или Spring Data.
Однако это руководство касается только одного, самого важного из этих проектов: Spring Framework [3]. Почему?
Потому что важно понимать, что Spring Framework является основой для всех других проектов. Spring Boot, Spring Data, Spring Batch — все это построено поверх Spring.
Это имеет два последствия:
По сути Spring Framework представляет собой просто контейнер внедрения зависимостей [4], с несколькими удобными слоями (например: доступ к базе данных, прокси, аспектно-ориентированное программирование, RPC, веб-инфраструктура MVC). Это все позволяет вам быстрее и удобнее создавать Java-приложения.
Только это не очень помогает, не так ли?
Остальная часть этого руководства.
Если вы уже знаете, что такое внедрение зависимостей, не стесняйтесь переходить прямо к следующему разделу Контейнер Spring IOC / Dependency Injection. В противном случае читайте дальше.
Представьте, что вы пишете Java класс, который позволяет вам получить доступ к таблице пользователей в вашей базе данных. Вы бы назвали эти классы DAO (объект доступа к данным) или репозитории. Итак, вы собираетесь написать класс UserDAO.
public class UserDao {
public User findById(Integer id) {
// execute a sql query to find the user
}
}
Ваш класс UserDAO имеет только один метод, который позволяет вам находить пользователей в вашей таблице базы данных по их идентификаторам.
Чтобы выполнить соответствующий SQL запрос, вашему классу UserDAO требуется соединение с базой данных. А в мире Java вы, обычно, получаете соединение с базой данных из другого класса, называемого DataSource. Итак, ваш код теперь будет выглядеть примерно так:
import javax.sql.DataSource;
public class UserDao {
public User findById(Integer id) throws SQLException {
try (Connection connection = dataSource.getConnection()) { // (1)
PreparedStatement selectStatement = connection.prepareStatement("select * from users where id = ?");
// use the connection etc.
}
}
}
Наивным решением было бы просто создать новый источник данных с помощью конструктора, каждый раз, когда он вам нужен. Итак, для подключения к базе данных MySQL ваш UserDAO может выглядеть так:
import com.mysql.cj.jdbc.MysqlDataSource;
public class UserDao {
public User findById(Integer id) {
MysqlDataSource dataSource = new MysqlDataSource(); // (1)
dataSource.setURL("jdbc:mysql://localhost:3306/myDatabase");
dataSource.setUser("root");
dataSource.setPassword("s3cr3t");
try (Connection connection = dataSource.getConnection()) { // (2)
PreparedStatement selectStatement = connection.prepareStatement("select * from users where id = ?");
// execute the statement..convert the raw jdbc resultset to a user
return user;
}
}
}
Это работает, но давайте посмотрим, что произойдет, когда мы расширим наш класс UserDao другим методом, findByFirstName.
К сожалению, этому методу также нужен источник данных для работы. Мы можем добавить этот новый метод к нашему UserDAO и применить некоторые рефакторинги, введя метод newDataSource.
import com.mysql.cj.jdbc.MysqlDataSource;
public class UserDao {
public User findById(Integer id) {
try (Connection connection = newDataSource().getConnection()) { // (1)
PreparedStatement selectStatement = connection.prepareStatement("select * from users where id = ?");
// TODO execute the select , handle exceptions, return the user
}
}
public User findByFirstName(String firstName) {
try (Connection connection = newDataSource().getConnection()) { // (2)
PreparedStatement selectStatement = connection.prepareStatement("select * from users where first_name = ?");
// TODO execute the select , handle exceptions, return the user
}
}
public DataSource newDataSource() {
MysqlDataSource dataSource = new MysqlDataSource(); // (3)
dataSource.setUser("root");
dataSource.setPassword("s3cr3t");
dataSource.setURL("jdbc:mysql://localhost:3306/myDatabase");
return dataSource;
}
}
Этот подход работает, но имеет два недостатка:
Что произойдет, если мы хотим создать новый класс ProductDAO, который также выполняет операторы SQL? Ваш ProductDAO также будет иметь зависимость DataSource, которая теперь доступна только в вашем классе UserDAO. Затем у вас будет другой подобный метод или извлечен вспомогательный класс, содержащий ваш DataSource.
Мы создаем совершенно новый источник данных для каждого запроса SQL. Учтите, что DataSource открывает реальное сокет-соединение от вашей Java-программы к вашей базе данных. Это занимает время и довольно дорого. Было бы намного лучше, если бы мы открыли только один источник данных и использовали его повторно, вместо того, чтобы открывать и закрывать их тонны. Одним из способов сделать это может быть сохранение источника данных в закрытом поле в нашем UserDao, чтобы его можно было повторно использовать между методами, но это не помогает при дублировании между несколькими DAO.
Чтобы решить эти проблемы, вы можете подумать о написании глобального класса Application, который выглядит примерно так:
import com.mysql.cj.jdbc.MysqlDataSource;
public enum Application {
INSTANCE;
private DataSource dataSource;
public DataSource dataSource() {
if (dataSource == null) {
MysqlDataSource dataSource = new MysqlDataSource();
dataSource.setUser("root");
dataSource.setPassword("s3cr3t");
dataSource.setURL("jdbc:mysql://localhost:3306/myDatabase");
this.dataSource = dataSource;
}
return dataSource;
}
}
Ваш класс UserDAO теперь может выглядеть так:
import com.yourpackage.Application;
public class UserDao {
public User findById(Integer id) {
try (Connection connection = Application.INSTANCE.dataSource().getConnection()) { // (1)
PreparedStatement selectStatement = connection.prepareStatement("select * from users where id = ?");
// TODO execute the select etc.
}
}
public User findByFirstName(String firstName) {
try (Connection connection = Application.INSTANCE.dataSource().getConnection()) { // (2)
PreparedStatement selectStatement = connection.prepareStatement("select * from users where first_name = ?");
// TODO execute the select etc.
}
}
}
Это улучшение по двум направлениям:
Однако у этого решения есть еще несколько недостатков:
Давайте сделаем еще один шаг вперед.
Было бы неплохо, если бы вам в классе UserDAO вообще не приходилось беспокоиться о поиске зависимостей? Вместо того, чтобы активно вызывать Application.INSTANCE.dataSource(), ваш UserDAO мог бы (как-то) кричать, что он ему нужен, но больше не контролирует, когда / как / откуда он его получает?
Это то, что называется инверсией управления (Inversion of Control).
Давайте посмотрим, как может выглядеть наш класс UserDAO, с новым конструктором.
import javax.sql.DataSource;
public class UserDao {
private DataSource dataSource;
private UserDao(DataSource dataSource) { // (1)
this.dataSource = dataSource;
}
public User findById(Integer id) {
try (Connection connection = dataSource.getConnection()) { // (2)
PreparedStatement selectStatement = connection.prepareStatement("select * from users where id = ?");
// TODO execute the select etc.
}
}
public User findByFirstName(String firstName) {
try (Connection connection = dataSource.getConnection()) { // (2)
PreparedStatement selectStatement = connection.prepareStatement("select * from users where first_name = ?");
// TODO execute the select etc.
}
}
}
С точки зрения UserDao это выглядит намного лучше. Он больше не знает ни о классе приложения, ни о том, как создавать сами источники данных. Он только объявляет миру, что «если вы хотите создать (то есть использовать) меня, вам нужно дать мне источник данных».
Но представьте, что вы хотите запустить свое приложение. Если раньше вы могли вызывать «new UserService()», то теперь вам нужно обязательно вызвать новый UserDao(dataSource).
public class MyApplication {
public static void main(String[] args) {
UserDao userDao = new UserDao(Application.INSTANCE.dataSource());
User user1 = userDao.findById(1);
User user2 = userDao.findById(2);
// etc ...
}
}
Следовательно, проблема в том, что вы, как программист, все еще создаете UserDAO с помощью их конструктора и, таким образом, устанавливаете зависимость DataSource вручную.
Разве не было бы хорошо, если бы кто-то знал, что ваш UserDAO имеет зависимость от конструктора DataSource, и знал, как ее создать? А затем волшебным образом сконструируйте для вас оба объекта: работающий DataSource и работающий UserDao?
Этот кто-то является контейнером внедрения зависимостей и является именно тем, что представляет собой среда Spring.
Как уже упоминалось в самом начале, Spring Framework по своей сути является контейнером внедрения зависимостей, который управляет написанными вами классами и их зависимостями для вас (см. Предыдущий раздел). Давайте выясним, как это происходит.
Тот, кто контролирует все ваши классы и может управлять ими соответствующим образом (читай: создайте их с необходимыми зависимостями), называется ApplicationContext во вселенной Spring.
Чего мы хотим добиться, так это следующего кода (я описал UserDao и DataSource в предыдущем разделе, перейдите по ссылке, если вы пришли сюда и пропустили его):
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import javax.sql.DataSource;
public class MyApplication {
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(someConfigClass); // (1)
UserDao userDao = ctx.getBean(UserDao.class); // (2)
User user1 = userDao.findById(1);
User user2 = userDao.findById(2);
DataSource dataSource = ctx.getBean(DataSource.class); // (3)
// etc ...
}
}
Это довольно круто, не правда ли? Вам, как вызывающей стороне, больше не нужно беспокоиться о создании классов, вы можете просто попросить ApplicationContext предоставить вам рабочие классы!
Но как это работает?
В приведенном выше коде мы помещаем переменную с именем someConfigClass в конструктор AnnotationConfigApplicationContext. Вот быстрое напоминание:
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MyApplication {
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(someConfigClass); // (1)
// ...
}
}
То, что вы действительно хотите передать в конструктор ApplicationContext, это ссылка на класс конфигурации, который должен выглядеть следующим образом:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MyApplicationContextConfiguration { // (1)
@Bean
public DataSource dataSource() { // (2)
MysqlDataSource dataSource = new MysqlDataSource();
dataSource.setUser("root");
dataSource.setPassword("s3cr3t");
dataSource.setURL("jdbc:mysql://localhost:3306/myDatabase");
return dataSource;
}
@Bean
public UserDao userDao() { // (3)
return new UserDao(dataSource());
}
}
@Configuration, который немного похож на класс Application.java из раздела Зависимости в глобальном классе приложения.@Bean для Spring.Этого класса конфигурации уже достаточно для запуска самого первого приложения Spring.
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MyApplication {
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(MyApplicationContextConfiguration.class);
UserDao userDao = ctx.getBean(UserDao.class);
// User user1 = userDao.findById(1);
// User user2 = userDao.findById(1);
DataSource dataSource = ctx.getBean(DataSource.class);
}
}
Теперь давайте выясним, что именно Spring и AnnotationConfigApplicationContext делают с тем классом конфигурации, который вы написали.
Существует много способов создания Spring ApplicationContext, например, с помощью файлов XML, аннотированных классов конфигурации Java или даже программно. Для внешнего мира это представлено через единый интерфейс ApplicationContext.
Посмотрите на класс MyApplicationContextConfiguration сверху. Это класс Java, который содержит аннотации Spring. Вот почему вам необходимо создать аннотацию ConfigApplicationContext.
Если вместо этого вы хотите создать свой ApplicationContext из файлов XML, вы должны создать ClassPathXmlApplicationContext.
Есть и много других, но в современном приложении Spring вы обычно начинаете с контекста приложения, основанного на аннотациях.
@Bean? Что такое Spring Bean?Вам придется думать о методах внутри вашего класса конфигурации ApplicationContext как о фабричных методах. На данный момент есть один метод, который знает, как создавать экземпляры UserDao, и один метод, который создает экземпляры DataSource.
Эти экземпляры, которые создаются этими фабричными методами, называются bean-компонентами. Это причудливое слово: я (контейнер Spring) создал их, и они находятся под моим контролем.
Но это приводит к вопросу: сколько экземпляров определенного компонента должно быть создано Spring?
Сколько экземпляров наших DAO следует создать в Spring? Чтобы ответить на этот вопрос, вам нужно узнать о bean scope (область применения бина).
Вы можете прочитать полный список доступных областей применения бинов [5] здесь, но пока достаточно знать, что вы можете повлиять на область действия с помощью еще одной аннотации.
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MyApplicationContextConfiguration {
@Bean
@Scope("singleton")
// @Scope("prototype") etc.
public DataSource dataSource() {
MysqlDataSource dataSource = new MysqlDataSource();
dataSource.setUser("root");
dataSource.setPassword("s3cr3t");
dataSource.setURL("jdbc:mysql://localhost:3306/myDatabase");
return dataSource;
}
}
Аннотация области (scope annotation) определяет, сколько экземпляров создаст Spring. И, как упоминалось выше, это довольно просто:
Суть: большинство приложений Spring почти полностью состоят из одноэлементных bean-компонентов, в которые время от времени добавляются другие области действия bean-компонента (прототип, запрос, сессия, websocket и т.д.).
Теперь, когда вы знаете о ApplicationContexts, Beans & Scopes, давайте еще раз рассмотрим зависимости или то, как наш UserDAO может получить DataSource.
До сих пор вы явно настраивали свои bean-компоненты в конфигурации ApplicationContext с помощью аннотированных @Bean методов Java.
Это то, что вы бы назвали Spring Java Config, в отличие от указания всего в XML, который исторически был подходом для Spring. Просто краткий обзор того, как это выглядит:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MyApplicationContextConfiguration {
@Bean
public DataSource dataSource() {
MysqlDataSource dataSource = new MysqlDataSource();
dataSource.setUser("root");
dataSource.setPassword("s3cr3t");
dataSource.setURL("jdbc:mysql://localhost:3306/myDatabase");
return dataSource;
}
@Bean
public UserDao userDao() { // (1)
return new UserDao(dataSource());
}
}
Вот где появляется другая аннотация @ComponentScan.
@ComponentScan?Первое изменение, которое вам нужно применить к своей конфигурации контекста, — это добавить к ней дополнительную аннотацию @ComponentScan.
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan // (1)
public class MyApplicationContextConfiguration {
@Bean
public DataSource dataSource() {
MysqlDataSource dataSource = new MysqlDataSource();
dataSource.setUser("root");
dataSource.setPassword("s3cr3t");
dataSource.setURL("jdbc:mysql://localhost:3306/myDatabase");
return dataSource;
}
// (2)
// no more UserDao @Bean method!
}
@ComponentScan.То, что делает эта аннотация @ComponentScan, это сказать Spring: Посмотрите на все классы Java в том же пакете, что и конфигурация контекста, если они выглядят как Spring Bean!
Это означает, что, если ваш MyApplicationContextConfiguration находится в пакете com.marcobehler, Spring будет сканировать каждый пакет, включая подпакеты, который начинается с com.marcobehler для поиска потенциальных компонентов Spring.
Как Spring узнает, является ли что-то бином Spring? Легко: Ваши классы должны быть помечены аннотацией маркера, называемой @Component.
@Component и @Autowired?Давайте добавим аннотацию @Component к вашему UserDAO.
import javax.sql.DataSource;
import org.springframework.stereotype.Component;
@Component
public class UserDao {
private DataSource dataSource;
private UserDao(DataSource dataSource) { // (1)
this.dataSource = dataSource;
}
}
@Bean, который вы написали ранее: эй, если вы обнаружите, что меня аннотируют с помощью @Component через ваш @ComponentScan, тогда я хочу быть бином Spring, управляемым вами, контейнером внедрения зависимостей!(Когда вы посмотрите на исходный код аннотаций, таких как @Controller, @Service или @Repository, позже, вы обнаружите, что все они состоят из нескольких дополнительных аннотаций, всегда включая @Component!).
Отсутствует только один маленький кусочек информации. Как Spring узнает, что он должен взять DataSource, который вы указали как метод @Bean, а затем создать новые UserDAO с этим конкретным DataSource?
Легко, с другой аннотацией: @Autowired. Следовательно, ваш окончательный код будет выглядеть следующим образом.
import javax.sql.DataSource;
import org.springframework.stereotype.Component;
import org.springframework.beans.factory.annotation.Autowired;
@Component
public class UserDao {
private DataSource dataSource;
private UserDao(@Autowired DataSource dataSource) {
this.dataSource = dataSource;
}
}
Теперь Spring имеет всю информацию, необходимую для создания bean-компонентов UserDAO:
@Component → Spring создаст его@Autowired → Spring автоматически внедрит источник данных, настроенный с помощью вашего метода @BeanЯ лгал вам чуть-чуть в предыдущем разделе. В более ранних версиях Spring (до 4.2) вам нужно было указывать @Autowired, чтобы внедрение зависимости работало.
В более новых версиях Spring действительно достаточно умен, чтобы внедрять эти зависимости без явной аннотации @Autowired в конструкторе. Так что это также будет работать.
@Component
public class UserDao {
private DataSource dataSource;
private UserDao(DataSource dataSource) {
this.dataSource = dataSource;
}
}
Почему я упомянул @Autowired тогда? Поскольку это не повредит, то есть делает вещи более явными и потому что вы можете использовать @Autowired во многих других местах, кроме конструкторов.
Давайте рассмотрим различные способы внедрения зависимостей — внедрение конструкторов — всего лишь один из них.
Проще говоря, Spring не должен использовать конструктор для внедрения зависимостей.
Он также может напрямую внедрять поля.
import javax.sql.DataSource;
import org.springframework.stereotype.Component;
import org.springframework.beans.factory.annotation.Autowired;
@Component
public class UserDao {
@Autowired
private DataSource dataSource;
}
Кроме того, Spring также может внедрять сеттеры.
import javax.sql.DataSource;
import org.springframework.stereotype.Component;
import org.springframework.beans.factory.annotation.Autowired;
@Component
public class UserDao {
private DataSource dataSource;
@Autowired
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
}
Эти два стиля внедрения (поля, сеттеры) имеют тот же результат, что и внедрение конструктора: вы получите работающий Spring Bean. На самом деле есть еще один метод, называемый внедрением метода, который мы не будем здесь описывать.
Но очевидно, что они отличаются друг от друга, что означает, что было очень много споров о том, какой стиль внедрения является лучшим и какой вы должны использовать в своем проекте.
Было много споров в сети что лучше внедрение зависимости через конструктор или через поле и множеством сгромких голосов, утверждавших, что внедрение с помощью метода установки (setter) вредна [7].
Чтобы не добавлять шума к этим аргументам сформулируем суть этой статьи:
К настоящему моменту вы должны знать почти все, что вам нужно знать о контейнере зависимостей Spring.
Конечно, это еще не все, но если вы хорошо разбираетесь в ApplicationContexts, Beans, зависимостях и различных методах внедрения зависимостей, то вы уже на правильном пути.
Давайте посмотрим, что еще может предложить Spring, кроме инъекций чистой зависимости.
Внедрение зависимостей поможет вести вас к более структурированным программам, но внедрение зависимости здесь и там не совсем то, в чем заключается суть экосистемы Spring. Давайте еще раз посмотрим на простую ApplicationContextConfiguration:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MyApplicationContextConfiguration {
@Bean
public UserService userService() { // (1)
return new UserService();
}
}
Вот где проявляется функция скрытая особенность Spring:
В этом контексте Spring читает конфигурацию, содержащую метод @Bean, который вы написали, и поэтому Spring знает, как создавать и внедрять компоненты UserService.
Однако Spring может обманывать и создавать что-то еще, кроме вашего класса UserService. Как? Почему?
Потому что под капотом любой метод Spring @Bean может вернуть вам то, что (в вашем случае) выглядит и ощущается как UserService, но на самом деле это не так.
Он может вернуть вам прокси.
Прокси-сервер в какой-то момент делегирует службу UserService, которую вы написали, но сначала он выполнит свою собственную функциональность.

В частности, Spring по умолчанию создаст динамические прокси-серверы Cglib [9], которым не нужен интерфейс для работы прокси-серверов (например, внутренний механизм прокси-сервера JDK): вместо этого Cglib может прокси-классы посредством их подклассов на лету. (Если вы не уверены относительно отдельных шаблонов прокси, прочитайте больше о прокси в Википедии [10].)
Потому что это позволяет Spring дать вашим компонентам дополнительные функции без изменения кода. В сущности, это то, что является аспектно-ориентированным (или: AOP) программированием.
Давайте рассмотрим самый популярный пример AOP — аннотацию Spring @Transactional.
@TransactionalВаша реализация UserService выше может выглядеть примерно так:
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
@Component
public class UserService {
@Transactional // (2)
public User activateUser(Integer id) { // (1)
// execute some sql
// send an event
// send an email
}
}
@Transactional для этого метода сигнализирует Spring, что для работы этого метода необходимо открытое соединение с базой данных / транзакция и что указанная транзакция также должна быть зафиксирована в конце. И Spring должна сделать это.Проблема: хотя Spring может создать ваш компонент UserService через конфигурацию applicationContext, он не может переписать ваш UserService. Он не может просто вставить туда код, который открывает соединение с базой данных и фиксирует транзакцию с базой данных.
Но что она может сделать, так это создать прокси вокруг вашего UserService, который будет транзакционным. Таким образом, только прокси-сервер должен знать, как открывать и закрывать соединение с базой данных, а затем может просто делегировать его вашему UserService.
Давайте еще раз посмотрим на эту безопасную ContextConfiguration.
@Configuration
@EnableTransactionManagement // (1)
public class MyApplicationContextConfiguration {
@Bean
public UserService userService() { // (2)
return new UserService();
}
}
@Transactional, которая автоматически включает прокси Cglib под капотом.Поначалу это может показаться немного не интуитивным, но большинство разработчиков Spring очень скоро сталкиваются с прокси в сеансах отладки. Из-за прокси трассировки стека Spring могут быть довольно длинными и незнакомыми: когда вы входите в метод, вы можете очень хорошо сначала войти в прокси — что отпугивает людей. Это, однако, совершенно нормальное и ожидаемое поведение.
Прокси являются выбором по умолчанию при программировании AOP с помощью Spring. Однако вы не ограничены использованием прокси, вы также можете пройти полный маршрут AspectJ, который при желании изменяет ваш фактический байт-код. Однако покрытие AspectJ выходит за рамки данного руководства.
Также смотрите раздел: В чем разница между Spring AOP и AspectJ?
Конечно, о аспектно-ориентированном программировании можно сказать гораздо больше, но это руководство дает представление о том, как работают наиболее популярные сценарии использования Spring AOP, такие как @Transactional или Spring Security, @Secured. Вы можете даже написать свои собственные аннотации AOP, если хотите.
В качестве утешения для неожиданного конца, если вы хотите получить больше информации о том, как подробно работает управление @Transactional в Spring, посмотрите мое руководство по @Transactional [11].
Мы уже давно говорим о внедрении зависимостей и прокси. Давайте теперь посмотрим на то, что я бы назвал важными утилитами удобства в среде Spring. Одной из таких утилит является поддержка ресурсов Spring.
Подумайте, как бы вы попытались получить доступ к файлу в Java через HTTP или FTP. Вы можете использовать класс URL в Java [12] и написать некоторый код.
Точно так же, как бы вы читали в файлах из пути к классам вашего приложения? Или из контекста сервлета, это означает из корневого каталога веб-приложений (по общему признанию, это становится все реже и реже в современном приложении packaged.jar).
Опять же, вам нужно написать довольно много стандартного кода, чтобы он работал, и, к сожалению, код будет отличаться для каждого варианта использования (URL-адреса, пути к классам, контексты сервлетов).
Но есть решение: абстракция ресурсов Spring. Это легко объяснить в коде.
import org.springframework.core.io.Resource;
public class MyApplication {
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(someConfigClass); // (1)
Resource aClasspathTemplate = ctx.getResource("classpath:somePackage/application.properties"); // (2)
Resource aFileTemplate = ctx.getResource("file:///someDirectory/application.properties"); // (3)
Resource anHttpTemplate = ctx.getResource("https://marcobehler.com/application.properties"); // (4)
Resource depends = ctx.getResource("myhost.com/resource/path/myTemplate.txt"); // (5)
Resource s3Resources = ctx.getResource("s3://myBucket/myFile.txt"); // (6)
}
}
Короче говоря, Spring дает вам возможность доступа к ресурсам с помощью приятного небольшого синтаксиса. Интерфейс ресурса имеет несколько интересных методов:
public interface Resource extends InputStreamSource {
boolean exists();
String getFilename();
File getFile() throws IOException;
InputStream getInputStream() throws IOException;
// ... other methods commented out
}
Как видите, он позволяет вам выполнять самые распространенные операции с ресурсом:
Это позволяет вам делать все, что вы хотите с ресурсом, независимо от того, живете ли вы в Интернете, на вашем пути к классам или на жестком диске.
Абстракция ресурсов выглядит как такая крошечная функция, но она действительно сияет в сочетании со следующей удобной функцией, предлагаемой Spring: Properties.
Большая часть любого приложения — это чтение свойств (properties), таких как имя пользователя и пароли базы данных, конфигурация почтового сервера, детализированная конфигурация оплаты и т.д.
В простейшем виде эти свойства находятся в файлах .properties, и их может быть много:
Spring пытается упростить вам регистрацию и автоматический поиск свойств во всех этих различных источниках с помощью абстракции environment.
import org.springframework.core.env.Environment;
public class MyApplication {
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(someConfigClass);
Environment env = ctx.getEnvironment(); // (1)
String databaseUrl = env.getProperty("database.url"); // (2)
boolean containsPassword = env.containsProperty("database.password");
// etc
}
}
Что такое environment?
@PropertySources?В двух словах, среда состоит из одного или многих источников свойств выполняемого Spring приложения. Например:
(Примечание. Среда также состоит из профилей, то есть профилей «dev» или «production», но мы не будем вдаваться в подробности о профилях в этом пересмотре этого руководства).
По умолчанию [14] среда веб-приложения Spring MVC состоит из параметра ServletConfig/Context, источников системных свойств JNDI и JVM. Они также являются иерархическими, это означает, что они имеют порядок важности и перекрывают друг друга.
Тем не менее, довольно легко определить новые @PropertySources самостоятельно:
import org.springframework.context.annotation.PropertySources;
import org.springframework.context.annotation.PropertySource;
@Configuration
@PropertySources(
{@PropertySource("classpath:/com/${my.placeholder:default/path}/app.properties"),
@PropertySource("file://myFolder/app-production.properties")})
public class MyApplicationContextConfiguration {
// your beans
}
Теперь стало намного понятнее, почему мы говорили об управлении ресурсами Spring раньше. Потому что обе функции идут рука об руку.
Аннотация @PropertySource работает с любым допустимым классом конфигурации Spring и позволяет вам определять новые дополнительные источники с помощью абстракции ресурсов Spring: помните, что все дело в префиксах: http://, file://, classpath: и т.д.,
Определение свойств через @PropertySources — это хорошо, но разве нет лучшего способа, чем пройти через среду, чтобы получить к ним доступ? Да, есть.
@Value и внедрение значений свойствВы можете внедрять значения свойств в ваши bean-компоненты, так же, как вы бы добавили зависимость с аннотацией @Autowired. Но для свойств вам нужно использовать аннотацию @Value.
import org.springframework.stereotype.Component;
import org.springframework.beans.factory.annotation.Value;
@Component
public class PaymentService {
@Value("${paypal.password}") // (1)
private String paypalPassword;
public PaymentService(@Value("${paypal.url}") String paypalUrl) { // (2)
this.paypalUrl = paypalUrl;
}
}
@Value работает непосредственно с полями ...Там действительно не так много всего. Всякий раз, когда вы используете аннотацию @Value, Spring будет проходить через вашу (иерархическую) среду и искать соответствующее свойство — или выдавать сообщение об ошибке, если такого свойства не существует.
Spring Web MVC, также известный как Spring MVC, является веб-средой Spring. Это позволяет создавать все, что связано с сетью, от небольших веб-сайтов до сложных веб-сервисов. Он также поддерживает фреймворки, такие как Spring Boot.
Если вы совершенно не знакомы с MVC, вы можете прочитать страницу Model-View-Controller [15] в Википедии, прежде чем продолжить чтение.
В контексте рендеринга HTML-страниц, скажем, страницы учетной записи пользователя, вот как выглядит MVC в Spring:
@Controller, который отвечает на HTTP-запрос /account и знает, как преобразовать HTTP-запрос в объекты Java, а также ваши объекты Java в ответ HTML.Итак, вашей конечной целью является написание контроллеров с помощью Spring, которые сопоставляются с конкретными HTTP-запросами, такими как (/account), и предоставляют пользователю соответствующую HTML-страницу, которая визуализируется путем объединения представления и данных, необходимых для этой страницы.
Та же логика верна для написания веб-сервисов JSON или XML.
Прежде чем мы рассмотрим реализацию класса типа контроллер, давайте сделаем шаг назад и посмотрим, как мы реализовали бы веб-страницу в низко-низко-низкоуровневом Java: с помощью старого доброго API сервлета Java (на котором основывается Spring MVC). ).
На данный момент игнорируем MVC: чтобы написать что-нибудь, связанное с HTTP с Java, вы бы использовали сервлеты или, в частности, HttpServlets (примечание для придир: да, есть и другие способы, спасибо за замечание). Сервлеты могут обрабатывать HTTP-запросы, а также возвращать браузеру или клиенту соответствующий HTTP-ответ.
После написания вашего сервлета вы должны зарегистрировать его в контейнере сервлетов, таком как Tomcat [16] или Jetty. [17] Регистрация сервлета всегда включает путь, чтобы указать, за какие URL в вашем веб-приложении отвечает ваш сервлет. Давайте предположим путь "/*", поэтому каждый входящий HTTP-запрос к вашему приложению обрабатывается одним сервлетом.
Вот как может выглядеть этот сервлет:
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyServlet extends HttpServlet { // (1)
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) { // (2)
if (request.getRequestURI().startsWith("/account")) {
String userId = request.getParameter("userId");
// return <html> or {json} or <xml> for an account get request
} else if (request.getRequestURI().startsWith("/status")) {
// return <html> or {json} or <xml> for a health status get request
} // etc
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) { // (3)
// return <html> or {json} or <xml> for a post request, like a form submission
}
}
Обрабатывать каждый запрос к вашему приложению всего двумя способами — это немного громоздко, и для того, чтобы получить правильный MVC, требуется довольно много работы.
Кроме того, вашему MyServlet (контроллеру) необходимо выполнить довольно много ручного HTTP-специфического подключения, проверки URI запроса, просмотра строк, преобразования requestParameters и ответов и так далее.
Было бы намного приятнее, если бы вам не пришлось заботиться обо всем этом слесарном деле и позволить Spring сделать это за вас. Вот тут и приходит DispatcherServlet.
Uber-контроллер в среде Spring MVC Spring представляет собой сервлет, который называется DispatcherServlet.
Он называется DispatcherServlet, потому что он может буквально обрабатывать любой входящий HTTP-запрос, анализировать его содержимое и пересылать данные в виде симпатичных маленьких объектов Java в класс Controller.
Он также достаточно умен, чтобы брать выходные данные с этих контроллеров и конвертировать их в HTML / JSON / XML, в зависимости от того, что подходит. Весь процесс выглядит следующим образом (с пренебрежением большим количеством промежуточных классов, потому что DispatcherServlet не выполняет всю работу сам).

Это именно то, что вы хотите.
Давайте посмотрим на эти классы типа Controller более подробно.
Наконец, мы можем написать наш Controller класс, который обрабатывает запросы /account.
Страница /account будет HTML-страницей с несколькими динамическими переменными, такими как имя пользователя, адрес, информация о подписке и т.д.
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
/**
* A class that responds to /account requests.
* Think of Netflix's account page, where you want to see your username/password/subscription info
*/
@Controller // (1)
public class AccountController {
@GetMapping("/account/{userId}")
public String account(@PathVariable Integer userId) { // (2)
// TODO retrieve name, address, subscription information
return "templates/account"; // (3)
}
}
В этих двух строчках происходит МНОГО, давайте посмотрим, что именно.
У вас есть класс AccountController, который аннотируется @Controller. Он сообщает Spring: этот класс хочет реагировать на HTTP-запросы и ответы, чтобы DispatcherServlet знал об этом.
У вас есть простой Java-метод, называемый account(). Что еще интереснее, метод аннотируется @GetMapping. Это сообщает DispatcherServlet, что все запросы, похожие на /account/{userId}, должны обрабатываться именно этим методом контроллера. Более того, dispatcherServlet возьмет {userId}, преобразует его в целое число и использует его в качестве параметра метода!
Наш метод Java возвращает строку с именем account. На самом деле это не просто строка, а ссылка на представление (HTML-шаблон). Давайте посмотрим на эти шаблоны на секунду.
Spring MVC по умолчанию предполагает, что вы хотите визуализировать некоторый HTML. И, конечно же, вы не хотите сами отображать HTML-строки с помощью конкатенации строк, скорее вы захотите использовать шаблонную среду, такую как Velocity [18] или Freemarker [19]. Spring интегрируется со всеми этими технологиями.
Итак, вы могли бы иметь следующее представление (шаблон):
classpath:/templates/account.vm
<html>
<body>
Hello $user.name, this is your account!
<!-- list subscriptions etc -->
</body>
</html>
Это очень простая HTML-страница, содержащая одну переменную, $user.name. Как эта переменная попадает в шаблон? Пока что этого не было в нашем контроллере аккаунта. Давайте вставим это.
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@Controller
public class AccountController {
@GetMapping("/account/{userId}")
public String account(Model model, @PathVariable Integer userId) { // (1)
// TODO validate user id
model.addAttribute("user", userDao.findById(userId)); // (2)
return "templates/account"; // (3)
}
}
Вот и все, разработка Spring MVC полностью завершена!
С веб-сервисами вы не генерируете HTML, а генерируете XML или JSON. Это довольно просто, с Spring MVC также.
Конечно, вам нужна соответствующая библиотека, такая как Jackson [20], добавленная в ваш проект, но тогда вы можете просто аннотировать свой Controller дополнительной аннотацией, чтобы сигнализировать Spring: пожалуйста, преобразуйте мои объекты Java напрямую в XML / JSON, вместо того, чтобы я давал вам ссылка на вид.
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class HealthController {
@GetMapping("/health")
@ResponseBody // (1)
public HealthStatus health() {
return new HealthStatus(); // (2)
}
}
@ResponseBody в метод контроллера, который сообщает Spring, что вы хотите записать свой Java-объект HealthStatus непосредственно в HttpResponse (например, в виде XML или JSON).Но как Spring узнает, должен ли он возвращать XML, JSON или что-то еще?
Существует множество способов, с помощью которых вы, как клиент, можете указать Spring MVC, какой формат ответа вы хотите при выполнении запроса к приложению Spring MVC.
Затем Spring знает: вы аннотировали этот метод с помощью @ResponseBody, и клиент хочет «JSON». У меня есть библиотека на пути к классам, как Джексон, которая может визуализировать JSON? Если да, хорошо, давайте преобразуем HealthStatus в JSON. В противном случае выведите исключение.
Если вы хотите узнать больше о согласовании контента, ознакомьтесь с официальной документацией [21].
Spring MVC понимает в основном все, что предлагает HTTP — с помощью сторонних библиотек.
Это означает, что вы можете выбросить в него тела запросов JSON, XML или HTTP (Multipart) Fileupload, и Spring удобно преобразует этот ввод в объекты Java.
Spring MVC может записывать все что угодно в HttpServletResponse — с помощью сторонних библиотек.
Будь то HTML, JSON, XML или даже тела ответов WebSocket. Более того, он берет ваши объекты Java и генерирует эти тела ответов для вас.
Официальная документация Spring MVC буквально содержит сотни страниц, описывающих, как работает веб-фреймворк.
Поэтому, если вы хотите узнать больше о RequestParams, Моделях, Представлениях, ViewHandlers, RootContexts, Фильтрах, Кэшировании и Безопасности, я приглашаю вас проверить это. Это просто не входит в рамки данного руководства, чтобы охватить все.
Однако в разделе часто задаваемых вопросов данного руководства есть ответы на несколько дополнительных вопросов.
Дополнительно: Кратко о Spring Boot
Хотя это руководство не касается Spring Boot, давайте немного отвлечемся, посмотрев на источник аннотации @RestController, с которой вы, возможно, уже сталкивались в своем проекте Spring Boot.
Вы могли бы подумать, что она присуща Spring Boot (как я ошибочно делал слишком долго), но, как правильно заметил Мацей Волковяк, это часть простого старого Spring MVC. Вот его источник:
// some other annotations left out
@Controller
@ResponseBody
public @interface RestController {
}
Это верно, Spring MVC @RestController — это не что иное, как Spring MVC @Controller в сочетании с аннотацией Spring MVC @ResponseBody — хотя вы можете подумать, что это что-то связанное со Spring Boot.
Spring MVC — это старый добрый фреймворк MVC, который позволяет довольно легко писать HTML / JSON / XML веб-сайты или веб-сервисы. Он прекрасно интегрируется с контейнером внедрения зависимостей Spring, включая все его вспомогательные утилиты.
Вкратце: он позволяет вам сосредоточиться на написании Java-классов, вместо того, чтобы иметь дело с сланцевым кодом сервлета, то есть работать с Http-запросами и ответами, и дает вам хорошее разделение проблем между вашей моделью и представлениями.
Мы рассмотрели контейнер IoC Spring, Spring Web MVC и несколько других небольших модулей Spring. Но есть еще много. Что они делают?
Spring Framework состоит из еще большего количества удобных утилит, чем вы видели до сих пор. Давайте назовем их модулями и не путайте эти модули с 20 другими проектами Spring на spring.io. Наоборот, все они являются частью рамочного проекта Spring.
Итак, о каком удобстве идет речь?
Вы должны понимать, что в основном все, что Spring предлагает в этих модулях, также доступно на чистой Java. Предлагается либо JDK, либо сторонней библиотекой. Spring Framework всегда опирается на эти существующие функции.
Вот пример: отправка вложений электронной почты с помощью Java API Почты, безусловно, выполнима, но немного неудобна в использовании. Смотрите здесь [22] примера кода.
Spring предоставляет приятную маленькую API-оболочку поверх Java Mail API [23], с тем дополнительным преимуществом, что все, что он предлагает, прекрасно вписывается в контейнер внедрения зависимостей Spring.
import org.springframework.core.io.FileSystemResource;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
public class SpringMailSender {
@Autowired
private JavaMailSender mailSender; // (1)
public void sendInvoice(User user, File pdf) throws Exception {
MimeMessage mimeMessage = mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true); // (2)
helper.setTo("john@rambo.com");
helper.setText("Check out your new invoice!");
FileSystemResource file = new FileSystemResource(pdf);
helper.addAttachment("invoice.pdf", file);
mailSender.send(mimeMessage);
}
}
Итак, подытоживая, цель Spring Framework состоит в том, чтобы «упростить» доступную функциональность Java, подготовить ее к внедрению зависимостей и, следовательно, упростить использование API в контексте Spring.
Я хотел бы дать вам краткий обзор наиболее распространенных утилит, функций и модулей, с которыми вы можете столкнуться в проекте Spring. Однако обратите внимание, что подробное описание всех этих инструментов невозможно в рамках данного руководства. Вместо этого взгляните на официальную документацию [24] для получения полного списка.
@Transactional, а также чистой интеграции JDBC и ORM (например, Hibernate).Выбор версии Spring относительно прост:
В реальности вы можете найти проекты на Spring версии 4.x-5.x, используемые компаниями, хотя имеются и редкие, унаследованные проекты на Spring 3.x (первоначальный выпуск: 2009).
Вот хороший маленький график, показывающий историю версий Spring:

Вы можете видеть, что первоначальная версия Spring была ~ 17 лет назад, а основные версии платформы выпускались каждые 3-4 года. Это не учитывает филиалы обслуживания, как бы то ни было.
Примечание. Текущий EOL() (больше никаких обновлений и поддержки) для всех версий Spring, кроме 5.2, в настоящее время настроен на 31 декабря 2020 года.
На самом деле существует только одна зависимость, которая вам нужна, когда вы хотите настроить проект Spring. Это называется spring-context. Это абсолютный минимум, чтобы заставить работать инъекционный контейнер Spring.
Если вы работаете над проектом Maven или Gradle, вы можете просто добавить к нему следующую зависимость (см. также вопрос выше: Какую версию Spring мне следует использовать?) — вместо загрузки указанного файла .jar и добавления его в ваш проект вручную.
<!-- Maven -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
// Gradle
compile group: 'org.springframework', name: 'spring-context', version: '5.2.3.RELEASE'
Для дополнительных функций Spring (таких как поддержка Spring JDBC или JMS) вам потребуются другие дополнительные библиотеки.
Вы можете найти список всех доступных модулей в официальной документации Spring [32], хотя с точки зрения зависимостей Maven artifactIds действительно следуют за именем модуля. Вот пример:
<!-- Maven -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
Подобно JVM, версии Spring безумно обратно совместимы, что означает, что вы можете (по существу) по-прежнему запускать файлы Spring 1.0 xml с последней версией Spring 5.0 (хотя я, по общему признанию, еще не пробовал это). Кроме того, обновление с, скажем, 3 до 5 также возможно с небольшими усилиями (см. Это руководство по миграции [33]).
Таким образом, в целом, новые версии Spring основаны на более старых версиях Spring и имеют минимальные критические изменения (по сравнению, скажем, с Python 2 против 3). Итак, все основные концепции, которые вы изучили для Spring версии 3 или 4, остаются верными для Spring версии 5.
Вы можете получить отличный обзор того, что изменилось за последние 7 лет в отдельных версиях Spring, здесь:
Чтобы дать вам резюме:
Ядро (внедрение зависимостей, управление транзакциями и т.д.) Всегда остается неизменным или расширяется. Однако Spring идет в ногу со временем и предлагает поддержку для новых версий языка Java, улучшений инфраструктуры тестирования, веб-сокетов, реактивного программирования и т.д.
В рамках данного руководства я не могу вдаваться в подробности всех различных проектов, но давайте рассмотрим те, с которыми вы, скорее всего, столкнетесь.
Вывод: все эти библиотеки расширяют Spring Framework и основываются на его основных принципах внедрения зависимостей.
Если вы прочитали это руководство, вы должны понимать, что Spring Boot построен поверх Spring. Несмотря на то, что скоро появится подробное руководство по Spring Boot, вот пример того, что означают «самоуверенные значения по умолчанию» в Spring Boot.
Spring предлагает вам возможность читать .properties файлы из разных мест, например с помощью аннотаций @PropertySource. Он также предлагает вам возможность писать контроллеры JSON REST с помощью своей инфраструктуры Web MVC.
Проблема в том, что вы должны сами написать и настроить все эти отдельные части. Spring Boot, с другой стороны, берет эти отдельные части и связывает их вместе. Пример:
@RestControllers.Все это запускается основным методом в классе Java, который снабжен аннотацией @SpringBootApplication. Более того, Spring Boot предлагает плагины Maven / Gradle, которые позволяют вам упаковать ваше приложение в файл .jar, который вы можете запустить следующим образом:
java -jar mySpringBootApp.jar
Итак, Spring Boot — это все, что нужно для того, чтобы собрать существующие части Spring Framework, предварительно сконфигурировать и упаковать их с минимальными затратами на разработку.
Как упомянуто выше в разделе Нужно ли Spring использовать прокси Cglib?" в Spring по умолчанию используется AOP на основе прокси. Он оборачивает ваши компоненты в прокси для достижения таких вещей, как управление транзакциями. Это имеет несколько ограничений и предостережений, но является довольно простым и понятным способом реализации наиболее распространенных проблем AOP, с которыми сталкиваются разработчики Spring.
AspectJ, с другой стороны, позволяет вам изменять фактический байт-код путем ткачества времени загрузки или ткачества времени компиляции. Это дает вам гораздо больше возможностей в обмен на гораздо большую сложность.
Однако вы можете настроить Spring на использование AOP AspectJ [41] вместо AOP по умолчанию на основе прокси.
Вот пара ссылок, если вы хотите получить больше информации по этой теме:
Spring Batch — это фреймворк, упрощающий написание пакетных заданий, т.е. «Читать эти 95 CSV-файлов каждую ночь в 3 часа ночи и вызывать внешнюю службу проверки для каждой записи».
Опять же, он построен на основе Spring Framework, но во многом является собственным проектом.
Тем не менее, обратите внимание, что по сути невозможно создать надежное пакетное задание без хорошего понимания общего управления транзакциями в Spring Framework и его отношения к Spring Batch.
Если вы прочитали это руководство, вы должны понимать, что Spring Web MVC является частью среды Spring.
На очень высоком уровне это позволяет вам превратить ваше Spring-приложение в веб-приложение с помощью DispatcherServlet, который маршрутизирует классы @Controller.
Это могут быть RestControllers (где вы отправляете XML или JSON клиенту) или старые добрые HTML-контроллеры, где вы генерируете HTML с такими фреймворками, как Thymeleaf, Velocity или Freemarker.
Вопрос должен быть: чем отличается Spring Web MVC от Struts?
Краткий исторический ответ таков: Spring Web MVC начинал как конкурент Struts, который, как утверждается, был плохо спроектирован разработчиками Spring (см. Википедию).
Современный ответ заключается в том, что, хотя Struts 2 [44], безусловно, все еще используется в странном устаревшем проекте, Spring Web MVC является основой для всего, что связано с сетью во вселенной Spring. От Spring Webflow до RestControllers Spring Boot.
Spring начинался только с конфигурации XML. Затем медленно, все больше и больше появлялись аннотации / возможности Java конфигурации.
Сегодня вы заметитите, что конфигурация XML в основном используется в более старых, унаследованных проектах, а в новых проектах используется конфигурации на основе Java или аннотаций.
Обратите внимание на две вещи:
Как уже упоминалось в разделе о внедрении зависимостей, этот вопрос вызывает много разных мнений. Самое главное, что ваш выбор должен быть одинаковым для всего вашего проекта: не используйте внедрение на основе конструктора для 83% ваших бинов и внедрение на основе поля для остальных 17%.
Разумным подходом будет использование рекомендованного способа в документации Spring [8]: использование внедрения конструктора для обязательных зависимостей, установка / установка поля для необязательных зависимостей, а затем проверка этих дополнительных зависимостей по всему классу на ноль.
Самое главное, имейте в виду: общий успех вашего программного проекта не будет зависеть от выбора вашего любимого метода внедрения зависимостей.
Да, две популярные в экосистеме Java:
Обратите внимание, что Dagger предлагает только внедрение зависимостей, без дополнительных удобных функций. Guice предлагает внедрение зависимостей и другие функции, такие как управление транзакциями (с помощью Guice Persist).
Если вы читали это далеко, то теперь у вас должно быть достаточно глубокое понимание того, что такое Spring Framework.
Вы узнаете, как это связано с другими библиотеками экосистемы Spring (такими как Spring Boot или Spring Data) в последующих руководствах, но сейчас я хочу, чтобы вы помнили эту метафору при попытке ответить на вопрос «Что такое Spring Фреймворк?»
Представьте, что вы хотите отремонтировать дом (~ = создать программный проект).
Spring Framework — это ваш DIY-магазин (~ = контейнер для инъекций зависимости), который предлагает множество различных инструментов, от горелок Бунзена (~ = ресурсы / свойства) до кувалд (~ = Web MVC) для вашего обновления. Эти инструменты просто помогут вам быстрее и удобнее отремонтировать ваш дом (создать приложение на Java).
(примечание: не спрашивайте меня, как я придумал эти сравнения;))
Вот и все на сегодня. Если у вас есть какие-либо вопросы или предложения, напишите мне по адресу marco@marcobehler.com или оставьте комментарий ниже. Для практических занятий ознакомьтесь с учебным курсом Spring Framework [47].
Спасибо за прочтение. Auf Wiedersehen.
Большое спасибо:
@RestController всегда был частью Spring MVC, а не Spring Boot.(Благодарности автора оригинальной статьи — прим. перев.)
Автор: val6852
Источник [48]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/java/348639
Ссылки в тексте:
[1] spring.io: https://spring.io/
[2] Spring Boot: https://spring.io/guides/gs/spring-boot/
[3] Spring Framework: https://spring.io/projects/spring-framework
[4] контейнер внедрения зависимостей: https://en.wikipedia.org/wiki/Dependency_injection
[5] областей применения бинов: https://docs.spring.io/spring/docs/5.2.x/spring-framework-reference/core.html#beans-factory-scopes
[6] внедрения прототипов в синглтоны: https://www.baeldung.com/spring-inject-prototype-bean-into-singletonp
[7] внедрение с помощью метода установки (setter) вредна: https://www.vojtechruzicka.com/field-dependency-injection-considered-harmful/
[8] официальной документации: https://docs.spring.io/spring/docs/4.3.25.RELEASE/spring-framework-reference/htmlsingle/
[9] прокси-серверы Cglib: https://github.com/cglib/cglib
[10] о прокси в Википедии: https://en.wikipedia.org/wiki/Proxy_pattern
[11] мое руководство по @Transactional: https://www.marcobehler.com/guides/spring-transaction-management-transactional-in-depth
[12] класс URL в Java: https://docs.oracle.com/javase/7/docs/api/java/net/URL.html
[13] здесь: https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#resources-resourceloaderaware
[14] По умолчанию: https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#beans-property-source-abstraction
[15] Model-View-Controller: https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller
[16] Tomcat: https://tomcat.apache.org/
[17] Jetty.: https://www.eclipse.org/jetty/
[18] Velocity: https://velocity.apache.org/
[19] Freemarker: https://freemarker.apache.org/
[20] Jackson: https://github.com/FasterXML/jackson
[21] официальной документацией: https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-config-content-negotiation
[22] Смотрите здесь: https://www.baeldung.com/java-email
[23] Java Mail API: https://javaee.github.io/javamail/docs/api/
[24] официальную документацию: https://docs.spring.io/spring/docs/current/spring-framework-reference/index.html
[25] Spring’s Data Access: https://docs.spring.io/spring/docs/current/spring-framework-reference/data-access.html#spring-data-tier
[26] Spring Integration модули: https://docs.spring.io/spring/docs/current/spring-framework-reference/integration.html#spring-integration
[27] Spring Expression Language (SpEL): https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#expressions
[28] Реактивные модули Spring: https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html#spring-webflux
[29] Фреймворк тестирования Spring: https://docs.spring.io/spring/docs/current/spring-framework-reference/testing.html#testing
[30] блоге Spring: https://spring.io/blog
[31] EOL анонсу: https://spring.io/blog/2019/12/03/spring-framework-maintenance-roadmap-in-2020-including-4-3-eol
[32] официальной документации Spring: https://docs.spring.io/spring/docs/4.3.x/spring-framework-reference/html/overview.html#overview-modules
[33] руководство по миграции: https://github.com/spring-projects/spring-framework/wiki/Upgrading-to-Spring-Framework-5.x
[34] Что нового в Spring 4: https://docs.spring.io/spring/docs/4.3.x/spring-framework-reference/htmlsingle/#spring-whats-new
[35] Что нового в Spring 5: https://github.com/spring-projects/spring-framework/wiki/What%E2%80%99s-New-in-Spring-Framework-5.x
[36] Spring Boot: https://spring.io/projects/spring-boot
[37] Spring Batch: https://spring.io/projects/spring-batch
[38] Spring Cloud: https://spring.io/projects/spring-cloud
[39] микросервисы: https://www.marcobehler.com/guides/java-microservices-a-practical-guide
[40] Spring Security: https://spring.io/projects/spring-security
[41] настроить Spring на использование AOP AspectJ: https://docs.spring.io/spring/docs/4.3.15.RELEASE/spring-framework-reference/html/aop.html
[42] Домашняя страница AspectJ: https://www.eclipse.org/aspectj/
[43] Spring AOP и AspectJ: https://stackoverflow.com/questions/1606559/spring-aop-vs-aspectj
[44] Struts 2: https://struts.apache.org/
[45] Google Guice: https://github.com/google/guice
[46] Google Dagger: https://github.com/google/dagger
[47] учебным курсом Spring Framework: https://www.marcobehler.com/academy/learning-spring?utm_campaign=spring_framework_guide&utm_medium=spring_framework_guide&utm_source=spring_framework_guide
[48] Источник: https://habr.com/ru/post/490586/?utm_campaign=490586&utm_source=habrahabr&utm_medium=rss
Нажмите здесь для печати.