本文仅限于记录自己学习java路上对于 IOC 的理解,主要是自己根据他人文献的总结和学习,文中也没啥有深度的东西,大牛和大佬可以忽略本文,以免浪费您宝贵的时间
1 原生开发的问题
传统Web开发存在硬编码所造成的过度程序耦合(例如:Service中作为属性Dao对象)。
部分Java EE API较为复杂,使用效率低(例如:JDBC开发步骤)。
侵入性强,移植性差(例如:DAO实现的更换,从Connection到SqlSession)。
2 Spring概述
Spring是一个项目管理框架,同时也是一套Java EE解决方案。
Spring是众多优秀设计模式的组合(工厂、单例、代理、适配器、包装器、观察者、模板、策略)。
Spring并未替代现有框架产品,而是将众多框架进行有机整合,简化企业级开发,俗称"胶水框架"。
官方网站:https://spring.io/
3 Spring架构组成
![](https://i-blog.csdnimg.cn/blog_migrate/90f15799e0b8ab985d1d7c56cca8a3f8.png)
4 Spring IOC
4.1 什么是IOC?
IOC是Inversion of Control的缩写,多数书籍翻译成“控制反转”。
控制反转,把对象创建和对象之间的调用过程,交给Spring进行管理。
IOC也叫 依赖注入
“哪些方面的控制被反转了呢?”,Martin Fowler经过详细地分析和论证后
他得出了答案:“获得依赖对象的过程被反转了”。控制被反转之后,获得依赖对象的过程由自身管理变为了由IOC容器主动注入。于是,他给“控制反转”取了一个更合适的名字叫做“依赖注入(Dependency Injection)”。
其实依赖注入,就是由IOC容器在运行期间,动态地将某种依赖关系注入到对象之中
底层
4.2 IOC 的主要作用是什么?
1996年,Michael Mattson在一篇有关探讨面向对象框架的文章中,首先提出了IOC 这个概念。借助于“第三方”实现具有依赖关系的对象之间的解耦.
反转了依赖关系的满足方式,由之前的自己创建依赖对象,变为由工厂推送。(变主动为被动,即反转)
解决了具有依赖关系的组件之间的强耦合,使得项目形态更加稳健.
扩充话题: 解耦
解耦通俗地说就是两个东西原来互相影响,现在让他们独立发展,每个地方都只做一件事情,只要一个地方负责了多项事情,就存在解耦的可能。
简单通俗的理解就是,现实生活中的很多方面都要解耦,比如usb接口使硬件解耦了,java虚拟机使软件与具体操作系统解耦了,tcp/ip协议使使用网络与具体的硬件网络解耦了
解耦带来的好处是:①提高问题的解决概率;②提高问题的解决效果;③提高问题的解决速度;④降低将来爆发隐患的可能性。
4.3 IOC初体验
@Data
public class User {
private Integer id;
private String username;
private String password;
private Car car;
}
在applicationContext.xml中的< beans >内部配置bean标签
<!-- bean创建对象并赋值
该id属性是标识单个 bean 定义的字符串。(不能重复)
该class属性定义 bean 的类型并使用完全限定的类名。
-->
<bean id="OneCar" class="com.pojo.Car">
<property name="cid" value="1"></property>
<property name="cname" value="五菱"></property>
</bean>
<bean id="user" class="com.pojo.User">
<property name="id" value="1"></property>
<property name="username" value="张三"></property>
<property name="password" value="123123"></property>
<property name="car" ref="OneCar"></property>
</bean>
4.4测试
//加载配置文件
ApplicationContext applicationContext =
new ClassPathXmlApplicationContext("applicationContext.xml");
//创建Bean
User user =(User)applicationContext.getBean("user");
//输出
System.out.println(user);
4.5 复杂类型的注入/集合类型的注入
1、用于给数组结构注入的标签:array
2、用于给List结构集合注入的标签:list set
3、用于个Map结构集合注入的标签:map properties
//实体类
@Data
public class CollectionVo {
private String [] arr;
private List list;
private Set set;
private Map map;
private Properties properties;
}
<bean id="sb" class="com.pojo.CollectionVo">
<property name="arr">
<!-- 数组 -->
<array>
<value>亢龙有悔</value>
<value>潜龙勿用</value>
<value>见龙在田</value>
</array>
</property>
<!-- list集合-->
<property name="list">
<list>
<value>战龙在野</value>
<value>泽里龙吟</value>
<value>泽里龙吟</value>
<ref bean="user"></ref>
</list>
</property>
<!-- set集合-->
<property name="set">
<set>
<value>神龙摆尾</value>
<value>龙跃在渊</value>
<value>龙涉大川</value>
<ref bean="user"></ref>
</set>
</property>
<!-- map集合-->
<property name="map">
<map>
<entry key="1" value="龙啸万里"></entry>
<entry key="2" value="群龙无首"></entry>
<entry key-ref="user" value-ref="OneCar"></entry>
</map>
</property>
<!-- properties -->
<property name="properties">
<props>
<prop key="username">root</prop>
<prop key="password">666666</prop>
<prop key="url">jdbc:mysql:///db_name</prop>
<prop key="driverClassName">com.mysql.cj.jdbc.Driver</prop>
</props>
</property>
</bean>
构造注入
@Data
public class User {
private Integer id;
private String username;
private String password;
private Car car;
public User(Integer id, String username, String password, Car car) {
this.id = id;
this.username = username;
this.password = password;
this.car = car;
System.out.println("第一个构造器");
}
//这里特意给id属性位置
public User(String username, String password, Car car, Integer id ) {
this.id = id;
this.username = username;
this.password = password;
this.car = car;
System.out.println("第二个构造器");
}
}
<!--
使用的标签:constructor-arg
标签出现的位置:bean标签的内部
标签中的属性:
name:用于指定给构造函数中指定名称的参数赋值
value:用于提供基本类型和String类型的数据
ref:用于指定其他的bean类型数据。它指的就是在spring的Ioc核心容器中出现过的bean对象
type:用于指定要注入的数据的数据类型,该数据类型也是构造函数中某个或某些参数的类型
index:用于指定要注入的数据给构造函数中指定索引位置的参数赋值。索引的位置是从0开始
-->
<bean id="car" class="com.pojo.Car">
<property name="cid" value="1"></property>
<property name="cname" value="白眉大虾"></property>
</bean>
<bean id="user" class="com.pojo.User">
<constructor-arg name="id" value="1" index="3"></constructor-arg>
<constructor-arg name="username" value="白毛猫王" index="0"></constructor-arg>
<constructor-arg name="password" value="6666" index="1"></constructor-arg>
<constructor-arg name="car" ref="car" index="2"></constructor-arg>
</bean>
5 Spring对bean的管理细节
注意:需要根据场景决定对象的单例(IOC中实例唯一)、多例模式(调用就是创建新对象)。
可以共用:Service、DAO、SqlSessionFactory(或者是所有的工厂)。
不可共用:Connection、SqlSession、ShoppingCart。
<!--
singleton(默认):每次调用工厂,得到的都是同一个对象。
prototype:每次调用工厂,都会创建新的对象。
-->
<bean id="mc" class="com.basic.MyClass" scope="singleton" />
6 bean对象的生命周期
生命周期属性:配置一个方法作为生命周期初始化方法. spring会在对象创建之后立即调用: init-method配置一个方法作为生命周期的销毁方法. spring容器在关闭并销毁所有容器中的对象之前调用: destory-method
<!--
该id属性是标识单个 bean 定义的字符串。(不能重复)
该class属性定义 bean 的类型并使用完全限定的类名。
scope:常用的是单例(singleton)和多例(prototype)
init-method:创建对象后执行的初始化方法
destroy-method:对象销毁后执行(如果是多例模式下不执行)
-->
<bean id = "user" class = "com.pojo.User"
scope="singleton" init-method="initUser" destroy-method="destroyUser">
</bean>
/**
* User实体类
*/
public class User {
private Integer id;
private String name;
private String password;
public User() {
System.out.println("无参构造");
}
/**
* 初始化方法,创建对象后执行
*/
public void initUser() {
System.out.println("User 初始化方法");
}
/**
* 销毁方法,销毁spring容器中对象后执行
*/
public void destroyUser() {
System.out.println("User 销毁方法");
}
}
/**
* 测试生命周期相关方法
*/
@Test
public void testLife() {
//创建ClassPathXmlApplicationContext对象
ClassPathXmlApplicationContext applicationContext
= new ClassPathXmlApplicationContext("applicationContext.xml");
//获取对象
User user = (User) applicationContext.getBean("user");
System.out.println(user);
//关闭
applicationContext.close();
}
7 IOC带来的好处
IOC核心思想 在于
第一,资源集中管理,实现资源的可配置和易管理
第二,降低了使用资源双方的依赖程度,也就是我们说的耦合度