(Этот пост был обновлен по адресу blog.hcf.dev более поздней версией Spring Boot.)
В этой серии статей будут рассмотрены функции Spring Boot . Эта третья статья основывается на серии, демонстрируя основы Spring Dependency Injection . Для создания наглядного кода в примере также создается реализация @RestController
, простой сервер общих свойств, на котором клиенты могут вводить и получать значения свойств.
Полный исходный код для серии и для этой части доступны на Github .
@ComponentScan и внедрение зависимостей
Это не исчерпывающее описание и описывает только самые простые (но, возможно, наиболее распространенные) функции внедрения зависимостей Spring.
Процесс загрузки Spring запускается в методе Launcher::main
(без изменений по сравнению с реализацией, описанной в частях 1 и 2 из этой серии) с помощью построения приложения Spring
и вызова его run
метода. Аннотирование класса с помощью @SpringBootApplication
эквивалентно аннотированию с помощью @Configuration
, @EnableAutoConfiguration
и @ComponentScan
.
@SpringBootApplication @NoArgsConstructor @ToString @Log4j2 public class Launcher { public static void main(String[] argv) throws Exception { SpringApplication application = new SpringApplication(Launcher.class); application.run(argv); } }
Приложение Spring/| начинает свой анализ с помощью приложения
. Пусковая установка класс. Параметр
@EnableAutoConfiguration указывает, что Spring Boot должен попытаться "угадать" по мере необходимости. Аннотация
@ComponentScan указывает, что Spring Boot должен начать сканирование классов в пакете
application (содержащем пакет для
Launcher ) для классов, помеченных
@Component . Примечание: Аннотации -типы, помеченные
@Component , также являются компонентами. Например,
@Configuration ,
@Controller и
@RestController все
@Component s, но не наоборот.
Для каждого класса, помеченного @Component
, Spring:
Создает один экземпляр,
Для каждого поля экземпляра, помеченного
@Value
, вычислите выражение SpEL 1 и инициализируйте поле с результатом,Для каждого метода, аннотированного
@Bean
в классе@Configuration
, вызовите метод ровно один раз, чтобы получить значение компонента, и,Для каждого поля, помеченного
@Autowired
, присвоите соответствующее значение, полученное путем вычисления метода@Bean
.
Опять же, вышесказанное является грубым упрощением, не исчерпывающим и опирается на размахивание руками, но этого должно быть достаточно для начала.
Пример кода для этой статьи не требует @Value
инъекции, но предыдущая статья предоставляет примеры в своем Конфигурация Mysqld
реализация:
@Value("${mysqld.home}") private File home; @Value("${mysqld.defaults.file:${mysqld.home}/my.cnf}") private File defaults; @Value("${mysqld.datadir:${mysqld.home}/data}") private File datadir; @Value("${mysqld.port}") private Integer port; @Value("${mysqld.socket:${mysqld.home}/socket}") private File socket; @Value("${logging.path}/mysqld.log") private File console;
Приведенный выше код использует преимущества указания значений по умолчанию в выражениях SpEL и автоматического преобразования типов.
Простой сервер свойств, реализованный здесь, создает компонент “словарь” внутри Конфигурация словаря
:
@Configuration @NoArgsConstructor @ToString @Log4j2 public class DictionaryConfiguration { @Bean public Mapdictionary() { return new ConcurrentSkipListMap<>(); } }
И этот компонент подключается к Dictionary RestController
следующим образом:
@RestController ... @NoArgsConstructor @ToString @Log4j2 public class DictionaryRestController { @Autowired private Mapdictionary = null; ... }
В следующем разделе описывается реализация @RestController
.
Реализация @RestController
Реализованный здесь @RestController
предоставляет следующий веб-API:
http://localhost:8080/dictionary/get | получить | Значение, связанное с ключом (может быть нулевым) | ключ |
http://localhost:8080/dictionary/put | ПОЛУЧИТЬ 2 | Предыдущее значение, связанное с ключом (может быть нулевым) | ключ=значение |
http://localhost:8080/dictionary/remove | получить | Значение, ранее связанное с ключом (может быть нулевым) | ключ |
http://localhost:8080/dictionary/size | получить | в | никто |
http://localhost:8080/dictionary/ Входной набор | получить | Массив пар ключ-значение | никто |
http://localhost:8080/dictionary/keySet | получить | Массив ключевых значений | никто |
Словарь RestController
аннотируется с помощью @RestController
и @RequestMapping
с значением = { "/dictionary/" }
указывающие пути запроса будут иметь префикс /dictionary/
и products
, указывающие , что HTTP
ответы должны быть закодированы в JSON
.
@RestController @RequestMapping(value = { "/dictionary/" }, produces = MediaType.APPLICATION_JSON_VALUE) @NoArgsConstructor @ToString @Log4j2 public class DictionaryRestController { @Autowired private Mapdictionary = null; ... }
Карта словаря имеет вид @Autowired
, как описано в предыдущем разделе.
Реализация метода /dictionary/put
заключается в:
@RequestMapping(method = { RequestMethod.GET }, value = { "put" }) public Optionalput(@RequestParam Map parameters) { if (parameters.size() != 1) { throw new IllegalArgumentException(); } Map.Entry entry = parameters.entrySet().iterator().next(); String result = dictionary.put(entry.getKey(), entry.getValue()); return Optional.ofNullable(result); }
Spring введет параметры запроса запроса в вызов метода как parameters
. Метод проверяет, что указан ровно один параметр запроса, помещает это значение ключа в словарь и возвращает результат (предыдущее значение для этого ключа на карте). Spring интерпретирует String
как литерал JSON
таким образом, метод оборачивает результат в Необязательный
, чтобы заставить Spring кодировать в JSON
.
Реализация метода /dictionary/get
заключается в:
@RequestMapping(method = { RequestMethod.GET }, value = { "get" }) public Optionalget(@RequestParam Map parameters) { if (parameters.size() != 1) { throw new IllegalArgumentException(); } Map.Entry entry = parameters.entrySet().iterator().next(); String result = dictionary.get(entry.getKey()); return Optional.ofNullable(result); }
Опять же, должен быть ровно один параметр запроса, и результат будет заключен в Необязательный
. Реализация запроса /dictionary/remove
практически идентична.
Реализация метода /dictionary/size
заключается в:
@RequestMapping(method = { RequestMethod.GET }, value = { "size" }) public int size(@RequestParam Mapparameters) { if (! parameters.isEmpty()) { throw new IllegalArgumentException(); } return dictionary.size(); }
Параметры запроса указывать не следует. Реализация /словаря/entrySet
почти идентичен типу, возвращаемому методом Set Запись<Строка,Строка>>
:
@RequestMapping(method = { RequestMethod.GET }, value = { "entrySet" }) public Set> entrySet(@RequestParam Map parameters) { if (! parameters.isEmpty()) { throw new IllegalArgumentException(); } return dictionary.entrySet(); }
И реализация /dictionary/key Set
следует тому же шаблону.
Maven project POM предоставляет spring-boot:run
профиль, описанный в первой статье этой серии, и сервер может быть запущен с помощью mvn -B -Pspring-boot:run
. При запуске с этим профилем доступен привод пружинного загрузчика . Сопоставления @RestController
обработчика могут быть проверены с помощью следующего запроса:
$ curl -X GET http://localhost:8081/actuator/mappings \ > | jq '.contexts.application.mappings.dispatcherServlets[][] | {handler: .handler, predicate: .predicate}' { "handler": "application.DictionaryRestController#remove(Map)", "predicate": "{GET /dictionary/remove, produces [application/json]}" } { "handler": "application.DictionaryRestController#get(Map)", "predicate": "{GET /dictionary/get, produces [application/json]}" } { "handler": "application.DictionaryRestController#put(Map)", "predicate": "{GET /dictionary/put, produces [application/json]}" } { "handler": "application.DictionaryRestController#size(Map)", "predicate": "{GET /dictionary/size, produces [application/json]}" } { "handler": "application.DictionaryRestController#entrySet(Map)", "predicate": "{GET /dictionary/entrySet, produces [application/json]}" } { "handler": "application.DictionaryRestController#keySet(Map)", "predicate": "{GET /dictionary/keySet, produces [application/json]}" } { "handler": "org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController#errorHtml(HttpServletRequest, HttpServletResponse)", "predicate": "{ /error, produces [text/html]}" } { "handler": "org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController#error(HttpServletRequest)", "predicate": "{ /error}" } { "handler": "ResourceHttpRequestHandler [\"classpath:/META-INF/resources/webjars/\"]", "predicate": "/webjars/**" } { "handler": "ResourceHttpRequestHandler [\"classpath:/META-INF/resources/\", \"classpath:/resources/\", \"classpath:/static/\", \"classpath:/public/\", \"/\"]", "predicate": "/**" }
Использование curl
для проверки операции put (обратите внимание на разницу в возвращаемых значениях при первом и втором вызовах):
$ curl -X GET -i http://localhost:8080/dictionary/put?foo=bar HTTP/1.1 200 Content-Type: application/json Transfer-Encoding: chunked Date: Wed, 11 Dec 2019 19:56:43 GMT null $ curl -X GET -i http://localhost:8080/dictionary/put?foo=bar HTTP/1.1 200 Content-Type: application/json Transfer-Encoding: chunked Date: Wed, 11 Dec 2019 19:56:44 GMT "bar"
А затем проверьте предыдущий put с помощью операции get:
$ curl -X GET -i http://localhost:8080/dictionary/get?foo HTTP/1.1 200 Content-Type: application/json Transfer-Encoding: chunked Date: Wed, 11 Dec 2019 19:59:22 GMT "bar"
Извлечение набора словарных записей демонстрирует сложную кодировку JSON:
$ curl -X GET -i http://localhost:8080/dictionary/entrySet HTTP/1.1 200 Content-Type: application/json Transfer-Encoding: chunked Date: Wed, 11 Dec 2019 20:00:24 GMT [ { "foo" : "bar" } ]
И предоставление параметра запроса для size демонстрирует обработку ошибок:
$ curl -X GET -i http://localhost:8080/dictionary/size?foo HTTP/1.1 500 Content-Type: application/json Transfer-Encoding: chunked Date: Wed, 11 Dec 2019 20:03:42 GMT Connection: close { "timestamp" : "2019-12-11T20:03:42.110+0000", "status" : 500, "error" : "Internal Server Error", "message" : "No message available", "trace" : "java.lang.IllegalArgumentException\n\tat application.DictionaryRestController.size(DictionaryRestController.java:65)\n...", "path" : "/dictionary/size" }
Резюме
В этой статье демонстрируется базовая инъекция зависимостей Spring, показывая, как можно вычислять и вводить ” @Value
s ” и ” @Bean
s ” может быть создан и ” @Autowired
” в реализации @RestController
.
В части 4 этой серии обсуждается Spring MVC и в качестве примера реализуется простое интернационализированное приложение clock.
[1] Spell также предоставляет доступ к свойствам, определенным в application.properties
resources. ↩
[2] К сожалению, метод GET
HTTP в сочетании с методом Map
put
может вызвать путаницу, и более сложное определение API может разумно использовать пост
или ПОМЕСТИТЕ
методы для их семантического значения. ↩
Оригинал: “https://dev.to/allenball/spring-boot-part-3-dependency-injection-and-restcontroller-3374”