1、什么是IOC
IoC(Inversion of Control:控制反转) 是一种设计思想,而不是一个具体的技术实现。IoC 的思想就是将原本在程序中手动创建对象的控制权,交由 Spring 框架来管理。不过, IoC 并非 Spring 特有,在其他语言中也有应用。
为什么叫控制反转?
- 控制 :指的是对象创建(实例化、管理)的权力
- 反转 :控制权交给外部环境(Spring 框架、IoC 容器)
没有引入IOC容器之前,对象A依赖于对象B,那么对象A在初始化或运行到某一点时,自己必须主动创建对象B或者使用已经创建的对象B,无论是创建还是使用对象B,控制权都在自己手里。
引入IOC容器之后,对象A和对象B失去了直接联系,当对象A运行到需要对象B的时候,IOC容器会主动创建一个对象B注入到对象A需要的地方。
对象A获得依赖对象B的过程,由主动变成被动,控制权颠倒,这就是控制反转
IOC容器:在项目启动的时候读取项目中的bean节点,根据全限定类名(全限定类名是指一个类的完整名称,包括它所在的包名和类名,用点号分隔。例如,java.lang.String就是一个全限定类名,表示String类位于java.lang包中。)使用反射创建对象放到map里,扫描到有(@service\@Compent等注解)的类也是通过反射创建对象放到map里。
将对象之间的相互依赖关系交给 IoC 容器来管理,并由 IoC 容器完成对象的注入。这样可以很大程度上简化应用的开发,把应用从复杂的依赖关系中解放出来。 IoC 容器就像是一个工厂一样,当我们需要创建一个对象的时候,只需要配置好配置文件/注解即可,完全不用考虑对象是如何被创建出来的。
在实际项目中一个 Service 类可能依赖了很多其他的类,假如我们需要实例化这个 Service,你可能要每次都要搞清这个 Service 所有底层类的构造函数,这可能会把人逼疯。如果利用 IoC 的话,你只需要配置好,然后在需要的地方引用就行了,这大大增加了项目的可维护性且降低了开发难度。
在 Spring 中, IoC 容器是 Spring 用来实现 IoC 的载体, IoC 容器实际上就是个 Map(key,value),Map 中存放的是各种对象。
接下来我没在代码中需要用到里面的对象时候,通过注入(@Autowired/@Resourse)等,项目启动时会根据类型或id注入;id为对象名,为类的首字母小写形式
Spring 时代我们一般通过 XML 文件来配置 Bean,后来开发人员觉得 XML 文件来配置不太好,于是 SpringBoot 注解配置就慢慢开始流行起来。
2、什么是Spring Bean
简单来说,Bean 代指的就是那些被 IoC 容器所管理的对象。
我们需要告诉 IoC 容器帮助我们管理哪些对象,这个是通过配置元数据来定义的。配置元数据可以是 XML 文件、注解或者 Java 配置类。
<!-- Constructor-arg with 'value' attribute -->
<bean id="..." class="...">
<constructor-arg value="..."/>
</bean>
下图简单地展示了 IoC 容器如何使用配置元数据来管理对象。
org.springframework.beans
和 org.springframework.context
这两个包是 IoC 实现的基础,如果想要研究 IoC 相关的源码的话,可以去看看
3、将一个类声明为bean的注解有哪些?
@Component
:通用的注解,可标注任意类为Spring
组件。如果一个 Bean 不知道属于哪个层,可以使用@Component
注解标注。@Repository
: 对应持久层即 Dao 层,主要用于数据库相关操作。@Service
: 对应服务层,主要涉及一些复杂的逻辑,需要用到 Dao 层。@Controller
: 对应 Spring MVC 控制层,主要用于接受用户请求并调用Service
层返回数据给前端页面。
4、@Compent和@bean的相同与区别
- 区别:
- @Compent注解作用于类上,表示该类是一个Spring的组件,可以被自动扫描和装配到容器中。
- @Bean注解作用于方法上,表示该方法返回一个对象,可以被注册到容器中。该方法所在的类必须被@Configuration或@Compent注解标注。
- @Compent注解通常用于自定义的类,而@Bean注解通常用于第三方库中的类或者需要自定义创建逻辑的类。
- @Compent注解只能创建单例的Bean(在多线程的条件下容易出并发问题),而@Bean注解可以通过scope属性指定创建单例或多例的Bean。
- 相同点:
- @Compent和@Bean注解都可以将一个类声明为Spring的Bean,让容器管理其生命周期和依赖关系。
- @Compent和@Bean注解都可以通过name属性指定Bean的名称,如果不指定,默认使用类名或方法名作为名称。
@Bean
注解使用示例:
@Bean注解只能作用于方法上,表示该方法返回一个对象,可以被注册到容器中。如果要给类注解,可以使用@Compent或其他派生的注解,如@Repository,@Service,@Controller等。
@Configuration
public class AppConfig {
@Bean
public TransferService transferService() {
return new TransferServiceImpl();
}
}
上面的代码相当于下面的 xml 配置
<beans>
<bean id="transferService" class="com.acme.TransferServiceImpl"/>
</beans>
下面这个例子是通过 @Component
无法实现的。
@Bean
public OneService getService(status) {
case (status) {
when 1:
return new serviceImpl1();
when 2:
return new serviceImpl2();
when 3:
return new serviceImpl3();
}
}
5、注入Bean的注解有哪些?
Spring 内置的 @Autowired
以及 JDK 内置的 @Resource
和 @Inject
都可以用于注入 Bean。
Annotaion | Package | Source |
---|---|---|
@Autowired | org.springframework.bean.factory | Spring 2.5+ |
@Resource | javax.annotation | Java JSR-250 |
@Inject | javax.inject | Java JSR-330 |
@Autowired
和@Resource
使用的比较多一些。
6、@Autowired和@Resource的区别是什么
Autowired
属于 Spring 内置的注解,默认的注入方式为byType
(根据类型进行匹配),也就是说会优先根据接口类型去匹配并注入 Bean (接口的实现类)。
这会有什么问题呢? 当一个接口存在多个实现类的话,byType
这种方式就无法正确注入对象了,因为这个时候 Spring 会同时找到多个满足条件的选择,默认情况下它自己不知道选择哪一个。
这种情况下,注入方式会变为 byName
(根据名称进行匹配),这个名称通常就是类名(首字母小写)。就比如说下面代码中的 smsService
就是我这里所说的名称,这样应该比较好理解了吧。
// smsService 就是我们上面所说的名称
@Autowired
private SmsService smsService;
举个例子,SmsService
接口有两个实现类: SmsServiceImpl1
和 SmsServiceImpl2
,且它们都已经被 Spring 容器所管理。
// 报错,byName 和 byType 都无法匹配到 bean
@Autowired
private SmsService smsService;
// 正确注入 SmsServiceImpl1 对象对应的 bean
@Autowired
private SmsService smsServiceImpl1;
// 正确注入 SmsServiceImpl1 对象对应的 bean
// smsServiceImpl1 就是我们上面所说的名称
@Autowired
@Qualifier(value = "smsServiceImpl1")
private SmsService smsService;
我们还是建议通过 @Qualifier
注解来显式指定名称而不是依赖变量的名称。
@Resource
属于 JDK 提供的注解,默认注入方式为 byName
。如果无法通过名称匹配到对应的 Bean 的话,注入方式会变为byType
。
@Resource
有两个比较重要且日常开发常用的属性:name
(名称)、type
(类型)。
public @interface Resource {
String name() default "";
Class<?> type() default Object.class;
}
如果仅指定 name
属性则注入方式为byName
,如果仅指定type
属性则注入方式为byType
,如果同时指定name
和type
属性(不建议这么做)则注入方式为byType
+byName
。
// 报错,byName 和 byType 都无法匹配到 bean
@Resource
private SmsService smsService;
// 正确注入 SmsServiceImpl1 对象对应的 bean
@Resource
private SmsService smsServiceImpl1;
// 正确注入 SmsServiceImpl1 对象对应的 bean(比较推荐这种方式)
@Resource(name = "smsServiceImpl1")
private SmsService smsService;
简单总结一下:
@Autowired
是 Spring 提供的注解,@Resource
是 JDK 提供的注解。Autowired
默认的注入方式为byType
(根据类型进行匹配),@Resource
默认注入方式为byName
(根据名称进行匹配)。- 当一个接口存在多个实现类的情况下,
@Autowired
和@Resource
都需要通过名称才能正确匹配到对应的 Bean。Autowired
可以通过@Qualifier
注解来显式指定名称,@Resource
可以通过name
属性来显式指定名称。
7、Bean的作用域通常有哪些?
Spring 中 Bean 的作用域通常有下面几种:
- singleton : IoC 容器中只有唯一的 bean 实例。Spring 中的 bean 默认都是单例的,是对单例设计模式的应用。
- prototype : 每次获取都会创建一个新的 bean 实例。也就是说,连续
getBean()
两次,得到的是不同的 Bean 实例。 - request (仅 Web 应用可用): 每一次 HTTP 请求都会产生一个新的 bean(请求 bean),该 bean 仅在当前 HTTP request 内有效。
- session (仅 Web 应用可用) : session作用域的Bean实例是针对每个http会话创建的,只在当前会话有效,当会话结束后,Bean实例将销毁。
- application/global-session (仅 Web 应用可用): 每个 Web 应用在启动时创建一个 Bean(应用 Bean),该 bean 仅在当前应用启动时间内有效。
- websocket (仅 Web 应用可用):每一次 WebSocket 会话产生一个新的 bean。
HTTP请求和HTTP会话的区别是:
HTTP请求是一种用于获取网络资源的通信协议,它由客户端(或浏览器)发起,由服务端(如Web服务器)响应,每个请求都包含一个方法(如Get或post),一个URL,一些标头和一个可选消息体。
HTTP会话是一种用于在多个请求之间保持状态信息的机制,它由客户端和服务端共同维护,通常使用HTTP Cookie来实现,每个会话都有唯一的标识符。
·
如何配置 bean 的作用域呢?
xml 方式:
<bean id="..." class="..." scope="singleton"></bean>
注解方式:
@Bean
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Person personPrototype() {
return new Person();
}
8、springAOP
AOP(Aspect-Oriented Programming:面向切面编程)能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性。
Spring AOP 就是基于动态代理的,如果要代理的对象,实现了某个接口,那么 Spring AOP 会使用 JDK Proxy,去创建代理对象,而对于没有实现接口的对象,就无法使用 JDK Proxy 去进行代理了,这时候 Spring AOP 会使用 Cglib 生成一个被代理对象的子类来作为代理,如下图所示:
什么是动态代理?
动态代理是一种编程技术,它可以在运行时动态地创建和修改代理类,而不需要事先为每个被代理的类编写固定的代理类。动态代理可以实现对目标对象的功能增强,如添加日志、事务、权限等。
动态代理的实现原理是利用Java的反射机制,通过字节码操作或者接口实现,来生成代理类的字节码文件,并加载到内存中,从而创建代理对象。动态代理可以在编译期、类加载期或运行期进行。
AOP 切面编程设计到的一些专业术语:
术语 | 含义 |
---|---|
目标(Target) | 被通知的对象 |
代理(Proxy) | 向目标对象应用通知之后创建的代理对象 |
连接点(JoinPoint) | 目标对象的所属类中,定义的所有方法均为连接点 |
切入点(Pointcut) | 被切面拦截 / 增强的连接点(切入点一定是连接点,连接点不一定是切入点) |
通知(Advice) | 增强的逻辑 / 代码,也即拦截到目标对象的连接点之后要做的事情 |
切面(Aspect) | 切入点(Pointcut)+通知(Advice) |
Weaving(织入) | 将通知应用到目标对象,进而生成代理对象的过程动作 |
切入点和连接点的区别:
- 连接点是在程序执行过程中能够插入切面的一个点,如方法执行、构造器调用、字段赋值等。在Spring AOP中,连接点只表示方法执行。
- 切入点是一些特定的连接点,是切面要织入的地方。切入点可以通过表达式或注解来匹配连接点。在Spring AOP中,切入点使用AspectJ的语法来定义。
- 连接点是切面可以应用的所有可能性,而切入点是切面实际应用的具体位置。因此,切入点一定是连接点,但连接点不一定是切入点。
举个例子,假设有一个类A,它有三个方法:m1(), m2(), m3()。那么这三个方法都是连接点,因为它们都可以被切面拦截。但是如果我们定义一个切入点,只匹配m1()方法,那么只有m1()方法是切入点,而m2()和m3()方法不是切入点。
通知类型有哪些?
- Before(前置通知):目标对象的方法调用之前触发
- After (后置通知):目标对象的方法调用之后触发
- AfterReturning(返回通知):目标对象的方法调用完成,在返回结果值之后触发
- AfterThrowing(异常通知) :目标对象的方法运行中抛出 / 触发异常后触发。AfterReturning 和 AfterThrowing 两者互斥。如果方法调用成功无异常,则会有返回值;如果方法抛出了异常,则不会有返回值。
- Around (环绕通知):编程式控制目标对象的方法调用。环绕通知是所有通知类型中可操作范围最大的一种,因为它可以直接拿到目标对象,以及要执行的方法,所以环绕通知可以任意的在目标对象的方法调用前后搞事,甚至不调用目标对象的方法
9、springboot注解
9.1 @SpringBootApplication
我们可以把 @SpringBootApplication
看作是 @Configuration
、@EnableAutoConfiguration
、@ComponentScan
注解的集合。
@SpringBootApplication注解源码
package org.springframework.boot.autoconfigure;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
......
}
package org.springframework.boot;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
}
@Target注解(修饰注解的注解)是一个元注解,也就是说,它只能用于修饰其他的注解。它的作用是指定一个注解可以应用于哪些程序元素,如类、方法、字段、参数等。它的唯一参数是一个ElementType枚举类型的数组,表示该注解可以修饰的元素类型。
@Retention注解是一个元注解,用于指定一个注解的保留策略,即该注解在什么时候被丢弃。它的唯一参数是一个RetentionPolicy枚举类型的值,表示该注解的生命周期。有三种保留策略:
RetentionPolicy.SOURCE: 该注解只保留在源代码中,编译器会忽略它。 RetentionPolicy.CLASS: 该注解保留在.class文件中,但是虚拟机会忽略它。这是默认的保留策略。 RetentionPolicy.RUNTIME: 该注解保留在.class文件中,并且可以在运行时被反射机制读取。
@EnableAutoConfiguration:是一个Spring Boot提供的注解,用于启用Spring应用上下文的自动配置功能。它会根据类路径中的组件和用户定义的Bean,自动创建和注册Bean。这样可以简化开发者的工作,无需手动配置各种Bean。这个注解通常包含在@SpringBootApplication注解中
- 如果类路径中有HSQLDB这个jar文件,而用户没有手动配置任何数据库连接的Bean,那么Spring Boot会自动配置一个内存数据库的Bean
- 如果类路径中有tomcat-embedded.jar这个jar文件,而用户没有手动配置任何ServletWebServerFactory的Bean,那么Spring Boot会自动配置一个TomcatServletWebServerFactory的Bean
- 如果类路径中有spring-webmvc.jar这个jar文件,而用户没有手动配置任何DispatcherServlet的Bean,那么Spring Boot会自动配置一个DispatcherServlet的Bean
@ComponentScan
: 扫描被@Component
(@Repository
,@Service
,@Controller
)注解的 bean,注解默认会扫描该类所在的包下所有的类。
@Configuration
:允许在 Spring 上下文中注册额外的 bean 或导入其他配置类
9.2 @Autowired
注入bean
9.3 @Component、@Repository、@Service、@Controller
我们一般使用 @Autowired
注解让 Spring 容器帮我们自动装配 bean。要想把类标识成可用于 @Autowired
注解自动装配的 bean 的类,可以采用以下注解实现:
@Component
:通用的注解,可标注任意类为Spring
组件。如果一个 Bean 不知道属于哪个层,可以使用@Component
注解标注。@Repository
: 对应持久层即 Dao 层,主要用于数据库相关操作。@Service
: 对应服务层,主要涉及一些复杂的逻辑,需要用到 Dao 层。@Controller
: 对应 Spring MVC 控制层,主要用于接受用户请求并调用 Service 层返回数据给前端页面。
9.4 @RestController
@RestController
注解是@Controller
和@ResponseBody
的合集,表示这是个控制器 bean,并且是将函数的返回值直接填入 HTTP 响应体中,是 REST 风格的控制器。
代码示例:
单独使用@controller
@Controller
public class HelloController {
@GetMapping("/hello")
public String greeting(@RequestParam(name = "name", required = false, defaultValue = "World") String name, Model model) {
model.addAttribute("name", name);
return "hello";
}
}
使用@Controller与@ResponseBody注解返回json格式数据
@Controller
public class HelloController {
@PostMapping("/hello")
@ResponseBody
public Person greeting(@RequestBody Person person) {
return person;
}
}
直接使用@RestController注解
@RestController
public class HelloController {
@PostMapping("/hello")
public Person greeting(@RequestBody Person person) {
return person;
}
}
单独使用 @Controller
不加 @ResponseBody
的话一般是用在要返回一个视图的情况,这种情况属于比较传统的 Spring MVC 的应用,对应于前后端不分离的情况。@Controller
+@ResponseBody
返回 JSON 或 XML 形式数据
Controller 返回一个页面
单独使用 @Controller
不加 @ResponseBody
的话一般使用在要返回一个视图的情况,这种情况属于比较传统的Spring MVC 的应用,对应于前后端不分离的情况。
SpringMVC 传统工作流程
@RestController 返回JSON 或 XML 形式数据
但@RestController
只返回对象,对象数据直接以 JSON 或 XML 形式写入 HTTP 响应(Response)中,这种情况属于 RESTful Web服务,这也是目前日常开发所接触的最常用的情况(前后端分离)。
SpringMVC+RestController
@Controller +@ResponseBody 返回JSON 或 XML 形式数据
如果你需要在Spring4之前开发 RESTful Web服务的话,你需要使用@Controller
并结合@ResponseBody
注解,也就是说@Controller
+@ResponseBody
= @RestController
(Spring 4 之后新加的注解)。
@ResponseBody
注解的作用是将Controller
的方法返回的对象通过适当的转换器转换为指定的格式之后,写入到HTTP 响应(Response)对象的 body 中,通常用来返回 JSON 或者 XML 数据,返回 JSON 数据的情况比较多。
Spring3.xMVC RESTfulWeb服务工作流程
@RestController vs @Controller
9.5 @Scope
声明 Spring Bean 的作用域,使用方法:
@Bean
@Scope("singleton")
public Person personSingleton() {
return new Person();
}
四种常见的 Spring Bean 的作用域:
- singleton : 唯一 bean 实例,Spring 中的 bean 默认都是单例的。
- prototype : 每次请求都会创建一个新的 bean 实例。
- request : 每一次 HTTP 请求都会产生一个新的 bean,该 bean 仅在当前 HTTP request 内有效。
- session : 每一个 HTTP Session 会产生一个新的 bean,该 bean 仅在当前 HTTP session 内有效。
9.6 @Configuration
一般用来声明配置类(第三方类),可以使用 @Component
注解替代,不过使用@Configuration
注解声明配置类更加语义化。
@Configuration
public class AppConfig {
@Bean
public TransferService transferService() {
return new TransferServiceImpl();
}
}
9.7 处理常见的http请求类型
5 种常见的请求类型:
- GET :请求从服务器获取特定资源。举个例子:
GET /users
(获取所有学生) - POST :在服务器上创建一个新的资源。举个例子:
POST /users
(创建学生) - PUT :更新服务器上的资源(客户端提供更新后的整个资源)。举个例子:
PUT /users/12
(更新编号为 12 的学生) - DELETE :从服务器删除特定的资源。举个例子:
DELETE /users/12
(删除编号为 12 的学生) - PATCH :更新服务器上的资源(客户端提供更改的属性,可以看做作是部分更新),使用的比较少,这里就不举例子了。
9.7.1 Get请求
@GetMapping("users")
等价于@RequestMapping(value="/users",method=RequestMethod.GET)
@GetMapping("/users")
public ResponseEntity<List<User>> getAllUsers() {
return userRepository.findAll();
}
9.7.2 POST请求
@PostMapping("users")
等价于@RequestMapping(value="/users",method=RequestMethod.POST)
@PostMapping("/users")
public ResponseEntity<User> createUser(@Valid @RequestBody UserCreateRequest userCreateRequest) {
return userRespository.save(userCreateRequest);
}
9.7.3 PUT请求
@PutMapping("/users/{userId}")
等价于@RequestMapping(value="/users/{userId}",method=RequestMethod.PUT)
@PutMapping("/users/{userId}")
public ResponseEntity<User> updateUser(@PathVariable(value = "userId") Long userId,
@Valid @RequestBody UserUpdateRequest userUpdateRequest) {
......
}
9.7.4 Delete请求
@DeleteMapping("/users/{userId}")
等价于@RequestMapping(value="/users/{userId}",method=RequestMethod.DELETE)
@DeleteMapping("/users/{userId}")
public ResponseEntity deleteUser(@PathVariable(value = "userId") Long userId){
......
}
9.7.5 PATCH请求
一般实际项目中,我们都是 PUT 不够用了之后才用 PATCH 请求去更新数据。
@PatchMapping("/profile")
public ResponseEntity updateStudent(@RequestBody StudentUpdateRequest studentUpdateRequest) {
studentRepository.updateDetail(studentUpdateRequest);
return ResponseEntity.ok().build();
}
9.8 前后端传值
9.8.1 @PathVariable和@RequestParam
@PathVariable和@RequestParam都是Spring MVC中用于从请求中获取参数值的注解,但是它们有一些相同和区别:
- 相同点:它们都可以用在控制器方法的参数上,它们都可以指定参数的名称,它们都可以设置参数是否必须。
- 区别点:@PathVariable用于获取路径中的占位符值,例如/user/ {id},而@RequestParam用于获取查询字符串中的参数值,例如/user?id=123。@PathVariable获取的值不会被URL解码,而@RequestParam获取的值会被URL解码。@PathVariable可以使用正则表达式来限制匹配的范围,而@RequestParam不能。@PathVariable可以从Spring 4.3.3开始设置为可选,而@RequestParam一直可以设置为可选。
@RequestParam 的使用示例
@RequestParam注解有一些属性可以设置,例如:
- value: 指定参数的名称,如果不指定,默认使用方法参数的名称。
- required: 指定参数是否必须,默认为true,如果为false,那么当参数缺失时不会报错,而是赋值为null。
- defaultValue: 指定参数的默认值,如果参数缺失或者无法转换为目标类型,那么使用该值。
@RequestParam用于获取查询字符串的中的参数值,
前端代码示例,当用户在表单中输入姓名和年龄,并点击提交按钮时,前端会向后端发送一个GET请求,例如:http://localhost:8080/user?name=Tom&age=20
<form action="/user" method="get">
<label for="name">Name:</label>
<input type="text" id="name" name="name">
<label for="age">Age:</label>
<input type="number" id="age" name="age">
<button type="submit">Submit</button>
</form>
后端控制器有一个方法,用于接收前端传来的姓名和年龄,并返回一个欢迎信息。后端控制器的代码如下:
// 在控制器类中使用@RequestMapping注解
@Controller
@RequestMapping("/user")
public class UserController {
// 在控制器方法中使用@RequestParam注解
@RequestMapping (method = RequestMethod.GET)
public String welcomeUser(@RequestParam (value = "name") String userName,
@RequestParam (value = "age") int userAge,
Model model) {
// 将用户信息添加到模型中
model.addAttribute("name", userName);
model.addAttribute("age", userAge);
// 返回一个视图名称
return "welcome";
}
}
后端会根据@RequestParam注解获取name和age的值,并将它们添加到模型中,然后返回一个视图名称welcome。视图层会根据模型中的数据渲染一个欢迎页面。
@PathVariable的使用示例
@PathVariable注解有一些属性可以设置,例如:
- value: 指定占位符的名称,如果不指定,默认使用方法参数的名称。
- required: 指定占位符是否必须,默认为true,如果为false,那么当占位符缺失时不会报错,而是赋值为null。这个属性从Spring 4.3.3开始支持。
- name: 指定占位符的名称,和value属性相同。这个属性从Spring 4.3.3开始支持。
传参案例:
前端:前端页面有一个列表,显示用户的姓名和年龄,并且每个用户都有一个详情按钮,点击后可以查看用户的详细信息。前端页面的代码如下:
<ul>
<li>Name: Tom, Age: 20 <a href="/user/1">Details</a></li>
<li>Name: Alice, Age: 18 <a href="/user/2">Details</a></li>
<li>Name: Bob, Age: 22 <a href="/user/3">Details</a></li>
</ul>
后端控制器有一个方法,用于接收前端传来的用户id,并返回一个用户详情页面。后端控制器的代码如下:
// 在控制器类中使用@RequestMapping注解
@Controller
@RequestMapping("/user")
public class UserController {
// 在控制器方法中使用@PathVariable注解
@RequestMapping (value = "/ {id}", method = RequestMethod.GET)
public String getUserDetails(@PathVariable (value = "id") int userId,
Model model) {
// 根据userId获取用户详情
User user = userService.getUserById(userId);
// 将用户详情添加到模型中
model.addAttribute("user", user);
// 返回一个视图名称
return "details";
}
}
当用户点击某个用户的详情按钮时,前端会向后端发送一个GET请求,例如:
http://localhost:8080/user/1
后端会根据@PathVariable注解获取1这个值,并根据它获取用户详情,并将它添加到模型中,然后返回一个视图名称details。视图层会根据模型中的数据渲染一个用户详情页面
9.8.2 @RequestBody
用于读取 Request 请求(可能是 POST,PUT,DELETE,GET 请求)的 body 部分并且Content-Type 为 application/json 格式的数据,接收到数据之后会自动将数据绑定到 Java 对象上去。系统会使用HttpMessageConverter
或者自定义的HttpMessageConverter
将请求的 body 中的 json 字符串转换为 java 对象
需要注意的是:**一个请求方法只可以有一个@RequestBody
,但是可以有多个@RequestParam
和@PathVariable
**。 如果你的方法必须要用两个 @RequestBody
来接受数据的话,大概率是你的数据库设计或者系统设计出问题了!
9.9 读取配置信息的注解
很多时候我们需要将一些常用的配置信息比如阿里云 oss、发送短信、微信认证的相关配置信息等等放到配置文件中。
下面我们来看一下 Spring 为我们提供了哪些方式帮助我们从配置文件中读取这些配置信息。
我们的数据源application.yml
内容如下:
wuhan2020: 2020年初武汉爆发了新型冠状病毒,疫情严重,但是,我相信一切都会过去!武汉加油!中国加油!
my-profile:
name: Guide哥
email: koushuangbwcx@163.com
library:
location: 湖北武汉加油中国加油
books:
- name: 天才基本法
description: 二十二岁的林朝夕在父亲确诊阿尔茨海默病这天,得知自己暗恋多年的校园男神裴之即将出国深造的消息——对方考取的学校,恰是父亲当年为她放弃的那所。
- name: 时间的秩序
description: 为什么我们记得过去,而非未来?时间“流逝”意味着什么?是我们存在于时间之内,还是时间存在于我们之中?卡洛·罗韦利用诗意的文字,邀请我们思考这一亘古难题——时间的本质。
- name: 了不起的我
description: 如何养成一个新习惯?如何让心智变得更成熟?如何拥有高质量的关系? 如何走出人生的艰难时刻?
9.9.1 @Value(常用)
使用 @Value("${property}")
读取比较简单的配置信息:
@Value("${wuhan2020}")
String wuhan2020;
9.9.2 @ConfigurationProperties(常用)
@ConfigurationProperties 的功能是将配置文件中的属性绑定到一个 Java 类的字段上,从而方便地获取和使用这些属性
使用 @ConfigurationProperties 的步骤如下:
- 在配置文件中定义一些属性,例如 application.properties 或 application.yml,可以使用前缀来区分不同的属性组。
- 在 Java 类中添加 @ConfigurationProperties 注解,并指定前缀,然后为每个要绑定的属性提供一个带有公共 setter 方法的字段。
- 在 Spring Boot 应用中激活 @ConfigurationProperties 类,可以通过以下几种方式之一:
- 在类上添加 @Component 注解,让其被 Component Scan 扫描到。
- 在类上添加 @Configuration 注解,并在方法上添加 @Bean 注解,返回一个 @ConfigurationProperties 类型的对象。
- 在其他类上添加 @EnableConfigurationProperties 注解,并指定要激活的 @ConfigurationProperties 类型。
使用示例:
myapp.mail.enabled=true
myapp.mail.default-subject=Test Mail
我们可以创建一个 MailModuleProperties 类来绑定这些参数:
@Component
@ConfigurationProperties(prefix = "myapp.mail")
public class MailModuleProperties {
private boolean enabled;
private String defaultSubject;
// getter and setter methods
}
然后我们可以在其他类中注入 MailModuleProperties 类型的 bean,并使用它的字段来访问配置文件中的属性值:
@Service
public class MailService {
@Autowired
private MailModuleProperties mailModuleProperties;
public void sendMail(String to, String content) {
if (mailModuleProperties.isEnabled()) {
// send mail with default subject and content
String enabled=mailModuleProperties.getEnabled();
}
}
}
10、参数校验注解
10.1 一些常用字段的注解
通常用于校验服务端传过来的参数是否合法
@NotEmpty 被注释的字符串的不能为 null 也不能为空
@NotBlank 被注释的字符串非 null,并且必须包含一个非空白字符
@Null 被注释的元素必须为 null
@NotNull 被注释的元素必须不为 null
@AssertTrue 被注释的元素必须为 true
@AssertFalse 被注释的元素必须为 false
@Pattern(regex=,flag=)被注释的元素必须符合指定的正则表达式
@Email 被注释的元素必须是 Email 格式。
@Min(value)被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@Max(value)被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@DecimalMin(value)被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@DecimalMax(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@Size(max=, min=)被注释的元素的大小必须在指定的范围内
@Digits(integer, fraction)被注释的元素必须是一个数字,其值必须在可接受的范围内
@Past被注释的元素必须是一个过去的日期
@Future 被注释的元素必须是一个将来的日期
10.2 验证请求体
即校验一个类是否合法
即在一个类中,用注解表明其属性的一些规范,接收客户端那边传过来的json对象,可以校验其参数是否合法
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person {
@NotNull(message = "classId 不能为空")
private String classId;
@Size(max = 33)
@NotNull(message = "name 不能为空")
private String name;
@Pattern(regexp = "((^Man$|^Woman$|^UGM$))", message = "sex 值不在可选范围")
@NotNull(message = "sex 不能为空")
private String sex;
@Email(message = "email 格式不正确")
@NotNull(message = "email 不能为空")
private String email;
}
我们在需要验证的参数上加上了@Valid
注解,如果验证失败,它将抛出MethodArgumentNotValidException
。
@RestController
@RequestMapping("/api")
public class PersonController {
@PostMapping("/person")
public ResponseEntity<Person> getPerson(@RequestBody @Valid Person person) {
return ResponseEntity.ok().body(person);
}
}
10.3 验证请求参数
一定一定不要忘记在类上加上 @Validated
注解了,这个参数可以告诉 Spring 去校验方法参数。
示例如下
@RestController
@RequestMapping("/api")
@Validated
public class PersonController {
@GetMapping("/person/{id}")
public ResponseEntity<Integer> getPersonByID(@Valid @PathVariable("id") @Max(value = 5,message = "超过 id 的范围了") Integer id) {
return ResponseEntity.ok().body(id);
}
}
11、全局异常处理
可见牛客项目,有具体的使用