菜鸟教你快速掌握 Spring 最核心的基础知识 之 Spring Bean 篇(小白)

Spring Bean 篇

The bean is an instance of a class managed by the Spring container

loC(lnversion of Control,控制反转) 容器是 Spring 框架最最核心的组件,没有 loC 容器就没有 Spring 框架。

loC(lnversion of Control,控制反转)是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度

在 Spring 框架当中,主要通过依赖注入(Dependency Injection简称DI)来实现 loC。

在 Spring 的世界中,所有的 Java 对象都会通过 loC 容器转变为Bean (Spring 对象的一种称呼,以后我们都用 Bean 来表示 Java 对象),构成应用程序主干和由 Spring loC 容器管理的对象称为beans,beans 和它们之间的依赖关系反映在容器使用的配置元数据中。基本上所有的 Bean 都是由接口+实现类完成的,用户想要获取Bean 的实例直接从 loC 容器获取就可以了,不需要关心实现类

在这里插入图片描述

Spring 主要有两种配置元数据的方式,一种是基于 XML、一种是基于 Annotation 方案的,目前主流的方案是基于 Annotation 的,所以我们这里也是以 Annotation 为基础方案来讲解,
org.springframework.context.Applicationcontext 接类定义容器的对外服务,通过这个接口,我们可以轻松的从 loC 容器中得到Bean 对象。我们在启动 Java 程序的时候必须要先启动 loC 容器

Annotation 类型的 loC 容器对应的类是

org.springframework.context.annotation.AnnotationConfigApplicationContext

在启动类里启动IoC容器

ApplicationContext context = new AnnotationConfigApplicationContext("fm.douban");

这段代码的含义就是启动 loC 容器, 并且会自动加载包 fm.douban下的 Bean,哪些 Bean 会被加载呢? 只要引用了 Spring 注解的类都可以被加载(前提是在这个包下哦)
AnnotationconfigApplicationcontext 这个类的构造函数有两种

  • AnnotationConfigApplicationContext(String …basePackages)根据包名实例化

  • AnnotationConfigApplicationContext(Class clazz)根据自定义包扫描行为实例化

我们的例子就是第一种,两者根据情况做选择,开始的时候一般用第一种方案
Spring 官方声明为 Spring Bean 的注解有如下几种

Spring Bean 的官方注解:

  • org.springframework.stereotype.Service

  • org.springframework.stereotype.Component

  • org.springframework.stereotype.Controller

  • org.springframework.stereotype.Repository

  • @Component注解是通用的 Bean 注解,其余三个注解都是扩展自Component

  • @Service正如这个名称一样,代表的是 Service Bean

  • @Controller作用于 Web Bean

  • @Repository作用于持久化相关 Bean

实际上这四个注解都可以被 IoC 容器加载,一般情况下,我们使用@Service;如果是Web服务就使用@Controller

loC 容器就像一个大型的工厂一样,我们不关心工厂如何生产,只
需使用工厂生产的产品。

依赖注入的第一步是完成容器的启动,第二步就是真正的完成依赖注入行为了
依赖注入这个词也是一种编程思想,他简单的来说就是一种获取其他实
例的规范

我们还是以豆瓣为例,来学习和理解依赖注入思想

  • 我们在前面的作业完成了豆瓣歌曲服务的定义,听过歌的同学应该都知道,有歌曲就有专辑,我们可以通过一个专辑获取这专辑单包含的歌曲,看一下 UML图

在这里插入图片描述

  • 我们新增了一个 接口SubjectService 和它的实现类subjectserviceImpl ,用来完成获取专辑的服务,在这个接口里我们定义了一个 get 方法,传入参数为 subjectld,我们期望得到专辑的信息(包括专辑包含的歌曲信息)

在这里插入图片描述

  • 我们仔细看一下我们的 songsubject 这两个 POJO 类,从这个模型上来看,我们如果去带着 Subject 的 id 去循环遍历所有的歌曲,筛选出来的歌曲应该就是专辑包含的歌曲。所以我们在 SongService 里又新增了一个 list 方法用于查询专辑歌曲
  • 回到 SubjectServiceImpl 类,如果我们想获取完整的专辑信息,就得引入 songservice 的实例,调用歌曲,对不? 我们来模拟一下这个 SujectServiceImpl
ectServiceImpl implements SubjectService {

    private SongService songService;

    //缓存所有专辑数据
    private static Map<String, Subject> subjectMap = new HashMap<>();

    static {
        Subject subject = new Subject();
        //... 省略初始化数据的过程
        subjectMap.put(subject.getId(), subject);
    }

    @Override
    public Subject get(String subjectId) {
        Subject subject = subjectMap.get(subjectId);
        //调用 SongService 获取专辑歌曲
        List<Song> songs = songService.list(subjectId);
        subject.setSongs(songs);
        return subject;
    }

    public void setSongService(SongService songService) {
        this.songService = songService;
    }
}

这段代码逻辑相信大家还是看的明白,那么我的问题来啦。我们如何获取 SongService 的实例呢?是不是得需要一个外部的工厂给我们传递,调用 setSongService 方法传入进来?相当麻烦

使用依赖注入 DI :

import fm.douban.model.Song;
import fm.douban.model.Subject;
import fm.douban.service.SongService;
import fm.douban.service.SubjectService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class SubjectServiceImpl implements SubjectService {

    @Autowired
    private SongService songService;

    //缓存所有专辑数据
    private static Map<String, Subject> subjectMap = new HashMap<>();

    static {
        Subject subject = new Subject();
        subject.setId("s001");
        //... 省略初始化数据的过程
        subjectMap.put(subject.getId(), subject);
    }

    @Override
    public Subject get(String subjectId) {
        Subject subject = subjectMap.get(subjectId);
        //调用 SongService 获取专辑歌曲
        List<Song> songs = songService.list(subjectId);
        subject.setSongs(songs);
        return subject;
    }
}

改动前的问题

在任何需要SubjectService的地方,都需要编码实例化对象:

SubjectService subjectService = new SujectServiceImpl();
SongService songService = new SongServiceImpl();
subjectService.setSongService(songService);

如果SubjectService依赖的xxxService太多,就需要new很多服务实例,代码会很长

如何解决问题(改动后)

加注解的作用,是让 Spring 系统 自动 管理 各种实例。
所谓 管理,就是用 @service 注解把``SubjectServiceImplSongServiceImpl 等等所有服务实现,都标记成 *Spring Bean* ;然后,在任何需要使用服务的地方,用@Autowired` 注解标记,告诉Spring 这里需要注入实现类的实例。

项目启动过程中,Spring 会 自动 实例化服务实现类,然后 自动 注入到变量中,不需要开发者大量的写 new 代码了,就解决了上述的开发者需要大量写代码而导致容易出错的问题。

@service@Autowired 是相辅相成的:如果 SongServicelmpl 没有加@Service,就意味着没有标记成 Spring Bean ,那么即使加 @Autowired 也无法注入实例; 而private songservice songservice;属性忘记加 @Autowired Spring Bean 亦无法注入实例。二者缺一不可

每个 Annotation (注解)都有各自特定的功能,Spring 检查到代码中有注解,就自动完成特定功能,减少代码量、降低系统复杂度。

初学 Spring ,需要 理解 并 牢记 常见的这些注解的功能和作用。

依赖注入小结

OK,上面的例子其实已经是解释了依赖注入的工作模式,我们整理下,你会发现依赖注入让我们得到其他 Bean 的实例相当简单,你只5需要在属性上添加注解,就像下面的代码

@Autowired
private SongService songService;

Autowired 完整的类路径是

org.springframework.beans.factory.annotation.Autowired

当然你还有一个前提条件,那就是当前的类是 Spring Bean 哦,比如这里我们添加了 @service

到目前为止,我们掌握了 Spring Bean 的知识,可以改进一些代码啦,让我们继续实战下去,你会发现 Spring 的道理比较复杂,但是运用起来其实很简单,因为这是各种设计模式综合运用的结果。以后我们也会逐步的了解设计模式的,到时候再精进 Spring 会更好的理解,现在还是让我们熟练的使用它

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值