Spring通过IOC容器来管理所有Java对象的实例化和初始化,控制对象与对象之间的依赖关系.将IoC容器管理的Java对象称之为Spring Bean,它与使用关键字new创建的Java对象没有任何区别.
IoC控制反转
IoC容器是Spring框架中最重要的核心组件之一.
在传统Java应用中,一个雷想要调用另一个类中的属性或方法,通常会现在其代码中通过new Object()的方式将后者的对象创建出来,然后才能实现属性或方法的调用.前者成为"调用者",后者称为"被调用者",也就是说,调用者掌握着被调用者的创建的控制权.
而在Spring中,Java的对象创建的控制权是掌握再IOC容器手里的.通过再XML配置文件,注解,Java配置类等方式,对Java对象进行定义;Spring启动时,IoC容器会自动根据对象定义,将这些对象创建并管理起来;当想要某个bean时,直接从IOC容器中获取,不需要手动通过代码创建.
IoC在代码层并没有多大的变化,是从思想层面发了"主从换位"的改变,云本调动着是主动的一方,需要什么资源主动自己创建.但在Spring应用中,IoC容器掌握着主动权,调用者则变成了被动的一方,等待IoC容器创建它所需要的对象(Bean).
依赖注入(DI)
在面向对象中,对象和对象之间是存在一种叫做"依赖"关系的.简单说,依赖关系是在一个对象中需要用到另外一个对象,即对象中存在一个属性,该属性是另外一个类的对象.
在对象创建过程中,Spring会自动根据依赖关系,将它依赖的对象注入到当前对象中,这就是所谓的"依赖注入".
IoC的工作原理
也是实现解耦的原理:由于对象的基本信息,对象之间的依赖关系都是在配置文件中定义的,并没有在代码中紧密耦合,即使对象发生改变,只需在配置文件中进行修改即可,无须对Java代码进行修改,这就是Spring IOC实现解耦的原理.
IoC底层通过工厂模式,Java反射机制,XML解析等技术,将代码的耦合度降到最低限度,其主要步骤如下:
(1).在配置文件中,对各个毒对象以及它们之间的依赖进行配置;
(2).把IoC容器当做一个工厂,Spring Bean是工厂的产品.容器启动时,会加载并解析配置文件,得到对象的基本信息以及它们之间的依赖关系;
(3).IOC利用反射机制,根据类名生成相应的对象,并根据依赖关系讲这个对象注入到依赖它的对象中.
IOC容器的两种实现
(1).BeanFactory
是IoC容器的基本实现,由org.springframework.beans.factory.BeanFactory接口定义.
BeanFactory才用懒加载机制,容器在加载配置文件时并不会创建Java对象,在程序获取这个对象时才会创建.
注意:BeanFactory是Spring内部使用接口,通常不提供给开发人员使用.
示例
public static void main(String[] args) {
BeanFactory context=new ClassPathXmlApplicationContext("application.xml");
Example example = context.getBean("example", Example.class);
System.out.println(example.toString());
}
(2).ApplicationContext
ApplicationContext是BeanFactory接口的子接口,在BeanFactory的基础上增加了许多企业级功能,例如AOP,事务支持等.
ApplicationContext接口有两个常用的实现类,如下图所示
示例
public static void main(String[] args) {
ApplicationContext context=new ClassPathXmlApplicationContext("application.xml");
Example example = context.getBean("example", Example.class);
System.out.println(example.toString());
}
Spring Bean定义
Spring配置文件有两种格式,即XML文件格式和properties文件格式:
(1).properties配置文件主要以key-value形式存在,不能进行其他操作,适用于简单配置.
(2).XML配置文件采用树形结构,结构清晰,相较于properties文件更加灵活,但XML配置比较繁琐,适合大型项目的复杂配置.
XML配置文件的根元素是<beans>,该元素包含多个子元素<bean>,一个<bean>元素都定义了一个Bean.
在XML配置的<bean>元素中可以包含多个属性或子元素,常用的属性或子元素如下图所示:
Spring主要通过一下两种方式实现属性注入:
(1).构造函数注入:使用<constructor-arg>元素,对构造函数内的属性进行赋值,Bean的构造函数有多少个参数,就需要使用多少个<constructor-arg>元素.
示例:
实体类
package com.demo.pojo;
public class Student {
private String name;
private String gender;
private int age;
private String sno;
private Class aclass;
public Student(String name, String gender, int age, String sno, Class aclass) {
this.name = name;
this.gender = gender;
this.age = age;
this.sno = sno;
this.aclass = aclass;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", gender='" + gender + '\'' +
", age=" + age +
", sno='" + sno + '\'' +
", aclass=" + aclass +
'}';
}
}
package com.demo.pojo;
public class Class {
private int floor;
private String cno;
public Class(int floor, String cno) {
this.floor = floor;
this.cno = cno;
}
@Override
public String toString() {
return "Class{" +
"floor=" + floor +
", cno='" + cno + '\'' +
'}';
}
}
XML配置文件
<bean id="student" class="com.demo.pojo.Student">
<constructor-arg name="name" value="潇潇"/>
<constructor-arg name="gender" value="女"/>
<constructor-arg name="age" value="20"/>
<constructor-arg name="sno" value="00000001"/>
<constructor-arg name="aclass" ref="aclass"/>
</bean>
<bean id="aclass" class="com.demo.pojo.Class">
<constructor-arg name="floor" value="5"/>
<constructor-arg name="cno" value="0001"/>
</bean>
测试
package com.demo.pojo;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApp {
public static void main(String[] args) {
ApplicationContext context=new ClassPathXmlApplicationContext("application.xml");
Student student = context.getBean("student", Student.class);
System.out.println(student.toString());
}
}
结果
简化p命名空间注入
在<beans>元素中导入一下XML约束
xmlns:p="http://www.springframework.org/schema/p"
导入约束后,通过一下形式实现属性注入
<bean id="Bean 唯一标志符" class="包名+类名" p:普通属性="普通属性值" p:对象属性-ref="对象的引用">
实体类和测试不变,改变XML配置文件
<bean id="student" class="com.demo.pojo.Student" p:name="盼盼" p:age="22" p:gender="女" p:sno="00000012" p:aclass-ref="aclass">
</bean>
<bean id="aclass" class="com.demo.pojo.Class" p:floor="8" p:cno="0001">
</bean>
结果
(2).setter注入:使用<property>元素对各个属性进行赋值
在Spring实例化Bean的过程中,IoC容器首先会调用默认的构造方法(无参构造方法)实例化Bean,然后通过Java的反射机制调用这个Bean的setXxx()方法,将属性值注入到Bean中.
示例:
实体类
package com.demo.pojo;
public class Student {
private String name;
private String gender;
private int age;
private String sno;
private Class aclass;
public void setName(String name) {
this.name = name;
}
public void setGender(String gender) {
this.gender = gender;
}
public void setAge(int age) {
this.age = age;
}
public void setSno(String sno) {
this.sno = sno;
}
public void setAclass(Class aclass) {
this.aclass = aclass;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", gender='" + gender + '\'' +
", age=" + age +
", sno='" + sno + '\'' +
", aclass=" + aclass +
'}';
}
}
package com.demo.pojo;
public class Class {
private int floor;
private String cno;
public Class(int floor, String cno) {
this.floor = floor;
this.cno = cno;
}
@Override
public String toString() {
return "Class{" +
"floor=" + floor +
", cno='" + cno + '\'' +
'}';
}
}
或
package com.demo.pojo;
public class Class {
private int floor;
private String cno;
public void setFloor(int floor) {
this.floor = floor;
}
public void setCno(String cno) {
this.cno = cno;
}
@Override
public String toString() {
return "Class{" +
"floor=" + floor +
", cno='" + cno + '\'' +
'}';
}
}
XML配置文件
<bean id="student" class="com.demo.pojo.Student">
<property name="name" value="胖墩"/>
<property name="age" value="21"/>
<property name="gender" value="男"/>
<property name="sno" value="00000002"/>
<property name="aclass" ref="aclass"/>
</bean>
<bean id="aclass" class="com.demo.pojo.Class">
<constructor-arg name="floor" value="10"/>
<constructor-arg name="cno" value="0004"/>
</bean>
或
<bean id="aclass" class="com.demo.pojo.Class">
<property name="floor" value="10"/>
<property name="cno" value="0004"/>
</bean>
测试
package com.demo.pojo;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApp {
public static void main(String[] args) {
ApplicationContext context=new ClassPathXmlApplicationContext("application.xml");
Student student = context.getBean("student", Student.class);
System.out.println(student.toString());
}
}
结果
c命名空间注入
现在XML文件中导入约束
xmlns:c="http://www.springframework.org/schema/c"
实现属性注入形式:
<bean id="Bean 唯一标志符" class="包名+类名" c:普通属性="普通属性值" c:对象属性-ref="对象的引用">
实体类和测试不变,改变XML配置文件
<bean id="student" class="com.demo.pojo.Student" c:name="麻薯" c:age="20" c:gender="女" c:sno="00000022" c:aclass-ref="aclass">
</bean>
<bean id="aclass" class="com.demo.pojo.Class" c:floor="5" c:cno="0009">
</bean>
结果
Spring内部注入Bean
将在<bean>元素的<property>或<constructor-arg>元素内部的Bean,称为内部Bean.
(1)setter方式注入内部Bean实体类
实体类
package com.demo.pojo;
public class Student {
private String name;
private String gender;
private int age;
private String sno;
private Class aclass;
public void setName(String name) {
this.name = name;
}
public void setGender(String gender) {
this.gender = gender;
}
public void setAge(int age) {
this.age = age;
}
public void setSno(String sno) {
this.sno = sno;
}
public void setAclass(Class aclass) {
this.aclass = aclass;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", gender='" + gender + '\'' +
", age=" + age +
", sno='" + sno + '\'' +
", aclass=" + aclass +
'}';
}
}
package com.demo.pojo;
public class Class {
private int floor;
private String cno;
public void setFloor(int floor) {
this.floor = floor;
}
public void setCno(String cno) {
this.cno = cno;
}
@Override
public String toString() {
return "Class{" +
"floor=" + floor +
", cno='" + cno + '\'' +
'}';
}
}
XML配置文件
<bean id="student" class="com.demo.pojo.Student">
<property name="name" value="胖墩"/>
<property name="age" value="21"/>
<property name="gender" value="男"/>
<property name="sno" value="00000002"/>
<property name="aclass">
<bean id="aClass" class="com.demo.pojo.Class">
<property name="floor" value="10"/>
<property name="cno" value="0004"/>
</bean>
</property>
</bean>
测试
package com.demo.pojo;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApp {
public static void main(String[] args) {
ApplicationContext context=new ClassPathXmlApplicationContext("application.xml");
Student student = context.getBean("student", Student.class);
System.out.println(student.toString());
}
}
结果
(2)构造方法注入内部Bean实体类
实体类
package com.demo.pojo;
public class Student {
private String name;
private String gender;
private int age;
private String sno;
private Class aclass;
public Student(String name, String gender, int age, String sno, Class aclass) {
this.name = name;
this.gender = gender;
this.age = age;
this.sno = sno;
this.aclass = aclass;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", gender='" + gender + '\'' +
", age=" + age +
", sno='" + sno + '\'' +
", aclass=" + aclass +
'}';
}
}
package com.demo.pojo;
public class Class {
private int floor;
private String cno;
public Class(int floor, String cno) {
this.floor = floor;
this.cno = cno;
}
@Override
public String toString() {
return "Class{" +
"floor=" + floor +
", cno='" + cno + '\'' +
'}';
}
}
配置文件
<bean id="student" class="com.demo.pojo.Student">
<constructor-arg name="name" value="二虎"/>
<constructor-arg name="age" value="19"/>
<constructor-arg name="gender" value="男"/>
<constructor-arg name="sno" value="00000022"/>
<constructor-arg name="aclass">
<bean id="aClass" class="com.demo.pojo.Class">
<constructor-arg name="floor" value="12"/>
<constructor-arg name="cno" value="0002"/>
</bean>
</constructor-arg>
</bean>
结果
Spring注入集合
在Bean标签下的<property>元素中,使用以下元素配置Java集合类型的属性和参数.
标签 | 说明 |
---|---|
<list> | 用于注入list类型的值,允许重复 |
<set> | 用于注入set类型的值,不允许重复 |
<map> | 用于注入key-value的集合,其中key和value都可以是任意类型 |
<props> | 用于注入key-value的集合,其中key和value都是字符串类型 |
示例
实体类
package com.demo.pojo;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class JavaCollection {
private String[] courses;
private List<String> list;
private Map<String,String> maps;
private Set<String> sets;
public void setCourses(String[] courses) {
this.courses = courses;
}
public void setList(List<String> list) {
this.list = list;
}
public void setMaps(Map<String, String> maps) {
this.maps = maps;
}
public void setSets(Set<String> sets) {
this.sets = sets;
}
@Override
public String toString() {
return "JavaCollection{" +
"courses=" + Arrays.toString(courses) +
", list=" + list +
", maps=" + maps +
", sets=" + sets +
'}';
}
}
XML配置文件
<bean id="javaCollection" class="com.demo.pojo.JavaCollection">
<property name="courses">
<array>
<value>Java</value>
<value>C语言</value>
<value>PS</value>
</array>
</property>
<property name="list">
<list>
<value>张三</value>
<value>张三丰</value>
<value>张丰丰</value>
</list>
</property>
<property name="maps">
<map>
<entry key="A" value="a"></entry>
<entry key="B" value="b"></entry>
<entry key="C" value="c"></entry>
</map>
</property>
<property name="sets">
<set>
<value>电脑</value>
<value>键盘</value>
<value>鼠标</value>
</set>
</property>
</bean>
测试
package com.demo.pojo;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApp {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
JavaCollection javaCollection = context.getBean("javaCollection", JavaCollection.class);
System.out.println(javaCollection.toString());
}
}
结果
在集合中设置对象的值
示例
实体类
package com.demo.pojo;
public class Student {
private String name;
private String gender;
private int age;
private String sno;
public void setName(String name) {
this.name = name;
}
public void setGender(String gender) {
this.gender = gender;
}
public void setAge(int age) {
this.age = age;
}
public void setSno(String sno) {
this.sno = sno;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", gender='" + gender + '\'' +
", age=" + age +
", sno='" + sno + '\'' +
'}';
}
}
package com.demo.pojo;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.demo.pojo.Student;
public class JavaCollection {
private Student[] courses;
private List<String> list;
private Map<String, String> maps;
private Set<String> sets;
public void setCourses(Student[] courses) {
this.courses = courses;
}
public void setList(List<String> list) {
this.list = list;
}
public void setMaps(Map<String, String> maps) {
this.maps = maps;
}
public void setSets(Set<String> sets) {
this.sets = sets;
}
@Override
public String toString() {
return "JavaCollection{" +
"courses=" + Arrays.toString(courses) +
", list=" + list +
", maps=" + maps +
", sets=" + sets +
'}';
}
}
XML配置
<bean id="student1" class="com.demo.pojo.Student">
<property name="name" value="胖墩1"/>
<property name="age" value="21"/>
<property name="gender" value="男"/>
<property name="sno" value="00000002"/>
</bean>
<bean id="student2" class="com.demo.pojo.Student">
<property name="name" value="胖墩2"/>
<property name="age" value="21"/>
<property name="gender" value="男"/>
<property name="sno" value="00000002"/>
</bean>
<bean id="student3" class="com.demo.pojo.Student">
<property name="name" value="胖墩3"/>
<property name="age" value="21"/>
<property name="gender" value="男"/>
<property name="sno" value="00000002"/>
</bean>
<bean id="javaCollection" class="com.demo.pojo.JavaCollection">
<property name="courses">
<array>
<ref bean="student1"></ref>
<ref bean="student2"></ref>
<ref bean="student3"></ref>
</array>
</property>
<property name="list">
<list>
<value>张三</value>
<value>张三丰</value>
<value>张丰丰</value>
</list>
</property>
<property name="maps">
<map>
<entry key="A" value="a"></entry>
<entry key="B" value="b"></entry>
<entry key="C" value="c"></entry>
</map>
</property>
<property name="sets">
<set>
<value>电脑</value>
<value>键盘</value>
<value>鼠标</value>
</set>
</property>
</bean>
测试
package com.demo.pojo;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApp {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
JavaCollection javaCollection = context.getBean("javaCollection", JavaCollection.class);
System.out.println(javaCollection.toString());
}
}
结果
Spring注入null值
配置文件
<bean id="student" class="com.demo.pojo.Student">
<property name="name" value="胖墩"/>
<property name="age" value="21"/>
<property name="gender" >
<null/>
</property>
<property name="sno" value="00000002"/>
</bean>
结果
Spring注入空字符串
配置文件
<bean id="student" class="com.demo.pojo.Student">
<property name="name" value="胖墩"/>
<property name="age" value="21"/>
<property name="gender" value=""/>
<property name="sno" value="00000002"/>
</bean>
结果
Spring Bean作用域
默认情况下,所有的Spring Bean都是单例的,也就是说在整个Spring应用中,Bean的实例只有一个.
可在<bean>元素中添加scope属性来配置作用域.例如,每次获取Bean时,都需要一个新的Bean示例,可以把scope属性定义为prototype;如果Spring每次需要返回一个相同的Bean实例,应将Bean的scope属性定义为singleton.
scope共有六种作用域,如下图所示:
注意:这六种作用域中,除了singleton和prototype可以直接在常规的Spring IoC容器中使用外,剩下的都只能在基于Web的ApplicationContext实现中才能使用,否则会抛出一个IIIegalStateException的异常.
Spring Bean生命周期
Spring中Bean的生命周期比较复杂,可分为5个阶段:
(1).Bean的实例化
(2).Bean属性赋值
(3).Bean的初始化
(4).Bean的使用
(5).Bean的销毁
Spring 生命周期流程
自定义Bean的生命周期
Bean的生命周期回调方法主要有两种:
(1).初始化回调方法:在Spring Bean被初始化后调用,执行一些自定义的回调操作.
(2).销毁会回调方法:在Spring Bean被销毁前调用,执行一些自定义的回调操作.
可以通过三种方式自定义Bean的生命周期回调方法:
如果一个Bean中有多种生命周期回调方法时,优先级顺序为:注解>接口>XML配置.
(1).通过接口实现.
InitializingBean和DisposableBean接口指定Bean的生命周期回调方法.
注意通常情况下,不建议使用这种方式指定生命周期回调方法,因为这种方式会导致代码的耦合度过高.
(2).通过XML配置实现.
通过<bean>元素中的inti-method和destory-method属性,指定Bean的生命周期回调方法.
(3).使用注解实现.
通过@PostConstructor和@PreDestroy注解,指定Bean的生命周期回调方法.
学习网址:Spring框架教程(非常详细)