IOC思想
IOC重要概述(重要)
提到IOC,第一反应就是控制反转,我以为SpringIOC就是控制反转,控制反转就是SpringIOC,现在知道了,这种理解是错误的。控制反转是一种思想,而SpringIOC容器是实现了这种思想的一个载体
SpringIOC容器可以在对象生成或初始化时就直接将数据注入到对象中,如果对象A的属性是另一个对象B,还可以将这个对象B的引用注入到A的数据域中
如果在初始化对象A的时候,对象B还没有进行初始化,而A又需要对象B作为自己的属性,那么就会使用一种递归的方式进行注入,这样就可以把对象的依赖关系清晰有序的建立起来
IOC容器解决问题的核心就是把创建和管理对象的控制权从具体的业务对象手中抢过来,由IOC容器来管理对象之间的依赖关系,并由IOC容器完成对象的注入。这样就把应用从复杂的对象依赖关系的管理中解放了出来,简化了程序的开发过程
IOC原理
1.程序中所有的Bean之间的依赖关系我们是放在一个XML文件中进行维护的,也就是ApplicationContext.xml,ConfigManager类完成的功能是读取xml,并将所有读取到有用的信息封装到我们创建的一个Map<String,Bean>集合中,用来在初始化容器时创建Bean对象
2.定义一个BeanFactory的接口,接口中有一个getBean(String name)方法,用来返回你想要创建的那个对象
3.然后定义一个BeanFactory接口的实现类ClassPathXmlApplicationContext,就是在这个类的构造方法中,初始化容器,通过调用ConfigManager的方法返回的Map集合,通过反射和内省–创建Bean对象,这里需要注意,对象的创建有两个时间点,这取决于bean标签中scope属性的值
4.如果scope=“singleton”,那么对象在容器初始化时就已创建好,用的时候只需要从容器中取即可
5.如果scope=“prototype”,那么容器中不保存这个Bean的实例对象,每次开发者需要使用这个对象时再进行创建
IOC底层代码
项目结构图与功能
结构图
功能
1.IOC容器能管理对象的创建以及对象之间的依赖关系
2.能够实现数据的自动类型转换(借助BeanUtils)
3.能够实现scope=“singleton” 和 scope="prototype"的功能,即能够控制对象是否为单例
使用的主要知识点
1.dom4j解析xml文件
2.xpath表达式用于解析xml中的标签
3.java反射机制
4.内省–获取Bean属性的set方法进行赋值
applicationContext.xml
<?xml version="1.0" encoding="utf-8"?>
<beans>
<bean name="student" class="com.entiry.Student">
<property name="name" value="王荣"></property>
</bean>
<bean name="teacher" class="com.entity.Teacher">
<property name="student" ref="student"></property>
</bean>
<bean name="person" class="com.entity.Person" scope="prototype">
<property name="teacher" ref="teacher"></property>
<property name="student" ref="student"></property>
</bean>
</beans>
实体类 Student Teacher Person
public class Person {
private Student student;
private Teacher teacher;
public Student getStudent() {
return student;
}
public void setStudent(Student student) {
this.student = student;
}
public Teacher getTeacher() {
return teacher;
}
public void setTeacher(Teacher teacher) {
this.teacher = teacher;
}
}
public class Student {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class Teacher {
private Student student;
public Student getStudent() {
return student;
}
public void setStudent(Student student) {
this.student = student;
}
}
用于封装Bean标签信息的Bean类
public class Bean{
private String name;
private String className;
private String scope="singleton";
private List<Property> properties = new ArrayList<Property>();
public String getScope(){
return scope;
}
public void setScope(String scope){
this.scope = scope;
}
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
public String getClassName(){
return className;
}
public void setClassName(String className){
this.className = className;
}
public List<Property> getProperties(){
return properties;
}
public void setProperties(List<Property> properties){
this.properties = properties;
}
}
用于封装Bean子标签property内容的Property类
public class Property{
private String name;
private String value;
private String ref;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public String getRef() {
return ref;
}
public void setRef(String ref) {
this.ref = ref;
}
}
ConfigManager类
public class ConfigManager{
private static Map<String,Bean> map = new HashMap<String,Bean>();
//读取配置文件并返回读取结果
//返回Map集合便于注入,key是每个Bean的name属性,value是对应的那个Bean对象
@SuppressWarnings("unchecked")
public static Map<String,Bean> getConfig(String path){
/*
dom4j实现1.创建解析器 2.加载配置文件,得到document对象 3.定义xpath表达式,取出所有的Bean对象
对bean元素进行遍历,将Bean元素的name/class属性封装到Bean类属性中
获取bean元素下的所有property子元素
将name/value/ref封装到Property类中
将Property对象封装到bean对象中
将bean对象封装到Map集合中,返回map
*/
//创建解析器
SAXReader reader = new SAXReader();
//加载配置文件 得到document对象
InputStream is = ConfigManager.class.getResourceAsStream(path);
Document doc = null;
try{
doc = reader.read(is);
}catch(DocumentException e){
e.printStackTrace();
throw new RuntimeException("请检查您的xml配置是否正确");
}
//定义xpath表达式 取出所有的bean元素
String xpath = "//bean";
List<Element> list = doc.selectNodes(xpath);
//对bean元素进行遍历
if(list != null){
//将bean元素的name/class属性封装到Bean类属性中、
//将name/value/ref分装到Property类属性中
for(Element bean : list){
Bean b = new Bean();
String name = bean.attributeValue("name");
String clazz = bean.attributeValue("class");
String scope = bean.attributeValue("scope");
b.setName(name);
b.setClassName(clazz);
if(scope != null){
b.scope = scope;
}
//获得bean下的所有property子元素
List<Element> children = bean.elements("property");
//将属性name/value/ref封装到类Property中
if(children != null){
for(Element child :children){
Property prop = new Property();
String pname = child.attributeValue("name");
String pvalue = child.attributeValue("value");
String pref = child.attributeValue("ref");
prop.setName(pname);
prop.setRef(pref);
prop.setValue(pvalue);
//将Property对象封装到bean对象中
b.getProperties().add(prop);
}
}
//将bean对象封装到Map集合中 返回map
map.put(name,b);
}
}
return map;
}
}
BeanFactory接口
public interface BeanFactory{
//核心方法getBean
Object getBean(String name);
}
ClassPathXmlApplicationContext类
public class ClassPathXmlApplicationContext implements BeanFactory{
//获得读取配置文件的Map信息
private Map<String,Bean> map;
//作为IOC容器使用,放置对象
private Map<String,Object> context = new HashMap<String,Object>();
//构造方法
public ClassPathXmlApplicationContext(String path){
map = ConfigManager.getConfig(path);
//遍历map 初始化bean
for(Map.Entry<String,Bean> entry : map.entrySet()){
String beanName = entry.getKey();
Bean bean = entry.getValue();
Object existBean = context.get(beanName);
//当容器中为空并且bean的scope属性为"singleton"时
if(existBean == null && bean.getScope().equals("singleton")){
//根据字符串创建Bean对象
Object beanObj = createBean(bean);
//把创建好的bean对象放置到map中去
context.put(beanName,beanObj);
}
}
}
//通过反射创建对象
@SuppressWarnings("rawtypes")
private Object createBean(Bean bean){
//创建该类对象
Class clazz = null;
try{
clazz = Class.forName(bean.getClassName());
}catch(ClassNotFoundException e){
e.printStackTrace();
throw new RuntimeException("没有找到该类"+bean.getClassName());
}
Object beanObj = null;
try{
beanObj = clazz.newInstance();
}catch(Exception e){
e.printStackTrace();
throw new RuntimeException("没有提供无参构造器");
}
//获得bean的属性 将其注入
if(bean.getProperties() != null){
for(Property prop : bean.getProperties()){
//注入分两种情况
//获得要注入的属性名称
String name = prop.getName();
String value = prop.getValue();
String ref = prop.getRef();
//使用BeanUtils工具类完成属性的注入,可以自动完成类型转换
//如果value不为null 说明存在value
if(value != null){
Map<String,String[]> parmMap = new HashMap<String,String[]>();
parmMap.put(name,new String[]{value});
try{
BeanUtils.populate(beanObj,parmMap);
}catch(Exception e){
e.printStackTrace();
throw new RuntimeException("请检查你的"+name+"属性");
}
}
if(ref != null){
//根据属性名获得一个注入属性对应的set方法
//Method setMethod = BeanUtils.getWriteMethod(beanObj,name);
//看一看当前IOC容器是否已存在该bean,有的话直接设置,没有的话则使用递归,创建该bean对象
Object existBean = context.get(prop.getRef());
if(existBean == null){
//递归创建一个bean
existBean = createBean(map.get(prop.getRef()));
//放置到context容器中
//只有当scope="singleton"时才往容器中存放
if(map.get(prop.getRef()).getScope().equals("singleton")){
context.put(prop.getRef(),existBean);
}
}
try{
//setMethod.invoke(beanObj,existBean)通过BeanUtils为beanObj设置属性
BeanUtils.setProperty(beanObj,name,existBean);
}catch(Exception e){
e.printStackTrace();
throw new RuntimeException("您的bean的属性"+name+"没有对应的set方法");
}
}
}
}
}
return beanObj;
}
@Override
public Object getBean(String name){
Object bean = context.get(name);
//如果为空则说明scope不是singleton,那么容器中是没有的,这里现场创建
if(bean == null){
bean = createBean(map.get(name));
}
return bean;
}
测试类TestBean
public class TestBean{
@Test
public void test(){
BeanFactory bf = new ClassPathXmlApplicationContext("/applicationContext.xml");
Person person1 = bf.getBean("person");
Person person2 = bf.getBean("person");
System.out.println(person1 == person2);
System.out.println(person1);
Student stu1 = bf.getBean("student");
Student stu2 = bf.getBean("student");
String name = stu1.getName();
System.out.println(name);
System.out.println(stu1 == stu2);
}
}
测试结果
false
com.entity.Person15353
王荣
true