SpringBoot2 学习笔记02 注解开发以及Web路由
更新记录 20241122:重构系列
1 须知
该章节会介绍 SpringBoot 中关于注解开发、请求参数获取。
2 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>3 基础
Main.java
@SpringBootApplication
public class Main {
public static void main(String[] args) {
SpringApplication.run(Main.class, args);
}
}3.1 静态资源
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=false4 注解开发
@Component 注解,还衍生出了其他三个注解
@Controller、@Service、@Repository,区分出这个类是属于表现层、业务层还是数据层的类。
在第一章我们了解到什么是 Beans,但是调用起来很麻烦,每次都要去 xml 中创建关联。
接下来我们可以使用一种更快捷的方法处理,注解。
4.1 创建配置类
但是现在我们先来了解下配置类的使用。
@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();4.2 创建 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");
}
}4.3 自动装配
@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;
// }
}4.4 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!" + "你好:";
}
}4.5 配置文件读取
先在配置文件 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);
}
}4.6 条件判断注入
@Conditional:条件判断是否进行组件注入。@ConditionalOnBean:存在则创建- 当存在
dog组件时,就会创建cat组件。
- 当存在
@ConditionalOnMissingBean:不存在则创建- 当存在
dog组件时,不会创建cat组件。
- 当存在
下面效果是一样的
@ConditionalOnBean(name = "dog")
public class MyConfig {
@ConditionalOnBean(name = "dog")
// 创建Bean
@Bean
public Cat cat() {
return new Cat();
}
}4.7 非单例
@Scope("prototype")
public class UserDao {}4.8 生命周期
@PostConstruct //在构造方法之后执行,替换 init-method
public void init() {
System.out.println("init ...");
}
@PreDestroy //在销毁方法之前执行,替换 destroy-method
public void destroy() {
System.out.println("destroy ...");
}4.9 注入
- 第一种情况
@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;5 Web
5.1 例子
- 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-张三";
}
}5.2 参数获取
@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;
}5.3 矩阵变量
@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;
}
}5.4 @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;
}
}5.5 页面数据传递
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;
}5.6 自定义对象参数
表单传递一个参数列表,我们通过 自定义对象参数 去规范接收参数。
<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}}