SpringBoot2 学习笔记02 注解开发以及Web路由

本文含有: SpringBoot2注解开发, 请求参数获取, 配置文件读取, 访问静态资源, lombok, 自动装配, 生命周期, 矩阵变量, 页面数据传递, 自定义对象参数

更新记录 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}}
Licensed under CC BY-NC-SA 4.0
最后更新于 Nov 22, 2024 00:00 UTC
本博客已稳定运行
发表了53篇文章 · 总计28.17k字
使用 Hugo 构建
主题 StackJimmy 设计