项目地址:https://gitee.com/chuyunfei/learn.git
一、springIOC和DI的概念
:spring的IOC和DI完成了对对象的创建任务和对象之间的依赖关系的管理任务,实现了对象之间的解耦。
1.因为要对对象,也就是Bean进行管理,所以需一个存放Bean的容器,低级的是:XmlBeanFactory,高级的是:ApplicationContext系列。
2.因为要管理对象,所以需要一个用来描述对象及其依赖关系的一个配置文件,也就是常见的:applicationContext.xml 用于描述对象之间的依赖关系
3.因为需要对配置文件进行解析,所以需要一个解析对应配置的解析器,可能是注解解析器,也可能是XML解析器。
4.容器本身还有自己的生命周期管理,包括容器的刷新,容器及其bean的加载、创建、销毁等。
二、spring容器的构建过程
:spring加载配置文件并创建bean以及维护组件关系的过程,以及bean的生命周期管理
1、加载配置文件-》解析配置文件生成并注册类信息-》生成bean并依赖注入
2、bean的生命周期:构造-》注入属性-》注入环境-》初始化相关方法-》销毁
1. 执行bean的构造函数构建一个bean。
2. 为bean的属性注入值
3. 如果实现了BeanNameAware接口,调用setBeanName(String beanId)方法。
4. 如果实现了BeanFactoryAware接口,调用setBeanFactory(),传递的是Spring工厂本身。
5. 如果实现了ApplicationContextAware接口,调用setApplicationContext(ApplicationContext)方法,传入Spring上下文。
6. 如果关联了BeanPostProcessor接口,调用postProcessBeforeInitialization(Object obj, String s)方法,BeanPostProcessor经常被用作是Bean内容的更改,并且由于这个是在Bean初始化结束时调用After方法,也可用于内存或缓存技术
7. 如果在Spring配置文件中配置了init-method属性会自动调用其配置的初始化方法
8. 如果关联了BeanPostProcessor接口,会调用postAfterInitialization(Object obj, String s)方法
注意:以上工作完成以后就可以用这个Bean了,那这个Bean是一个single的,所以一般情况下我们调用同一个ID的Bean会是在内容地址相同的实例
9. 当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean接口,会调用其实现的destroy方法
10. 最后,如果这个Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的销毁方法
三、springIOC的配置文件相关配置基础知识
:运用配置文件对spring容器的bean进行关系管理,学会使用spring容器
1.配置文件:
-》位置:注意classpath是指编译过后的classes路径,不同的框架对这个的定义不同,一定要注意。
-》名称:最好遵循一些默认规则,但是配置文件的名称是可以随便写的
2.基础语法如下面的配置文件所示:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-4.0.xsd">
<!--
《bean节点的主要属性注释》
id和name属性的详细解释:
https://blog.csdn.net/shjniu3000/article/details/70226182
1、一个bean可以只有id,只有name或多个name,既有id也有name或多个name,还能id和name都没有(这个bean将是一个匿名的对象,不可见);
2、id只能有0或者1个,name可以有0或多个,但是多个name不能相同;
3、当有id时,id作为这个bean在容器内的唯一标识符,name作为这个bean的别名
当没有id时,第一个name作为这个bean在容器内的唯一标识符,其他的name作为这个bean的别名
4、可以通过别名和标识符来获取一个bean,因为他们都可以唯一标识一个bean,一个bean可以拥有一个标识符和多个别名
5、可以显式的为一个bean起别名:<alias name="[一个已经存在的标识符或者别名]" alias="[新的别名]"/>
6、name可以同时起多个,用 ';',' ',','进行分割,但是id不行,因为id是唯一的
class属性的详细解释:
1、springIOC容器使用反射的方法注册bean,所以需要一个默认的无参构造函数(非必须:使用构造注入就不需要默认构造器)
2、全限定类名用于反射
scope属性详细解释:
1、singleton为默认值,即单例,整个容器里面只有一个实例,而prototype会在每一次获取的时候都会创建一个新的实例
2、singleton的生命周期由容器来管理,但是prototype的生命周期得你自己管理
primary属性详细解释:
1、是否首要,当bean在autowired是byType时,如果首选为true就将其作为注入的bean,否则,当找到多余的相同类型的bean时将报错。
2、如果设置两个首要时也会报错,所以要保证在进行同一种的byType的自动注入时只有一个首要的bean
depends-on属性详细解释:
1、标识该bean在进行实例化之前,所依赖的bean必须先实例化,该bean要依赖那个bean,所以当依赖的bean没有时候实例化失败
-->
<bean id="person" name="person1;person2 person3,person4" class="springBasic.pojo.basic.Person"
scope="prototype" primary="true" depends-on="environment">
<!--
《使用property节点为bean的属性进行注入》
1、简单赋值可以使用:
-》简单的数据类型如字符串、数字可以使用value属性直接进行赋值,name是bean的属性名称
-》如果有特殊的字符需要使用特殊的赋值方式:<![CDATA[特殊字符]]>
-》赋值为null需要一节点的方式进行赋值:<value><null/></value>.
-》value既可以作为属性也可以作为一个子节点:<value>
2、如果使用<property>进行值注入时,必须为类的相应字段配置setter和getter,将字段变成属性
-->
<property name="idCard" value="2016211050"/>
<property name="name">
<value><![CDATA[<~^楚云飞^~>]]></value>
</property>
<property name="age" value="20"/>
</bean>
<!--起别名-->
<alias name="person" alias="person5"/>
<!--直接使用P命名空间进行属性赋值,值用属性,引用值用属性值-ref进行赋值-->
<bean name="personp" class="springBasic.pojo.basic.Person" p:idCard="2016211050" p:name="楚云飞" p:age="20" />
<!--
1、与student形成依赖闭环
2、自动装配
-->
<bean name="clazz" class="springBasic.pojo.basic.Clazz" depends-on="student" autowire="byName">
<property name="id" value="03011603"/>
<!--引用一个独立的集合:将自动的找到容器里面的域属性名称相同的bean给注入进来-->
<!--<property name="students" ref="students"/>-->
<property name="studentMap">
<!--在内部配置map,也可以在外部配置为一个独立的map,类似的还有set,property-->
<map>
<!--spel表达式:https://blog.csdn.net/a1610770854/article/details/51908390-->
<entry key="#{student.studentNumber != null?'2016211051':'2016211050'}" value-ref="student"/>
</map>
</property>
</bean>
<bean name="student" class="springBasic.pojo.basic.Student">
<!--
《使用指定的构造函数进行实例的构造》
1、使用<name,value/ref,[index],[type]>属性座标唯一确定一个构造函数
2、注意依赖回环,所以clazz使用字段依赖,而不是构造器依赖,否则将形成依赖闭环,导致创建失败
3、依赖闭环的避免:破坏构造器依赖闭环,spring会使用三级缓存来解决字段闭环依赖,但是无法解决构造器闭环依赖
依赖闭环的解决依赖于提前曝光实例引用的方法,所以必须先构造函数,否则将依赖管理失败。
https://blog.csdn.net/u010853261/article/details/77940767 //依赖闭环
-->
<!--Student(String name,Long idCard,Integer age,Long studentNumber,Double liveCost)-->
<constructor-arg name="name" value="楚云飞" index="0" type="java.lang.String"/>
<constructor-arg name="idCard" value="2016211050" index="1" type="java.lang.Long"/>
<constructor-arg name="age" value="20" index="2" type="java.lang.Integer"/>
<constructor-arg name="studentNumber" value="2016211050" index="3" type="java.lang.Long"/>
<constructor-arg name="liveCost" value="1200.20" index="4" type="java.lang.Double"/>
<!--使用字段依赖,因为和clazz形成了闭环依赖,所以使用字段依赖来解决闭环-->
<property name="clazz" ref="clazz"/>
<!--
为级联属性进行赋值必须先实例化后才能进行级联赋值:这个修改会对clazz本身产生影响
1、在进行bean注册时,先解析到clazz实例,但是其依赖student实例,所以转移到student实例来进行初始化,同时将clazz的
引用放置到三级缓存,在实例student的过程中需要依赖clazz实例,于是去一级缓存里面找,没有,于是重新回到clazz实例
的创建过程,并将student实例的引用放入三级缓存,然后clazz将构建完成并将其放置到一级缓存里面,然后将再次回到student
的构建过程,于是student的clazz字段将引用一个创建完成的clazz实例,在进行如下的级联赋值时,容器里面的clazz实例的
属性也将同时改变,至此,clazz的id属性为:03011602而不是03011603
-->
<property name="clazz.id" value="03011602"/>
</bean>
<!--一个独立的集合,是一个ArrayList,元素是Student-->
<util:list id="students" scope="singleton" list-class="java.util.ArrayList" value-type="springBasic.pojo.basic.Student">
<ref bean="student"/>
</util:list>
<!--通过一个配置文件配置出一个properties
id=2016211050;name=楚云飞;age=20
-->
<util:properties id="testProperties" location="classpath:test.properties"/>
<!--加载外部配置文件-->
<context:property-placeholder location="classpath:database.properties" order="1" file-encoding="UTF-8"/>
<bean name="database" class="springBasic.pojo.basic.Database">
<property name="user" value="${jdbc.user}"/>
<property name="password" value="${jdbc.password}"/>
<property name="driverClass" value="${jdbc.driverClass}"/>
<property name="jdbcUrl" value="${jdbc.jdbcUrl}"/>
</bean>
</beans>
类如下:
public class Clazz {
private Long id;
private List<Student> students;
private Map<Long,Student> studentMap;
public Clazz(){
}
public Clazz(Long id,List<Student> students,Map<Long,Student> studentMap){
this.id = id;
this.students = students;
this.studentMap = studentMap;
}
/****setter and getter ****/
}
public class Database {
private String user;
private String password;
private String driverClass;
private String jdbcUrl;
/****setter and getter ****/
}
public class Person {
private String name;
private Long idCard;
private Integer age;
public Person(){
}
public Person(String name,Long idCard,Integer age){
this.name = name;
this.idCard = idCard;
this.age = age;
}
/****setter and getter ****/
}
public class Student extends Person {
private Long studentNumber;
private Clazz clazz;
private Double liveCost;
public Student(){
}
public Student(Long studentNumber,Clazz clazz,Double liveCost){
this.studentNumber = studentNumber;
this.clazz = clazz;
this.liveCost = liveCost;
}
public Student(String name,Long idCard,Integer age,Long studentNumber,Clazz clazz,Double liveCost){
super(name,idCard,age);
this.studentNumber = studentNumber;
this.clazz = clazz;
this.liveCost = liveCost;
}
public Student(Long studentNumber,Double liveCost){
this.studentNumber = studentNumber;
this.liveCost = liveCost;
}
public Student(String name,Long idCard,Integer age,Long studentNumber,Double liveCost){
super(name,idCard,age);
this.studentNumber = studentNumber;
this.liveCost = liveCost;
}
/****setter and getter ****/
}