IOC:Inversion of control控制反转-使用对象时由程序中主动new对象转为外部提供对象。
此过程中对象创建控制权由程序内部转为外部,此思想称为控制反转。
Spring技术对IOC思想提供了实现:
Spring提供一个容器,称为IOC容器,用来充当IOC思想的“外部”
IOC容器负责对象的创建、初始化等工作,被创建的对象在IOC容器中被称为Bean
适合交给容器管理的对象:表现层对象、业务层对象、数据层对象、工具对象
不适合交给容器管理的对象:封装实体的域对象
<!-- 1.pom文件中导入Spring对应的坐标spring-context,对应的版本是5.3.21-->
<dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.21</version>
</dependency>
<!-- 2.配置bean-->
<bean id="bookDao" class="com.kxg.exercise.dao.impel.BookDaoImpel" name="bookDao1,bokDao2" scope="prototype"/>
bean标签属性介绍:
id属性:给bean起名字
class属性:表示给bean定义类型
name属性:给bean起别名。可以定义多个,是用逗号、分号、空格分隔
scope属性:定义bean的作用范围。singleton:单例(默认) prototype:非单例
程序内部获取bean可以通过id或name,如果两个都没有得到将抛出异常NoSuchBeanDefinition
// 3.在程序中获取容器
ApplicationContext ctx = new ClassPathXmlApplicationContext("ApplicationContext.xml");// 可以使用文件系统配置路径:new ClassPathXmlApplicationContext("绝对路径")// 一次获取多个容器:new ClassPathXmlApplicationContext("a.xml","b.xml");
// 4.获取容器中的bean// 方法一:按照bean名称获取
BookDao bookDao1 = (BookDao)ctx.getBean("bookDao");// 方法二:按照bean类型获取(类型唯一)
BookDao bookDao2 = ctx.getBean("BookDao.class");// 方法三:使用bean名称获取指定类型
BookDao bookDao3 = ctx.getBean("bookDao", BookDao.class);
经过上面几步,对象的创建成功由程序内部转为程序外部
DI:Dependency Injection-依赖注入
在容器中建立bean与bean之间的依赖关系的整个过程,成为依赖注入
<!-- 在spring文件中配置类与类之间的关系:service与dao-->
<bean id="bookService" class="com.kxg.exercise.service.impel.BookServiceImpel"><property name="bookDao" ref="bookDao"/>
</bean>
property标签:表示配置当前bean的属性
name:表示配置哪一个具体的属性
ref:表示参照哪一个bean(两个类的绑定)
依赖注入的目的:充分解耦
通过控制反转、依赖注入,对象可以直接从外部获取,并且获取到的bean已经绑定了所有的依赖
这样虽然实现了解耦但是同时也出现了很多新问题:
bean类中的成员变量怎样实现初始化???
bean是怎样进行实例化的???
bean的生命周期???
外部bean怎样进行管理???
因此Spring也提供了很多解决方案。
依赖注入的方式:setter注入、构造器注入
setter注入:
简单数据类型:在bean类中设置set方法;xml文件中使用property标签value属性注入
引用数据类型:在bean类中设置set方法;xml文件中使用property标签ref属性注入建立关系
<property name="connectNumber" value="10"/>
<property name="bookDao" ref="bookDao"/>
构造器注入:
简单类型:bean类中提供构造方法;xml文件中<constructor-atg>标签中name、value属性
引用类型:bean类中提供构造方法;xml文件中<constructor-atg>标签中name、ref属性
<!-- 引用类型 -->
<constructor-arg name="bookDao" ref="bookDao"/>
<!-- 简单数据类型 --><!-- 通过参数名称匹配:没有达到程序解耦的问题 -->
<constructor-arg name="connectNumber" value="55"/>
<constructor-arg name="databaseName" value="mysql"/><!-- 通过参数值类型进行匹配,试图实现解耦问题 -->
<constructor-arg type="int" value="55"/>
<constructor-arg type="java.lang.String" value="mysql"/><!-- 通过参数前后位置匹配,试图实现解耦问题 -->
<constructor-arg index="1" value="55"/>
<constructor-arg index="2" value="mysql"/>
通过依赖注入的不同方式,第一个问题得到了解决
bean的实例化:
构造方法:调用类中的无参构造创建实例对象
静态工厂:调用工厂类中的静态方法实例化对象
1.添加factory-method属性创建类,不能仅配工厂名。否则仅仅创建的是工厂类不是bean对象
2.factory工厂类中方法添加static才能创建对象
// 静态工厂-static
public static BookDao getBookDao() {System.out.println("factory setup...");return new BookDaoImpel();
}
<bean id="bookDaoFactory" class="com.kxg.exercise.factory.BookDaoFactory" factory-method="getBookDao"/>
实例工厂:调用静态类中的非静态方法
容器中配置工厂类
容器中factory-bean找到相应的工厂类,factory-method找到对应的方法
// 非静态工厂
public BookDao getBookDao_() {System.out.println("factory2 setup...");return new BookDaoImpel();
}
<!--非静态工厂-->
<bean id="bookDaoFactory1" class="com.kxg.exercise.factory.BookDaoFactory"/>
<bean id="bookDao1" factory-method="getBookDao_" factory-bean="bookDaoFactory1"/>
使用FactoryBean实例化
创建相关FactoryBean类实现FactoryBean接口,实现相关的创建方法getObject
容器中加载FactoryBean类
public class bookDaoFactoryBean implements FactoryBean<BookDao> {// 得到bean实例:代替原始实例工厂中创建对象的方法@Overridepublic BookDao getObject() throws Exception {System.out.println("FactoryBean...");return new BookDaoImpel();}
}
<!-- FactoryBean-->
<bean id="bookDao2" class="com.kxg.exercise.factory.bookDaoFactoryBean"/>
BookDao bookDao5 = (BookDao) ctx.getBean("bookDao2");
System.out.println(bookDao5);
bean生命周期:
bean生命周期控制:从bean创建后到销毁前做的一些事情
1.类中提供生命周期的控制方法,xml文件中配置生命周期的方法
2.类实现InitializingBean、DisposableBean接口
初始化容器:创建对象(内存分配);执行构造方法;执行属性注入(set);执行bean初始化方法
使用bean:执行业务操作
关闭/销毁容器:执行bean销毁方法
bean销毁时机:容器关闭前触发bean的销毁
关闭容器的方法:
1.手动关闭容器:ConfigurableApplicationContext接口close()操作
2.注册关闭钩子,在虚拟机退出前先关闭容器在退出虚拟机:ConfigurableApplicationContext接口registerShutdownHook()
接下来就到了最后一个问题:外部bean怎样管理?
以数据源对象为例:
1.spring中开启context命名空间xmlns:context="http://www.springframework.org/schema/context"
2.在context空间中加载properties文件
system-properties-mode="NEVER":不加载系统属性 location="jdbc1.properties,jdbc2.properties:加载多个properties文件 location="classpath:*.properties:加载路径下的所有properties文件标准格式 location="classpath*:*.properties:从类路径或jar包中搜索并加载properties文件
3.使用属性占位符${}读取properties文件中的属性
<context:property-placeholder location="classpath:*.properties"/>
<bean class="com.alibaba.druid.pool.DruidDataSource"><property name="driverClassName" value="${jdbc.driver}"/><property name="url" value="${jdbc.url}"/><property name="username" value="${jdbc.username}"/><property name="password" value="${jdbc.password}"/>
</bean>
// 创建数据源对象
DataSource dataSource1 = (DataSource) ctx.getBean("dataSource1");
System.out.println(dataSource1);
此外spring还提供了依赖自动装配:直接在bean标签中使用autowire属性
按类型:autowire="byType"
使用类型装配时,必须保障容器中相同类型的bean唯一
按名称:autowire="byName"
使用名称自动装配时,必须保障容器中具有指定名称的bean
1.自动装配用于引用类型注入,不适用于简单类型
2.自动装配优先级低于setter注入与构造器注入,同时出现时自动装配失效