Spring IOC的应用
目录
一、IOC基础
1、maven导入spring的 jar包 和 单测包
2、bean的配置
2.1 纯xml模式
2.1.1 xml文件头
2.1.2 实例化Bean的三种方式
2.1.3 Bean的生命周期
2.1.4 Bean标签属性
2.1.5 DI依赖注入的xml配置
2.1.5.1 构造函数注入
2.1.5.2 set方法注入
2.1.5.3 复杂数据类型注入
2.2 xml与注解相结合模式(最常用)
2.2.1 xml中标签与注解的对应关系
2.2.2 DI依赖注入的注解实现方式
2.3 纯注解模式
3、启动IOC的方式
3.1 Java环境下启动IOC容器
3.2 Web环境下启动IOC容器
3.2.1 从xml启动容器
3.2.2 从配置类启动容器
二、IOC高级特性
1、lazy-Init延迟加载
2、自定义Bean的创建过程:FactoryBean
IOC的实现,主要靠 xml/注解 + 工厂模式 + 反射 实现的。通过xml或注解配置需要管理的bean,通过工厂模式+反射获取bean的实例。
spring中文文档
一、IOC基础
1、maven导入spring的 jar包 和 单测包
基础依赖context里面包含context、aop、beans、core、expression这些jar包
<!-- spring 基础依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.19</version>
</dependency>
<!--junit 单元测试依赖 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
2、bean的配置
bean的配置主要通过三种模式:纯xml模式、xml+注解模式、纯注解模式
2.1 纯xml模式
2.1.1 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
https://www.springframework.org/schema/beans/spring-beans.xsd">
2.1.2 实例化Bean的三种方式
方式一:使用无参构造函数
在默认情况下,它会通过反射调⽤⽆参构造函数来创建对象。如果类中没有⽆参构造函数,将创建失败。
<!--配置service对象-->
<bean id="userService" class="com.lagou.service.impl.TransferServiceImpl">
</bean>
方式二:使⽤静态⽅法创建
在实际开发中,我们使用的对象有时候并不是通过构造函数直接创建的,在创建过程中可能做很多额外操作,这时提供创建对象的方法恰好是个静态static方法,比如获取数据库连接DriverManager.getConnection()。在历史代码没有用spring框架,但是使用了工厂模式解耦,在接入spring后,可以通过这类对象就可以如下配置:
<!--使⽤静态⽅法创建对象的配置⽅式-->
<bean id="userService" class="com.lagou.factory.BeanFactory"
factory-method="getTransferService"></bean>
方式三:使用实例化方法创建
同使用静态方法创建类似,区别是用于获取对象的方法不再是static修饰的,而是普通方法
<!--使⽤实例⽅法创建对象的配置⽅式-->
<bean id="beanFactory"
class="com.lagou.factory.instancemethod.BeanFactory"></bean>
<bean id="transferService" factory-bean="beanFactory"
factory-method="getTransferService"></bean>
2.1.3 Bean的生命周期
spring支持6种作用范围,不同的作用范围生命周期不同。spring默认bean对象都是单例的。
实际开发中,用到做多的作⽤范围就是singleton(单例模式)和prototype(原型模式,也叫多例模式)。配置⽅式参考下⾯的代码:
<!--配置service对象-->
<bean id="transferService"
class="com.lagou.service.impl.TransferServiceImpl" scope="singleton">
</bean>
不同作⽤范围的⽣命周期:
-
单例模式:singleton
对象出⽣:当创建容器时,对象就被创建了。
对象活着:只要容器在,对象⼀直活着。
对象死亡:当销毁容器时,对象就被销毁了。
⼀句话总结:单例模式的bean对象⽣命周期与容器相同。
-
多例模式:prototype
对象出⽣:当使⽤对象时,创建新的对象实例。
对象活着:只要对象在使⽤中,就⼀直活着。
对象死亡:当对象⻓时间不⽤时,被java的垃圾回收器回收了。
⼀句话总结:多例模式的bean对象,spring框架只负责创建,不负责销毁。
2.1.4 Bean标签属性
-
id属性: ⽤于给bean提供⼀个唯⼀标识。在⼀个标签内部,标识必须唯⼀。
-
class属性:⽤于指定创建Bean对象的全限定类名。
-
name属性:⽤于给bean提供⼀个或多个名称。多个名称⽤空格分隔。
-
factory-bean属性:⽤于指定创建当前bean对象的⼯⼚bean的唯⼀标识。当指定了此属性之后,class属性失效。
-
factory-method属性:⽤于指定创建当前bean对象的⼯⼚⽅法,如配合factory-bean属性使⽤,则class属性失效。如配合class属性使⽤,则⽅法必须是static的。
-
scope属性:⽤于指定bean对象的作⽤范围。通常情况下就是singleton。当要⽤到多例模式时,可以配置为prototype。
-
init-method属性:⽤于指定bean对象的初始化⽅法,此⽅法会在bean对象装配后调⽤。必须是⼀个⽆参⽅法。
-
destory-method属性:⽤于指定bean对象的销毁⽅法,此⽅法会在bean对象销毁前执⾏。它只能为scope是singleton时起作⽤。
2.1.5 DI依赖注入的xml配置
依赖注⼊分类
按照注⼊的⽅式分类
构造函数注⼊:顾名思义,就是利⽤带参构造函数实现对类成员的数据赋值。
set⽅法注⼊:它是通过类成员的set⽅法实现数据的注⼊。(使⽤最多的)
按照注⼊的数据类型分类
基本类型和String:注⼊的数据类型是基本类型或者是字符串类型的数据。
其他Bean类型:注⼊的数据类型是对象类型,称为其他Bean的原因是,这个对象是要求出现在IoC容器中的。那么针对当前Bean来说,就是其他Bean了。
复杂类型(集合类型):注⼊的数据类型是Aarry,List,Set,Map,Properties中的⼀种类型
2.1.5.1 构造函数注入
使⽤要求是,类中提供的构造函数参数个数必须和配置的参数个数⼀致,且数据类型匹配。
同时需要注意的是,当没有⽆参构造时,则必须提供构造函数参数的注⼊,否则Spring框架会报错。
<bean id="userDao" class="com.test.UserDao">
<constructor-arg name="name" value="zhangsan"/>
<constructor-arg name="age" value="18"/>
<constructor-arg name="friend" ref="user"/>
</bean>
在使⽤构造函数注⼊时,涉及的标签是
constructor-arg
,该标签有如下属性:
name
:⽤于给构造函数中指定名称的参数赋值。
index
:⽤于给构造函数中指定索引位置的参数赋值。
value
:⽤于指定基本类型或者
String
类型的数据。
ref
:⽤于指定其他
Bean
类型的数据。写的是其他
bean
的唯⼀标识。
2.1.5.2 set方法注入
利⽤字段的set⽅法实现赋值的注⼊⽅式,需要有无参构造方法。此种⽅式在实际开发中是使⽤最多的注⼊⽅式。
<bean id="userDao" class="com.test.UserDao">
<property name="name" value="zhangsan"/>
<property name="age" value="18"/>
<property name="friend" ref="user"/>
</bean>
在使⽤
set
⽅法注⼊时,需要使⽤
property
标签,该标签属性如下:
name
:指定注⼊时调⽤的
set
⽅法名称。(注:不包含
set
这三个字⺟
,druid
连接池指定属性名称)
value
:指定注⼊的数据。它⽀持基本类型和
String
类型。
ref
:指定注⼊的数据。它⽀持其他
bean
类型。写的是其他
bean
的唯⼀标识。
2.1.5.3 复杂数据类型注入
复杂类型数据指的是集合类型数据。集合分为两类,⼀类是List结构(数组结构),⼀类是Map接⼝(键值对)
以set方法注入为例:类型分别是String[]、Map、List、Properties
<bean id="userDao" class="com.test.UserDao">
<property name="myArray">
<array>
<value>array1</value>
<value>array2</value>
<value>array3</value>
</array>
</property>
<property name="myMap">
<map>
<entry key="key1" value="value1"/>
<entry key="key2" value="value2"/>
</map>
</property>
<property name="mySet">
<set>
<value>set1</value>
<value>set2</value>
</set>
</property>
<property name="myProperties">
<props>
<prop key="prop1">value1</prop>
<prop key="prop2">value2</prop>
</props>
</property>
</bean>
在
List
结构的集合数据注⼊时,
array
,
list
,
set
这三个标签通⽤,另外注值的
value
标签内部
可以直接写值,也可以使⽤
bean
标签配置⼀个对象,或者⽤
ref
标签引⽤⼀个已经配合的
bean
的唯⼀标识。
在
Map
结构的集合数据注⼊时,
map
标签使⽤
entry
⼦标签实现数据注⼊,
entry
标签可以使
⽤
key
和
value
属性指定存⼊
map
中的数据。使⽤
value-ref
属性指定已经配置好的
bean
的引⽤。
同时
entry
标签中也可以使⽤
ref
标签,但是不能使⽤
bean
标签。⽽
property
标签中不能使
⽤
ref
或者
bean
标签引⽤对象
2.2 xml与注解相结合模式(最常用)
用xml的bean:第三⽅jar中的bean,⽐如德鲁伊数据库连接池
用注解的bean:⾃⼰开发的bean定义
xml+注解结合模式,xml⽂件依然存在,所以,spring IOC容器的启动仍然从加载xml开始
2.2.1 xml中标签与注解的对应关系
xml形式
|
对应的注解形式
|
标签
|
@Component("accountDao"),注解加在类上
bean的id属性内容直接配置在注解后面如果不配置,默认定义个这个bean的id为类的类名首字母小写;
另外,针对分层代码开发提供了@Componenet的三种别名@Controller、@Service、@Repository分别用于控制层类、服务层类、dao层类的bean定义,这四个注解的用法完全一样,只是为了更清晰的区分而已
|
标签的scope属性
|
@Scope("prototype"),默认单例,注解加在类上
|
标签的init-method属性
|
@PostConstruct,注解加在方法上,该方法就是初始化后调用的方法
|
标签的destory-method属性
|
@PreDestory,注解加在方法上,该方法就是销毁前调用的方法
|
2.2.2 DI依赖注入的注解实现方式
方法一:@Autowired(推荐使⽤)
@Autowired为Spring提供的注解,需要导⼊包org.springframework.beans.factory.annotation.Autowired。
@Autowired采取的策略为按照类型注⼊。
@Autowired
private AccountDao accountDao;
这样会去spring容器中找到类型为AccountDao的类,然后将其注⼊进来。这样会产⽣⼀个问题,当⼀个类型有多个bean值的时候,会造成⽆法选择具体注⼊哪⼀个的情况,这个时候我们需要配合着@Qualifier使⽤。
@Qualifier告诉Spring具体去装配哪个对象
@Autowired
@Qualifier(name="jdbcAccountDaoImpl")
private AccountDao accountDao;
方法二:@Resource
@Resource 注解由 J2EE 提供,需要导⼊包 javax.annotation.Resource。
@Resource 默认按照 ByName ⾃动注⼊。
@Resource
private AccountDao accountDao;
@Resource(name="studentDao")
private StudentDao studentDao;
@Resource(type="TeacherDao")
private TeacherDao teacherDao;
@Resource(name="manDao",type="ManDao")
private ManDao manDao;
如果同时指定了 name 和 type,则从Spring上下⽂中找到唯⼀匹配的bean进⾏装配,找不到则抛出异常。
如果指定了 name,则从上下⽂中查找名称(id)匹配的bean进⾏装配,找不到则抛出异常。
如果指定了 type,则从上下⽂中找到类似匹配的唯⼀bean进⾏装配,找不到或是找到多个,都会抛出异常。
如果既没有指定name,⼜没有指定type,则⾃动按照byName⽅式进⾏装配;
注意:
@Resource 在 Jdk 11中已经移除,如果要使⽤,需要单独引⼊jar包
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>
2.3 纯注解模式
改造xml+注解模式,将xml中遗留的内容全部以注解的形式迁移出去,最终删除xml,从Java配置类启动
对应注解
@Configuration 注解,表名当前类是⼀个配置类
@ComponentScan 注解,替代 context:component-scan
@PropertySource,引⼊外部属性配置⽂件
@Import 引⼊其他配置类
@Value 对变量赋值,可以直接赋值,也可以使⽤ ${} 读取资源配置⽂件中的信息
@Bean 将⽅法返回对象加⼊ SpringIOC 容器
3、启动IOC的方式
3.1 Java环境下启动IOC容器
-
ClassPathXmlApplicationContext:从类的根路径下加载配置⽂件(推荐使⽤)
-
FileSystemXmlApplicationContext:从磁盘路径上加载配置⽂件
-
AnnotationConfigApplicationContext:纯注解模式下启动Spring容器
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
UserDao userDao = (UserDao)context.getBean("userDao");
3.2 Web环境下启动IOC容器
3.2.1 从xml启动容器
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<!--配置Spring ioc容器的配置⽂件-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!--使⽤监听器启动Spring的IOC容器-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
3.2.2 从配置类启动容器
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<!--告诉ContextloaderListener知道我们使⽤注解的⽅式启动ioc容器-->
<context-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</context-param>
<!--配置启动类的全限定类名-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>com.lagou.edu.SpringConfig</param-value>
</context-param>
<!--使⽤监听器启动Spring的IOC容器-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
二、IOC高级特性
1、lazy-Init延迟加载
ApplicationContext 容器的默认⾏为是在启动服务器时将所有 singleton bean 提前进⾏实例化。提前实例化意味着作为初始化过程的⼀部分,ApplicationContext 实例会创建并配置所有的singleton bean。
默认:lazy-init="false",⽴即加载,表示在spring启动时,⽴刻进⾏实例化
设置 lazy-init 为 true 的 bean 将不会在 ApplicationContext 启动时提前被实例化,⽽是第⼀次向容器通过 getBean 索取 bean 时实例化的。
<bean id="testBean" calss="com.test.LazyBean" lazy-init="true" />
如果⼀个设置了⽴即加载的 bean1,引⽤了⼀个延迟加载的 bean2 ,那么 bean1 在容器启动时被实例化,⽽ bean2 由于被 bean1 引⽤,所以也被实例化,这种情况也符合延时加载的 bean 在第⼀次调⽤时才被实例化的规则。
也可以在容器层次中通过在 元素上使⽤ "default-lazy-init" 属性来控制延时初始化。如下⾯配置:
<beans default-lazy-init="true">
<!-- no beans will be eagerly pre-instantiated... -->
</beans>
如果⼀个 bean 的 scope 属性为 scope="pototype" 时,即使设置了 lazy-init="false",容器启动时也不会实例化bean,⽽是调⽤ getBean ⽅法实例化的。
2、自定义Bean的创建过程:FactoryBean
FactoryBean可以生成某一个类型的Bean实例,spring框架一些组件中会使用,还有其他框架和spring框架整合时会使用
实现类
public class UserFactoryBean implements FactoryBean<User> {
private String userInfo;//名字,年龄
public void setUserInfo(String userInfo) {
this.userInfo = userInfo;
}
//自定义bean的创建过程
@Override
public User getObject() throws Exception {
User user = new User();
String[] strings = userInfo.split(",");
user.setName(strings[0]);
user.setAge(Integer.parseInt(strings[1]));
return user;
}
//返回bean类型
@Override
public Class<?> getObjectType() {
return User.class;
}
//返回作用域是否单例,默认单例
@Override
public boolean isSingleton() {
return FactoryBean.super.isSingleton();
}
}
xml配置
<bean id="userFactoryBean" class="com.test.UserFactoryBean">
<property name="userInfo" value="张三,19"/>
</bean>
测试
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
User userDao = (User)context.getBean("userFactoryBean");
System.out.println(userDao);