SpringBootWeb

介绍

Spring Boot可以帮助我们非常快速的构建应用程序、简化开发、提高效率。


SpringBootWeb快速入门

idea创建SpringBoot项目

(需要联网)

定义请求处理类

package com.study.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Auther lmy
 * @date 2024-04-05 16:08
 * @Description This is description of code
 */
@RestController
public class HelloController {
    @RequestMapping("/hello")
    public String hello() {
        System.out.println("Hello world~");
        return "Hello world~";
    }
}

运行测试


Web分析

浏览器:

  • 输入网址:http://192.168.100.11:8080/hello

    • 通过IP地址192.168.100.11定位到网络上的一台计算机

      我们之前在浏览器中输入的localhost,就是127.0.0.1(本机)

    • 通过端口号8080找到计算机上运行的程序

      localhost:8080 , 意思是在本地计算机中找到正在运行的8080端口的程序

    • /hello是请求资源位置

      • 资源:对计算机而言资源就是数据

        • web资源:通过网络可以访问到的资源(通常是指存放在服务器上的数据)

      localhost:8080/hello ,意思是向本地计算机中的8080端口程序,获取资源位置是/hello的数据

      • 8080端口程序,在服务器找/hello位置的资源数据,发给浏览器

服务器:(可以理解为ServerSocket)

  • 接收到浏览器发送的信息(如:/hello)

  • 在服务器上找到/hello的资源

  • 把资源发送给浏览器

我们在JavaSE阶段学习网络编程时,有讲过网络三要素:

  • IP :网络中计算机的唯一标识

  • 端口 :计算机中运行程序的唯一标识

  • 协议 :网络中计算机之间交互的规则

问题:浏览器和服务器两端进行数据交互,使用什么协议?

答案:http协议


HTTP协议

概述

HTTP协议是无状态协议,比如:账户登录后才能访问某个页面信息数据,但HTTP协议是无状态协议,在执行登录后请求响应已经结束,第二次访问信息数据时,又是另一次请求,两次请求响应都是独立的,在第二次请求时无法记录第一次请求的信息是否登录。后续学习到“web会话”能解决此问题。

请求协议

响应协议

常见的响应状态码

一般记住三个

200      客户端请求成功。
404      请求资源不存在,般是URL输入有误,或者网站资源被删除了。
500      服务器发生不可预期的错误。

响应头

协议解析

web服务器


Web服务器 - Tomcat

简介

Tomcat基本使用

(Springboot已经内置Tomcat)

安装+卸载+启动+停止

常见问题

配置Tomcat端口号

Tomcat项目部署


Springboot - 入门程序解析


请求响应

概述

请求 

后端开发过程中,每开发完一个功能就需要对这个功能接口进行测试由于前后端分离开发,所以我们是没有前端页面的,这个时候我们该怎么测试?在入门程序阶段,直接打开浏览器,然后在浏览器的地址栏直接输入一个地址,这样就访问到开发的web应用,但是浏览器地址栏所发起的请求全部都是get请求,如果要测试post方式的请求,需要自己去写前端代码,然后再进行后端的功能接口测试,那这是比较繁琐的,因此我们就可以借助一款功能强大的接口测试工具postman,通过postman就可以轻松地解决各种接口测试的需求。

接口测试工具---postman

                           

postman下载:https://app.getpostman.com/app/download/win64

Apifox下载:https://apifox.com/     纯中文,推荐

接口测试工具---Apifox

1.创建项目

2.创建接口

                                         

运行springboot项目启动类---(以上案例)

请求方式   get       

请求路径   http://localhost:8080/hello

点击发送

简单参数

 代码:

Apifox接口工具测试

springboot方式

get请求

post请求

以上方式如果前端参数名和形参变量名不一致,不会报错,但接受不到参数的value,会显示null

如:

如果方法形参名称与请求参数名称不匹配,可以使用@RequestParam 完成映射。

以下两种方式均可,name和value接收请求参数名都行,可直接写参数值,不加name和value

@RequestParam中的required属性默认为true,代表该请求参数必须传递,如果不传递将报错,如下,不传递报400错误,代表客户端错误(未传递参数)。如果该参数是可选的,可以将required属性设置为false。

控制台报错:

Resolved [org.springframework.web.bind.MissingServletRequestParameterException: Required request parameter 'username' for method parameter type String is not present]

required属性设置为false


简单实体参数

定义User实体类:

package com.study.pojo;

/**
 * @Auther lmy
 * @date 2024-04-07 23:08
 * @Description This is description of code
 */
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 +
                '}';
    }
}

在RequestController类中定义simplePojo(User user)方法

package com.study.controller;

import com.study.pojo.User;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Auther lmy
 * @date 2024-04-07 23:10
 * @Description This is description of code
 */
@RestController
public class RequestController {

    @RequestMapping("/simplePojo")
    public String simplePojo(User user) {
        System.out.println(user);
        return user.getName() + " " + user.getAge();
    }
}

请求中传递name、age两个参数


复杂实体参数

定义User1实体类:三个属性:姓名、年龄、Address类型的地址

package com.study.pojo;

/**
 * @Auther lmy
 * @date 2024-04-07 23:28
 * @Description This is description of code
 */
public class User1 {
    private String name;
    private Integer age;
    private Address address;

    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 Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "User1{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", address=" + address +
                '}';
    }
}

定义Address实体类:两个属性:

private String province; //省
private String city; //市

package com.study.pojo;

/**
 * @Auther lmy
 * @date 2024-04-07 23:32
 * @Description This is description of code
 */
public class Address {
    private String province;    //省
    private String city;    //市

    public String getProvince() {
        return province;
    }

    public void setProvince(String province) {
        this.province = province;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    @Override
    public String toString() {
        return "Address{" +
                "province='" + province + '\'' +
                ", city='" + city + '\'' +
                '}';
    }
}

在RequestController类中定义complexPojo(User1 user)方法

请求测试:

控制台输出:


数组集合参数

数组参数


集合参数

集合参数相对集合参数。多一个@RequestParam绑定参数关系

默认情况下,请求中参数名相同的多个值,是封装到数组。如果要封装到集合,要使用@RequestParam绑定参数关系


日期参数

因为日期的格式多种多样(如:2022-12-12 10:05:45 、2022/12/12 10:05:45),那么对于日期类型的参数在进行封装的时候,需要通过@DateTimeFormat注解,以及其pattern属性来设置日期的格式。

  • @DateTimeFormat注解的pattern属性中指定了哪种日期格式,前端的日期参数就必须按照指定的格式传递。

  • 后端controller方法中,需要使用Date类型或LocalDateTime类型,来封装传递的参数。

Controller方法:


Json参数

json格式数据需要放在请求体中

服务端Controller方法接收JSON格式数据:

  • 传递json格式的参数,在Controller中会使用实体类进行封装。

  • 封装规则:JSON数据键名与形参对象属性名相同,定义POJO类型形参即可接收参数。需要使用 @RequestBody标识。

  • @RequestBody注解:将JSON数据映射到形参的实体类对象中(JSON中的key和实体类中的属性名保持一致)


路径参数

在现在的开发中,经常还会直接在请求的URL中传递参数。例如:

http://localhost:8080/user/1        
http://localhost:880/user/1/0

上述的这种传递请求参数的形式呢,我们称之为:路径参数。

学习路径参数呢,主要掌握在后端的controller方法中,如何接收路径参数。

路径参数:

  • 前端:通过请求URL直接传递参数

  • 后端:使用{…}来标识该路径参数,需要使用@PathVariable获取路径参数

单个路径参数

多个路径参数


响应

@ResponseBody

package com.study.controller;

import com.study.pojo.Address;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.List;

/**
 * @Auther lmy
 * @date 2024-04-08 2:39
 * @Description 测试响应数据
 */
@RestController
public class ResponseController {
    @RequestMapping("/hello1")
    public String hello() {
        System.out.println("Hello World ~");
        return "Hello World ~";
    }

    @RequestMapping("/getArr")
    public Address getAddr() {
        Address address = new Address();
        address.setProvince("陕西");
        address.setCity("西安");
        System.out.println(address);
        return address;
    }

    @RequestMapping("/listAddr")
    public List<Address> listAddr() {
        List<Address> list = new ArrayList<>();
        Address addr1 = new Address();
        addr1.setProvince("陕西");
        addr1.setCity("西安");

        Address addr2 = new Address();
        addr2.setProvince("山西");
        addr2.setCity("运城");

        list.add(addr1);
        list.add(addr2);

        System.out.println(list);
        return list;
    }
}


统一响应结果

在前后端分离开发中,用上面形式返回字符串、json、json格式数组,前端接受到的响应数据很随意,无任何规范。如果没有一套统一的项目规范,前端人员发起请求,访问到后端所开发的接口,响应回去的数据各式各样,前端需要拿到结果进行解析,前后端开发成本就会增加,最终我们会发项目,不便管理,而且很难维护。所以,在项目开发中,一般都会给所有的功能接口设置一个统一的响应结果,而既然是一个统一的响应结果,就需要做到通用性,基本上要能够满足所有的业务场景,而这个统一的响应结果,可以考虑使用一个实体对象result来进行接收

编写一个Result类

package com.study.pojo;

/**
 * 统一响应结果封装类
 */
public class Result {
    private Integer code;//1 成功 , 0 失败
    private String msg; //提示信息
    private Object data; //数据 data

    public Result() {
    }

    public Result(Integer code, String msg, Object data) {
        this.code = code;
        this.msg = msg;
        this.data = data;
    }

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }

    public static Result success(Object data) {
        return new Result(1, "success", data);
    }

    public static Result success() {
        return new Result(1, "success", null);
    }

    public static Result error(String msg) {
        return new Result(0, msg, null);
    }

    @Override
    public String toString() {
        return "Result{" +
                "code=" + code +
                ", msg='" + msg + '\'' +
                ", data=" + data +
                '}';
    }
}

测试


案例

步骤

1.在pom.xml文件中引入dom4j的依赖,用于解析XML文件

  <!--dom4j依赖,解析xml-->
        <!-- https://mvnrepository.com/artifact/org.dom4j/dom4j -->
        <dependency>
            <groupId>org.dom4j</groupId>
            <artifactId>dom4j</artifactId>
            <version>2.1.3</version>
        </dependency>

2.引入资料中提供的解析XML的工具类XMLParserUtils、对应的实体类EmpXML文件 emp.xml

(1)解析XML的工具类XMLParserUtils

package com.study.utils;

import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.File;
import java.lang.reflect.Constructor;
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;
    }

}

(2)实体类Emp

package com.study.pojo;

public class Emp {
    private String name;
    private Integer age;
    private String image;
    private String gender;
    private String job;

    public Emp() {
    }

    public Emp(String name, Integer age, String image, String gender, String job) {
        this.name = name;
        this.age = age;
        this.image = image;
        this.gender = gender;
        this.job = job;
    }

    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 String getImage() {
        return image;
    }

    public void setImage(String image) {
        this.image = image;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public String getJob() {
        return job;
    }

    public void setJob(String job) {
        this.job = job;
    }

    @Override
    public String toString() {
        return "Emp{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", image='" + image + '\'' +
                ", gender='" + gender + '\'' +
                ", job='" + job + '\'' +
                '}';
    }
}

(3)XML文件 emp.xml(放在resources配置下)

<?xml version="1.0" encoding="UTF-8" ?>
<emps>
    <emp>
        <name>金毛狮王</name>
        <age>55</age>
        <image>https://web-framework.oss-cn-hangzhou.aliyuncs.com/web/1.jpg</image>
        <!-- 1: 男, 2: 女 -->
        <gender>1</gender>
        <!-- 1: 讲师, 2: 班主任 , 3: 就业指导 -->
        <job>1</job>
    </emp>

    <emp>
        <name>白眉鹰王</name>
        <age>65</age>
        <image>https://web-framework.oss-cn-hangzhou.aliyuncs.com/web/2.jpg</image>
        <gender>1</gender>
        <job>1</job>
    </emp>

    <emp>
        <name>青翼蝠王</name>
        <age>45</age>
        <image>https://web-framework.oss-cn-hangzhou.aliyuncs.com/web/3.jpg</image>
        <gender>1</gender>
        <job>2</job>
    </emp>

    <emp>
        <name>紫衫龙王</name>
        <age>38</age>
        <image>https://web-framework.oss-cn-hangzhou.aliyuncs.com/web/4.jpg</image>
        <gender>2</gender>
        <job>3</job>
    </emp>
</emps>

3.引入资料中提供的静态页面文件,放在resources下的static目录下

(文件在附件中)

Springboot项目的静态资源(htmlcssjs等前端资源)默认存放目录为:classpath:/static classpath:/public classpath:/resources

(classpath:类路径,对于maven来说resources就是类路径)

                                    

4.编写EmpController类,处理请求,响应数据

package com.study.controller;

import com.study.pojo.Emp;
import com.study.pojo.Result;
import com.study.utils.XmlParserUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
 * @Auther lmy
 * @date 2024-04-11 1:14
 * @Description 加载员工数据
 */
@RestController
public class EmpController {
    @RequestMapping("/listEmp")    //与前端页面emp.html中的请求路径一致
    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);
        System.out.println(empList);
        //2.对数据进行转换处理  -gender,job
        for (Emp emp : empList) {
            //处理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("就业指导");
            }

        }
        System.out.println(empList);


        // 3.响应数据
        return Result.success(empList);
    }
}

Apifox测试接口

访问前端页面


分析

上述案例的功能,我们虽然已经实现,但是呢,我们会发现案例中:解析XML数据,获取数据的代码,处理数据的逻辑的代码,给页面响应的代码全部都堆积在一起了,全部都写在controller方法中了。

当前程序的这个业务逻辑还是比较简单的,如果业务逻辑再稍微复杂一点,我们会看到Controller方法的代码量就很大了。

  • 当我们要修改操作数据部分的代码,需要改动Controller

  • 当我们要完善逻辑处理部分的代码,需要改动Controller

  • 当我们需要修改数据响应的代码,还是需要改动Controller

这样呢,就会造成我们整个工程代码的复用性比较差,而且代码难以维护。 那如何解决这个问题呢?其实在现在的开发中,有非常成熟的解决思路,那就是分层开发。


分层解耦

三层架构

在我们进行程序设计以及程序开发时,尽可能让每一个接口、类、方法的职责更单一些(单一职责原则)。

 单一职责原则:一个类或一个方法,就只做一件事情,只管一块功能。
 这样就可以让类、接口、方法的复杂度更低,可读性更强,扩展性更好,也更利用后期的维护。

那其实我们上述案例的处理逻辑呢,从组成上看可以分为三个部分:

  • 数据访问:负责业务数据的维护操作,包括增、删、改、查等操作。

  • 逻辑处理:负责业务逻辑处理的代码。

  • 请求处理、响应数据:负责,接收页面的请求,给页面响应数据。

按照上述的三个组成部分,在我们项目开发中呢,可以将代码分为三层:

代码实现:

数据访问层:负责数据的访问操作,包含数据的增、删、改、查

  • 数据访问接口

package com.study.dao;

import com.study.pojo.Emp;

import java.util.List;

/**
 * @Auther lmy
 * @date 2024-04-11 2:08
 * @Description This is description of code
 */
public interface EmpDao {
    //访问员工列表数据
    public List<Emp> listEmp();
}
  • 数据访问实现类

package com.study.dao.impl;

import com.study.dao.EmpDao;
import com.study.pojo.Emp;
import com.study.utils.XmlParserUtils;

import java.util.List;

/**
 * @Auther lmy
 * @date 2024-04-11 2:09
 * @Description This is description of code
 */
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);
        System.out.println(empList);
        return empList;
    }
}

业务逻辑层:处理具体的业务逻辑

  • 业务接口

package com.study.service;

import com.study.pojo.Emp;

import java.util.List;

/**
 * @Auther lmy
 * @date 2024-04-11 2:12
 * @Description This is description of code
 */
public interface EmpService {
    //获取员工列表
    public List<Emp> listEmp();
}
  • 业务实现类

package com.study.service.impl;

import com.study.dao.EmpDao;
import com.study.dao.impl.EmpDaoA;
import com.study.pojo.Emp;
import com.study.service.EmpService;

import java.util.List;

/**
 * @Auther lmy
 * @date 2024-04-11 2:14
 * @Description This is description of code
 */
public class EmpServiceA implements EmpService {

    private EmpDao empDao = new EmpDaoA();

    @Override
    public List<Emp> listEmp() {
        //1.调用dao,获取数据
        List<Emp> empList = empDao.listEmp();

        //2.对数据进行转换处理  -gender,job
        for (Emp emp : empList) {
            //处理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("就业指导");
            }

        }
        System.out.println(empList);
        return empList;
    }
}

控制层:接收前端发送的请求,对请求进行处理,并响应数据

package com.study.controller;

import com.study.pojo.Emp;
import com.study.pojo.Result;
import com.study.service.EmpService;
import com.study.service.impl.EmpServiceA;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
 * @Auther lmy
 * @date 2024-04-11 2:24
 * @Description This is description of code
 */
@RestController
public class EmpControllerA {
    private EmpService empService = new EmpServiceA();

    @RequestMapping("listEmp")
    public Result list() {
        //调用service,获取数据
        List<Emp> empList = empService.listEmp();
        // 2.响应数据
        return Result.success(empList);
    }
}

效果和案例效果一致


分层解耦

耦合问题

首先需要了解软件开发涉及到的两个概念:内聚和耦合。

  • 内聚:软件中各个功能模块内部的功能联系。

  • 耦合:衡量软件中各个层/模块之间的依赖、关联的程度。

软件设计原则:高内聚低耦合。

高内聚指的是:一个模块中各个元素之间的联系的紧密程度,如果各个元素(语句、程序段)之间的联系程度越高,则内聚性越高,即 "高内聚"。

低耦合指的是:软件中各个层、模块之间的依赖关联程序越低越好。

程序中高内聚的体现:

  • EmpServiceA类中只编写了和员工相关的逻辑处理代码

程序中耦合代码的体现:

  • 把业务类变为EmpServiceB时,需要修改controller层中的代码

高内聚、低耦合的目的是使程序模块的可重用性、移植性大大增强。


解耦思路

之前我们在编写代码时,需要什么对象,就直接new一个就可以了。 这种做法呢,层与层之间代码就耦合了,当service层的实现变了之后, 我们还需要修改controller层的代码。

那应该怎么解耦呢?

  • 首先不能在EmpController中使用new对象。代码如下:

  • 此时,就存在另一个问题了,不能new,就意味着没有业务层对象(程序运行就报错),怎么办呢?

    • 我们的解决思路是:

      • 提供一个容器,容器中存储一些对象(例:EmpService对象)

      • controller程序从容器中获取EmpService类型的对象

我们想要实现上述解耦操作,就涉及到Spring中的两个核心概念:


IOC&DI

IOC&DI入门

任务:完成Controller层、Service层、Dao层的代码解耦

思路:

1. 删除Controller层、Service层中new对象的代码
2. Service层及Dao层的实现类,交给IOC容器管理
3. 为Controller及Service注入运行时依赖的对象
   - Controller程序中注入依赖的Service层对象
   - Service程序中注入依赖的Dao层对象

第1步:删除Controller层、Service层中new对象的代码

第2步:Service层及Dao层的实现类,交给IOC容器管理

  • 使用Spring提供的注解:@Component ,就可以实现类交给IOC容器管理

第3步:为Controller及Service注入运行时依赖的对象

  • 使用Spring提供的注解:@Autowired ,就可以实现程序运行时IOC容器自动注入需要的依赖对象

完整的三层代码:

Dao层:

package com.study.dao.impl;

import com.study.dao.EmpDao;
import com.study.pojo.Emp;
import com.study.utils.XmlParserUtils;
import org.springframework.stereotype.Component;

import java.util.List;

/**
 * @Auther lmy
 * @date 2024-04-11 2:09
 * @Description This is description of code
 */

@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);
        System.out.println(empList);
        return empList;
    }
}

Service层:

package com.study.service.impl;

import com.study.dao.EmpDao;
import com.study.pojo.Emp;
import com.study.service.EmpService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.List;

/**
 * @Auther lmy
 * @date 2024-04-11 2:14
 * @Description This is description of code
 */

@Component  //将当前类交给ioc容器管理,成为ioc容器中的bean
public class EmpServiceA implements EmpService {

   
    @Autowired  //运行时,ioc容器会提供该类型的bean对象,并赋值给该变量  ---依赖注入
    private EmpDao empDao;

    @Override
    public List<Emp> listEmp() {
        //1.调用dao,获取数据
        List<Emp> empList = empDao.listEmp();

        //2.对数据进行转换处理  -gender,job
        for (Emp emp : empList) {
            //处理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("就业指导");
            }

        }
        System.out.println(empList);
        return empList;
    }
}

Controller层:

package com.study.controller;

import com.study.pojo.Emp;
import com.study.pojo.Result;
import com.study.service.EmpService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
 * @Auther lmy
 * @date 2024-04-11 2:24
 * @Description This is description of code
 */
@RestController
public class EmpControllerA {

    @Autowired  //运行时,ioc容器会提供该类型的bean对象,并赋值给该变量  ---依赖注入
    private EmpService empService;

    @RequestMapping("listEmp")
    public Result list() {
        //调用service,获取数据
        List<Emp> empList = empService.listEmp();
        // 2.响应数据
        return Result.success(empList);
    }
}


IOC详解

通过IOC和DI的入门程序呢,我们已经基本了解了IOC和DI的基础操作。接下来呢,我们学习下IOC控制反转和DI依赖注入的细节。

 bean的声明

前面我们提到IOC控制反转,就是将对象的控制权交给Spring的IOC容器,由IOC容器创建及管理对象。IOC容器创建的对象称为bean对象。

在之前的入门案例中,要把某个对象交给IOC容器管理,需要在类上添加一个注解:@Component

而Spring框架为了更好的标识web应用程序开发当中,bean对象到底归属于哪一层,又提供了@Component的衍生注解:

  • @Controller (标注在控制层类上)

  • @Service (标注在业务层类上)

  • @Repository (标注在数据访问层类上)


组件扫描

问题:使用前面学习的四个注解声明的bean,一定会生效吗?

答案:不一定。(原因:bean想要生效,还需要被组件扫描)

下面我们通过修改项目工程的目录结构,来测试bean对象是否生效:

运行程序后报错:

为什么没有找到bean对象呢?

  • 使用四大注解声明的bean,要想生效,还需要被组件扫描注解@ComponentScan扫描

@ComponentScan注解虽然没有显式配置,但是实际上已经包含在了引导类声明注解 @SpringBootApplication 中,==默认扫描的范围是SpringBoot启动类所在包及其子包==。

  •  解决方案:手动添加@ComponentScan注解,指定要扫描的包 (==仅做了解,不推荐==)

推荐做法(如下图):

  • 将我们定义的controller,service,dao这些包呢,都放在引导类所在包com.study的子包下,这样我们定义的bean就会被自动的扫描到


DI详解

上一小节我们讲解了控制反转IOC的细节,接下来呢,我们学习依赖注解DI的细节。

依赖注入,是指IOC容器要为应用程序去提供运行时所依赖的资源,而资源指的就是对象。

在入门程序案例中,我们使用了@Autowired这个注解,完成了依赖注入的操作,而这个Autowired翻译过来叫:自动装配。

@Autowired注解,默认是按照类型进行自动装配的(去IOC容器中找某个类型的对象,然后完成注入操作)

入门程序举例:在EmpController运行的时候,就要到IOC容器当中去查找EmpService这个类型的对象,而我们的IOC容器中刚好有一个EmpService这个类型的对象,所以就找到了这个类型的对象完成注入操作。

那如果在IOC容器中,存在多个相同类型的bean对象,会出现什么情况呢?

  • 程序运行会报错

如何解决上述问题呢?Spring提供了以下几种解决方案:

  • @Primary

  • @Qualifier

  • @Resource

使用@Primary注解:当存在多个相同类型的Bean注入时,加上@Primary注解,来确定默认的实现。

使用@Qualifier注解:指定当前要注入的bean对象。 在@Qualifier的value属性中,指定注入的bean的名称。

  • @Qualifier注解不能单独使用,必须配合@Autowired使用

使用@Resource注解:是按照bean的名称进行注入。通过name属性指定要注入的bean的名称。

面试题 : @Autowird 与 @Resource的区别

  • @Autowired 是spring框架提供的注解,而@Resource是JDK提供的注解

  • @Autowired 默认是按照类型注入,而@Resource是按照名称注入

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值