Spring Boot第3部分:依赖注入和@RestController
介绍
本系列文章将研究Spring Boot的功能。 第三篇文章通过演示Spring Dependency Injection的基础知识来构建该系列文章。 为了创建可演示的代码,该示例还创建了@RestController
实现,这是一个简单的共享属性服务器,客户端可以在其中放置和获取属性值。
@ComponentScan
和依赖注入
这不是详尽的描述,仅描述了Spring依赖项注入的最简单(但可以说是最常见)的功能。
Spring Boot进程从Launcher::main
方法(与本系列的第1部分和第2部分描述的实现保持不变)开始,并构造 SpringApplication
并调用其run
方法。 使用@SpringBootApplication
注释类等同于使用@Configuration
@EnableAutoConfiguration
, @ComponentScan
@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 );
}
}
SpringApplication
从application.Launcher
类开始分析。 @EnableAutoConfiguration
指示Spring Boot应该在必要时尝试“猜测”。 @ComponentScan
批注指示Spring Boot应该开始在application
包(包含Launcher
包)中扫描用@Component
批注的@Component
。 注意:用@Component
注释的注释类型也是组件。 例如, @Configuration
, @Controller
和@RestController
都是@Component
S,而不是相反。
对于每个用@Component
注释的@Component
,Spring:
实例化一个实例,
对于
@Configuration
类中每个以@Bean
注释的方法,请只调用一次该方法以获得Bean值,然后,对于每个用
@Autowired
注释的字段,分配通过评估@Bean
方法获得的相应值。
同样,以上内容是一个过分的简化,并非详尽无遗,它依赖于挥手,但应该足以上手。
本文的示例代码不需要@Value
注入,但是上一篇文章在其MysqldConfiguration
实现中提供了示例:
@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表达式中指定默认值和自动类型转换的优势。
在此实现的简单属性服务器在DictionaryConfiguration
中创建一个“字典” bean:
@Configuration
@NoArgsConstructor @ToString @Log4j2
public class DictionaryConfiguration {
@Bean
public Map < String , String > dictionary () {
return new ConcurrentSkipListMap <>();
}
}
并将该bean连接到DictionaryRestController
,如下所示:
@RestController
...
@NoArgsConstructor @ToString @Log4j2
public class DictionaryRestController {
@Autowired private Map < String , String > dictionary = null ;
...
}
下一节描述@RestController
的实现。
@RestController实现
@RestController
实现的@RestController
提供以下Web API:
方法 | URI | 查询参数 | 退货 |
---|---|---|---|
GET | http://localhost:8080/dictionary/get | 键 | 与key关联的值(可以为null ) |
GET 2 | http://localhost:8080/dictionary/put | 键 = 值 | 与key关联的先前值(可以为null ) |
GET | http://localhost:8080/dictionary/remove | 键 | 先前与key关联的值(可以为null ) |
GET | http://localhost:8080/dictionary/size | 没有 | 整型 |
GET | http://localhost:8080/dictionary/entrySet | 没有 | 键值对数组 |
GET | http://localhost:8080/dictionary/keySet | 没有 | 键值数组 |
DictionaryRestController
用@RestController
和@RequestMapping
注释,其value = { "/dictionary" }
指示请求路径将以/dictionary
前缀,并produces = "application/json"
指示HTTP
响应应以JSON
编码。
@RestController
@RequestMapping ( value = { "/dictionary" }, produces = MediaType . APPLICATION_JSON_VALUE )
@NoArgsConstructor @ToString @Log4j2
public class DictionaryRestController {
@Autowired private Map < String , String > dictionary = null ;
...
}
如上一节所述,字典映射为@Autowired
。
/dictionary/put
方法的实现是:
@RequestMapping ( method = { RequestMethod . GET }, value = { "/put" })
public Optional < String > put ( @RequestParam Map < String , String > parameters ) {
if ( parameters . size () != 1 ) {
throw new IllegalArgumentException ();
}
Map . Entry < String , String > entry =
parameters . entrySet (). iterator (). next ();
String result = dictionary . put ( entry . getKey (), entry . getValue ());
return Optional . ofNullable ( result );
}
Spring将在方法调用中将请求的查询参数作为parameters
注入。 该方法验证是否恰好指定了一个查询参数,将该键值放入字典中,然后返回结果(映射中该键的先前值)。 Spring将String
解释为文字JSON
因此该方法将结果包装在Optional
以强制Spring编码为JSON
。
/dictionary/get
方法的实现是:
@RequestMapping ( method = { RequestMethod . GET }, value = { "/get" })
public Optional < String > get ( @RequestParam Map < String , String > parameters ) {
if ( parameters . size () != 1 ) {
throw new IllegalArgumentException ();
}
Map . Entry < String , String > entry =
parameters . entrySet (). iterator (). next ();
String result = dictionary . get ( entry . getKey ());
return Optional . ofNullable ( result );
}
同样,必须完全有一个查询参数,并将结果包装在Optional
。 /dictionary/remove
请求的实现几乎相同。
/dictionary/size
方法的实现是:
@RequestMapping ( method = { RequestMethod . GET }, value = { "/size" })
public int size ( @RequestParam Map < String , String > parameters ) {
if (! parameters . isEmpty ()) {
throw new IllegalArgumentException ();
}
return dictionary . size ();
}
不应指定查询参数。 /dictionary/entrySet
的实现与Set<Map.Entry<String,String>>
的方法返回类型几乎相同:
@RequestMapping ( method = { RequestMethod . GET }, value = { "/entrySet" })
public Set < Map . Entry < String , String >> entrySet ( @RequestParam Map < String , String > parameters ) {
if (! parameters . isEmpty ()) {
throw new IllegalArgumentException ();
}
return dictionary . entrySet ();
}
/dictionary/keySet
的实现遵循相同的模式。
Maven项目POM提供了本系列第一篇文章中描述的spring-boot:run
配置文件,并且服务器可以通过mvn -B -Pspring-boot:run
。 使用此配置文件启动后 ,即可使用Spring Boot执行器 。 @RestServer
处理程序映射可以通过以下查询进行验证:
$ 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"
然后使用get操作验证先前的put:
$ 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"
} ]
提供查询参数大小可演示错误处理:
$ 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\t at application.DictionaryRestController.size(DictionaryRestController.java:65) \n ..." ,
"path" : "/dictionary/size"
}
摘要
本文通过展示如何“演示基本春天依赖注入@Value
的”可以被计算并注入“ @Bean
的”可以创建和“ @Autowired
在” @RestController
实现。
本系列的第4部分将讨论Spring MVC,并以一个简单的国际化时钟应用程序为例。
SpEL还提供对
application.properties
资源中定义的属性的访问。 ↩不幸的是,
GET
HTTP方法与Map
put
方法结合使用可能会造成混乱,并且更复杂的API定义可能会合理地使用POST
或PUT
方法获取其语义值。 ↩
From: https://dev.to/allenball/spring-boot-part-3-dependency-injection-and-restcontroller-3374