这篇文章主要介绍了Spring工厂的反射和配置文件源码分析的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Spring工厂的反射和配置文件源码分析文章都会有所收获,下面我们一起来看看吧。
反射和配置文件
学习 Spring 的时候,虽然可以知道是通过反射和配置文件的方式来获取 JavaBean 对象,但是一直没有机会自己尝试一次,探究以下内部原理,虽然有人推荐阅读源码,但是可能还是感觉学的不好,一直没有尝试过。现在刚好学习设计模式刚好遇到了这部分的内容了,感觉自己对这个有了一个较好的理解了。
设计模式中,为了满足开闭原则,大都引入了抽象层,如工厂方法模式、抽象工厂模式等。客户端针对抽象层编程,而在程序运行的时候再指定其子类,根据里氏代换原则和面向对象的多态性,子类对象再运行时将覆盖父类对象。如果需要对系统进行扩展,只需要修改子类类名即可。在具体实现时,通过引入配置文件可以使得用户再不修改客户端任何代码的前提下增加或替换子类,
其基本实现过程过程为:
客户端针对抽象层编程,客户端代码中不能出现具体的类名,即客户端不直接实例化对象。
引入纯文本格式的配置文件,通常是 XML 文件,将具体类类名存储在配置文件中。
通过 DOM(Document Object Model,文档对象模型)、SAX(SimpleAPI for XML)等 XML 解析技术获取存储在配置文件中类名。
在客户端代码中通过反射机制根据类名创建对象,用反射所创建的对象代替父类对象的引用,程序运行时,将调用子类方法来实现业务功能。
如果需要扩展功能,只需要添加一个新的子类继承抽象父类,再修改配置文件,重新运行程序即可;如果需要替换功能,只需要另一个子类类名替换存储再配置文件中的原有子类类名。无论是扩展还是替换都无须修改既有类库和客户端源码,完全符合开闭原则。
Talk is cheap, show me the code.
直接通过代码来理解上面的内容吧。
<?xml version="1.0" encoding="UTF-8"?>
<beans>
    <bean>com.reflect.Dog</bean>
</beans>测试实体类
package com.reflect;
public class Dog {
    private String name;
    private Integer age;
    public Dog() {}
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "Dog [name=" + name + ", age=" + age + "]";
    }
}对象工厂类(类似Spring工厂的简单实现)
package com.reflect;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.*;
public class Factory {
    public static Object getBean() {
        try {
            Document doc = DocumentBuilderFactory
                    .newInstance()
                    .newDocumentBuilder()
                    .parse("src/beans.xml");
            //获取包含类名的文本节点
            NodeList nodeList  = doc.getElementsByTagName("bean");
            Node node = nodeList.item(0).getFirstChild();
            String name = node.getNodeValue();
            //通过类名生成实例对象并将其返回
            Class<?> clazz = Class.forName(name);
            return clazz.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
            return null; 
        } 
    }
}测试类
package com.reflect;
public class Test {
    public static void main(String[] args) {
        Dog dog = null;
        dog = (Dog)Factory.getBean();
        dog.setName("小黑");
        dog.setAge(12);
        System.out.println(dog.toString());
    }
}项目目录结构
注意这里需要使用 dom4j 的jar包,下载导入即可!
运行结果
现在需求变了,不想使用 Dog 类,而要使用 HuntDog 类,只要增加 HuntDog 类, 并继承 Dog 类即可,项目源码不需要改动,满足开闭原则。
package com.reflect;
public class HuntDog extends Dog {
    private String name;
    private Integer age;
    public HuntDog() {}
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "HuntDog [name=" + name + ", age=" + age + "]";
    }
    public void say() {
        System.out.println("Hello, I am HuntDog: "+this.toString());
    }
}修改 XML 如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans>
    <bean>com.reflect.HuntDog</bean>
</beans>修改后的运行结果