# spring

返回:java开发

spring-cloud Spring整合mongoDb
spring-batch spring-security
alibaba-nacos 新方式-webFlux
Spring MVC九大组件

# Spring 终于对 JVM 动手了

# Spring相关技术

返回spring首页

# Spring依赖注入的注解区别

# spring依赖注入使用方式

  1. @Autowired是spring框架提供的实现依赖注入的注解,主要支持在set方法,field,构造函数中完成bean注入,注入方式为通过类型查找bean,即byType的,如果存在多个同一类型的bean,则使用@Qualifier来指定注入哪个beanName的bean。
    @Autowired作用就和在xml配置文件中的bean标签中写一个< property >标签的作用是一样的。
  2. 与JDK的@Resource的区别:@Resource是基于bean的名字,即beanName,来从spring的IOC容器查找bean注入的,而@Autowried是基于类型byType来查找bean注入的。
  3. 与JDK的@Inject的区别:@Inject也是基于类型来查找bean注入的,如果需要指定名称beanName,则可以结合使用@Named注解,而@Autowired是结合@Qualifier注解来指定名称beanName。

# spring依赖注入注解的实现原理

  • 注解处理器

在spring框架内部实现当中,注解实现注入主要是通过bean后置处理器BeanPostProcessor接口的实现类来生效的。BeanPostProcessor后置处理器是在spring容器启动时,创建bean对象实例后,马上执行的,对bean对象实例进行加工处理。
@Autowired是通过BeanPostProcessor接口的实现类AutowiredAnnotationBeanPostProcessor来实现对bean对象对其他bean对象的依赖注入的;
@Resource@Inject是通过BeanPostProcessor接口的实现类CommonAnnotationBeanPostProcessor来实现的,其中如名字所述,即公共注解CommonAnotationCommonAnnotationBeanPostProcessor是spring中统一处理JDK中定义的注解的一个BeanPostProcessor。该类会处理的注解还包括@PostConstruct,@PreDestroy等。

  • 注解处理器的激活条件

AutowiredAnnotationBeanPostProcessorCommonAnnotationBeanPostProcessor添加到spring容器的BeanPostProcessor的条件,即激活这些处理器的条件如下:

1.基于xml的spring配置

在对应的spring容器的配置xml文件中,如applicationContext.xml,添加<context:annotation-config /><context:component-scan />,或者只使用<context:component-scan />
两者的区别是<context:annotation-config />只查找并激活已经存在的bean,如通过xml文件的bean标签生成加载到spring容器的,而不会去扫描如@Controller等注解的bean,查找到之后进行注入;
<context:component-scan />除了具有<context:annotation-config />的功能之外,还会去加载通过basePackages属性指定的包下面的,默认为扫描@Controller,@Service,@Component,@Repository注解的类。
不指定basePackages则是类路径下面,或者如果使用注解@ComponentScan方式,则是当前类所在包及其子包下面。

2.基于配置类的spring配置

如果是基于配置类而不是基于applicationContext.xml来对spring进行配置,如SpringBoot,则在内部使用的IOC容器实现为AnnotationConfigApplicationContext或者其派生类,在AnnotationConfigApplicationContext内部会自动创建和激活以上的BeanPostProcessor
如果同时存在基于xml的配置和配置类的配置,而在注入时间方面,基于注解的注入先于基于XML的注入所以基于XML的注入会覆盖基于注解的注入

  • 总结
  1. @Autowired是Spring自带的,@Inject和@Resource都是JDK提供的,其中@Inject是JSR330规范实现的,@Resource是JSR250规范实现的,而Spring通过BeanPostProcessor来提供对JDK规范的支持。
  2. @Autowired、@Inject用法基本一样,不同之处为@Autowired有一个required属性,表示该注入是否是必须的,即如果为必须的,则如果找不到对应的bean,就无法注入,无法创建当前bean。
  3. @Autowired、@Inject是默认按照类型匹配的@Resource是按照名称匹配的。如在spring-boot-data项目中自动生成的redisTemplate的bean,是需要通过byName来注入的。如果需要注入该默认的,则需要使用@Resource来注入,而不是@Autowired。
  4. 对于@Autowire和@Inject,如果同一类型存在多个bean实例,则需要指定注入的beanName。@Autowired和@Qualifier一起使用@Inject和@Name一起使用

# 解决循环依赖的问题

返回spring首页

关于Spring bean的创建,其本质上还是一个对象的创建,既然是对象,读者朋友一定要明白一点就是,一个完整的对象包含两部分:当前对象实例化对象属性的实例化。在Spring中,对象的实例化是通过反射实现的,而对象的属性则是在对象实例化之后通过一定的方式设置的。

TIP

  • 如果循环依赖都是构造器注入,则失败
  • 如果循环依赖不完全是构造器注入,则可能成功,可能失败,具体跟BeanName的字母序有关系。
    • Spring 容器是按照字母序创建 Bean 的,A 的创建永远排在 B 前面 。
@Component
public class A {
 private B b;
 public void setB(B b) {
 this.b = b;
 }
}
@Component
public class B {
 private A a;
 public void setA(A a) {
 this.a = a;
 }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

Spring实例化bean是通过ApplicationContext.getBean()方法来进行的。如果要获取的对象依赖了另一个对象,那么其首先会创建当前对象,然后通过递归的调用ApplicationContext.getBean()方法来获取所依赖的对象,最后将获取到的对象注入到当前对象中。

# spring中解决

在 Spring 中,只有同时满足以下两点才能解决循环依赖的问题:

  • 依赖的 Bean 必须都是单例
  • 依赖注入的方式,必须 不全是 构造器注入,且 beanName 字母序在前的不能是构造器注入

在 Spring 中创建 Bean 分三步:

  • 实例化,createBeanInstance,就是 new 了个对象
  • 属性注入,populateBean, 就是 set 一些属性值
  • 初始化,initializeBean,执行一些 aware 接口中的方法,initMethod,AOP代理等

# 源码讲解

AbstractBeanFactory.doGetBean()

  • Spring是通过递归的方式获取目标bean及其所依赖的bean的;
  • Spring实例化一个bean的时候,是分两步进行的,首先实例化目标bean,然后为其注入属性。

Spring在实例化一个bean的时候,是首先递归的实例化其所依赖的所有bean,直到某个bean没有依赖其他bean,此时就会将该实例返回,然后反递归地将获取到的bean设置为各个上层bean的属性中。

# Spring中使用了哪些设计模式

返回spring首页

# 简单工厂模式

又叫做静态工厂方法(StaticFactory Method)模式,但不属于23种GOF设计模式之一。简单工厂模式的实质是由一个工厂类根据传入的参数,动态决定应该创建哪一个产品类.spring中的BeanFactory就是简单工厂模式的体现,根据传入一个唯一的标识来获得bean对象,但是否是在传入参数后创建还是传入参数前创建这个要根据具体情况来定。

# 工厂方法模式

通常由应用程序直接使用new创建新的对象,为了将对象的创建和使用相分离,采用工厂模式,即应用程序将对象的创建及初始化职责交给工厂对象。

# BeanFactory与FactoryBean

返回spring首页

# BeanFactory

ApplicationContext就是一个BeanFactory。我们通过bean的名称或者类型都可以从BeanFactory来获取bean

# FactoryBean

public interface FactoryBean<T> {

/**
  - Return an instance (possibly shared or independent) of the object
  - managed by this factory.
  - <p>As with a {@link BeanFactory}, this allows support for both the
  - Singleton and Prototype design pattern.
  - <p>If this FactoryBean is not fully initialized yet at the time of
  - the call (for example because it is involved in a circular reference),
  - throw a corresponding {@link FactoryBeanNotInitializedException}.
  - <p>As of Spring 2.0, FactoryBeans are allowed to return {@code null}
  - objects. The factory will consider this as normal value to be used; it
  - will not throw a FactoryBeanNotInitializedException in this case anymore.
  - FactoryBean implementations are encouraged to throw
  - FactoryBeanNotInitializedException themselves now, as appropriate.
  - @return an instance of the bean (can be {@code null})
  - @throws Exception in case of creation errors
  - @see FactoryBeanNotInitializedException
  */
@Nullable
T getObject() throws Exception;

/**
  - Return the type of object that this FactoryBean creates,
  - or {@code null} if not known in advance.
  - <p>This allows one to check for specific types of beans without
  - instantiating objects, for example on autowiring.
  - <p>In the case of implementations that are creating a singleton object,
  - this method should try to avoid singleton creation as far as possible;
  - it should rather estimate the type in advance.
  - For prototypes, returning a meaningful type here is advisable too.
  - <p>This method can be called <i>before</i> this FactoryBean has
  - been fully initialized. It must not rely on state created during
  - initialization; of course, it can still use such state if available.
  - <p><b>NOTE:</b> Autowiring will simply ignore FactoryBeans that return
  - {@code null} here. Therefore it is highly recommended to implement
  - this method properly, using the current state of the FactoryBean.
  - @return the type of object that this FactoryBean creates,
  - or {@code null} if not known at the time of the call
  - @see ListableBeanFactory#getBeansOfType
  */
@Nullable
Class<?> getObjectType();

/**
  - Is the object managed by this factory a singleton? That is,
  - will {@link #getObject()} always return the same object
  - (a reference that can be cached)?
  - <p><b>NOTE:</b> If a FactoryBean indicates to hold a singleton object,
  - the object returned from {@code getObject()} might get cached
  - by the owning BeanFactory. Hence, do not return {@code true}
  - unless the FactoryBean always exposes the same reference.
  - <p>The singleton status of the FactoryBean itself will generally
  - be provided by the owning BeanFactory; usually, it has to be
  - defined as singleton there.
  - <p><b>NOTE:</b> This method returning {@code false} does not
  - necessarily indicate that returned objects are independent instances.
  - An implementation of the extended {@link SmartFactoryBean} interface
  - may explicitly indicate independent instances through its
  - {@link SmartFactoryBean#isPrototype()} method. Plain {@link FactoryBean}
  - implementations which do not implement this extended interface are
  - simply assumed to always return independent instances if the
  - {@code isSingleton()} implementation returns {@code false}.
  - <p>The default implementation returns {@code true}, since a
  - {@code FactoryBean} typically manages a singleton instance.
  - @return whether the exposed object is a singleton
  - @see #getObject()
  - @see SmartFactoryBean#isPrototype()
  */
default boolean isSingleton() {
  return true;
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74

1.T getObject() 获取泛型T的实例。用来创建Bean。当IoC容器通过getBean方法来FactoryBean创建的实例时实际获取的不是FactoryBean 本身而是具体创建的T泛型实例。等下我们会来验证这个事情。
2.Class<?> getObjectType() 获取 T getObject()中的返回值 T 的具体类型。这里强烈建议如果T是一个接口,返回其具体实现类的类型。
3.default boolean isSingleton() 用来规定 Factory创建的的bean是否是单例。这里通过默认方法定义为单例。

# FactoryBean使用场景

FactoryBean 用来创建一类bean。比如你有一些同属鸟类的bean需要被创建,但是它们自己有各自的特点,你只需要把他们的特点注入FactoryBean中就可以生产出各种鸟类的实例。举一个更加贴近实际生产的例子。甚至这个例子你可以应用到实际java开发中去。我们需要自己造一个定时任务的轮子。用FactoryBean 再合适不过了。

# 构建一个FactoryBean

# spring获取bean的6种方法

返回spring首页

我们一般称BeanFactory为IoC容器,而称ApplicationContext为应用上下文
ApplicationContext的初始化和BeanFactory有一个重大的区别:BeanFactory在初始化容器时,并未实例化Bean,直到第一次访问某个Bean时才实例目标Bean;
而ApplicationContext则在初始化应用上下文时就实例化所有单实例的Bean。因此ApplicationContext的初始化时间会比BeanFactory稍长一些

  • 方法一:在初始化时保存ApplicationContext对象
ApplicationContext ac = new FileSystemXmlApplicationContext("applicationContext.xml");
ac.getBean("beanId");
1
2

这种方式适用于采用Spring框架的独立应用程序,需要程序通过配置文件手工初始化Spring的情况。

  • 方法二:通过Spring提供的utils类获取ApplicationContext对象
ApplicationContext ac1 = WebApplicationContextUtils.getRequiredWebApplicationContext(ServletContext sc);
ApplicationContext ac2 = WebApplicationContextUtils.getWebApplicationContext(ServletContext sc);
ac1.getBean("beanId");
ac2.getBean("beanId");
1
2
3
4

这种方式适合于采用Spring框架的B/S系统,通过ServletContext对象获取ApplicationContext对象,然后在通过它获取需要的类实例。上面两个工具方式的区别是,前者在获取失败时抛出异常,后者返回null。

  • 方法三:继承自抽象类ApplicationObjectSupport
WebApplicationContext wac = (WebApplicationContext)servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
1

抽象类ApplicationObjectSupport提供getApplicationContext()方法,可以方便的获取ApplicationContext。
Spring初始化时,会通过该抽象类的setApplicationContext(ApplicationContext context)方法将ApplicationContext 对象注入。

  • 方法四:继承自抽象类WebApplicationObjectSupport

类似上面方法,调用getWebApplicationContext()获取WebApplicationContext

  • 方法五:实现接口ApplicationContextAware
public class SpringContextUtil implements ApplicationContextAware {
 // Spring应用上下文环境
 private static ApplicationContext applicationContext;
 /**
 - 实现ApplicationContextAware接口的回调方法,设置上下文环境
 *
 - @param applicationContext
 */
 public void setApplicationContext(ApplicationContext applicationContext) {
 SpringContextUtil.applicationContext = applicationContext;
 }
 /**
 - @return ApplicationContext
 */
 public static ApplicationContext getApplicationContext() {
 return applicationContext;
 }
 /**
 - 获取对象
 *
 - @param name
 - @return Object
 - @throws BeansException
 */
 public static Object getBean(String name) throws BeansException {
 return applicationContext.getBean(name);
 }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

实现该接口的setApplicationContext(ApplicationContext context)方法,并保存ApplicationContext 对象。Spring初始化时,会通过该方法将ApplicationContext对象注入。
虽然,spring提供的后三种方法可以实现在普通的类中继承或实现相应的类或接口来获取spring 的ApplicationContext对象,但是在使用是一定要注意实现了这些类或接口的普通java类一定要在Spring 的配置文件applicationContext.xml文件中进行配置。否则获取的ApplicationContext对象将为null。

  • 方法六:通过Spring提供的ContextLoader
WebApplicationContext wac = ContextLoader.getCurrentWebApplicationContext();
wac.getBean(beanID);
1
2

最后提供一种不依赖于servlet,不需要注入的方式。但是需要注意一点,在服务器启动时,Spring容器初始化时,不能通过以下方法获取Spring 容器,细节可以查看spring源码org.springframework.web.context.ContextLoader

# 循环依赖的问题解决

返回spring首页

  • 构造函数注入方式

优点:
构造函数可以保证一些重要的属性在bean实例化的时候就设置好,避免因为一些重要的属性没有提供而导致一个无用的Bean 实例情况
不需要为每个属性提供Setter方法,减少了类的方法个数
可以更好的封装类变量,不需要为每个属性提供Setter方法,避免外部错误的调用

缺点:
如果一个类属性太多,那么构造函数的参数将变成一个庞然大物,可读性较差
灵活性不强,在有些属性是可选的情况下,如果通过构造函数注入,也需要为可选的参数提供一个null值
如果有多个构造函数,则需要考虑配置文件和具体构造函数匹配歧义的问题,配置上相对复杂
构造函数不利于类的继承和拓展,因为子类需要引用父类复杂的构造函数
构造函数注入有时会造成循环依赖的问题(将相互依赖的两个Bean中的其中一个Bean采用Setter注入的方式即可。)

# 错误集锦

返回spring首页

  • org.springframework.data.mongodb.UncategorizedMongoDbException:Exception authenticating MongoCredential{mechanism=SCRAM-SHA-1, userName='htring', source='httest', password=<hidden>, mechanismProperties={}}; nested exception is com.mongodb.MongoSecurityException: Exception authenticating MongoCredential{mechanism=SCRAM-SHA-1, userName='htring', source='httest', password=<hidden>, mechanismProperties={}}

这个问题说明一件事儿,我们连接数据库没有连接上,为什么连接不上呢,比如我们用mongoDB下面的一个database叫做httest,但是我们的用户名密码是一个database叫做admin下的用户,这样就连不上了。因此,我们在database为httest下面建立一个用户,然后用这个用户进行登录即可(需先退出再认证)。

> use httest
switched to db httest
> db.createUser({user:"test",pwd:"test",roles:["readWrite"]})
Successfully added user: { "user" : "test", "roles" : [ "readWrite" ] }
1
2
3
4
  • mongodb无法访问com.querydsl.core.types.OrderSpecifier

增加依赖

<!-- https://mvnrepository.com/artifact/com.querydsl/querydsl-mongodb -->
<dependency>
   <groupId>com.querydsl</groupId>
   <artifactId>querydsl-mongodb</artifactId>
   <version>4.2.1</version>
</dependency>
1
2
3
4
5
6
  • Can not deserialize instance of java.lang.String out of START_OBJECT token
    前端和后端对应得数据类型不一致,前端传得是对象中得一个类型是json,后端是字符串类型

# 常见问题提示

返回spring首页

# 1.使用@Autowired时

对于有final修饰的变量不好使

Spring的IOC对待属性的注入使用的是set形式,但是final类型的变量在调用class的构造函数的这个过程当中就得初始化完成,这个是基于字段的依赖注入做不到的地方.只能使用基于构造函数的依赖注入的方式

与Spring的IOC机制紧密耦合

当你使用基于字段的依赖注入方式的时候,确实可以省略构造方法和setter这些个模板类型的方法,但是,你把控制权全给Spring的IOC了,别的类想重新设置下你的某个注入属性,没法处理(当然反射可以做到).
本身Spring的目的就是解藕和依赖反转,结果通过再次与类注入器(在本例中为Spring)耦合,失去了通过自动装配类字段而实现的对类的解耦,从而使类在Spring容器之外无效.

隐藏依赖性

当你使用Spring的IOC的时候,被注入的类应当使用一些public类型(构造方法,和setter类型方法)的方法来向外界表达:我需要什么依赖.但是基于字段的依赖注入的方式,基本都是private形式的,private把属性都给封印到class当中了.

无法对注入的属性进行安检

基于字段的依赖注入方式,你在程序启动的时候无法拿到这个类,只有在真正的业务使用的时候才会拿到,一般情况下,这个注入的都是非null的,万一要是null怎么办,在业务处理的时候错误才爆出来,时间有点晚了,如果在启动的时候就暴露出来,那么bug就可以很快得到修复(当然你可以加注解校验).

在使用spring框架中的依赖注入注解@Autowired时,idea报了一个警告 大部分被警告的代码都是不严谨的地方,

@Autowired
   UserDao userDao;
1
2

警告内容是
Field injection is not recommended
意思就是使用变量依赖注入的方式是不被推荐的

使用idea解决策略是这样的:
Always use constructor based dependency injection in your beans. Always use assertions for mandatory dependencies
意思就是总是使用构造器的方式强制注入。
依赖注入有三种方式:

  • (字段)变量(filed)注入
  • 构造器注入
  • set方法注入

先各自看一下实现方式

  • 变量(filed)注入
    @Autowired
    UserDao userDao;
1
2
  • 构造器注入
    final
    UserDao userDao;

    @Autowired
    public UserServiceImpl(UserDao userDao) {
        this.userDao = userDao;
    }
1
2
3
4
5
6
7
  • set方法注入
    private UserDao userDao;

    @Autowired
    public void setUserDao (UserDao userDao) {
        this.userDao = userDao;
    }
1
2
3
4
5
6

相比较而言:
优点:变量方式注入非常简洁,没有任何多余代码,非常有效的提高了java的简洁性。即使再多几个依赖一样能解决掉这个问题。
缺点:不能有效的指明依赖。相信很多人都遇见过一个bug,依赖注入的对象为null,在启动依赖容器时遇到这个问题都是配置的依赖注入少了一个注解什么的,然而这种方式就过于依赖注入容器了,当没有启动整个依赖容器时,这个类就不能运转,在反射时无法提供这个类需要的依赖

  • 总结下:

变量方式注入应该尽量避免,使用set方式注入或者构造器注入,这两种方式的选择就要看这个类是强制依赖的话就用构造器方式选择依赖的话就用set方法注入

# RequestMapping定义访问路径

@RestController
@RequestMapping("/requestMapping/controller")
public class RequestMappingController {

    @PostMapping("/demo")
    public String demo() {
        return "HelloWord";
    }
}
1
2
3
4
5
6
7
8
9

# 支持Restful风格

 @RequestMapping(value = "/restful", method = RequestMethod.GET)
  public String get() {
      //查询
      return "get";
  }

  @RequestMapping(value = "/restful", method = RequestMethod.POST)
  public String post() {
      //创建
      return "post";
  }

  @RequestMapping(value = "/restful", method = RequestMethod.PUT)
  public String put() {
      //更新
      return "put";
  }

  @RequestMapping(value = "/restful", method = RequestMethod.DELETE)
  public String del() {
      //删除
      return "post";
  }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 支持Ant风格

//匹配 /antA 或者 /antB 等URL
@RequestMapping("/ant?")
public String ant() {
    return "ant";
}

//匹配 /ant/a/create 或者 /ant/b/create 等URL
@RequestMapping("/ant/*/create")
public String antCreate() {
    return "antCreate";
}

//匹配 /ant/create 或者 /ant/a/b/create 等URL
@RequestMapping("/ant/**/create")
public String antAllCreate() {
    return "antAllCreate";
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 使用HandlerFunction

最后一种是使用HandlerFunction函数式接口,这是Spring5.0后引入的方式,主要用于做响应式接口的开发,也就是Webflux的开发。

# 接收参数

# @RequestHeader绑定请求头属性

 @RequestMapping("/head")
  public String head(@RequestHeader("Accept-Language") String acceptLanguage) {
      return acceptLanguage;
  }
1
2
3
4

# @CookieValue绑定请求的Cookie值

 @RequestMapping("/cookie")
  public String cookie(@CookieValue("_ga") String _ga) {
      return _ga;
  }
1
2
3
4

# @InitBinder解决接收多对象时属性名冲突

 //user和address都有id和name这两个属性
 @RequestMapping(value = "/twoBody", method = RequestMethod.POST)
  public String twoBody(User user, Address address) {
      return user.toString() + "," + address.toString();
  }


 @InitBinder("user")
  public void initBindUser(WebDataBinder webDataBinder) {
      webDataBinder.setFieldDefaultPrefix("u.");
  }

  @InitBinder("address")
  public void initBindAddress(WebDataBinder webDataBinder) {
      webDataBinder.setFieldDefaultPrefix("addr.");
  }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# HttpServletRequest解析参数

直接使用HttpServletRequest来获取请求参数,属于比较原始,但是灵活性最高的使用方法了

ServletRequest.getParameter(参数名)
1

# 方法参数

@GetMapping(path = "arg")
public String argParam(String name, Integer age) {
    return "name: " + name + " age: " + age;
}
1
2
3
4
  • 正好两个参数,与定义一直
  • 缺少一个请求参数
  • 多一个请求参数
  • 参数类型不一致
# 参数解析正常
➜  ~ curl 'http://127.0.0.1:8080/get/arg?name=%E4%B8%80%E7%81%B0%E7%81%B0&age=19'
name: 一灰灰 age: 19%
# 缺少一个参数时,为null
➜  ~ curl 'http://127.0.0.1:8080/get/arg?name=%E4%B8%80%E7%81%B0%E7%81%B0'
name: 一灰灰 age: null% 
# 多了一个参数,无法被解析
➜  ~ curl 'http://127.0.0.1:8080/get/arg?name=%E4%B8%80%E7%81%B0%E7%81%B0&age=19&id=10'
name: 一灰灰 age: 19%                                                              
# 类型不一致,500 
➜  ~ curl 'http://127.0.0.1:8080/get/arg?name=%E4%B8%80%E7%81%B0%E7%81%B0&age=haha' -i
HTTP/1.1 500
Content-Length: 0
Date: Sat, 24 Aug 2019 01:45:14 GMT
Connection: close
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  • 方法参数与GET传参,通过参数签名进行绑定
  • 方法参数类型,需要与接收的GET传参类型一致
  • 方法参数非基本类型时,若传参没有,则为null;(也就是说如果为基本类型,无法转null,抛异常)
  • 实际的GET传参可以多于方法定义的参数

# RequestParam 注解

@GetMapping(path = "ano")
public String anoParam(@RequestParam(name = "name") String uname,
        @RequestParam(name = "age", required = false) Integer age,
        @RequestParam(name = "uids", required = false) Integer[] uids) {
    return "name: " + uname + " age: " + age + " uids: " + (uids != null ? Arrays.asList(uids) : "null");
}
1
2
3
4
5
6
public enum TYPE {
    A, B, C;
}

@GetMapping(path = "enum")
public String enumParam(TYPE type) {
    return type.name();
}

@GetMapping(path = "enum2")
public String enumParam2(@RequestParam TYPE type) {
    return type.name();
}

@GetMapping(path = "mapper")
public String mapperParam(@RequestParam Map<String, Object> params) {
    return params.toString();
}

// 注意下面这个写法,无法正常获取请求参数,这里用来对比列出
@GetMapping(path = "mapper2")
public String mapperParam2(Map<String, Object> params) {
    return params.toString();
}


@GetMapping(path = "ano1")
public String anoParam1(@RequestParam(name = "names") List<String> names) {
    return "name: " + names;
}

// 注意下面这个写法无法正常解析数组
@GetMapping(path = "arg3")
public String anoParam2(List<String> names) {
    return "names: " + names;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
➜  ~ curl 'http://localhost:8080/get/enum?type=A'
A%
➜  ~ curl 'http://localhost:8080/get/enum2?type=A'
A%
➜  ~ curl 'http://localhost:8080/get/mapper?type=A&age=3'
{type=A, age=3}%
➜  ~ curl 'http://localhost:8080/get/mapper2?type=A&age=3'
{}%
➜  ~ curl 'http://localhost:8080/get/ano1?names=yi,hui,ha'
name: [yi, hui, ha]%
➜  ~ curl 'http://localhost:8080/get/arg3?names=yi,hui,ha' -i
HTTP/1.1 500
Content-Length: 0
Date: Sat, 24 Aug 2019 13:50:55 GMT
Connection: close
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  • GET传参映射到枚举时,根据enum.valueOf()来实例的
  • 如果希望使用Map来容纳所有的传参,需要加上注解@RequestParam
  • 如果参数为List类型,必须添加注解@RequestParam;否则用数组来接收

# PathVariable

从path中获取参数时,对url有相对严格的要求

# POJO

@Data
public class BaseReqDO implements Serializable {
    private static final long serialVersionUID = 8706843673978981262L;

    private String name;

    private Integer age;

    private List<Integer> uIds;
}


@GetMapping(path = "bean")
public String beanParam(BaseReqDO req) {
    return req.toString();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 参数类型转换