Java教程

Spring-day01

本文主要是介绍Spring-day01,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

1. Spring Boot 简介

在这里插入图片描述

1.1 Spring 是什么?

Spring makes programming Java quicker, easier, and safer for everybody. Spring’s focus on speed, simplicity, and productivity has made it the world’s most popular Java framework.

Spring 包括了用来简化 java 开发的多种框架,用于不同的场景,其中最为基础的是 Spring Framework,功能有:

  • IoC - 整合其他框架,Spring 两大核心技术之一
  • AOP - 通用操作的横切处理,Spring 两大核心技术之一
  • 事务 - 无需编写代码,即可实现数据库事务管理
  • 测试 - 与测试框架集成、web 单元测试
  • MVC - 开发 web 应用程序
  • 缓存 - 对缓存进行抽象
  • 调度 - 延时任务、定时任务

Spring Framework 在开发中的作用:

  • 分层解耦 - 让单体应用的可扩展性更强
  • 整合框架 - 整合第三方框架,使之协同工作
  • 实用技术 - 自身强大,提供各种实用功能

1.2 Spring Boot 是什么?

Spring Boot 可以帮助我们开发【基于Spring的】、【独立的】、【生产级的】应用程序

它的主要目标是:

  • 为所有 Spring 开发提供更快的入门体验
  • 开箱即用,提供了自动配置
  • 提供一系列大型项目通用的非功能性特性
    • 外部化配置
    • 嵌入式服务器
    • 安全性
    • 健康检查
    • 指标
  • 完全不需要代码生成,也不需要XML配置

1.3 为何从 Spring Boot 开始

传统的 Spring 课程教学顺序一般为

  1. 从 Spring Framework 开始讲起,介绍 IoC、AOP、声明式事务等核心特性
  2. Spring WebMvc,RESTful 开发
  3. 框架整合、例如 Spring + Spring Mvc + Mybatis 等
  4. 在此基础上学习 Spring Boot

利弊

  • 优点是从基础学起,有利于打牢 Spring 基础
  • 缺点是入门难度较大,有众多的配置,学到 Spring Boot 后,一下又变得简单,之前学习的完全用不上,前后联系也不紧密

本课程的设计理念

  • 从 Spring Boot 讲起,利用它配置简单的优越性,让同学们上手容易
  • 用 Spring Boot 讲 IoC,AOP 等基础知识,在简单的同时兼顾打牢基础

1.4 入门案例

Spring Boot 提供了方便的手段来快速生成项目骨架

步骤1 - 生成项目骨架
https://start.spring.io/

步骤2 - 解压压缩包,用 IDEA 打开 pom.xml 文件
步骤3 - 编写控制器类,见下面的代码片段
步骤4 - 运行引导类(程序的入口),此时可以用浏览器访问 http://localhost:8080/hello 来进行测试
步骤5 - 运行 mvn package 打 jar 包,即可作为最终产品发布

控制器类需要我们自己编写

@RestController
public class HelloController {
    
    @RequestMapping("/hello")
    public String hello() {
        return "Hello, Spring Boot!";
    }
}

其中

  • @RestController 表示该类【受控】于 Spring Boot
    • 以后使用的很多类,如控制器类、服务类、包括 mapper 等都要交给 Spring 来管理,这是 Spring 的一大核心理念(IoC),后续课程还会讲到
  • @RequestMapping 类似于之前 @WebServlet,用于映射 url 路径,区别是
    • @WebServlet 加在类上,一个类只能映射一个路径
    • @RequestMapping 加在方法上,因此一个类可以映射多个路径

引导类代码如下,由骨架向导生成

@SpringBootApplication
public class Demo1Application {

    public static void main(String[] args) {
        SpringApplication.run(Demo1Application.class, args);
    }

}
  • 其中 @SpringBootApplication 代表此类是 Spring Boot 程序的入口,也称为引导类,它的细节后续课程中会讲到

如果使用 idea 编辑器,步骤1和步骤2可以进一步简化,利用自带的向导完成

注意 starter 地址有如下选择

  • https://start.spring.io 官网向导地址,网络不好时会连接失败
  • http://start.aliyun.com 国内地址,速度快,依赖全,提供的 Spring Boot 版本稍低,生成的 pom 文件有差异

2. IoC

2.1 单凭接口就能解耦吗

Spring 的一大作用就是能够降低代码耦合,让程序的扩展性更好。

1) 问题演示

某业务接口

public interface UserService {
    public void foo();
}

其中一种实现 A

public class UserServiceImplA implements UserService{
    @Override
    public void foo() {
        System.out.println("UserServiceImplA.foo()...");
    }
}

业务功能使用者

public class UserController {

    private UserService userService = new UserServiceImplA();
    
    public void foo() {
        // 调用业务方法
        userService.foo();
    }

}

测试

UserController controller = new UserController();
controller.foo();

如果业务功能发生了调整,变成了实现 B

public class UserServiceImplB implements UserService{
    @Override
    public void foo() {
        System.out.println("UserServiceImplB.foo()...");
    }
}

可以看到,业务功能的使用者(UserController)也会受到影响,使用实现 A 的地方都需要修改:

public class UserController {

	// 这里要修改为实现 B
    private UserService userService = new UserServiceImplB();
    
    public void foo() {
        userService.foo();
    }

}

之前老师应该都这么讲:为了降低耦合,需要在分层调用时去使用接口,不要直接使用实现类。但看看下面例子,你会发现,Controller 使用 Service 的实现类是无法避免的!上面的问题在于 Controller 与 Service 产生了耦合:即一方的改变影响了另一方。能否达到如下的效果,Service 发生变更,但 Controller 不受影响?

2) 解决思路

  1. Service 对象创建该不该由 Controller 自己来做?

    • 对象自己创建,就没法解耦了,创建权应该交出去,即交给容器来创建
  2. 一个新的问题是,对于 Controller 来说,应该主动去容器获取依赖对象,还是被动从容器接收依赖对象?

    • 被动好,Spring 说:don’t call me,I’ll call you

名词解释:容器

  • 英文对应 Container,指对象的运行环境,同时负责管理这些对象的创建、初始化、销毁等
  • 见过的容器有 Tomcat 容器,管理 Servlet,Filter 等对象
  • 而 Spring 也是一种容器,管理 Controller,Service,Dao 等对象

⭐️2.2 Spring 解耦

1) 交出对象的控制权

Spring 的解决方法是,将刚才的 UserServiceImplA 或 UserServiceImplB 的创建权就交给 Spring 容器,这样就无需我们来关心对象的创建

让 Spring 来管理 Service 的创建,通过在 Service 的实现类A上添加 @Service 注解可以让它被 Spring 管理起来

@Service
public class UserServiceImplA implements UserService {
    @Override
    public void foo() {
        System.out.println("UserServiceImplA.foo()...");
    }
}

测试通过 Spring 的容器获取到该 Service 对象,ConfigurableApplicationContext 即是容器类型

@SpringBootApplication
public class Demo1Application {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = 
            SpringApplication.run(Demo1Application.class, args);
        System.out.println(context.getBean(UserService.class));
    }

}

会输出

com.itheima.demo.service.impl.UserServiceImplA@495b0487

如果要切换成另一个实现类,只需要在另一个实现类B上添加 @Service 注解即可,记得去掉实现类A上的 @Service

@Service
public class UserServiceImplB implements UserService {
    @Override
    public void foo() {
        System.out.println("UserServiceImplB.foo()...");
    }
}

注意

  • 在 UserServiceImplB 上添加 @Service,需要在 UserServiceImplA 上去掉 @Service,即暂时不能同时把他俩交给 Spring 管理,否则会报错
  • 让他俩同时交给 Spring 管理的解决办法,后面再讲

此时会输出

com.itheima.demo.service.impl.UserServiceImplB@3212a8d7

此步达到的效果

  • 可以看到,通过 @Service 注解 UserServiceImplA 或 UserServiceImplB 对象的创建已经交给了 Spring 容器

2) 建立对象依赖关系

把控制器也交给 Spring 管理起来,这回用 @Controller 注解标注,注意此时已无需自己创建 UserService 的实现类,改由用 @Autowired 指明该成员变量的取值来自于 Spring 容器,@Autowired 会根据类型去找容器中的对象

@Controller
public class UserController {

    @Autowired
    private UserService userService;

    public void foo() {
        // 调用业务方法
        userService.foo();
    }

}

测试一下控制器功能是否改变

@SpringBootApplication
public class Demo1Application {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = 
            SpringApplication.run(Demo1Application.class, args);
        System.out.println(context.getBean(UserService.class));
        context.getBean(UserController.class).foo();
    }

}

输出

com.itheima.demo.service.impl.UserServiceImplB@76012793
UserServiceImplB.foo()...

此步达到的效果

  • Service 切换实现,Controller 的代码再也无需改动,达到了低耦合

检查自己理解程度

  • 来回切换实现类,体会【低耦合】含义
  • 如果实现类A、实现类B 的 @Service 都去掉,会发生什么,设想一下

✒️练习 - Spring 解耦

  • 题目代码及说明见 exercise1
  • 预计完成时间:10 分钟

2.3 单元测试

Spring 可以结合 JUnit 完成单元测试,测试用例中也可以利用 @Autowired 来注入待测对象,只要它被 Spring 所管理

@SpringBootTest
class Demo1ApplicationTests {
    @Autowired
    private UserController controller; // 注入任意由容器管理的对象
    @Test
    void test1() {
        // 使用 controller
    }
}

注意

  • Spring 中用了 JUnit5,因此测试方法不必是 public 的

容器对象自身也可以使用 @Autowired 注入,容器类型可以使用接口类型:ApplicationContext

@SpringBootTest
class Demo1ApplicationTests {
    @Autowired
    private ApplicationContext context; // 注入容器自身
    @Test
    void test2() {
        // 使用 context
    }
}

2.4 控制反转

刚才解耦的例子中,涉及到 Spring 的两个核心概念

  • 把对象的控制权交给容器管理,称之为控制反转
  • 建立容器管理对象之间的依赖关系,称之为依赖注入

这篇关于Spring-day01的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!