Spring IOC 复习
inversion of control
将创建对象的权利交给框架,包括di(dependency injection,依赖注入)和dl(dependency lookup,依赖查找),能削减计算机程序的耦合,即解除代码中的依赖关系
应用
xml
建立maven工程
-
导入jar包,pom.xml中加入spring-context依赖
<dependency> <groupid>org.springframework</groupid> <artifactid>spring-context</artifactid> <version>5.0.2.release</version> </dependency>
-
创建配置文件,在resources目录下新建bean.xml文件,并导入约束
<?xml version="1.0" encoding="utf-8"?> <beans xmlns:xsi="http://www.w3.org/2001/xmlschema-instance" xmlns="http://www.springframework.org/schema/beans" xsi:schemalocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> </beans>
-
配置bean,在bean.xml中的beans标签中加入bean标签配置bean
- 使用默认构造函数,如果类中无默认构造函数则无法创建
<bean id="bean的唯一标识(名字)" class="全限定类名"></bean>
- 使用某个类(工厂)的某个方法创建对象
<bean id="beanid" factory-bean="工厂类的全限定类名" factory-method="方法名"></bean>
- 使用某个类(静态工厂)的某个静态方法创建对象
<bean id="beanid" class="全限定类名" factory-method="方法名"></bean>
bean标签还有两个属性init-method和destroy-method指定构造方法和销毁方法
-
依赖注入
在bean中需要的数据或其他类的对象都由spring提供,只需配置,但不适合注入常变的数据
能注入的数据有三类:基本类型和string,其他bean,复杂类型/集合类型
注入方式有三种,在bean标签内用一个标签代表一个参数:
构造函数注入
<constructor-arg type(不常)="类型的全限定名" index(不常)="参数序号,从0开始" name(常用)="参数名" value="基本类型值" ref="引用其他bean"></constructor-arg>
优:获取bean对象时注入数据是必须的,否则无法创建成功
弊:改变了bean的实例化方式,在创建对象时如果用不到这些数据也必须提供
set方法注入(常用)
<property name="set方法对应名" value="基本类型值" ref="其他bean"></property>
优:创建对象时没有明确的限制,可以直接使用默认构造函数
弊:set方法可能不执行,如果某个成员必须有值,此方法不能保证
复杂类型的注入需要在参数标签中再加标签array,list,set,map,props
<property name="listpropname"> <array> <value>...</value> ... </array> </property> <property name="mappropname"> <map> <entry key="keyname" value="值"></entry> <entry key="keyname"> <value>值</value> </entry> </map> </property>
list结构的用list,array,set都一样,map结构的用map,props都一样
-
获取bean,在java代码中
//获取核心容器对象,也可以用beanfactory代替applicationcontext applicationcontext ac = new classpathxmlapplicationcontext("bean.xml"); //根据id获取bean对象 classofbean bean = (classofbean)ac.getbean("beanid");
- applicationcontext的三个常用实现类
classpathxmlapplicationcontext: 可以加载类路径下的配置文件
filesystemxmlapplicationcontext: 可以加载磁盘任意路径(有访问权限)下的配置文件
annotationconfigapplicationcontext: 用于读取注解创建容器
- applicationcontext和beanfactory的区别
前者在构建容器时采用立即加载的方式,一读取完配置文件就马上创建bean
后者在构建窗口时采用延迟加载的方式,当获取bean时才真正创建对象
注解
用xml文件 bean.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" xmlns:context="http://www.springframework.org/schema/context" xsi:schemalocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 告知spring在创建容器时要扫描的包 --> <context:component-scan base-package="包路径"></context:component-scan> </beans>
不用xml文件
创建一个配置类,类上加
@configuration指定当前类是一个配置类,获取容器时作为参数传入的配置类可以不加
@componentscan(basepackages="包路径")指定要扫描的包,basepackages可以换成value
获取容器时用new annotationconfigapplicationcontext(配置类.class)
@import 导入其他配置类,被导入的类不用再加@configuration
@propertysource("classpath:路径/文件名") 用指定properties文件的位置,属性value
@propertysources 有多个properties文件的时候用
导入配置文件后可以用@value(${...})读取
与bean有关的注解
@bean 用于将当前方法的返回值作为bean存入ioc容器中,属性name指定beanid默认值为当前方法的名称,如果方法有参数,spring框架会去容器中查找 有没有可用的bean对象,查找方式同autowired
-
用于创建对象的,相当于bean标签
@component 作用:把当前对象存入spring容器中;属性:value:指定bean的id,默认是当前类名且首字母小写;@controller @service @repository与@component完全一样,体现三层架构
-
用于注入数据的,相当于property标签
@autowired 作用:自动按照类型注入,如果容器中有唯一一个类匹配该数据的类型则注入,若没有匹配类型则报错,如果有多个则通过变量名寻找beanid对应的那个bean注入;位置:类上,方法上,变量上
@qualifier 作用:在按照类型注入的基础上再按照名称注入,在给类成员注入时不能单独使用必须和@autowired一起,但在给方法参数注入时可以;属性:value用于指定beanid
以上三种只能注入bean,不基本类型和string,复杂类型必须用xml配置
@value 作用:用于注入基本类型和string;属性:value用于指定数据的值,可以使用spel(spring的el表达式:${表达式})
@resource 作用:直接按照bean的id注入,可以独立使用;属性:name指定beanid
-
用于改变作用范围的,相当于bean标签的scope属性
@scope 作用:指定bean的作用范围;属性:value一般取singleton(默认)或prototype
-
和生命周期相关的,相当于bean标签的init-method和destroy-method属性(不常用)
@postconstruct
@predestroy
与junit整合
-
添加spring-test和junit依赖,spring5.x要求junit 4.12及以上
<dependency> <groupid>org.springframework</groupid> <artifactid>spring-test</artifactid> <version>5.0.2.release</version> </dependency> <dependency> <groupid>junit</groupid> <artifactid>junit</artifactid> <version>4.12</version> <scope>test</scope> </dependency>
-
使用junit提供的一个注解@runwith把原有的main方法替换成spring提供的
@runwith(springjunit4classrunner.class)
-
告知spring的运行器,是基于xml还是注解,并说明位置
@contextconfiguration
locations: 指定xml文件位置
classes: 指定注解类所在位置
常见问题
-
bean的作用范围
bean标签和@component注解的scope属性:用于指定bean的作用范围
取值:singleton 单例的(常用)
prototype 多例的(常用)
request 作用于web应用的请求范围
session 作用于web应用的会话范围
global-session 集群环境的会话范围(全局会话范围),若不是集群环境,则为session
-
bean的生命周期
singleton: 容器创建时对象创建(applicationcontext),容器销毁时对象销毁
prototype: 使用对象时创建对象,当对象长时间不用且长时间没有别的对象引用时,由java的垃圾回收器回收
原理
ioc只需要导入spring-context依赖
阅读源码:github下载源码,导入idea,运行调试加注释
spring项目启动后,会扫描所有类,找到符合bean标准(加注释或xml配置)的类,通过一个描述类(beandefinition)存放bean的信息,issingleton属性默认为true,再将描述类存入一个map(beandefinationmap)中
扫描之后bean的获取:通过beandefinationmap里的key(bean名)找到value(beandefination)的class属性进行反射,找到bean对应的类。与class名没有关系,可以通过自己实现beanfactory接口,从beanfactory中获取描述类beandefinition,setbeanclass(another.class)将bean和class的对应关系更改
扫描前beandefinationmap中已有7个bean,包括appconfig
main() --> annotationconfigapplicationcontext(appconfig.class) --> refresh() --> invokebeanfactorypostprocessors(beanfactory)(扫描并存入bean)
后置处理器beanpostprocessors的子类会被自动扫描并对bean进行多层代理,默认有九次(进入依赖注入等操作),实现此接口可以对bean的创建过程进行干预
spring上下文或环境:为了实现ioc或aop所需要的各种spring组件的集合,如beandefination,beanpostprocessor(后置处理器),defaultlistablebeanfactory,beandefinitionmap,单例池等等
扫描只是将bean对应的类的信息存入map中,并不会实例化类,即lazy(懒加载)
factorybean是一个特殊的bean,beanfactory是bean工厂
单例bean第一次被实例化后就会存入名为singleobjects的map(单例池)中,再次getbean()时就不会new,(保证单例)
spring容器:不止单例池,还包括其他ioc组件
-
循环依赖:
单例bean的循环依赖
issingletoncurrentlyincreation判断这个bean是否在创建过程中
单例池只存放完整的bean
get(a) --> new a --> auto b --> get(b) --> new b --> auto a --> get(a)
获取a,新建a,发现a需要注入b且单例池中没有b且b不在创建过程中,获取b,发现b需要注入a且单例池中没有a且a正在创建过程中,获取a并注入b,将b注入a,将a放入单例池