关于SpringBootWeb的入门学习

06-01 1224阅读

一、关于Spring

        Spring的官网(https://spring.io)

        Spring提供了很多开源项目,通过Spring Boot可以帮我们快速的构建应用程序。

        Spring主要是为我们简化配置,快速开发。

二、Spring入门项目

        需求:基于SpringBoot的方式开发一个web应用,浏览器发起请求/hello后,给浏览器返回字符串 “Hello Spring  xxxx”

关于SpringBootWeb的入门学习

        在左侧选择Spring Boot生成源,右侧会为我们自动生成相应配置,我们需要注意的是:

        1.IDEA自动为我们配置了Spring的服务器地址(若是网络不好,可用阿里云提供的脚手架:https://start.aliyun.com)

        2.开发语言我们选择Java

        3.构建工具要选择Maven(项目中要导入许多相关jar包作为依赖)

        4.JDK版本必须是17及以上

        5.打包方式设置为Jar即可

关于SpringBootWeb的入门学习

        我们是面向网页做项目,所以最基础的依赖要勾选Spring Web

        Spring Boot版本号要选择正式版

        关于SpringBootWeb的入门学习

        在com.itheima包下创建一个HelloSpring类

关于SpringBootWeb的入门学习

        运行自动创建的引导类(标识有@SpringBootApplication注解的类)

        打开浏览器,输入 http://localhost:8080/hello?name=张三

关于SpringBootWeb的入门学习

三、SpringBootWeb案例1

        需求:基于SpringBoot开发web程序,完成用户列表的渲染展示

        准备工作:将user.txt文档以及网页前端内容都放在resources目录下 

        关于SpringBootWeb的入门学习

        在pom.xml文件中导入lombok依赖(初始化项目时不能勾选,否则会出现编译异常)

	org.projectlombok
	lombok
	true

        首先在新建的项目包下新建一个pojo包,专门存放实体类,在pojo下新建一个User类,类中属性要与user.txt中属性名相同,避免后期对接属性产生不必要的麻烦。

package org.itheima.pojo;
import java.time.LocalDateTime;
/**
 * @Author: Jaymr
 * @Date: 2025/5/2 12:54
 * @Version: v1.0.0
 * @Description: 封装用户信息
 **/
@Data//getter setter等方法
@AllArgsConstructor//有参构造
@NoArgsConstructor//无参构造
public class User {
    private Integer id;
    private String username;
    private String password;
    private String name;
    private Integer age;
    private LocalDateTime updateTime;
}

        由于在案例中,需要读取文本中的数据,并且还需要将对象转为json格式,所以这里呢,我们在项目中再引入一个非常常用的工具包hutool。 然后调用里面的工具类,就可以非常方便快捷的完成业务操作。

    cn.hutool
    hutool-all
    5.8.27

        在项目包下创建一个子包controller,在其中创建一个UserController类

package org.itheima.controller;
import cn.hutool.core.io.IoUtil;
import org.itheima.pojo.User;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.io.FileInputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
 * @Author: Jaymr
 * @Date: 2025/5/2 13:08
 * @Version: v1.0.0
 * @Description: 完成用户列表的渲染展示
 **/
@RestController//标识为请求处理类
public class UserController {
    @RequestMapping("/list")
    public List list(){
        //1.加载并读取数据 获取用户信息
        //IO流获取文件信息
        // InputStream in = new FileInputStream(new File("user.txt"));
        InputStream in = this.getClass().getClassLoader().getResourceAsStream("user.txt");
        //定义一个User类型的ArrayList存放用户数据
        ArrayList lines = IoUtil.readLines(in, StandardCharsets.UTF_8, new ArrayList());
        //2.解析数据,封装成对象 ->集合
        //定义一个User属性的List集合 userList
        //stream() 将lines对集合转化为流对象
        //map() 方法将lines集合中的每个元素应用一个函数,然后将结果映射为User对象
        List userList = lines.stream().map(line -> {
            String[] parts = line.split(",");//按照,拆分字符串 拆分的字符串存放到part数组内
            Integer id = Integer.parseInt(parts[0]);//Integer.parseInt() 方法将字符串转换为整数
            String username = parts[1];
            String password = parts[2];
            String name = parts[3];
            Integer age = Integer.parseInt(parts[4]);
            LocalDateTime updateTime = LocalDateTime.parse(parts[5], DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
            return new User(id, username, password, name, age, updateTime);
        }).collect(Collectors.toList());
        // collect() 将流处理后的结果收集起来
        // Collectors.toList() 告诉程序把所有处理后的元素放进userList中
        //3.响应数据
        /**
         * 底层会直接将list集合转换为json格式(@RestController注解底层用到@ResponseBody注解
         * 将Controller返回值直接作为响应体的数据直接响应;
         * 返回值是对象/集合,则将其转为json再作为响应数据返回)
         */
        return userList;
    }
}

        启动服务测试,在浏览器中访问:http://localhost:8080/user.html

关于SpringBootWeb的入门学习

四、分层解耦

        为了解决后期代码维护时的便捷性,以及提高代码的可复用性,增强代码的可读性,我们可以把一个项目分为三层架构(单一职责原则)。

        以以上项目为例,大体可分为三个部分:

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

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

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

            所以我们对上述案例以三层架构的模式进行优化:

            在项目包下创建三个包:dao,service,controller

            dao包下新建一个接口UserDao以规范Dao层代码

    package org.itheima.dao;
    import org.itheima.pojo.User;
    import java.util.List;
    /**
     * @Author: Jaymr
     * @Date: 2025/5/2 13:56
     * @Version: v1.0.0
     **/
    public interface UserDao {
        public List findAll();
    }
    

            在dao包下新建一个包Impl,里面存放实现类,在Impl包里新建UserDaoImpl类实现UserDao接口:

    package org.itheima.dao.Impl;
    import cn.hutool.core.io.IoUtil;
    import org.itheima.dao.UserDao;
    import java.io.InputStream;
    import java.nio.charset.StandardCharsets;
    import java.util.ArrayList;
    import java.util.List;
    /**
     * @Author: Jaymr
     * @Date: 2025/5/2 13:59
     * @Version: v1.0.0
     * @Description: TODO
     **/
    public class UserDaoImpl implements UserDao {
        @Override
        public List findAll() {
            //1.加载并读取数据 获取用户信息
            //IO流获取文件信息
            // InputStream in = new FileInputStream(new File("user.txt"));
            InputStream in = this.getClass().getClassLoader().getResourceAsStream("user.txt");
            //定义一个User类型的ArrayList存放用户数据
            ArrayList lines = IoUtil.readLines(in, StandardCharsets.UTF_8, new ArrayList());
            return lines;//ArrayList 是 List 接口的一个实现类 多态
        }
    }

            在service包下进行同样的操作:

            创建UserService接口:

    package org.itheima.service;
    import org.itheima.pojo.User;
    import java.util.List;
    /**
     * @Author: Jaymr
     * @Date: 2025/5/2 14:06
     * @Version: v1.0.0
     **/
    public interface UserService {
        public List findall();
    }
    

             创建在service包下新建一个包Impl,里面存放实现类,在Impl包里新建UserServiceImpl类实现UserService接口:

    package org.itheima.service.Impl;
    import org.itheima.dao.Impl.UserDaoImpl;
    import org.itheima.dao.UserDao;
    import org.itheima.pojo.User;
    import org.itheima.service.UserService;
    import java.time.LocalDateTime;
    import java.time.format.DateTimeFormatter;
    import java.util.List;
    import java.util.stream.Collectors;
    /**
     * @Author: Jaymr
     * @Date: 2025/5/2 14:08
     * @Version: v1.0.0
     * @Description: TODO
     **/
    public class UserServiceImpl implements UserService {
        //创建userDao对象
        private UserDao userDao = new UserDaoImpl();
        @Override
        public List findall() {
            //1.调用Dao获取数据
            List lines = userDao.findAll();
            //2.解析数据,封装成对象 ->集合
            //定义一个User属性的List集合 userList
            //stream() 将lines对集合转化为流对象
            //map() 方法将lines集合中的每个元素应用一个函数,然后将结果映射为User对象
            List userList = lines.stream().map(line -> {
                String[] parts = line.split(",");//按照,拆分字符串 拆分的字符串存放到part数组内
                Integer id = Integer.parseInt(parts[0]);//Integer.parseInt() 方法将字符串转换为整数
                String username = parts[1];
                String password = parts[2];
                String name = parts[3];
                Integer age = Integer.parseInt(parts[4]);
                LocalDateTime updateTime = LocalDateTime.parse(parts[5], DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
                return new User(id, username, password, name, age, updateTime);
            }).collect(Collectors.toList());
            // collect() 将流处理后的结果收集起来
            // Collectors.toList() 告诉程序把所有处理后的元素放进userList中
            return userList;
        }
    }
    

            controller包中,仅需一个UserController类,因为直接与服务器对接,无需创建接口。

    package org.itheima.controller;
    import cn.hutool.core.io.IoUtil;
    import org.itheima.pojo.User;
    import org.itheima.service.Impl.UserServiceImpl;
    import org.itheima.service.UserService;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    import java.io.FileInputStream;
    import java.io.InputStream;
    import java.nio.charset.StandardCharsets;
    import java.time.LocalDateTime;
    import java.time.format.DateTimeFormatter;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.stream.Collectors;
    /**
     * @Author: Jaymr
     * @Date: 2025/5/2 13:08
     * @Version: v1.0.0
     * @Description: TODO
     **/
    @RestController//标识为请求处理类
    public class UserController {
        private UserService userService = new UserServiceImpl();
        @RequestMapping("/list")
        public List list(){
            List userList = userService.findall();
            //3.响应数据
            /**
             * 底层会直接将list集合转换为json格式(@RestController注解底层用到@ResponseBody注解
             * 将Controller返回值直接作为响应体的数据直接响应;
             * 返回值是对象/集合,则将其转为json再作为响应数据返回)
             */
            return userList;
        }
    }
    

           启动服务测试,在浏览器中访问:http://localhost:8080/user.html,结果与原案例相同。

            进一步解耦

            我们注意到,每次需要一个对象,我们都要new一个相应的对象。如果我们日后要更换实现类,就需要修改调用原实现类类中的代码。(如:我们现在弃用了UserDaoImpl,使用UserDaoImpl2,那么就需要在UserServiceImpl中修改new的对象)。

            解耦思路:

            1.首先就不能new一个具体的对象。但是不new对象,就意味着没有上一层对象。

            那么我们可以提供一个容器,容器中存储相应对象(如:UserDaoImpl,UserDaoImpl2)

            然后UserServiceImpl直接从容器中获取对应对象

            这里,Spring提供了两个核心概念:(专有名词和解释不看也罢,看我的解释)

    • 控制反转: Inversion Of Control,简称IOC。对象的创建控制权由程序自身转移到外部(容器),这种思想称为控制反转。

      • 对象的创建权由程序员主动创建转移到容器(由容器创建、管理对象)。这个容器称为:IOC容器或Spring容器。

      • 依赖注入: Dependency Injection,简称DI。容器为应用程序提供运行时,所依赖的资源,称之为依赖注入。

        • 程序运行时需要某个资源,此时容器就为其提供这个资源。

        • 例:EmpController程序运行时需要EmpService对象,Spring容器就为其提供并注入EmpService对象。

        • bean对象:IOC容器中创建、管理的对象,称之为:bean对象。

                  简单来说,就是你想喝水,但是你杯子里没有水,你只想用自己的杯子喝水。那就需要别人把水倒在你的杯子里,你去用自己的杯子喝水,你并不关心水是哪来的,只要有水喝就就行。

                  用自己的杯子喝水 → 控制反转(IOC)

                  别人把水倒进杯子 → 依赖注入( DI )

                  我自己的杯子就是DI,那个容器就是IOC

          IOC/DI的核心是——通过反转控制权,将依赖关系的管理从组件内部转移到外部容器​

                  那我们怎么实现呢?

                  IOC/DI实现

                  1.将Service以及Dao层的实现类,交给IOC容器管理

                  在实现类上加@Component,就代表把当前类的对象交给IOC

                  如:UserDaoImpl类

          @Component
          public class UserDaoImpl implements UserDao {
              @Override
              public List findAll() {
                  InputStream in = this.getClass().getClassLoader().getResourceAsStream("user.txt");
                  ArrayList lines = IoUtil.readLines(in, StandardCharsets.UTF_8, new ArrayList());
                  return lines;
              }
          }

                  2.为Controller 及 Service注入运行时所依赖的对象

                  使用@Autowired注解

                  如:UserServiceImpl类

                  

          @Component
          public class UserServiceImpl implements UserService {
              @Autowired
              private UserDao userDao;
              
              @Override
              public List findAll() {
                  List lines = userDao.findAll();
                  List userList = lines.stream().map(line -> {
                      String[] parts = line.split(",");
                      Integer id = Integer.parseInt(parts[0]);
                      String username = parts[1];
                      String password = parts[2];
                      String name = parts[3];
                      Integer age = Integer.parseInt(parts[4]);
                      LocalDateTime updateTime = LocalDateTime.parse(parts[5], DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
                      return new User(id, username, password, name, age, updateTime);
                  }).collect(Collectors.toList());
                  return userList;
              }
          }

                  启动测试服务,在服务器中访问:http://localhost:8080/user.html,效果同上。

          五、IOC详解

                  之前把对象交给IOC容器时,我们用到@Component注解,但具体是哪一层我们看不出,所以Spring给@Component加了几个衍生注解:

          注解

          说明

          位置

          @Component

          声明bean的基础注解

          不属于以下三类时,用此注解

          @Controller

          @Component的衍生注解

          标注在控制层类上

          @Service

          @Component的衍生注解

          标注在业务层类上

          @Repository

          @Component的衍生注解

          标注在数据访问层类上(由于与mybatis整合,用的少)

          六、DI详解  

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

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

                  1.构造器注入(推荐):通过在类的构造方法上使用@Autowired注解,Spring会自动将匹配的Bean注入到构造器的参数中,从而实现Bean的自动装配。

          @Component
          public class UserService {
              private UserDao userDao;
              @Autowired
              public UserService(UserDao userDao) {
                  this.userDao = userDao;
              }
          }
          

                  2.属性注入(代码简洁,但原理是利用暴力反射,破坏代码的封装性):通过在类的属性上使用@Autowired注解,Spring会自动将匹配的Bean注入到属性中,从而实现Bean的自动装配。

          @Component
          public class UserService {
              @Autowired
              private UserDao userDao;
          }
          

                  3.方法注入:通过在类的方法上使用@Autowired注解,Spring会自动将匹配的Bean注入到方法的参数中,从而实现Bean的自动装配。

          @Component
          public class UserService {
              private UserDao userDao;
              @Autowired
              public void setUserDao(UserDao userDao) {
                  this.userDao = userDao;
              }
          }
          

                  注意事项: 

                  如果同时准备了两个UserService的实现类并且都交给了IOC容器管理,通常情况下会导致Spring无法确定要注入哪个实现类,从而出现冲突。

                  为了解决这个问题,可以使用@Primary、@Qualifier和@Resource三种注解来指定要注入的实现类。

                  1.Primary注解:可以标注在一个实现类上,告诉Spring优先选择注入这个实现类

          @Component
          @Primary
          public class UserServiceImpl1 implements UserService {
              // 实现类1的代码
          }
          @Component
          public class UserServiceImpl2 implements UserService {
              // 实现类2的代码
          }
          

                  2.Qualifier注解:可以和@Autowired注解一起使用,指定具体要注入的实现类

          @Autowired
          @Qualifier("serviceImpl1")
          private UserService userService;
          
          @Component("serviceImpl1")
          public class UserServiceImpl1 implements UserService {
              // 实现类1的代码
          }
          @Component("serviceImpl2")
          public class UserServiceImpl2 implements UserService {
              // 实现类2的代码
          }
          

                  3.@Resource注解:可以通过指定的name属性来指定要注入的实现类

            @Resource(name = "serviceImpl1")
            private UserService userService;
            
            @Component("serviceImpl1")
            public class UserServiceImpl1 implements UserService {
                // 实现类1的代码
            }
            @Component("serviceImpl2")
            public class UserServiceImpl2 implements UserService {
                // 实现类2的代码
            }
            

            通过以上三种方式,可以解决同一接口有多个实现类时的冲突问题,并指定Spring注入的实现类。

    免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们。

    相关阅读

    目录[+]

    取消
    微信二维码
    微信二维码
    支付宝二维码