参数名与形参变量名相同,定义形参即可接收参数。
@RestController
@RequestMapping("hello")
public class HelloController {
@GetMapping("show")
public String show(String name,String age){
System.out.println(name+"! Hello World! !" + age);
return name+" Hello World! " + age ;
}
}
参数名或形参名不同 传入传出数值为空Null
使用注解 @RequestParam
当形参名字不同时,绑定与参数对应的新名字!通过注解完成映射!
// name : 命名
// required : 默认为TRUE 即必须有;FALSE允许没有即为可以空Null
@RequestParam(name = "age",required = false) String age
挂载注解后,默认required默认为TRUE,即必须传参,否则报错!
@Data
public class User {
private String name;
private Integer age;
}
@GetMapping("show1")
public String show1(User user){
System.out.println(user);
return user.toString();
}
Address 实体类:
@Data
public class Address {
private String province;
private String city;
}
contoller:
@GetMapping("show1")
public String show1(User user){
System.out.println(user);
return user.toString();
}
数组集合参数的使用场景:在HTML的表单中,有一个表单项是支持多选的(复选框),可以提交选择的多个值。
多个值是怎么提交的呢?其实多个值也是一个一个的提交。
后端程序接收上述多个值的方式有两种:
controller
@GetMapping("show2")
public String show2(String[] hobby){
System.out.println(Arrays.toString(hobby));
return Arrays.toString(hobby);
}
集合参数:请求参数名与形参集合对象名相同且请求参数为多个,@RequestParam 绑定参数关系
默认情况下,请求中参数名相同的多个值,是封装到数组。如果要封装到集合,要使用@RequestParam绑定参数关系
@GetMapping("show3")
public String show3(@RequestParam List<String> hobby){
System.out.println(hobby);
return hobby.toString();
}
在一些特殊的需求中,可能会涉及到日期类型数据的封装:
因为日期的格式多种多样(如:2023-12-12 10:05:45 、2023/12/12 10:05:45),那么对于日期类型的参数在进行封装的时候,需要通过@DateTimeFormat
注解,以及其pattern属性
来设置日期的格式。
@GetMapping("show4")
public String show4(@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime updateTime){
System.out.println(updateTime);
return updateTime.toString();
}
@DateTimeFormat
注解的pattern属性
中指定了哪种日期格式,前端的日期参数就必须按照指定的格式传递。Date
类型或LocalDateTime
类型,来封装传递的参数。服务端Controller方法接收JSON格式数据:
@RequestBody注解
:将JSON数据映射到形参的实体类对象中(JSON中的key和实体类中的属性名保持一致)controller
@PostMapping("list")
public String list(@RequestBody User user){
System.out.println(user);
return user.toString();
}
路径参数:直接在请求的URL中传递参数:
http://localhost:8080/hello/path/wake/20
@GetMapping("path/{name}/{id}")
public String path(@PathVariable String name,@PathVariable Integer id){
System.out.println(name+" "+id);
return name+" "+id;
}
@GetMapping("head")
public String head(@RequestHeader String name){
System.out.println(name);
return name;
}
直接在类上加
@RestController
即可 :
@RestController = @Controller + @ResponseBody
@RestController源码:
@Target({ElementType.TYPE}) //元注解(修饰注解的注解)
@Retention(RetentionPolicy.RUNTIME) //元注解
@Documented //元注解
@Controller
@ResponseBody
public @interface RestController {
@AliasFor(
annotation = Controller.class
)
String value() default "";
}
类上有@RestController
注解或@ResponseBody
注解时:
表示当前类下所有的方法返回值做为响应数据
方法的返回值,如果是一个POJO对象或集合时,会先转换为JSON格式,在响应给浏览器
测试:
@GetMapping("show1")
public User show1(User user){
System.out.println(user);
user.setAge(99);
return user;
}
@PostMapping("list")
public List<User> list(@RequestBody User user){
List<User> list = new ArrayList<>();
user.setName("DougWake");
list.add(user);
return list;
}
前面所编写的这些Controller方法中,返回值各种各样,没有任何的规范:
在真实的项目开发中,无论是哪种方法,我们都会定义一个统一的返回结果。方案如下:
前端:只需要按照统一格式的返回结果进行解析(仅一种解析方案),就可以拿到数据。
统一的返回结果使用类来描述,在这个结果中包含:
响应状态码 code:当前请求是成功,还是失败
状态码信息 msg:给页面的提示信息
返回的数据 data:给前端响应的数据(字符串、对象、集合)
Result工具类:
package com.wake.utils;
/**
* 全局统一返回结果类
*/
public class Result<T> {
// 返回码
private Integer code;
// 返回消息
private String message;
// 返回数据
private T data;
public Result(){}
// 返回数据
protected static <T> Result<T> build(T data) {
Result<T> result = new Result<T>();
if (data != null)
result.setData(data);
return result;
}
public static <T> Result<T> build(T body, Integer code, String message) {
Result<T> result = build(body);
result.setCode(code);
result.setMessage(message);
return result;
}
public static <T> Result<T> build(T body, ResultCodeEnum resultCodeEnum) {
Result<T> result = build(body);
result.setCode(resultCodeEnum.getCode());
result.setMessage(resultCodeEnum.getMessage());
return result;
}
/**
* 操作成功
* @param data baseCategory1List
* @param
* @return
*/
public static<T> Result<T> ok(T data){
Result<T> result = build(data);
return build(data, ResultCodeEnum.SUCCESS);
}
public Result<T> message(String msg){
this.setMessage(msg);
return this;
}
public Result<T> code(Integer code){
this.setCode(code);
return this;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
Result 响应码 枚举类:
package com.wake.utils;
/**
* 统一返回结果状态信息类
*/
public enum ResultCodeEnum {
SUCCESS(200, "success"),
USERNAME_ERROR(501, "usernameError"),
PASSWORD_ERROR(503, "passwordError"),
NOTLOGIN(504, "notLogin"),
USERNAME_USED(505, "userNameUsed");
private Integer code;
private String message;
private ResultCodeEnum(Integer code, String message) {
this.code = code;
this.message = message;
}
public Integer getCode() {
return code;
}
public String getMessage() {
return message;
}
}
返回统一的响应结果:
资源地址可以在yml配置文件中修改,改完就直接覆盖默认路径。
在SpringBoot项目中,静态资源默认可以存放的目录:
- classpath:/static/
- classpath:/public/
- classpath:/resources/
- classpath:/META-INF/resources/
classpath:
- 代表的是类路径,在maven的项目中,其实指的就是 src/main/resources 或者 src/main/java,但是java目录是存放java代码的,所以相关的配置文件及静态资源文档,就放在 src/main/resources下。
【SpringBoot3】整合SpringMVC_静态资源处理
前端资源放这了:
dom4j的依赖,用于解析XML文件:
<dependency>
<groupId>org.dom4jgroupId>
<artifactId>dom4jartifactId>
<version>2.1.3version>
dependency>
解析XML的工具类XMLParserUtils:
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
public class XmlParserUtils {
public static <T> List<T> parse(String file , Class<T> targetClass) {
ArrayList<T> list = new ArrayList<T>(); //封装解析出来的数据
try {
//1.获取一个解析器对象
SAXReader saxReader = new SAXReader();
//2.利用解析器把xml文件加载到内存中,并返回一个文档对象
Document document = saxReader.read(new File(file));
//3.获取到根标签
Element rootElement = document.getRootElement();
//4.通过根标签来获取 user 标签
List<Element> elements = rootElement.elements("emp");
//5.遍历集合,得到每一个 user 标签
for (Element element : elements) {
//获取 name 属性
String name = element.element("name").getText();
//获取 age 属性
String age = element.element("age").getText();
//获取 image 属性
String image = element.element("image").getText();
//获取 gender 属性
String gender = element.element("gender").getText();
//获取 job 属性
String job = element.element("job").getText();
//组装数据
Constructor<T> constructor = targetClass.getDeclaredConstructor(String.class, Integer.class, String.class, String.class, String.class);
constructor.setAccessible(true);
T object = constructor.newInstance(name, Integer.parseInt(age), image, gender, job);
list.add(object);
}
} catch (Exception e) {
e.printStackTrace();
}
return list;
}
}
案例:读取xml 加载数据转换解析 显示在前端页面
实体类:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Emp {
private String name;
private Integer age;
private String image;
private String gender;
private String job;
}
controller:
@RestController
public class EmpController {
@RequestMapping("/listEmp")
public Result list(){
//1. 加载并解析emp.xml
String file = this.getClass().getClassLoader().getResource("emp.xml").getFile();
//System.out.println(file);
List<Emp> empList = XmlParserUtils.parse(file, Emp.class);
//2. 对数据进行转换处理 - gender, job
empList.stream().forEach(emp -> {
//处理 gender 1: 男, 2: 女
String gender = emp.getGender();
if("1".equals(gender)){
emp.setGender("男");
}else if("2".equals(gender)){
emp.setGender("女");
}
//处理job - 1: 讲师, 2: 班主任 , 3: 就业指导
String job = emp.getJob();
if("1".equals(job)){
emp.setJob("讲师");
}else if("2".equals(job)){
emp.setJob("班主任");
}else if("3".equals(job)){
emp.setJob("就业指导");
}
});
//3. 响应数据
return Result.success(empList);
}
}
http://localhost:8080/emp.html :
我们会发现案例中:解析XML数据,获取数据的代码,处理数据的逻辑的代码,给页面响应的代码全部都堆积在一起了,全部都写在controller方法中了。
单一职责原则:一个类或一个方法,就只做一件事情,只管一块功能。
- 数据访问:负责业务数据的维护操作,包括增、删、改、查等操作。Dao/Mapper
- 逻辑处理:负责业务逻辑处理的代码。service
- 请求处理、响应数据:负责,接收页面的请求,给页面响应数据。controller
基于三层架构的程序执行流程:
程序中高内聚的体现:
程序中耦合代码的体现:
高内聚、低耦合的目的是使程序模块的可重用性、移植性大大增强。
不能在EmpController中使用new对象。
IOC容器中创建、管理的对象,称之为:bean对象
使用IOC容器 把需要的资源 装进去,使用DI将容器内的资源拿来用!
【Spring】IoC容器 控制反转 与 DI依赖注入 概念 第一期
【Spring】IoC容器 控制反转 与 DI依赖注入 三种实现方式 总结 第五期
注解 | 说明 | 位置 |
---|---|---|
@Controller | @Component的衍生注解 | 标注在控制器类上 |
@Service | @Component的衍生注解 | 标注在业务类上 |
@Repository | @Component的衍生注解 | 标注在数据访问类上(由于与mybatis整合,用的少) |
@Component | 声明bean的基础注解 | 不属于以上三类时,用此注解 |
其实底层都是@Component注解
在IOC容器中,每一个Bean都有一个属于自己的名字,可以通过注解的value属性
指定bean的名字。
如果没有指定,默认为类名首字母小写。
注意事项:
- 声明bean的时候,可以通过value属性指定bean的名字,如果没有指定,默认为类名首字母小写。
- 使用以上四个注解都可以声明bean,但是在springboot集成web开发中,声明控制器bean只能用@Controller。
bean想要生效,还需要被组件扫描
@ComponentScan
扫描@ComponentScan注解虽然没有显式配置,但是实际上已经包含在了引导类声明注解
@SpringBootApplication
中,
默认扫描的范围是SpringBoot启动类所在包及其子包。
@Autowired 注解:
如果在IOC容器中,存在多个相同类型的bean对象,会出现什么情况呢?
程序报错:显示需要一个单一Bean,但是存在两个。
如何解决上述问题呢?Spring提供了以下几种解决方案:
@Primary
@Qualifier
@Resource
面试题 : @Autowird 与 @Resource的区别
- @Autowired 是spring框架提供的注解,而@Resource是JDK提供的注解
- @Autowired 默认是按照类型注入,而@Resource是按照名称注入
使用自动构建SpringBoot 报错!
错误: 找不到或无法加载主类 com.wake.SpringbootTestPartApplication 原因: java.lang.ClassNotFoundException: com.wake.SpringbootTestPartApplication
解决: maven
clean
comlile