更新记录 20231205:第一版 20231231:补充一些补充说明 20241122:重构系列
须知
该章节会介绍 SpringBoot
中关于注解开发、请求参数获取。
参考以下资料:
黑马程序员 SSM 框架教程_Spring+SpringMVC+Maven 高级+SpringBoot+MyBatisPlus 企业实用开发技术
黑马程序员 2023 最新 Java 项目实战《苍穹外卖》,最适合新手的 SpringBoot+SSM 的企业级 Java 项目实战
尚硅谷 Springboot2 核心技术(好评如潮)
【狂神说 Java】Spring5 最新完整教程 IDEA 版通俗易懂
pom.xml
打开 pom.xml
文件,添加一些基础依赖。
这里你需要注意 dependencyManagement
的管理当前项目需要下载那些依赖,而 dependencies
是导入哪些依赖。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<!-- 组名 -->
<groupId>org.example</groupId>
<!-- 项目ID -->
<artifactId>demo</artifactId>
<!-- 项目版本 -->
<version>1.0-SNAPSHOT</version>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-boot.version>2.7.18</spring-boot.version>
<lombok.version>1.18.30</lombok.version>
</properties>
<!-- 管理依赖版本 -->
<dependencyManagement>
<dependencies>
<!-- SpringBoot的依赖配置-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<!-- 应用依赖 -->
<dependencies>
<!-- AOP -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- Web模块 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
基础
Main.java
@SpringBootApplication
public class Main {
public static void main(String[] args) {
SpringApplication.run(Main.class, args);
}
}
静态资源
spring.mvc.static-path-pattern
:添加静态资源访问前缀- 当设置这个以后,访问主页时不再显示
index.html
- 当设置这个以后,访问主页时不再显示
spring.web.resources.static-locations
:设置静态资源路径#spring.web.resources.add-mappings=false
:禁用静态资源
# 添加静态资源访问前缀
localhost:8088/a.png >>> localhost:8088/res/a.png
#spring.mvc.static-path-pattern=/res/**
# 设置静态资源路径
spring.web.resources.static-locations=classpath:/haha
# 禁用所有静态资源
#spring.web.resources.add-mappings=false
注解开发
@Component 注解,还衍生出了其他三个注解
@Controller
、@Service
、@Repository
,区分出这个类是属于表现层
、业务层
还是数据层
的类。
在第一章我们了解到什么是 Beans
,但是调用起来很麻烦,每次都要去 xml
中创建关联。
接下来我们可以使用一种更快捷的方法处理,注解。
创建配置类
但是现在我们先来了解下配置类的使用。
@Configuration
:创建配置Full(proxyBeanMethods = true)
:保证每个 @Bean 方法被调用多少次返回的组件都是单实例的(只有一个实例)Lite(proxyBeanMethods = false)
:每个@Bean 方法被调用多少次返回的组件都是新创建的- 组件依赖必须使用 Full 模式默认。其他默认是否 Lite 模式
- Full 会复用容器中的
Bean
,而Lite
是直接创建新的。
config/MyConfig.java
@Configuration(proxyBeanMethods = false) // 声明配置类, 默认false非单例
public class MyConfig {
}
App 调用
ConfigurableApplicationContext ctx = SpringApplication.run(DemoApplication.class, args);
// 创建Bean
UserDao userDao = ctx.getBean(UserDao.class);
userDao.show();
// 关闭IOC容器
ctx.close();
创建 Bean 三种方式
- 首先,导入 Bean 一共有三种方式
@Import
:可以在任何类上使用,并不局限于在配置类中使用@Bean
:在配置类中创建@Component + @ComponentScan
:在组件上面创建Component
放在组件上ComponentScan
在主类中定义扫描包范围
总结:只推荐第三种
第一种 @Import
@Import({UserDao.class}) // 导入类/创建Bean
@Configuration(proxyBeanMethods = false) // 声明配置类, 默认false非单例
public class MyConfig {
}
第二种 @Bean
@Configuration(proxyBeanMethods = false) // 声明配置类, 默认false非单例
public class MyConfig {
// 创建Bean
@Bean
public UserDao userDao() {
return new UserDao();
}
}
第三种 @component
+ @ComponentScan
@ComponentScan("org.example")
@SpringBootApplication
public class Main {
public static void main(String[] args) {
SpringApplication.run(Main.class, args);
}
}
UserDao
类
import org.springframework.stereotype.Component;
@Component
public class UserDao {
public void show() {
System.out.println("UserDao");
}
}
自动装配
@Autowired
可以省略掉 Setter
@Component
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
public void show() {
userDao.show();
System.out.println("UserServiceImpl");
}
// public void setUserDao(UserDao userDao) {
// this.userDao = userDao;
// }
}
lombok
@NoArgsConstructor
:生成一个无参数的构造方法(默认)@AllArgsConstructor
:生成一个包含所有参数的构造方法@Data
:无需再写setter/getter
方法,此外还提供了 equals()、hashCode()、toString() 方法。
// @AllArgsConstructor // 全参数构造器
// @NoArgsConstructor // 无参构造器
@Data // 省略 setter/getter
@Component
@ConfigurationProperties(prefix = "mycat")
public class Cat {
private String name;
private Integer age;
public void show() {
System.out.println();
System.out.println("cat," + this.name + "," + this.age);
}
}
输出
Cat cat = run.getBean(Cat.class);
cat.show(); // cat,catName,22
System.out.println(cat); // Cat(name=catName, age=22)
System.out.println(cat.toString()); // Cat(name=catName, age=22)
- log 日志
@Slf4j // 日志
@RestController
public class HelloController {
@RequestMapping("/hello")
public String hello() {
log.info("请求");
return "Hello, Spring Boot 2!" + "你好:";
}
}
配置文件读取
先在配置文件 application.yml
中添加
server:
port: 8080
my:
name: Tom
age: 18
- 第一种:
@Value
@Value("${server.port}")
private String port;
- 第二种:使用
Environment
读取配置文件
@RestController
@Slf4j
public class HelloController {
@Autowired
private Environment environment;
@GetMapping("/")
public String hello() {
System.out.println(environment.getProperty("server.port"));
return "HELLO";
}
}
- 第三种:
@ConfigurationProperties
配置前缀。
注意,我这里使用了 lombok
的 @Data
注解省略掉 setter/getter
的编写。
package org.example.dao;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Data
@Component
@ConfigurationProperties(prefix = "my")
public class UserDao {
private String name;
private Integer age;
// public String getName() {
// return name;
// }
//
// public void setName(String name) {
// this.name = name;
// }
//
// public Integer getAge() {
// return age;
// }
//
// public void setAge(Integer age) {
// this.age = age;
// }
public void show() {
System.out.println("UserDao" + this.name + "," + this.age);
}
}
条件判断注入
@Conditional
:条件判断是否进行组件注入。@ConditionalOnBean
:存在则创建- 当存在
dog
组件时,就会创建cat
组件。
- 当存在
@ConditionalOnMissingBean
:不存在则创建- 当存在
dog
组件时,不会创建cat
组件。
- 当存在
下面效果是一样的
@ConditionalOnBean(name = "dog")
public class MyConfig {
@ConditionalOnBean(name = "dog")
// 创建Bean
@Bean
public Cat cat() {
return new Cat();
}
}
非单例
@Scope("prototype")
public class UserDao {}
生命周期
@PostConstruct //在构造方法之后执行,替换 init-method
public void init() {
System.out.println("init ...");
}
@PreDestroy //在销毁方法之前执行,替换 destroy-method
public void destroy() {
System.out.println("destroy ...");
}
注入
- 第一种情况
@Qualifier
@Autowired
是按照类型注入的,当有 2 个类都是继承于BookDao
那怎么办?- 可以使用
@Qualifier
通过名称注入
@Repository("bookDao1")
public class BookDaoImpl implements BookDao {
public void save() {
System.out.println("book dao save ..." );
}
}
@Repository("bookDao2")
public class BookDaoImpl2 implements BookDao {
public void save() {
System.out.println("book dao save ...2" );
}
}
注入
@Autowired
@Qualifier("bookDao1")
private BookDao bookDao;
- 第二种 简单类型注入
@Value("myName")
private String name;
- 第三种 读取配置文件
@Value("${name}")
private String name;
Web
例子
- REST 风格:/user GET-获取用户 DELETE-删除用户 PUT-修改用户 POST-保存用户
- 现在只推荐使用
GET/POST
因为其他两个请求会被拦截(https://www.zhihu.com/question/38770182)
- 现在只推荐使用
<form action="/user" method="post">
<input name="_method" type="hidden" value="delete" />
<input name="_m" type="hidden" value="delete" />
<input value="REST-DELETE 提交" type="submit" />
</form>
<form action="/user" method="post">
<input name="_method" type="hidden" value="PUT" />
<input value="REST-PUT 提交" type="submit" />
</form>
@RequestMapping
:这是通用方法,可以请求 GET/POST/PUT/DELETE@PostMapping
:只能接受 POST@GetMapping
:只能接受 GET
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello() {
return "hello";
}
@RequestMapping(value = "/user", method = RequestMethod.GET)
public String getUser() {
return "GET-张三";
}
@RequestMapping(value = "/user", method = RequestMethod.POST)
public String saveUser() {
return "POST-张三";
}
@RequestMapping(value = "/user", method = RequestMethod.PUT)
public String putUser() {
return "PUT-张三";
}
@RequestMapping(value = "/user", method = RequestMethod.DELETE)
public String deleteUser() {
return "DELETE-张三";
}
}
参数获取
@PathVariable
:路径变量@RequestHeader
:获取请求头@RequestParam
:获取请求参数@CookieValue
:获取 cookie 值@RequestBody
:获取请求体[POST]- 一般用于获取表单内容
1、
@PathVariable
:路径变量- 最后
Map
代表所有变量的集合。
- 最后
// http://localhost:8080/car/3/owner/list
@GetMapping("/car/{id}/owner/{username}")
public Map<String, Object>
getCar(@PathVariable("id") Integer id,
@PathVariable("username") String name,
@PathVariable Map<String, String> pv
) {
Map<String, Object> map = new HashMap<>();
map.put("id", id); // 3
map.put("name", name); // lisi
map.put("pv", pv); // {"id":"3","username":"lisi"}
return map;
}
- 2、
@RequestHeader
:获取请求头
@GetMapping("/car/{id}/owner/{username}")
public Map<String, Object>
getCar(@RequestHeader("User-Agent") String userAgent,
@RequestHeader Map<String, String> header
) {
Map<String, Object> map = new HashMap<>();
// 获取 Header
map.put("userAgent", userAgent);
map.put("headers", header);
return map;
}
- 3、
@RequestParam
:获取请求参数
// car?age=18&inters=123&inters=321
@GetMapping("/car")
public Map<String, Object>
getCar(@RequestParam("age") Integer age,
@RequestParam("inters") List<String> inters,
@RequestParam Map<String, String> params
) {
Map<String, Object> map = new HashMap<>();
// Params 信息
map.put("age", age); // 18
map.put("inters", inters); // ["123","321"]
map.put("params", params); // {"age":"18","inters":"123"}
return map;
}
- 4、
@CookieValue
:获取 cookie 值
@GetMapping("/car")
public Map<String, Object>
getCar(@CookieValue("_ga") String _ga,
@CookieValue("_ga") Cookie cookie
) {
Map<String, Object> map = new HashMap<>();
// cookie
// 请先添加 Cookie
map.put("_ga", _ga);
System.out.println(cookie.getName() + "===>" + cookie.getValue());
return map;
}
- 5、
@RequestBody
:获取请求体
// RequestBody 获取表单提交内容
@PostMapping("/save")
public Map postMethod(@RequestBody String content) {
Map<String, Object> map = new HashMap<>();
map.put("content", content);
return map;
}
矩阵变量
@MatrixVariable
:矩阵变量
例如访问路径是 /cars/sell;low=34;brand=byd,audi,yd
,其中 sell
是必须存在的。
@RestController
public class ParameterTestController {
// /cars/sell;low=34;brand=byd,audi,yd
@GetMapping("/cars/{path}")
public Map carsSell(
@MatrixVariable("low") Integer low,
@MatrixVariable("brand") List<String> brand,
@PathVariable("path") String path
) {
Map<String, Object> map = new HashMap<>();
map.put("path", path); // sell
map.put("low", low); // 34
map.put("brand", brand); // byd,audi,yd
return map;
}
// /boss/1;age=20/2;age=10
@GetMapping("/boss/{bossId}/{empId}")
public Map boss(
@MatrixVariable(value = "age", pathVar = "bossId") Integer bossAge,
@MatrixVariable(value = "age", pathVar = "empId") Integer empAge,
@PathVariable("bossId") String path
) {
Map<String, Object> map = new HashMap<>();
map.put("path", path); // 1
map.put("bossAge", bossAge); // 20
map.put("empAge", empAge); // 10
return map;
}
}
@RequestAttribute
@RequestAttribute
:获取 request 域属性@ResponseBody
:将方法的返回值转换为响应数据
下面这个例子中,goToPage()
中我们通过 request.setAttribute(name, value)
添加了属性。
然后可以通过 request.getAttribute
获取也可以通过 注释 获取。
@Controller
public class RequestController {
@GetMapping("/goto")
public String goToPage(HttpServletRequest request) {
// 添加属性
request.setAttribute("msg", "成功了...");
request.setAttribute("code", 200);
// 注意是转发不是跳转
return "forward:/success"; // 转发到 /success请求
}
@ResponseBody
@GetMapping("/success")
public Map success(
@RequestAttribute(value = "msg", required = false) String msg,
@RequestAttribute(value = "code", required = false) Integer code,
HttpServletRequest request
) {
Object msg1 = request.getAttribute("msg");
Map<String, Object> map = new HashMap<>();
map.put("reqMethod_msg", msg1); // 通过 request.getAttribute 获取
map.put("annotation_msg", msg); // 通过 注释获取
return map;
}
}
页面数据传递
Map、Model
:里面的数据会被放在 request 的请求域request.setAttribute
,然后一同转发到别的地方。
下面例子中,我们访问 /params
路由,将数据存储在 Map、Model、request
中,然后一同转发到 /success
页面。
@GetMapping("/params")
public String testParam(
Map<String, Object> map,
Model model,
HttpServletRequest request,
HttpServletResponse response
) {
// 保存数据在 map, model, request, cookie 中就是保存在 request 请求域中
map.put("hello", "world666");
model.addAttribute("world", "hello666");
request.setAttribute("message", "HelloWorld");
// 添加 Cookie 到 request 请求域中
Cookie cookie = new Cookie("c1", "v1");
response.addCookie(cookie);
return "forward:/success";
}
@ResponseBody
@GetMapping("/success")
public Map success(
@RequestAttribute(value = "msg", required = false) String msg,
@RequestAttribute(value = "code", required = false) Integer code,
HttpServletRequest request
) {
Map<String, Object> map = new HashMap<>();
// 获取 Map 保存在 request 请求域的数据
Object hello = request.getAttribute("hello");
Object world = request.getAttribute("world");
Object message = request.getAttribute("message");
map.put("hello", hello); // world666
map.put("world", world); // hello666
map.put("message", message); // HelloWorld
return map;
}
自定义对象参数
表单传递一个参数列表,我们通过 自定义对象参数 去规范接收参数。
<form action="/saveuser" method="post">
姓名: <input name="userName" value="zhangsan" /> <br />
年龄: <input name="age" value="18" /> <br />
生日: <input name="birth" value="2019/12/10" /> <br />
宠物姓名:<input name="pet.name" value="阿猫" /><br />
宠物年龄:<input name="pet.age" value="5" />
<input type="submit" value="保存" />
</form>
创建类
@Data
public class Person {
private String userName;
private Integer age;
private Date birth;
private Pet pet;
}
@Data
public class Pet {
private String name;
private Integer age;
}
然后创建请求
@PostMapping("/saveuser")
public Person saveuser(Person person) {
return person;
}
// {"userName":"zhangsan","age":18,"birth":"2019-12-09T16:00:00.000+00:00","pet":{"name":"阿猫","age":5}}