Spring Boot第3部分:依赖注入和@RestController

Spring Boot第3部分:依赖注入和@RestController

介绍

本系列文章将研究Spring Boot的功能。 第三篇文章通过演示Spring Dependency Injection的基础知识来构建该系列文章。 为了创建可演示的代码,该示例还创建了@RestController实现,这是一个简单的共享属性服务器,客户端可以在其中放置和获取属性值。

Github上提供了该系列以及本部分的完整源代码。

@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 );
    }
}

SpringApplicationapplication.Launcher类开始分析。 @EnableAutoConfiguration指示Spring Boot应该在必要时尝试“猜测”。 @ComponentScan批注指示Spring Boot应该开始在application包(包含Launcher包)中扫描用@Component批注的@Component 。 注意:用@Component注释的注释类型也是组件。 例如, @Configuration@Controller@RestController都是@Component S,而不是相反。

对于每个用@Component注释的@Component ,Spring:

  1. 实例化一个实例,

  2. 对于每个带@Value注释的实例字段,请评估SpEL表达式1并使用结果初始化该字段,

  3. 对于@Configuration类中每个以@Bean注释的方法,请只调用一次该方法以获得Bean值,然后,

  4. 对于每个用@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,并以一个简单的国际化时钟应用程序为例。


  1. SpEL还提供对application.properties资源中定义的属性的访问。

  2. 不幸的是, GET HTTP方法与Map put方法结合使用可能会造成混乱,并且更复杂的API定义可能会合理地使用POSTPUT方法获取其语义值。

From: https://dev.to/allenball/spring-boot-part-3-dependency-injection-and-restcontroller-3374

Spring Boot中,正确处理依赖注入主要包括以下几个步骤: 1. **添加依赖**:如果你的项目需要使用某个第三方库,确保在pom.xml(Maven项目)或build.gradle(Gradle项目)中添加了对应的依赖声明。 2. **使用注解**:Spring通过注解如`@Autowired`, `@Qualifier`, 或者`@Resource`来识别和管理依赖关系。例如,在控制器类的成员变量上使用`@Autowired`,Spring会自动查找并注入相应的服务类实例。 ```java @Service public class GoodsServiceImpl implements GoodsService { // ... } @RestController public class GoodsController { @Autowired private GoodsService goodsService; // ... } ``` 3. **组件扫描**:如果你的实体类、服务类等不是标准的位置(如src/main/java),记得在application.properties或application.yml文件中启用组件扫描,让Spring能够发现它们。 4. **异常处理**:如果遇到像`TypeNotPresentException`这样的依赖注入问题,检查是否存在类路径问题,或者检查类名、包名是否拼写正确,确保依赖库已经被正确地引入到项目中。 5. **检查日志**:查看Spring Boot的日志,它可能会提供关于问题根源的线索。如果问题仍然难以定位,考虑使用断点调试或打印debug信息帮助定位。 6. **使用@ComponentScan`配置**:如果你的bean不在默认的扫描路径下,可以使用@ComponentScan来指定扫描路径。 7. **避免循环依赖**:确保你的bean定义不存在循环依赖,这可能导致构造函数抛出异常。 通过以上步骤,你应该能有效地处理Spring Boot中的依赖注入问题。如果还有疑问,建议查阅Spring官方文档或在线社区资源。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值