Spring - 5 ( 8000 字 Spring 入门级教程 )

一:Spring IoC&DI

1.1 方法注解 @Bean

类注解是添加到某个类上的, 但是存在两个问题:

  1. 使用外部包里的类, 没办法添加类注解
  2. ⼀个类, 需要多个对象, ⽐如多个数据源

这种场景, 我们就需要使用方法注解 @Bean

我们先来看方法注解如何使用:

public class BeanConfig {
    @Bean
    public User user(){
        User user = new User();
        user.setName("zhangsan");
        user.setAge(18);
        return user;
    }
}

然而,当我们写完以上代码,尝试获取 bean 对象中的 user 时却发现,根本获取不到:

@SpringBootApplication
public class SpringIocDemoApplication {
    public static void main(String[] args) {
		//获取Spring上下⽂对象
        ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);
		//从Spring上下⽂中获取对象
        User user = context.getBean(User.class);
		//使⽤对象
        System.out.println(user);
    }
}

以上程序的执行结果如下:

在这里插入图片描述

这是为什么呢?

1.1.1 方法注解要配合类注解使用

在 Spring 框架的设计中,方法注解 @Bean 要配合类注解才能将对象正常的存储到 Spring 容器中,如下代码所示:

@Component
public class BeanConfig {
    @Bean
    public User user(){
        User user = new User();
        user.setName("zhangsan");
        user.setAge(18);
        return user;
    }
}

再次执行以上代码,运行结果如下:

在这里插入图片描述

1.1.2 定义多个对象

对于同⼀个类, 如何定义多个对象呢,比如多数据源的场景, 类是同⼀个, 但是配置不同, 指向不同的数据源.

我们看下 @Bean 的使用

@Component
public class BeanConfig {
    @Bean
    public User user1(){
        User user = new User();
        user.setName("zhangsan");
        user.setAge(18);
        return user;
    }
    @Bean
    public User user2(){
        User user = new User();
        user.setName("lisi");
        user.setAge(19);
        return user;
    }
}

定义了多个对象的话, 我们根据类型获取对象, 获取的是哪个对象呢?

@SpringBootApplication
public class SpringIocDemoApplication {
    public static void main(String[] args) {
		//获取Spring上下⽂对象
        ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);
		//从Spring上下⽂中获取对象
        User user = context.getBean(User.class);
		//使⽤对象
        System.out.println(user);
    }
}

运行结果:
在这里插入图片描述

报错信息显示: 期望只有⼀个匹配, 结果发现了两个, user1, user2,从报错信息中, 可以看出来, @Bean 注解的 bean, bean 的名称就是它方法名

接下来我们根据名称来获取bean对象

@SpringBootApplication
public class SpringIocDemoApplication {
    public static void main(String[] args) {
		//获取Spring上下⽂对象
        ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);
		//根据bean名称, 从Spring上下⽂中获取对象
        User user1 = (User) context.getBean("user1");
        User user2 = (User) context.getBean("user2");
        System.out.println(user1);
        System.out.println(user2);
    }
}

运行结果:
在这里插入图片描述
可以看到, @Bean 可以针对同⼀个类, 定义多个对象.

1.1.3 重命名 Bean

可以通过设置 name 属性给 Bean 对象进行重命名操作,如下代码所示:

@Bean(name = {"u1","user1"})
public User user1(){
        User user = new User();
        user.setName("zhangsan");
        user.setAge(18);
        return user;
}

@Bean(name = {“u1”,“user1”}) 这一行是 @Bean 注解的一种变体,它不仅定义了一个Bean,还给这个 Bean 起了两个不同的名字,分别是 u1 和 user1。

此时我们使用 u1 就可以获取到 User 对象了,如下代码示:

@SpringBootApplication
public class SpringIocDemoApplication {
    public static void main(String[] args) {
		//获取Spring上下⽂对象
        ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);
		//从Spring上下⽂中获取对象
        User u1 = (User) context.getBean("u1");
		//使⽤对象
        System.out.println(u1);
    }
}

1.2 扫描路径

使用前面学习的四个注解声明的 bean,⼀定会生效吗?答案是否定的,因为 bean 想要生效,还需要被 Spring 扫描

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

在这里插入图片描述
再运行代码:

@SpringBootApplication
public class SpringIocDemoApplication {
    public static void main(String[] args) {
		//获取Spring上下⽂对象
        ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);
		//从Spring上下⽂中获取对象
        User u1 = (User) context.getBean("u1");
		//使⽤对象
        System.out.println(u1);
    }
}

运行结果:

在这里插入图片描述

报错原因:没有 bean 的名称为u1,为什么没有找到 bean 对象呢?

原因是使用五大注解声明的 bean,要想生效, 还需要配置扫描路径, 让 Spring 扫描到这些注解,也就是通过 @ComponentScan 来配置扫描路径.

@ComponentScan({"com.example.demo"})
@SpringBootApplication
public class SpringIocDemoApplication {
    public static void main(String[] args) {
		//获取Spring上下⽂对象
        ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);
		//从Spring上下⽂中获取对象
        User u1 = (User) context.getBean("u1");
		//使⽤对象
        System.out.println(u1);
    }
}

那为什么前⾯没有配置 @ComponentScan 注解也可以呢?

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

在这里插入图片描述
推荐做法:把启动类放在我们希望扫描的包的路径下, 这样我们定义的 bean 就都可以被扫描到

在这里插入图片描述

1.3 DI 详解

依赖注入是⼀个过程,是指 IoC 容器在创建 Bean 时, 去提供运行时所依赖的资源,而资源指的就是对象.

在上面程序案例中,我们使用了 @Autowired 这个注解,完成了依赖注入的操作,简单来说, 就是把对象取出来放到某个类的属性中.

关于依赖注入, Spring 也给我们提供了三种方式:

  1. 属性注入
  2. 构造方法注入
  3. Setter 注入

1.3.1 属性注入

属性注入是使⽤ @Autowired 实现的,将 Service 类注入到 Controller 类中.

  1. Service 类的实现代码如下:
import org.springframework.stereotype.Service;
@Service
public class UserService {
    public void sayHi() {
        System.out.println("Hi,UserService");
    }
}
  1. Controller 类的实现代码如下:
@Controller
public class UserController {
    //注⼊⽅法1: 属性注⼊
    @Autowired
    private UserService userService;

    public void sayHi() {
        System.out.println("hi,UserController...");
        userService.sayHi();
    }
}
  1. 获取 Controller 中的 sayHi⽅法:
@SpringBootApplication
public class SpringIocDemoApplication {
    public static void main(String[] args) {
		//获取Spring上下⽂对象
        ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);
		//从Spring上下⽂中获取对象
        UserController userController = (UserController) context.getBean("Controller");
		//使⽤对象
        userController.sayHi();
    }
}

最终结果如下:

在这里插入图片描述

去掉 @Autowired , 再运行一下程序看看结果

在这里插入图片描述

1.3.2 构造方法注入

构造方法注入是在类的构造方法中实现注入,如下代码所示:

@Controller
public class UserController2 {
    //注⼊⽅法2: 构造⽅法
    private UserService userService;
    @Autowired
    public UserController2(UserService userService) {
        this.userService = userService;
    }
    public void sayHi(){
        System.out.println("hi,UserController2...");
        userService.sayHi();
    }
}

注意事项:如果类只有⼀个构造方法,那么 @Autowired 注解可以省略;如果类中有多个构造方法,那么需要添加上 @Autowired 来明确指定到底使用哪个构造方法。

1.3.3 Setter 注入

Setter 注入和属性的 Setter 方法实现类似,只不过在设置 set ⽅法的时候需要加上 @Autowired 注解 ,如下代码所示:

@Controller
public class UserController3 {
    //注⼊⽅法3: Setter⽅法注⼊
    private UserService userService;
    @Autowired
    public void setUserService(UserService userService) {
        this.userService = userService;
    }
    public void sayHi(){
        System.out.println("hi,UserController3...");
        userService.sayHi();
    }
}

1.3.4 三种注入优缺点分析

第一种:属性注入

优点:

  • 简洁,使用方便

缺点:

  • 只能用于 IoC 容器,如果是非 IoC 容器不可用,并且只有在使用的时候才会出现 NPE(空指针异常)
  • 不能注入一个 Final 修饰的属性

第二种:构造函数注入 (Spring 4.X 推荐)

优点:

  • 可以注入 final 修饰的属性
  • 注入的对象不会被修改
  • 依赖对象在使用前一定会被完全初始化,因为依赖是在类的构造方法中执行的,而构造方法是在类加载阶段就会执行的方法。
  • 通用性好,构造方法是 JDK 支持的,所以更换任何框架都适用

缺点:

  • 注入多个对象时,代码会比较繁琐

第三种:Setter 注入 (Spring 3.X 推荐)

优点

  • 方便在类实例之后,重新对该对象进行配置或者注入

缺点:

  • 不能注入一个 Final 修饰的属性
  • 注入对象可能会被改变,因为 setter 方法可能会被多次调用,就有被修改的风险

1.3.5 @Autowired 存在问题

当同⼀类型存在多个 bean 时, 使⽤ @Autowired 会存在问题

@Component
public class BeanConfig {
    @Bean("u1")
    public User user1(){
        User user = new User();
        user.setName("zhangsan");
        user.setAge(18);
        return user;
    }
    @Bean
    public User user2() {
        User user = new User();
        user.setName("lisi");
        user.setAge(19);
        return user;
    }
}
@Controller
public class UserController {
    @Autowired
    private UserService userService;
    //注⼊user
    @Autowired
    private User user;
    public void sayHi(){
        System.out.println("hi,UserController...");
        userService.sayHi();
        System.out.println(user);
    }
}

运行结果:
在这里插入图片描述
报错的原因是,非唯⼀的 Bean 对象,如何解决上述问题呢?Spring 提供了以下几种解决方案:

  • @Primary
  • @Qualifier
  • @Resource

下面 一 一 进行讲解

  1. 使用 @Primary 注解:当存在多个相同类型的 Bean 注入时,加上 @Primary 注解,来确定默认的实现.
@Component
public class BeanConfig {
    @Primary //指定该bean为默认bean的实现
    @Bean("u1")
    public User user1(){
        User user = new User();
        user.setName("zhangsan");
        user.setAge(18);
        return user;
    }
    @Bean
    public User user2() {
        User user = new User();
        user.setName("lisi");
        user.setAge(19);
        return user;
    }
}
  1. 使用 @Qualifier 注解:指定当前要注入的 bean 对象。 在 @Qualifier 的 value 属性中,指定注入的 bean 的名称。

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

@Controller
public class UserController {
    @Qualifier("user2") //指定bean名称
    @Autowired
    private User user;
    public void sayHi(){
        System.out.println("hi,UserController...");
        System.out.println(user);
    }
}
  1. 使用 @Resource 注解:是按照 bean 的名称进行注入。通过 name 属性指定要注入的 bean 的名称。
@Controller
public class UserController {
    @Resource(name = "user2")
    private User user;
    public void sayHi(){
        System.out.println("hi,UserController...");
        System.out.println(user);
    }
}

@Autowird 与 @Resource 的区别

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

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

相比于 @Autowired 来说,@Resource 支持更多的参数设置,例如 name 设置,根据名称获取 Bean。

1.4 总结

Spring, Spring Boot 和 Spring MVC 的关系以及区别:

  1. Spring:

简单来说, Spring 是⼀个开发应用框架,目的是用于简化企业级应用程序开发.

  1. Spring MVC:

Spring MVC 是 Spring 的⼀个子框架, Spring 诞生之后, 大家觉得很好用, 于是按照MVC模式设计了⼀个 MVC 框架,用于开发 web 应用和网络接口。

  1. Spring Boot:

Spring Boot 是对 Spring 的⼀个封装, 为了简化 Spring 用的开发而出现的,中小型企业,没有成本研究自己的框架, 使用 Spring Boot 可以更加快速的搭建框架, 降级开发成本, 让开发人员更加专注于 Spring 应用的开发,而无需过多关注 XML 的配置和⼀些底层的实现.

Spring, Spring Boot 和 Spring MVC 的功能:

  1. Spring

spring 主要用于管理对象和对象之间的依赖关系

  1. Spring MVC

基于 Spring 进行开发的, 天生的与 Spring 框架集成. 可以让我们更简洁的进行 Web 层开发, 支持灵活的 URL 到页面控制器的映射

  1. Spring Boot

Spring Boot 是个脚手架, 插拔式搭建项⽬, 可以快速的集成其他框架进来,比如想使用SpringBoot 开发 Web 项目, 只需要引入 Spring MVC 框架即可

  1. ⼀句话总结

Spring MVC 和 Spring Boot 都属于 Spring,Spring MVC 是基于 Spring的⼀个MVC 框架,而 Spring Boot 是基于 Spring 的⼀套快速开发整合包.

比如我们的图书系统代码中:

  • 整体框架是通过 SpringBoot 搭建的
  • IoC, DI 功能是 Spring 的提供的,
  • web 相关功能是 Spring MVC 提供的

这三者专注的领域不同,解决的问题也不⼀样, 总的来说,Spring 就像⼀个大家族,有众多衍生产品, 但他们的基础都是 Spring

在这里插入图片描述

在这里插入图片描述

相关推荐

  1. Spring Boot 入门教程

    2024-04-26 03:10:02       46 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-04-26 03:10:02       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-04-26 03:10:02       100 阅读
  3. 在Django里面运行非项目文件

    2024-04-26 03:10:02       82 阅读
  4. Python语言-面向对象

    2024-04-26 03:10:02       91 阅读

热门阅读

  1. Linux:web服务基于IP和域名部署

    2024-04-26 03:10:02       32 阅读
  2. Spring从零开始学使用系列(三)--依赖注入(DI)

    2024-04-26 03:10:02       34 阅读
  3. Basic TCP Server & Client

    2024-04-26 03:10:02       38 阅读
  4. 责任链模式

    2024-04-26 03:10:02       36 阅读
  5. 三、搭建 VLC,实战点播功能

    2024-04-26 03:10:02       38 阅读
  6. 安卓基础知识-跨进程通信IPC篇

    2024-04-26 03:10:02       36 阅读
  7. TypeScript学习笔记(一) 概述

    2024-04-26 03:10:02       36 阅读
  8. 嵌入式学习57-ARM6(内核编译)

    2024-04-26 03:10:02       35 阅读
  9. vue 3 + TS 组合式标注类型

    2024-04-26 03:10:02       34 阅读
  10. leetcode152 乘积最大子数组

    2024-04-26 03:10:02       40 阅读
  11. Spark调优-解决job任务运行超时或者慢的问题

    2024-04-26 03:10:02       38 阅读