Spring框架IOC容器和DI依赖注入
介绍
IOC(Invertion Of Control):控制反转,使用对象时,由使用new创建对象转变为由外部提供对象,此过程中对象的创建控制权由程序转移到外部的思想称之为控制反转.文章来源:https://www.toymoban.com/news/detail-408362.html
DI(Dependency Injection):依赖注入,在容器中建立bean与bean之间的关系的过程,称之为依赖注入文章来源地址https://www.toymoban.com/news/detail-408362.html
入门案例
pom.xml依赖引入
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.shifan</groupId>
<artifactId>spring-01-ioc-di-bean</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<dependencies>
<!--导入spring依赖,导入后才有spring类型的配置文件-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
</dependency>
</dependencies>
</project>
BookDao
public interface BookDao {
void save();
}
BookDaoImpl
public class BookDaoImpl implements BookDao {
@Override
public void save() {
System.out.println("BookDao save...");
}
}
BookService
public interface BookService {
void save();
}
BookServiceImpl
public class BookServiceImpl implements BookService {
private BookDao bookDao;
//给spring容器注入提供set方法
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
@Override
public void save() {
bookDao.save();
}
}
applicationContext.xml
<?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,将该类对象的创建交由Spring容器管理
id为bean的唯一标识,不可重复
class为bean的类型,该属性不能写接口全类名,因为接口无法创建对象
name为bean的别名,ref属性可以使用其他bean的别名,别名可以有多个,用逗号,空格,分号隔开都行
scope为bean的作用范围,默认是singleton单例,可设置为prototype非单例
若是有状态的对象,即该对象有成员变量用于存储数据,则不适合交给容器管理,可能存在线程安全问题
spring通过反射获取类中的无参构造创建对象
-->
<bean id="bookDaoImpl" class="com.shifan.dao.impl.BookDaoImpl" name="bookDao"/>
<!--
配置service和dao的关系
使用property标签配置当前bean中的的属性
name="bookDao"中`bookDao`的作用是让Spring的IOC容器在获取到名称后,将首字母大写,前面加set找对应的`setBookDao()`方法进行对象注入
ref为参照的容器中的bean
autowire="byType" 按照类型自动注入,要求容器中相同类型的bean唯一
autowire="byName" 按照名称注入,要求容器中有指定名称的bean,因变量名与配置耦合,不推荐使用
自动装配优先级低于setter注入与构造器注入,同时出现时自动装配配置失效
-->
<bean id="bookServiceImpl" class="com.shifan.service.impl.BookServiceImpl" autowire="byType">
<property name="bookDao" ref="bookDaoImpl"/>
</bean>
</beans>
测试类
//获取spring容器
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
@Test
public void testGetBean(){
//获取bean
/* BookDao bookDao = (BookDao) applicationContext.getBean("bookDaoImpl");
bookDao.save();*/
BookService bookServiceImpl = (BookService) applicationContext.getBean("bookServiceImpl");
bookServiceImpl.save();
}
@Test
public void testBeanAlias(){
//根据别名获取bean
BookDao bookDao = (BookDao) applicationContext.getBean("bookDao");
bookDao.save();
}
Bean实例化
静态工厂实例化
OrderDao
public interface OrderDao {
void order();
}
OrderDaoImpl
public class OrderDaoImpl implements OrderDao {
@Override
public void order() {
System.out.println("ordering orders...");
}
}
OrderDaoFactory
package com.shifan.factory;
import com.shifan.dao.OrderDao;
import com.shifan.dao.impl.OrderDaoImpl;
public class OrderDaoFactory {
public static OrderDao getOrderDao(){
System.out.println("order dao service options...");//模拟对象创建前的必要业务操作
return new OrderDaoImpl();
}
}
applicatoinContext.xml
<!--
静态工厂实例化(了解)
指定静态工厂类全类名及其创建对象的方法
-->
<bean id="orderDao" class="com.shifan.factory.OrderDaoFactory" factory-method="getOrderDao"/>
测试类
@Test
public void testStaticFactory(){
//获取通过静态工厂创建的对象
OrderDao orderDao = (OrderDao) applicationContext.getBean("orderDao");
orderDao.order();
}
实例工厂实例化
UserDao
public interface UserDao {
void save();
}
UserDaoImpl
public class UserDaoImpl implements UserDao {
@Override
public void save() {
System.out.println("user dao save...");
}
}
UserDaoFactory
public class UserDaoFactory {
public UserDao getUserDao(){
System.out.println("user dao service options...");//模拟对象创建前的必要业务操作
return new UserDaoImpl();
}
}
applicationContext.xml
<!--
实例工厂实例化(了解)
配置实例工厂bean
配置实例工厂创建的对象的bean:指定工厂bean,工厂创建bean的方法
-->
<bean id="userDaoFactory" class="com.shifan.factory.UserDaoFactory"/>
<bean id="userDao" factory-method="getUserDao" factory-bean="userDaoFactory"/>
测试类
@Test
public void testInstanceFactory(){
//获取通过实例工厂bean创建的对象
UserDao userDao = (UserDao) applicationContext.getBean("userDao");
userDao.save();
}
使用实例工厂实现FactoryBean接口实例化
UserDaoFactoryBean
/**
* 使用FactoryBean简化实例工厂方式实例化bean
* FactoryBean还有一个方法boolean isSingleton()
* 用于设置是否为单例,不重写则默认返回true,表示是单例
*/
public class UserDaoFactoryBean implements FactoryBean<UserDao> {
/**
* 被重写后,在方法中进行对象的创建并返回
* @return
* @throws Exception
*/
@Override
public UserDao getObject() throws Exception {
return new UserDaoImpl();
}
/**
* 被重写后,主要返回的是被创建类的Class对象
* @return
*/
@Override
public Class<?> getObjectType() {
return UserDao.class;
}
}
applicationContext.xml
<!--
使用实例工厂实现FactoryBean接口实例化bean(掌握)
这种方式Spring整合其他框架时会用到
-->
<bean id="userDaoFactoryBean" class="com.shifan.factory.UserDaoFactoryBean"/>
测试类
//使用FactoryBean简化
@Test
public void testFactoryBean(){
UserDao userDao = (UserDao) applicationContext.getBean("userDaoFactoryBean");
userDao.save();
}
Bean的生命周期
BrandService
public interface BrandService {
void save();
}
BrandServiceImpl
/**
实现InitializingBean和DisposableBean接口,重写方法,简化初始化方法和销毁方法的编写与配置,实现bean生命周期控制(了解)
*/
public class BrandServiceImpl implements BrandService , InitializingBean, DisposableBean {
private BrandDao brandDao;
//使用构造器注入brandDao
public BrandServiceImpl(BrandDao brandDao){
this.brandDao = brandDao;
}
//使用set方法注入brandDao
/* public void setBrandDao(BrandDao brandDao) {
System.out.println("set...");
this.brandDao = brandDao;
}*/
@Override
public void save() {
brandDao.save();
}
@Override
public void destroy() throws Exception {
System.out.println("release resource...");
}
/*
afterPropertiesSet:属性设置后
即brandDao被注入后才执行
*/
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("initialize resource...");
}
}
BrandDao
public interface BrandDao {
void save();
}
BrandDaoImpl
public class BrandDaoImpl implements BrandDao {
private String name;
private int price;
public void setName(String name) {
this.name = name;
}
public void setPrice(int price) {
this.price = price;
}
@Override
public void save() {
System.out.println("brand dao save..."+name+","+price);
}
//初始化方法
public void init(){
System.out.println("init resource...");
}
//销毁方法
public void destroy(){
System.out.println("rel resource...");
}
}
applicatonContext.xml
<!--
bean的生命周期控制
bean创建后方法:init-method,可用于初始化资源
bean销毁前方法:destroy-method,可用于释放资源
-->
<bean id="brandDao" class="com.shifan.dao.impl.BrandDaoImpl" init-method="init" destroy-method="destroy">
<!--普通参数注入-->
<property name="name" value="飘柔"/>
<property name="price" value="20"/>
</bean>
<bean id="brandService" class="com.shifan.service.impl.BrandServiceImpl">
<!--<property name="brandDao" ref="brandDao"/>-->
<!--
构造器注入引用数据类型(name和ref)
构造器也可用于注入普通参数(name和value)
name属性需要保证和构造方法中的形参名一致,那么这就出现了耦合问题
解决方案一:
类型注入,使用type属性替换name属性,按照属性注入,但是当存在多个相同类型时就会无法精准注入
方案二:
索引注入,使用index属性替换name属性,按照索引下标0,1...注入,但当形参顺序发生变化时,又会出现耦合问题
如何选择:
强制依赖使用构造器注入,使用setter注入有概率不进行注入导致null对象出现
强制依赖指对象创建过程中必须的参数
可选依赖使用setter方法注入,灵活性更强
可选依赖指对象创建过程中可有可无的参数
自己开发的模块推荐使用setter注入
-->
<constructor-arg name="brandDao" ref="brandDao"/>
</bean>
测试类
@Test
public void testBeanLifeCycle(){
/* BrandDao brandDao = (BrandDao) applicationContext.getBean("brandDao");
brandDao.save();*/
BrandService brandService = (BrandService) applicationContext.getBean("brandService");
brandService.save();
/*
调用ClassPathXmlApplicationContext类的close方法关闭容器
避免IOC容器因为JVM退出而来不及销毁bean对象,导致销毁方法不执行
((ClassPathXmlApplicationContext)applicationContext).close();
*/
/*
注册钩子关闭容器
在容器未关闭之前,设置回调函数,让JVM在退出之前回调此函数关闭容器
与close方法相比:两种方法都能关闭容器,close方法是调用后就关闭容器,
而registerShutdownHook方法是在JVM退出之前调用关闭容器
*/
((ClassPathXmlApplicationContext)applicationContext).registerShutdownHook();
}
其他类型依赖注入
applicationContext.xml
<!--
集合注入
- property标签表示setter方式注入,构造方式注入constructor-arg标签内部也可以写`<array>`、`<list>`、`<set>`、`<map>`、`<props>`标签
- List的底层也是通过数组实现的,所以`<list>`和`<array>`标签是可以混用
- 集合中要添加引用类型,只需要把`<value>`标签改成`<ref>`标签,这种方式用的比较少
-->
<!--
数组类型数据
<property name="array">
<array>
<value>100</value>
<value>200</value>
<value>300</value>
</array>
</property>
-->
<!--
List类型数据
<property name="list">
<list>
<value>itcast</value>
<value>itheima</value>
<value>boxuegu</value>
<value>chuanzhihui</value>
</list>
</property>
-->
<!--
set类型数据
<property name="set">
<set>
<value>itcast</value>
<value>itheima</value>
<value>boxuegu</value>
<value>boxuegu</value>
</set>
</property>
-->
<!--
map类型数据
<property name="map">
<map>
<entry key="country" value="china"/>
<entry key="province" value="henan"/>
<entry key="city" value="kaifeng"/>
</map>
</property>
-->
<!--
Properties类型数据
<property name="properties">
<props>
<prop key="country">china</prop>
<prop key="province">henan</prop>
<prop key="city">kaifeng</prop>
</props>
</property>
-->
到了这里,关于Spring框架IOC容器和DI依赖注入的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!