IoC和DI要结合使用;
1,什么是IoC:(Inversion of Control)
使用对象时,由程序内部主动new创建对象转换为由外部提供对象,此过程中创建对象控制权由程序内部转移到外部,此思想称为控制反转;
创建对象的控制权由内部转移到外部就称为 : 控制反转;
Spring技术对IoC思想进行了实现:
- Spring提供了一个容器,称为IoC容器,用来充当IoC思想中的“外部”;
- IoC容器负责对象的创建、初始化等一系列工作,被创建或被管理的对象在IoC容器中统称为Bean;
bean 就是对象; IOC容器 : 也可以称为Spring容器;
IoC的容器结构:
- Service层依赖Dao层对象运行;简单来说,就是在IoC容器中:Service与Dao之间构建依赖关系;**(IoC容器用于管理依赖对象)**![image.png](https://cdn.nlark.com/yuque/0/2022/png/25975946/1647997211603-980c958d-4732-4c51-96b7-28360db2b85d.png#clientId=u010c2aa6-1b79-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=131&id=uded02d03&margin=%5Bobject%20Object%5D&name=image.png&originHeight=197&originWidth=817&originalType=binary&ratio=1&rotation=0&showTitle=false&size=13206&status=done&style=none&taskId=u62854f47-f58f-4b1d-ae3f-08a09f5b2e2&title=&width=544.6666666666666)
A,什么是DI:(Dependency Injection)
- **在IoC容器中建立bean与bean之间的依赖关系的整个过程,称为依赖注入;**
IoC的核心概念:(好处)
- **充分解决耦合度高的问题:(解耦)**
- **使用IoC容器管理bean(IoC)**; 或者可以这样说:将Bean交给Spring容器进行管理;
- 在IoC容器内将有依赖关系的bean进行关系绑定(DI)
- 最终的效果:
- 使用对象时不需要在程序**内部**中主动创建对象,需要使用对象的时候,直接到**外部**获取即可**;**
- 获取到的bean已经绑定了所有的依赖关系
IoC的使用思路:
- 管理什么?(Service与Dao)
- 如何将被管理的对象告知IoC容器?(配置)
- 被管理的对象交给IoC容器,如何获取到IoC容器(接口与实现类)
- IoC容器得到后,如何从容器中获取bean?(接口方法)
- 使用Spring导入哪些坐标?(pom.xml)
2,* IoC的创建(使用)步骤:
文件结构:
1. **导入依赖坐标:(pom.xml):**
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
2. **定义Spring管理的类和接口:**
//业务层的实现类
public class BookServiceImpl implements BookService {
@Override
public void sava () {
}
}
//业务层的接口
public interface BookService {
void sava();
}
3. **创建Spring配置文件,配置对应类作为Spring管理的bean:**![image.png](https://cdn.nlark.com/yuque/0/2022/png/25975946/1648000421344-a232891d-c9a5-4f1b-9100-29cead035cfb.png#clientId=u911002d7-c729-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=57&id=uf2ac4232&margin=%5Bobject%20Object%5D&name=image.png&originHeight=86&originWidth=383&originalType=binary&ratio=1&rotation=0&showTitle=false&size=4569&status=done&style=none&taskId=u21fb4a5c-1086-4a28-b06a-ce510d0736e&title=&width=255.33333333333334)
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="bookService" class="top.jztice5.service.impl.BookServiceImpl"></bean>
</beans>
4. **初始化IoC容器(Spring核心容器/Spring容器),通过容器获取bean:(这里用测试类进行举例):**
使用类路径创建IoC容器连接的方法:ClassPathXmlApplicationContext(“配置文件类路径”);
@Test
public void testBookService(){
//创建IoC容器对象,加载Spring核心配置文件
ClassPathXmlApplicationContext cxt = new ClassPathXmlApplicationContext("applicationContext.xml");
//从IoC容器中获取bean对象
BookService bookService = (BookService) cxt.getBean("bookService");
//调用bean对象实现save方法
bookService.save();
//关闭容器
cxt.close();
}
这里创建对象的时机:启动Spring容器的时候就已经把所有的bean创建;因为没有设置创建时机;(详见下方)
简单来说,bean 就是根据注入的配置 准备 去 new 的一个对象;(bean = 一个对象) 获取bean:就是实现Spring框架将这个bean”生产”出来到这个bean被获取的这个过程/直接将已经生产好的bean获取;
3,什么是DI:(Dependency Injection)
- 在IoC容器中建立bean与bean之间的依赖关系的整个过程,称为依赖注入;
简单来说,就是设置Spring框架创建什么(类型)对象,这个对象与哪个对象产生依赖,创建哪个对象及怎么获取对象,放到哪里;
A,实现依赖注入的步骤:
- 提供依赖对象对应的setter方法:
调用getBean就会由Spring框架创建;再通过set方法获取这个对象回来放到BookServiceImpl(实现类)中;
- 提供依赖对象对应的setter方法:
/**
业务层(service)的实现类
*/
public class BookServiceImpl implements BookService {
//定义数据层(dao)对象;
//到时候, 调用getBean就会由Spring框架创建;再通过set方法获取这个对象回来放到BookServiceImpl(实现类)中;
private BookDao bookDao;
//设置从容器中获取对象的set方法
public void setBookDao (BookDao bookDao) {
this.bookDao = bookDao;
}
@Override
public void save() {
}
}
2. **在Spring核心配置文件中配置service和dao之间的关系**:
<!-- 创建业务对象bean:将对象放到IoC容器中。使用实现类的全名,记住:不是接口 -->
<bean id = "bookService" class = "top.jztice5.service.impl.BookServiceImpl">
<!-- 调用set方法: name:指定set方法中被注入的参数属性名字 ; ref:是注入的bean对应id(引用类型) value:简单类型 -->
<property name = "bookDao" ref = "bookDao"></property>
<!--
//参数属性名
public void setBookDao (BookDao bookDao) {
this.bookDao = bookDao;
}
-->
</bean>
<!-- DAO对象也由Spring管理 -->
<bean class = "top.jztice5.dao.impl.BookDaoImpl" id = "bookDao"/>
注入类型的参数: ref:用于注入“引用类型”; value:用于注入“简单类型”(简单类型=基本数据类型+String类型);
B,* 依赖注入的方式及注入的主要类型:(其他的类型在下面)
- **setter注入*: <property>**
- **引用类型:**`<property name = "bookDao" ref = "bookDao"></property>`
<bean id = "bookService" class = "top.jztice5.service.impl.BookServiceImpl">
<!-- 调用set方法: name:指定set方法中被注入的属性名字 ; ref:是注入的bean对应id(引用类型) value:简单类型 -->
<property name = "bookDao" ref = "bookDao"></property>
</bean>
<!-- DAO对象也由Spring管理 -->
<!-- id:bean的id(引用类型) -->
<bean class = "top.jztice5.dao.impl.BookDaoImpl" id = "bookDao"/>
- **简单类型:**`<property name="connectionNumber" **value**="10"/>`
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
<property name="connectionNumber" value="10"/>
</bean>
- 和引用类型不同的是,不用绑定引用类型的id;
- **构造器注入: <constructor-arg>**
- **引用类型:**
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
<constructor-arg name="bookDao" ref="bookDao"/>
</bean>
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
public class BookDaoImpl implements BookDao {
private int connectionNumber;
//有参构造器
public BookDaoImpl(int connectionNumber) {
this.connectionNumber = connectionNumber;
}
}
- **简单类型:**
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
<constructor-arg name="connectionNumber" value="10"/>
</bean>
public class BookDaoImpl implements BookDao {
private int connectionNumber;
//有参构造器
public BookDaoImpl(int connectionNumber) {
this.connectionNumber = connectionNumber;
}
}
- **构造器注入——参数适配:(要有对应的有参构造方法)了解**
1. 按**形参类型**注入:
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
<constructor-arg type="int" value="10"/>
<constructor-arg type="java.lang.String" value="mysql"/>
</bean>
2. 按**形参位置**注入:
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
<constructor-arg index="0" value="10"/>
<constructor-arg index="1" value="mysql"/>
</bean>
C,注入方式的选择:
1. 强制依赖使用构造器进行,使用setter注入有概率不进行注入导致null对象出现;
1. 可选依赖使用setter注入进行,灵活性强;
1. Spring框架倡导使用构造器,第三方框架内部大多数采用构造器注入的形式进行数据初始化,相对严谨;
1. 如果有必要可以两者同时使用,使用构造器注入完成强制依赖的注入,使用setter注入完成可选依赖的注入;
1. 实际开发过程中还要根据实际情况分析,如果受控对象没有提供setter方法就必须使用构造器注入;
1. 自己开发的模块推荐使用setter注入;
除了 setter注入 和 构造器注入 还有 注解注入 ;一个三种注入方式;
· IoC的配置属性(xml方式):
4,* 整合的IoC代码:
//建议从dao层开始写起:
//dao层:**********
/**
* dao层接口
*/
public interface BookDao {
}
/**
* dao层实现类
*/
public class BookDaoImpl implements BookDao {
}
//业务层(service):**********
/**
* 业务层接口(存放实现方法)
*/
public interface BookService {
void save();
}
/**
* 业务层实现类
*/
public class BookServiceImpl implements BookService {
//private BookService bookService;
private BookDao bookDao;
//设置从容器中获取对象的set方法
public void setBookDao (BookDao bookDao) {
this.bookDao = bookDao;
}
@Override
public void save () {
System.out.println("Spring IoC");
}
}
//Spring的核心配置:**********
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 创建业务对象bean:将对象放到IoC容器中。使用实现类的全名,记住:不是接口 -->
<bean id = "bookService" class = "top.jztice5.service.impl.BookServiceImpl">
<!-- 调用set方法: name:指定set方法中被注入的属性名字 ; ref:是注入的bean对应id -->
<property name = "bookDao" ref = "bookDao"/>
</bean>
<!-- DAO对象也由Spring管理 -->
<bean class = "top.jztice5.dao.impl.BookDaoImpl" id = "bookDao"/>
</beans>
//初始化IoC容器,通过容器获取bean对象:
/**
* 测试类
*/
public class test {
@Test
public void testBookService(){
//创建IoC容器对象,加载Spring核心配置文件
ClassPathXmlApplicationContext cxt = new ClassPathXmlApplicationContext("applicationContext.xml");
//从IoC容器中获取bean对象
BookService bookService = (BookService) cxt.getBean("bookService");
//用接口对象(bean)调用save方法
bookService.save();
//关闭容器
cxt.close();
}
}
5,Bean的配置:
· 什么是bean:简单来说,bean 就是一个根据注入的配置 new 好的对象;
A,bean的基础配置: bean
简单来说,bean 就是一个根据注入的配置 new 好的对象; 获取bean:就是实现Spring框架将这个bean”生产”出来到这个bean被获取的这个过程/直接将已生产的bean获取; IoC是装bean的一个容器(仓库),其实理解为Spring工厂的生产线更为合理;
B,bean的别名配置: name
获取bean无论是通过id还是name获取,如果无法获取到,将抛出异常NoSuchBeanDefinitionException NoSuchBeanDefinitionException: No bean named ‘bookServiceImpl’ available
C,bean的作用范围配置: scope
在Spring中,默认每个类只会创建一个对象(单例模式),目的是有利于节省资源;
- 因此,哪怕是有创建多个对象的代码,也只会存在一个对象,所以,每个对象都是相等的;
- **设置单例与非单例模式:**
- `<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"scope="prototype" />`
- *** 单例与非单例创建对象的时机:**
- **单例**:当Spring核心文件被加载的时候,就会创建对应配置的对象;
- **非单例**:只有调用**getBean()**方法调用对应的bean才会创建对应的对象;
- **bean作用范围的说明**:
- **适合交给容器进行管理的bean**:
- 控制器对象 (以前用的Servlet做为控制器);
- 业务层对象 (如:BookService);
- 数据层对象 (如:BookDao或BookMapper);
- 工具对象 (如:CartUtils对象);
- **不适合交给容器进行管理的bean:**
- 封装实体的域对象(实体类对象 Book 包名:pojo,entity, domain);
, bean的生命周期:
第一种:默认情况下,启动 spring 容器便创建对象(遇到bean便创建对象)
*:设置创建bean对象的时机:(单例模式下的懒加载机制:)
第二种:在中添加属性lazy-init 设置为懒加载,默认值为false。
在spring的配置文件bean中有一个属性(懒加载) lazy-init=“default/true/false”
<bean lazy-init="false/ture">
A,true:在context.getBean的时候才要创建对象
- **优点**:
- 如果该bean中有大数据存在,则什么时候context.getBean,什么时候创建对象可以防止数据过早的停留在内存中,做到了懒加载;
- **缺点**:
- 如果spring配置文件中,该bean的配置有错误,那么在tomcat容器启动的时候,发现不了
B,false:在启动spring容器的时候创建对象
- **优点**:
- 如果在启动tomcat时要启动spring容器,那么如果spring容器会错误,这个时候tomcat容器不会正常启动;
- **缺点**: 如果存在大量的数据,会过早的停留在内存中;
注意:该配置只适用于单例模式,而非单例模式:默认调用getBean才创建对象; 懒加载机制只对单例bean有作用,对于多例bean设置懒加载没有意义。 懒加载只是延后了对象创建的时机,对象仍然是单例的。
- **生命周期:从创建到消亡的完整过程;**
- **bean生命周期:bean从创建到销毁的整体过程:**
1. 初始化容器:
1. 创建对象(内存分配);
1. 执行构造方法;
1. 执行属性注入(set操作);
1. 执行bean初始化方法;
2. 使用bean:
1. 执行业务方法操作;
3. 关闭/销毁容器:
1. 执行bean销毁方法;
- **bean生命周期:在bean创建后到销毁前做的一些事情;**
① bean的销毁时机:
- **关闭容器的方式:**
- 手动关闭:`close();`
- 注册关闭钩子,在虚拟机退出前关闭容器再退出虚拟机:`registerShutdownHook();操作`
②,bean的生命周期的控制:
init() | bean的初始化方法; |
---|---|
destory() | bean的销毁方法; |
- **生命周期控制方法及配置:**
- `**init-method="init"**`**:初始化方法指向;**
- `**destroy-method="destory"**`**:销毁方法指向;**
//配置文件代码:
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl" init-method="init" destroy-method="destory"/>
//定义类中的控制方法:
public class BookDaoImpl implements BookDao {
public void init () {
System.out.println("book init ...");
}
public void destory () {
System.out.println("book destory ...");
}
}
第一种:默认情况下,启动 spring 容器便创建对象(遇到bean便创建对象) 第二种:在spring的配置文件bean中有一个属性(懒加载) lazy-init=“default/true/false” ①、如果lazy-init为”default/false”在启动spring容器时创建对象(默认情况) ②、如果lazy-init为”true”,在context.getBean时才要创建对象
6,创建对象的三种方式:
A,使用无参构造方法实例化bean/创建对象(一种):
Spring默认使用无参的构造方法,因此,在出现有参的构造方法时,必须创建一个无参的构造方法(参考JavaSE中的标准类格式);
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
public class BookDaoImpl implements BookDao {
//无参构造器
public BookDaoImpl() {
}
//定义类的方法;
}
无参构造器在Spring的用途:仅能创建无属性数据对象及调用无属性数据的方法; 大部分情况下创建对象,都需要用到有参构造器实现对象属性依赖注入;
B,使用静态和实例工厂模式创建对象(两种):(不推荐)
- **静态工厂:**![image.png](https://cdn.nlark.com/yuque/0/2022/png/25975946/1648009610657-f726bb27-ac10-4439-b6f1-3f7aa9690d7d.png#clientId=u911002d7-c729-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=485&id=u4d98a02f&margin=%5Bobject%20Object%5D&name=image.png&originHeight=728&originWidth=1764&originalType=binary&ratio=1&rotation=0&showTitle=false&size=430377&status=done&style=none&taskId=u7cb8e14c-7ed9-42cd-a8fd-caea7ec65d7&title=&width=1176)
- **实例工厂:**![image.png](https://cdn.nlark.com/yuque/0/2022/png/25975946/1648009623248-e979616a-b056-4299-92c6-835c05ec0913.png#clientId=u911002d7-c729-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=424&id=u29ae3c57&margin=%5Bobject%20Object%5D&name=image.png&originHeight=636&originWidth=1746&originalType=binary&ratio=1&rotation=0&showTitle=false&size=466805&status=done&style=none&taskId=uc18a7f01-75f1-4551-82e4-5067283d50c&title=&width=1164)
- 两者区别:是否静态;
7,依赖的自动装配:
A,自动装配的概念:
- `**IoC容器根据bean所依赖的资源在容器中自动查找并注入到bean中的过程称为自动装配;**`
- **自动装配的配置目录所在的文件:spring.factories ;**
B,自动装配的方式:
- 按类型(常用)byType
- 按名称 byName
- 按构造方法 constructor
- 不启用自动装配 no
C,配置中使用bean标签中的autowire属性设置自动装配的类型:
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl" autowire="byType"/>
D,依赖自动装配的特征:
- 自动装配用于引用类型依赖注入,不能对简单类型进行操作
- 使用按类型装配时(byType)必须保障容器中相同类型的bean唯一,推荐使用;
- 使用按名称装配时(byName)必须保障容器中具有指定名称的bean,因变量名与配置耦合,不推荐使用;
- 自动装配优先级低于setter注入与构造器注入,同时出现时自动装配配置失效;
8,* 集合属性对象的注入:
A,注入数组对象:
//配置文件:
<bean id="bookService" class="top.jztice5.service.impl.BookServiceImpl">
<property name = "array">
//数组
<array>
<value>100</value>
<value>200</value>
<value>300</value>
</array>
</property>
</bean>
//定义类:
public class BookServiceImpl implements BookService {
private int[] array;
public void setArray (int[] array) {
this.array = array;
}
}
B,* 注入list对象(重点):
//配置文件:
<bean id="bookService" class="top.jztice5.service.impl.BookServiceImpl">
<property name = "array">
//数组
<list>
<value>100</value>
<value>200</value>
<value>300</value>
</list>
</property>
</bean>
//定义类:
public class BookServiceImpl implements BookService {
private List<Integer> list;
public void setList (List <Integer> list) {
this.list = list;
}
}
C,注入set对象:和List同理,略
这里的List,set对象和array(数组)对象的区别只是在语义上有不同,实际上用哪一个都是一样的效果的;
D,* 注入Map对象(重点):
//配置文件:
<bean id="bookService" class="top.jztice5.service.impl.BookServiceImpl">
<property name="map">
<map>
<entry key="country" value="china"/>
<entry key="province" value="henan"/>
<entry key="city" value="kaifeng"/>
</map>
</property>
</bean>
//定义类:
public class BookServiceImpl implements BookService {
private Map<String, String> map;
public void setMap (Map <String, String> map) {
this.map = map;
}
}
注意:这里注入的是简单类型;如果想更换为引用类型的话,将key和value改为key-ref和value-ref即可;
E,注入Properties对象:
(注意:Properties对象的结构是String类型的键值对)
//配置文件
<bean id="bookService" class="top.jztice5.service.impl.BookServiceImpl">
<property name="properties">
<props>
<prop key="country">china</prop>
<prop key="province">henan</prop>
<prop key="city">kaieng</prop>
</props>
</property>
</bean>
//定义类:
public class BookServiceImpl implements BookService {
Properties properties = new Properties();
public void setProperties (Properties properties) {
this.properties = properties;
}
}