一, 前言
本篇博客分享一些关于 Spring 中一个核心概念, IoC.
IoC:Inversion of Control , 控制反转.
通常情况下对于实例化一个对象, 我们会通过关键字 new 创建出来. 但是在实际项目开发中不可能有一个实例化对象, 而多个对象就需要多个 new 创建. 显然, 这势必造成多个对象之间的耦合, 以及对程序的维护性带来困难.
控制反转 , 顾名思义就是将控制权力交给 Spring 容器. 简单理解就是将创建 bean 的工作交给 Spring 来做, 对于开发人员来说就不需要再手动创建. 并且 Spring 容器会自动维护 bean 之间的关系, 及 bean 的生命周期等, 大大降低了程序之间的耦合.
接着再说说和 IoC 相关的另一个概念:
DI:Dependency Injection, 依赖注入.
依赖注入, 在程序运行时, 动态的向对象中注入它所依赖的对象. 举例, A 对象依赖 B 对象中的某些属性或方法, 但是在 Spring 容器创建 A 对象之前, 并不知道 A 的依赖关系. 因此在真正创建 A 对象时, 发现依赖了 B 对象, 则此时也会将 B 对象创建出来并注入到 A 中, 这就是依赖注入.
我个人的理解就是 DI 是 IoC 的一种体现方式, 这也是 DI 和 IoC 两者的区别.
二, IoC 核心接口
BeanFactory 和 ApplicationContext 是 IoC 容器体现形态的两大核心接口, 请看下图两者之间的联系.
两者之间是继承关系, BeanFactory 作为最顶层接口, 里面只实现了容器的基本功能, 是一种简单容器. 而 ApplicationContext 作为最底层的子类, 则自然拥有父类的基本功能, 并且还增加了很多面向框架的特性, 如国际化, 时间发布等, 是一种高级容器.
ApplicationContext:
它在构建容器时, 创建对象采用的策略是采用立即加载的方式, 也就是说当读取完配置文件时, 容器就是马上创建对象.
BeanFactory:
它在构建核心容器时, 创建对象采取的策略是延时加载, 什么时候根据 id 获取对象时, 容器才会真正创建对象.
这里我们只总结关于 ApplicationContext 接口的使用.
三, ApplicationContext
ApplicationContext 是一个接口, 那么要想使用该接口就要实例化它的实现类, 请看如下所示:
其中常用的三个实现类为:
ClassPathXmlApplicationContext: 加载类路径下的配置文件.
FileSystemXmlApplicationContext: 加载磁盘中任意位置的配置文件 (要有访问权限).
AnnotationConfigApplicationContext: 读取注解标记.
以上三种都是获取 bean 的实例化对象, 那么我们又如何定义 bean 对象呢, 同样也是有三种方式.
第一种: 使用默认的构造函数创建
在 spring 的配置文件中使用 bean 标签, 配置 id 和 class 属性使用就是默认的构造函数创建的对象.
注意: 如果类中没有无参构造函数, 则创建对象失败.
首先定义 bean.cml 文件:
- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="
- http://www.springframework.org/schema/beans
- https://www.springframework.org/schema/beans/spring-beans.xsd">
- <!-- 第一种方式, 使用默认的构造函数 -->
- <bean id="userService" class="com.frame.service.impl.UserServiceImpl"></bean>
- </beans>
定义接口 IUserService 的实现类:
- public class UserServiceImpl implements IUserService {
- /**
- * 无参构造
- */
- public UserServiceImpl() {
- System.out.println("创建对象");
- }
- @Override
- public void addUser() {
- System.out.println("添加用户");
- }
- }
主方法中获取该实例对象:
- public class Demo {
- public static void main(String[] args) {
- // 1, 加载类路径下的配置文件
- ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
- // 2, 通过 getBean() 获取实例化对象
- IUserService userService = (IUserService) ac.getBean("userService");
- // 3, 通过 bean 对象调用方法
- userService.addUser();
- }
- }
运行结果为:
第二种, 普通工厂模式
使用类中的方法创建对象, 并注入到 spring 容器中.
思路: 模拟一个工厂, 创建并返回上述实现类对象.
- // 模拟 bean 工厂
- public class InstanceBean {
- public IUserService userservice(){
- return new UserServiceImpl();
- }
- }
- <bean id="instanceBean" class="com.frame.factory.InstanceBean"></bean>
- <bean id="userService" factory-bean="instanceBean" factory-method="userservice"></bean>
而主方法中的代码和上面是一样的, 这种创建 bean 的方式比较灵活, 通过配置文件去加载自定义的工厂.
第三种, 普通工厂模式中的静态方法
- // 将方法用 static 修饰
- public class StaticInstanceBean {
- public static IUserService userservice(){
- return new UserServiceImpl();
- }
- }
- <bean id="userService" class="com.frame.factory.StaticInstanceBean" factory-method="userservice"></bean>
四, Bean 的作用范围
通过 IoC 可以将创建对象这个事交给 Spring 容器, 那么 Bean 的作用范围容器是怎么定义的.
bean 标签的 scope 属性:
singleton: 单例模式 (默认使用)
prototype: 多例模式
request:(Spring2.0 之后)web 应用的请求范围
session:(Spring2.0 之后)Web 用用的会话范围
global-session:(Spring2.0 之后) 全局会话范围.
在 Spring 容器中, 默认是使用单例模式, 如下代码:
- <bean id="userService" class="com.frame.service.impl.UserServiceImpl" scope="singleton"></bean>
- // 1, 加载配置文件
- ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
- // 2, 获取 Bean 对象
- IUserService userService = (IUserService) ac.getBean("userService");
- // 3, 再次获取
- IUserService userService1 = (IUserService) ac.getBean("userService");
- System.out.println(userService == userService1);
运行结果为:
证明多次获取 Bean 对象, 在单例模式下是同一个对象. 同样, 将配置文件中 scope 值改为多例模式, 如下代码:
<bean id="userService" class="com.frame.service.impl.UserServiceImpl" scope="prototype"></bean>
主方法中的代码同上, 那么我们直接看运行结果:
很明显, 在多例模式下 IoC 创建了两次对象.
五, DI
上面我们总结了 DI 的概念, 那么依赖注入都能注入什么类型呢, 又可以使用什么方式进行进入呢.
注入的数据有三类:
1, 基本数据类型
2, 其他 bean 类型
3, 复杂类型 (Array,List,Map,Set 等)
注入的三种方式:
1, 使用函数构造
2, 使用 set 方法提供
3, 使用注解提供
第一种, 使用函数构造
- public class UserServiceImpl{
- private Integer id;
- private String name;
- private Date birthday;
- public UserServiceImpl(){}
- public UserServiceImpl(Integer id,String name,Date birthday) {
- this.id = id;
- this.name = name;
- this.birthday = birthday;
- }
- }
- <bean id="userService" class="com.frame.service.impl.UserServiceImpl">
- <constructor-arg name="id" value="10010"></constructor-arg>
- <constructor-arg name="name" value="小明"></constructor-arg>
- <constructor-arg name="birthday" ref="now"></constructor-arg>
- </bean>
- <!-- 声明 Date 对象 -->
- <bean id="now" class="java.util.Date"></bean>
标签中属性使用:
type: 指定要注入数据的数据类型.
index: 指定要注入的数据的索引位置的参数进行赋值, 索引值从 0 开始.
name: 指定给构造函数中指定名称的参数赋值.
value: 提供基本类型和 String 类型的数据.
ref: 引用其他 bean 类型数据.
第二种, 使用 set 方法提供
- public class UserServiceImpl{
- private Integer id;
- private String name;
- private Date birthday;
- public void setId(Integer id) {
- this.id = id;
- }
- public void setName(String name) {
- this.name = name;
- }
- public void setBirthday(Date birthday) {
- this.birthday = birthday;
- }
- }
- <bean id="userService" class="com.frame.service.impl.UserServiceImpl">
- <property name="id" value="10010"></property>
- <property name="name" value="张三"></property>
- <property name="birthday" ref="now"></property>
- </bean>
- <!-- 声明 Date 对象 -->
- <bean id="now" class="java.util.Date"></bean>
标签的属性:
name: 指定注入时 set 方法名称.
value: 提供基本类型和 String 类型的数据.
ref: 引用其他 bean 类型数据.
第三种, 注解形式
@Autowired: 自动注入
@Qualifier: 与 @Autowired 搭配使用, 按照名称进行注入.
@Resource: 通过 id 进行注入, 可单独使用.
注意: 这三种只能注入 bean 类型.
@Value: 用于注入基本类型和 String 类型的数据.
六, 总结
本篇博客是帮助理解 IoC 的概念, 通过上述总结能有一个更好更简单的理解. 对于 Spring 还有一个核心概念就是 AOP 思想, 这将会在下一篇博客种总结分享.
以上内容均是自主学习总结, 如有不适之处欢迎留言指正.
来源: https://www.cnblogs.com/fenjyang/p/11520646.html