赞
踩
在SpringBoot进行web程序开发时,它内置了一个核心的Servlet程序 DispatcherServlet,称之为 核心控制器。 DispatcherServlet 负责接收页面发送的请求(HttpServletRequest 对象就可以获取到请求数据),然后根据执行的规则,将请求再转发给后面的请求处理器Controller,请求处理器处理完请求之后,最终再由DispatcherServlet给浏览器响应数据(通过HttpServletResponse这个对象,我们就可以给浏览器设置响应数据)
Browser/Server,浏览器/服务器架构模式。客户端只需要浏览器,应用程序的逻辑和数据都存储在服务端。
如何接收页面传递过来的请求数据。
测试时可以使用专业的接口测试工具(如Postman工具)
Postman是一款功能强大的网页调试与发送网页HTTP请求的Chrome插件。
原始方式
SpringBoot方式
原始方式(了解)
Tomcat接收到http请求时:把请求的相关信息封装到HttpServletRequest对象中
- //根据指定的参数名获取请求参数的数据值
- String request.getParameter("参数名")
- @RestController
- public class RequestController {
- //原始方式
- @RequestMapping("/simpleParam")
- public String simpleParam(HttpServletRequest request){
- // http://localhost:8080/simpleParam?name=Tom&age=10
- // 请求参数: name=Tom&age=10 (有2个请求参数)
- // 第1个请求参数: name=Tom 参数名:name,参数值:Tom
- // 第2个请求参数: age=10 参数名:age , 参数值:10
-
- String name = request.getParameter("name");//name就是请求参数名
- String ageStr = request.getParameter("age");//age就是请求参数名
-
- int age = Integer.parseInt(ageStr);//需要手动进行类型转换
- System.out.println(name+" : "+age);
- return "OK";
- }
- }
SpringBoot方式
简单参数,定义同名的形参即可接收参数。
- @RestController
- public class RequestController {
- // http://localhost:8080/simpleParam?name=Tom&age=10
- // 第1个请求参数: name=Tom 参数名:name,参数值:Tom
- // 第2个请求参数: age=10 参数名:age , 参数值:10
-
- //springboot方式
- @RequestMapping("/simpleParam")
- public String simpleParam(String name , Integer age ){//形参名和请求参数名保持一致
- System.out.println(name+" : "+age);
- return "OK";
- }
- }
结论:不论是GET请求还是POST请求,对于简单参数来讲,只要保证请求参数名和Controller方法中的形参名保持一致,就可以获取到请求参数中的数据值。
注:如果方法形参名称与请求参数名称不一致运行没有报错。对应值为null,无法接收到请求数据
可以使用Spring提供的@RequestParam注解完成映射
- @RestController
- public class RequestController {
- // http://localhost:8080/simpleParam?name=Tom&age=20
- // 请求参数名:name
-
- //springboot方式
- @RequestMapping("/simpleParam")
- public String simpleParam(@RequestParam("name") String username , Integer age ){
- System.out.println(username+" : "+age);
- return "OK";
- }
- }
注意事项:
@RequestParam中的required属性默认为true(默认值也是true),代表该请求参数必须传递,如果不传递将报错
如果该参数是可选的,可以将required属性设置为false
simpleParam(@RequestParam(name = "name", required = false) String username, Integer age)
请求参数比较多时,将请求参数封装到一个实体类对象中。 要想完成数据封装,需要遵守请求参数名与实体类的属性名相同
- //实体类
- public class User {
- 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;
- }
-
- @Override
- public String toString() {
- return "User{" +
- "name='" + name + '\'' +
- ", age=" + age +
- '}';
- }
- }
-
- //Controller方法:
-
- @RestController
- public class RequestController {
- //实体参数:简单实体对象
- @RequestMapping("/simplePojo")
- public String simplePojo(User user){
- System.out.println(user);
- return "OK";
- }
- }
参数名和实体类属性名不一致时,为null
复杂实体对象:在实体类中有一个或多个属性,也是实体对象类型的。
(请求参数名与形参对象属性名相同,按照对象层次结构关系即可接收嵌套实体类属性参数。)
先定义POJO实体类
- @RestController
- public class RequestController {
- //实体参数:复杂实体对象
- @RequestMapping("/complexPojo")
- public String complexPojo(User user){
- System.out.println(user);
- return "OK";
- }
- }
情景:提交多个值(复选框)
后端程序接收上述多个值的方式有两种:数组和集合
数组参数
- //数组集合参数,提交多个值(后端程序用数组或者集合接收)
- // 数组参数:请求参数名与形参数组名称相同且请求参数为多个,定义数组类型形参即可接收参数
- @RequestMapping("/arrayParam")
- //地址表示方式一: xx?hobby=game&hobby=java
- //方式二:xx?hobby=game,java
- public String arrayParam(String[] hobby)
- {
- System.out.println(Arrays.toString(hobby));
- return "ok";
- }
集合参数:请求参数名与形参集合对象名相同且请求参数为多个,@RequestParam 绑定参数关系
默认情况下,请求中参数名相同的多个值,是封装到数组。如果要封装到集合,要使用@RequestParam绑定参数关系
- @RequestMapping("/listParam")
- public String list(@RequestParam List<String> hobby)
- {
- System.out.println(hobby);
- return "ok";
- }
涉及到日期类型数据的封装,通过@DateTimeFormat注解,以及其pattern属性来设置日期的格式。前端的日期参数必须按照指定的格式传递。
后端controller方法中,需要使用Date类型或LocalDateTime类型,来封装传递的参数。
- @RestController
- public class RequestController {
- //日期时间参数
- @RequestMapping("/dateParam")
- public String dateParam(@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime updateTime){
- System.out.println(updateTime);
- return "OK";
- }
- }
(JSON是开发中最常用的前后端数据交互方式,使用JSON格式的数据进行传输。)
使用实体类进行封装。
JSON数据键名与形参对象属性名相同,定义实体类类型形参即可接收参数。需要使用 @RequestBody标识。
@RequestBody注解:将JSON数据映射到形参的实体类对象中(JSON中的key和实体类中的属性名保持一致)
- @RestController
- public class RequestController {
- //JSON参数
- @RequestMapping("/jsonParam")
- public String jsonParam(@RequestBody User user){
- System.out.println(user);
- return "OK";
- }
- }
传统的开发中请求参数是放在请求体(POST请求)传递或跟在URL后面通过?key=value的形式传递(GET请求)。
在现在的开发中,经常还会直接在请求的URL中传递参数
http://localhost:8080/user/1 http://localhost:880/user/1/0
这种传递请求参数的形式:路径参数。
路径参数:
前端:通过请求URL直接传递参数
后端:使用{…}来标识该路径参数,需要使用@PathVariable获取路径参数
- @RestController
- public class RequestController {
- //路径参数
- @RequestMapping("/path/{id}")
- public String pathParam(@PathVariable Integer id){
- System.out.println(id);
- return "OK";
- }
- //传递多个路径参数
- @RequestMapping("/path/{id}/{name}")
- public String pathParam2(@PathVariable Integer id, @PathVariable String name){
- System.out.println(id+ " : " +name);
- return "OK";
- }
- }
Controller程序呢,除了接收请求外,还可以进行响应。
在我们前面所编写的controller方法中,都已经设置了响应数据。
controller方法中的return的结果,通过使用@ResponseBody注解可以响应给浏览器
位置:书写在Controller方法上或类上
作用:将方法返回值直接响应给浏览
@RestController = @Controller + @ResponseBody,在类上添加@RestController就相当于添加了@ResponseBody注解。
类上有@RestController注解或@ResponseBody注解时:表示当前类下所有的方法返回值做为响应数据
方法的返回值,如果是一个实体类对象或集合时,会先转换为JSON格式,在响应给浏览器
前端:只需要按照统一格式的返回结果进行解析(仅一种解析方案),就可以拿到数据。
统一的返回结果使用类来描述,在这个结果中包含:
响应状态码:当前请求是成功,还是失败
状态码信息:给页面的提示信息
返回的数据:给前端响应的数据(字符串、对象、集合)
- //实体类保存统一的响应结果
- public class Result {
- private Integer code;//响应码,1 代表成功; 0 代表失败
- private String msg; //响应码 描述字符串
- private Object data; //返回的数据
-
- public Result() { }
- public Result(Integer code, String msg, Object data) {
- this.code = code;
- this.msg = msg;
- this.data = data;
- }
-
- //gets方法和sets方法
-
- //增删改 成功响应(不需要给前端返回数据)
- public static Result success(){
- return new Result(1,"success",null);
- }
- //查询 成功响应(把查询结果做为返回数据响应给前端)
- public static Result success(Object data){
- return new Result(1,"success",data);
- }
- //失败响应
- public static Result error(String msg){
- return new Result(0,msg,null);
- }
- }
-
- 改造Controller:
-
- @RestController
- public class ResponseController {
- //响应统一格式的结果
- @RequestMapping("/hello")
- public Result hello(){
- System.out.println("Hello World ~");
- //return new Result(1,"success","Hello World ~");
- return Result.success("Hello World ~");
- }
-
-
-
- //响应统一格式的结果
- @RequestMapping("/listAddr")
- public Result listAddr(){
- List<Address> list = new ArrayList<>();
-
- Address addr = new Address();
- addr.setProvince("广东");
- addr.setCity("深圳");
-
- Address addr2 = new Address();
- addr2.setProvince("陕西");
- addr2.setCity("西安");
-
- list.add(addr);
- list.add(addr2);
- return Result.success(list);
- }
- }
需求
准备工作
XML文件导入进来,放在 src/main/resources目录下
准备好解析XML文件的工具类,将工具类拷贝进来
前端页面资源,放在src/main/resources下的static目录下
在SpringBoot项目中,静态资源默认可以存放的目录:
classpath:/static/
classpath:/public/
classpath:/resources/
classpath:/META-INF/resources/
classpath:
代表的是类路径,在maven的项目中,其实指的就是 src/main/resources 或者 src/main/java,但是java目录是存放java代码的,所以相关的配置文件及静态资源文档,就放在 src/main/resources下。
- @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测试
单一职责原则:一个类或一个方法,就只做一件事情,只管一块功能。
这样就可以让类、接口、方法的复杂度更低,可读性更强,扩展性更好,也更利用后期的维护。
在项目开发中呢,可以将代码分为三层:
Controller:控制层。接收前端发送的请求,对请求进行处理,并响应数据。
Service:业务逻辑层。处理具体的业务逻辑。
Dao:数据访问层(Data Access Object),也称为持久层。负责数据访问操作,包括数据的增、删、改、查。
- //控制层:接收前端发送的请求,对请求进行处理,并响应数据
-
- @RestController
- public class EmpController {
- //业务层对象
- private EmpService empService = new EmpServiceA();
-
- @RequestMapping("/listEmp")
- public Result list(){
- //1. 调用service层, 获取数据
- List<Emp> empList = empService.listEmp();
-
- //3. 响应数据
- return Result.success(empList);
- }
- }
-
- //业务逻辑层:处理具体的业务逻辑
-
- //业务逻辑接口(制定业务标准)
- public interface EmpService {
- //获取员工列表
- public List<Emp> listEmp();
- }
-
-
- //业务逻辑实现类(按照业务标准实现)
- public class EmpServiceA implements EmpService {
- //dao层对象
- private EmpDao empDao = new EmpDaoA();
-
- @Override
- public List<Emp> listEmp() {
- //1. 调用dao, 获取数据
- List<Emp> empList = empDao.listEmp();
-
- //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("就业指导");
- }
- });
- return empList;
- }
- }
-
- //数据访问层:负责数据的访问操作,包含数据的增、删、改、查
-
-
- //数据访问层接口(制定标准)
- public interface EmpDao {
- //获取员工列表数据
- public List<Emp> listEmp();
- }
- 数据访问实现类
-
- //数据访问实现类
- public class EmpDaoA implements EmpDao {
- @Override
- public List<Emp> listEmp() {
- //1. 加载并解析emp.xml
- String file = this.getClass().getClassLoader().getResource("emp.xml").getFile();
- System.out.println(file);
- List<Emp> empList = XmlParserUtils.parse(file, Emp.class);
- return empList;
- }
- }
内聚:软件中各个功能模块内部的功能联系。
耦合:衡量软件中各个层/模块之间的依赖、关联的程度。
软件设计原则:高内聚低耦合。
不在类里new一个其他类对象而是提供一个容器,容器中存储一些对象
controller程序从容器中获取EmpService类型的对象
其中有两个核心概念IOC和DI
控制反转: Inversion Of Control,简称IOC。对象的创建控制权由程序自身转移到外部(容器),这种思想称为控制反转。
对象的创建权由程序员主动创建转移到容器(由容器创建、管理对象)。这个容器称为:IOC容器或Spring容器
依赖注入: Dependency Injection,简称DI。容器为应用程序提供运行时,所依赖的资源,称之为依赖注入。
程序运行时需要某个资源,此时容器就为其提供这个资源。
例:EmpController程序运行时需要EmpService对象,Spring容器就为其提供并注入EmpService对象
IOC容器中创建、管理的对象,称之为:bean对象
任务:完成Controller层、Service层、Dao层的代码解耦
第1步:删除Controller层、Service层中new对象的代码
第2步:Service层及Dao层的实现类,交给IOC容器管理
使用Spring提供的注解:@Component ,就可以实现类交给IOC容器管理
第3步:为Controller及Service注入运行时依赖的对象
使用Spring提供的注解:@Autowired ,就可以实现程序运行时IOC容器自动注入需要的依赖对象
- //Controller层:
-
- @RestController
- public class EmpController {
-
- @Autowired //运行时,从IOC容器中获取该类型对象,赋值给该变量
- private EmpService empService ;
-
- @RequestMapping("/listEmp")
- public Result list(){
- //1. 调用service, 获取数据
- List<Emp> empList = empService.listEmp();
-
- //3. 响应数据
- return Result.success(empList);
- }
- }
- //Service层:
-
- @Component //将当前对象交给IOC容器管理,成为IOC容器的bean
- public class EmpServiceA implements EmpService {
-
- @Autowired //运行时,从IOC容器中获取该类型对象,赋值给该变量
- private EmpDao empDao ;
-
- @Override
- public List<Emp> listEmp() {
- //1. 调用dao, 获取数据
- List<Emp> empList = empDao.listEmp();
-
- //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("就业指导");
- }
- });
- return empList;
- }
- }
-
- //Dao层:
-
- @Component //将当前对象交给IOC容器管理,成为IOC容器的bean
- public class EmpDaoA implements EmpDao {
- @Override
- public List<Emp> listEmp() {
- //1. 加载并解析emp.xml
- String file = this.getClass().getClassLoader().getResource("emp.xml").getFile();
- System.out.println(file);
- List<Emp> empList = XmlParserUtils.parse(file, Emp.class);
- return empList;
- }
- }
IOC容器创建的对象称为bean对象。
在之前的入门案例中,要把某个对象交给IOC容器管理,需要在类上添加一个注解:@Component
@Component的衍生注解:
@Controller (标注在控制层类上)
@Service (标注在业务层类上)
@Repository (标注在数据访问层类上)
要把某个对象交给IOC容器管理,需要在对应的类上加上如下注解之一:
注解 | 说明 | 位置 |
---|---|---|
@Controller | @Component的衍生注解 | 标注在控制器类上 |
@Service | @Component的衍生注解 | 标注在业务类上 |
@Repository | @Component的衍生注解 | 标注在数据访问类上(由于与mybatis整合,用的少) |
@Component | 声明bean的基础注解 | 不属于以上三类时,用此注解 |
在IOC容器中,每一个Bean都有一个属于自己的名字,可以通过注解的value属性指定bean的名字。如果没有指定,默认为类名首字母小写。
注意事项:
声明bean的时候,可以通过value属性指定bean的名字,如果没有指定,默认为类名首字母小写。
使用以上四个注解都可以声明bean,但是在springboot集成web开发中,声明控制器bean只能用@Controller。
bean想要生效,还需要被组件@ComponentScan扫描
@ComponentScan注解虽然没有显式配置,但是实际上已经包含在了引导类声明注解 @SpringBootApplication 中,默认扫描的范围是SpringBoot启动类所在包及其子包。
bean不生效解决方案:
手动添加@ComponentScan注解,指定要扫描的包 (了解)
将我们定义的controller,service,dao这些包呢,都放在引导类所在包的子包下
依赖注入,是指IOC容器要为应用程序去提供运行时所依赖的资源,而资源指的就是对象。
@Autowired注解,默认是按照类型进行自动装配的(去IOC容器中找某个类型的对象,然后完成注入操作)
如果在IOC容器中,存在多个相同类型的bean对象,程序运行会报错
解决方案:
@Primary
@Qualifier
@Resource
面试题
@Autowird 与 @Resource的区别
@Autowired 是spring框架提供的注解,而@Resource是JDK提供的注解
@Autowired 默认是按照类型注入,而@Resource是按照名称注入
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。