Java 反射和内省实现spring的IOC和DI

1.构造两个JavaBean 

package com.spring.model;


public class People {
    
    private Car car;

    public Car getCar() {
        return car;
        
    }

    public void setCar(Car car) {
        this.car = car;
    }

}
package com.spring.model;

public class Car {
    
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    
    public void show() {
        System.out.println("我是"+name+"");
    }

}

2.构建一个类似于spring配置的xml文件 spring-bean.xml

  按照spring一样的格式配置好节点和属性

<?xml version="1.0" encoding="UTF-8"?>
<beans>
    <bean id="car" class="com.spring.model.Car">
    </bean>

    <bean id="people" class="com.spring.model.People">
        <property name="car" ref="car"></property>
    </bean>
</beans>

3.构建一个类似spring加载配置文件的类 里面运用了反射和内省的机制

package com.spring.core;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;

/**
 * 
 * 模仿spring IOC、DI 底层实现
 * @author GET_CHEN
 *
 */
public class ClassPathXmlApplicationContext {
    private String configXml;
    private Map<String,Object> map = new HashMap<String,Object>();//保存配置文件中的id和class所实例化出来的对象
    public ClassPathXmlApplicationContext(String configXml) {
        this.configXml = configXml;
        this.doImplement();
    }
    
    

    /**
     * 实现文档的解析
     * 通过反射实现IOC
     * 通过内省实现DI
     */
    private void doImplement() {
        SAXReader saxReader = new SAXReader();
        try {
            //通过dom4j解析文档
            Document doucment = saxReader.read(this.getClass().getClassLoader().getResourceAsStream(configXml));
            
            //获取beans下面的所有bean节点
            List<Node> beanNodes = doucment.selectNodes("beans/bean");
            
            if(beanNodes == null || beanNodes.size() <= 0) return;
            //遍历所有的bean节点,将id和class添加到map中
            for (Node bean : beanNodes) {
                //将节点转为元素
                Element element = (Element)bean;
                //获取元素的相关属性内容
                String beanId = element.attributeValue("id");
                String beanClass = element.attributeValue("class");
                //——————————————IOC的实现————————————————
                //通过反射将class所对应的对象,实例化出来,保存到map中  --------------> 这一步实现了IOC
                map.put(beanId, Class.forName(beanClass).newInstance());
            }
            
            
            //——————————————————DI的实现——————————————————
            //获取所有的属性标签
            List<Node> propertyNodes = doucment.selectNodes("beans/bean/property");
            
            if(propertyNodes != null && propertyNodes.size() > 0) {
                //遍历获取name属性和ref属性
                
                for (Node property : propertyNodes) {
                    //将节点转为元素
                    Element element = (Element) property;
                    //获取name属性和ref属性
                    String proName = element.attributeValue("name");
                    String proRef = element.attributeValue("ref");
                    
                    //获取当前元素的直接父元素
                    Element parent = element.getParent();
                    //获取父元素所对应的id属性
                    String parentId = parent.attributeValue("id");
                    
                    //—————————————————— 内省实现依赖注入 ———————————
                    //获取父元素的字节码对象
                    Class parentClass = map.get(parentId).getClass();
                    //通过内省类,获取父元素所指向的类的所有信息(内省对象)
                    //第二个参数为不需要内省的类,除去Object,为了防止遍历到Object类中的set方法中的参数
                    BeanInfo beanInfo = Introspector.getBeanInfo(parentClass,Object.class);
                    //通过内省对象,获取父元素所指向的类的所有属性描述
                    PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
                    //遍历属性元素
                    for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
                        //获取set方法所对应的属性名
                        String name = propertyDescriptor.getName();
                        System.out.println(name+"-------");
                        //如果 父元素所指向的类中的setter方法的名称 存在 与 配置中其子标签property中name属性相同的值就进行注入
                        //即:<property name="car"/> 中的 car 与 People类中的 public void setCar;中的car全小写相同时候
                        if(proName.equals(name)) {
                            //注入
                            //将ref对应的对象,注入给父级元素指向对象(内省对象)的set方法参数所对应的对象
                            //即:完成 new People().setCar(car) 的工作
                            propertyDescriptor.getWriteMethod().invoke(map.get(parentId), new Object[] {map.get(proRef)});
                        }
                    }
                    
                }
                
            }
            
            
            
        } catch (Exception e) {
            
            e.printStackTrace();
        }
        
    }

    /**
     * 
     * 获取保存在map中的实例对象,并返回
     * @param beanName
     * @return
     */
    public  Object getBean(String beanName) {
        return map.get(beanName);
    }

}

4.测试代码

package com.spring.test;

import org.junit.Test;

import com.spring.core.ClassPathXmlApplicationContext;
import com.spring.model.Car;
import com.spring.model.People;

public class TestDemo {
    
    @Test
    public void h() {
        ClassPathXmlApplicationContext bean = new ClassPathXmlApplicationContext("com/spring/config/spring-bean.xml");
        
        People people = (People) bean.getBean("people");
        
        Car car = people.getCar();
        car.setName("奔驰");
        car.show();
    }

}

5.运行结果

car-------
我是奔驰车

总结:高大上的spring就是利用反射和内省的机制完成对于一个类的管理,和相关类的注入的。控制反转主要使用的是反射机制,通过Class.fromName,获取类的字节码对象并实例化。依赖注入就是通过内省获取一个类并类中的set方法所set的一个对象,通过这个对象所对应的名称,获取在map中与之对应的实例化对象之后。通过内省的对象调用 真实的set方法,将已实例好的对象赋值给内省对象中所对应的成员变量

原文地址:https://www.cnblogs.com/getchen/p/7886139.html