1. 应用分层
应用分层是一种软件开发设计思想,它将应用程序分成 n 个层次,这 n 个层次分别负责各自的职责,多个层次之间协同提供完整的功能,根据项目的复杂度,可以分成三层,四层或更多层,mvc 就是把整体的程序分成了 model(模型), view(视图), controller(控制器)三个层次
由于后端开发,不需要过多的关注前端,所以又有了一种分层架构:把整体架构分为表现层,业务逻辑层,数据层,又称为“三层架构”
- 表现层:用来展示数据结果和接收用户指令,是最接近用户的一层
- 业务逻辑层:负责处理业务逻辑,包含业务逻辑的具体实现
- 数据层:负责存储和管理与应用程序相关的数据
在 spring 的实现中可以分为下面三个部分:
controller:控制层。接收前端发送的请求,对请求进行处理,并响应数据
service:业务逻辑层。处理具体的业务逻辑
dao:数据访问层,也被称为持久层。负责数据访问,操作(增删查改)
2. ioc 的介绍
ioc:也就是控制反转
spring ioc 是一种设计模式,用于解耦对象之间的依赖关系,在之前创建的项目中对象通常会主动创建和管理自己所依赖的对象,例如,一个userservice
类可能会在自己的内部使用new
关键字来创建一个userrepository
对象用于数据访问,这样设计看似没有问题,但是可维护性却很低,当有很多类创建了各自的对象时,并且这些对象之间还有依赖关系,例如创建 car ,framework,bottom,tire 类,从左到右依次存在依赖关系,当其中有一个类的底层代码改变之后,调用链上的代码都需要修改
public class newcarexample { public static void main(string[] args) { car car = new car(20); car.run(); } //car类 static class car { private framework framework; public car(int size) { framework = new framework(size); system.out.println("car init...."); } public void run(){ system.out.println("car run..."); } } //车身类 static class framework { private bottom bottom; public framework(int size) { bottom = new bottom(size); system.out.println("framework init..."); } } //底盘类 static class bottom { private tire tire; public bottom(int size) { this.tire = new tire(size); system.out.println("bottom init..."); } } //轮胎类 static class tire { // 尺⼨ private int size; public tire(int size){ this.size = size; system.out.println("轮胎尺⼨:" + size); } } }
修改轮胎的构造方法之后,底盘也需要修改,当修改底盘之后,上层调用也需要修改
而在 ioc 模式下,对象的创建和管理这些控制权被反转了,不再由对象自身来控制,而是交给外部的容器(ioc 容器)来管理,下面演示一下使用 ioc 的思想来管理对象
public class ioccarexample { public static void main(string[] args) { tire tire = new tire(20); bottom bottom = new bottom(tire); framework framework = new framework(bottom); car car = new car(framework); car.run(); }
通过这样的形式,各个组件的依赖关系就发生了反转,统一对对象进行管理,谁需要这个对象直接传过去一个对象,不需要他自己调用方法进行创建
ioc 容器的工作就是把这些对象进行统一管理
通过这种方式进行资源的统一管理,在创建实例时不需要了解其中的细节,降低了使用资源双方的依赖程度
3. ioc 容器的使用
3.1. bean 的存储
如果想要把一个对象交给 ioc 容器来管理,需要在类上添加一个 @component
注解,此外还有其它的一些注解可以实现:
- 类注解:
@controller
、@service
、@repository
、@component
、@configuration
. - 方法注解:
@bean
.
@controller public class usercontroller { public void say(){ system.out.println("usercontroller"); } }
@springbootapplication public class springiocapplication { public static void main(string[] args) { applicationcontext context = springapplication.run(springiocapplication.class, args); usercontroller bean = context.getbean(usercontroller.class); bean.say(); } }
applicationcontext 可以理解为 spring 的上下文,这个上下文就是指当前的运行环境和其他功能,也可以看作是一个容器,容器中存储了很多内容,这些内容是当前的运行环境
之后就可以通过拿到的 context 来获取 bean,关于获取 bean 有多种方式:
object getbean(string var1) throws beansexception; | 根据bean名称获取bean |
t getbean(string var1, class var2) throws beansexception; | 根据bean名称和类型获取bean |
t getbean(class var1) throws beansexception; | 根据类型获取bean |
object getbean(string var1, object... var2) throws beansexception | 按bean名称和构造函数参数动态创建bean,只适⽤于具有原型(prototype)作⽤域的bean |
t getbean(class var1, object... var2) throws beansexception; | 按bean类型和构造函数参数动态创建bean, 只适⽤于具有原型(prototype)作⽤域的 bean |
根据类型获取 bean 的话,如果存在多个相同类型的 bean 那么就不能确定具体要获取的是哪个 bean ,同理,如果只是根据名称来获取 bean,如果重名的话也是不能正确获取到 bean 的,所以就有了第三种方式,同时根据类型和名称来获取 bean
上面这三种获取 bean 的方式是比较常用的
关于 ioc 中 bean 的名称转化规则:
如果是 usercontroller 会被转成 usercontroller,如果是 ucontroller 就还是 ucontroller
usercontroller bean1 = (usercontroller) context.getbean("usercontroller"); bean1.say();
由于是根据名称来获取 bean,所以获取到的 bean 不确定是什么类型,会返回一个 object 类型,需要强转一下
同时指定类型和名称:
usercontroller bean2 = context.getbean("usercontroller", usercontroller.class); bean2.say();
把上面获取的 bean ,打印一下,发现获取的实例的地址是一样的,由此可以知道,通过这种方式获取对象是基于单例模式实现的
接下来演示一下 @service
注解:
@service public class userservice { public void say(){ system.out.println("userservice"); } }
userservice service = context.getbean(userservice.class); service.say();
然后发现和上面一样也是可以运行的,其它的几个类注解也是一样的
那么为什么实现的功能一样,还需要分这么多不同的注解,这个是和之前的应用分层是对应的,通过不同的类注解来了解当前类的用途
@controller
:控制层,接受请求,对请求进行处理,并进行响应
@servie
:业务逻辑层,处理具体的业务逻辑
@repository
:数据访问层, 也称为持久层,负责数据访问操作
@configuration
:配置层,处理项目中的一些配置信息
3.2. 方法注解@bean
上面四个注解都是 @component
的衍生注解
类注解是添加到某个类上的,但是存在两个问题:
- 如果使用外部包里的类,没办法添加注解
- 同时,由于类注解默认创建的对象是单例对象,如果需要多个对象就需要调整
方法注解 @bean
就可以解决上述问题
例如,在一个外部包里有一个 userinfo 类
@data @allargsconstructor @noargsconstructor public class userinfo { private string name; private integer age; }
可以通过在获取对象的方法上加上@bean
@component public class userinfocomponent { @bean public userinfo userinfo(){ return new userinfo("zhangsan",20); } }
如果说需要创建对个对象的话:
这个问题就是在获取 bean 的时候发现了具有相同类型的 bean,可以直接通过获取 bean 的名称,这里的名称和方法注解下方法名是对应的
userinfo bean = (userinfo) context.getbean("userinfo"); system.out.println(bean);
上面的注解,无论是类注解还是方法注解,都可以实现重命名
重命名之后就需要使用改之后的名字了
4. 扫描路径
如果说把启动类放到其他目录下再运行就会报错,是因为上面介绍的注解如果想要生效,是需要配置扫描路径的,默认的扫描范围是 spring boot 启动类所在的包和它的子包,可以通过@componentscan
来配置扫描路径
在 @componentscan
源码中,是支持传入一个数组的,如果想要配置多个扫描路径可以直接传入一个数组
@componentscan({"com.example.service","com.example.controller"})
到此这篇关于springioc 容器的使用的文章就介绍到这了,更多相关springioc 容器使用内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论