1. 回顾

经常前三天的学习,已初步掌握了spring ioc容器的实现原理,今天,试着自己来实现一个最简单的ioc容器;

2. bean定义

关于bean的定义,先使用最简单的定义方式,两个属性nameclass,对应到xml的配置文件为:

<bean id="name" class="samples......."/>

代码定义如下:

public interface BeanDefinition {
    String getBeanName();
    String getBeanClassName();
}

public class DefaultBeanDefinition implements BeanDefinition {
    private String beanName;
    private String beanClassName;

    @Override
    public String getBeanName() {
        return beanName;
    }

    @Override
    public String getBeanClassName() {
        return beanClassName;
    }

    public void setBeanClassName(String beanClassName){
        this.beanClassName = beanClassName;
    }

    public void setBeanName(String beanName){
        this.beanName = beanName;
    }
}

3. xml解析

xml解析我们使用dom接口来实现,最简单的实现方式:

public class XmlBeanDefinitionReader {
    private String configLocation;

    private BeanRegistry beanRegistry;

    public XmlBeanDefinitionReader(){

    }

    public void loadBeanDefinitions() throws ParserConfigurationException, IOException, SAXException {
        URL configUrl = Thread.currentThread().getContextClassLoader().getResource(configLocation);
        File configFile = new File(configUrl.getFile());

        DocumentBuilder documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
        Document document = documentBuilder.parse(configFile);

        List<BeanDefinition> beanDefinitions = parseBeanDefinition(document);

        if(null != beanDefinitions){
            for (BeanDefinition beanDefinition : beanDefinitions) {
                try {
                    beanRegistry.registry(beanDefinition.getBeanName(), beanDefinition);
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private List<BeanDefinition> parseBeanDefinition(Document document){
        List<BeanDefinition> beanDefinitions = new ArrayList<>();

        NodeList beanNodes = document.getElementsByTagName("bean");

        for(int i=0; i<beanNodes.getLength(); i++){
            Node node = beanNodes.item(i);
            if(node.getNodeType() == Node.ELEMENT_NODE){
                Element eleBean = (Element) node;
                DefaultBeanDefinition defaultBeanDefinition =  new DefaultBeanDefinition();

                defaultBeanDefinition.setBeanName(eleBean.getAttribute("id"));
                defaultBeanDefinition.setBeanClassName(eleBean.getAttribute("class"));

                beanDefinitions.add(defaultBeanDefinition);
            }
        }

        return beanDefinitions;
    }

    public BeanRegistry getBeanRegistry() {
        return beanRegistry;
    }

    public void setBeanRegistry(BeanRegistry beanRegistry) {
        this.beanRegistry = beanRegistry;
    }

    public String getConfigLocation(){
        return configLocation;
    }

    public void setConfigLocation(String configLocation){
        this.configLocation = configLocation;
    }
}

主要完成的功能:

  1. xml配置文件中读取bean的定义;
  2. 回调BeanRegistry的注册方法,完成bean的注册;

3.1. BeanRegistry

public interface BeanRegistry {
    void registry(String name, Object beanObject) throws ClassNotFoundException;
}

4. 工厂定义

我们知道,ioc的核心其实是一个工厂,spring中我们常用的所有容器如:ClassPathXmlApplicationContext等都是BeanFactory工厂的实现类,spring的工厂很复杂,我们在这里只考虑最简单的实现:

public interface BeanFactory {
    Object getBean(String name);

    <T> T getBean(Class<T> requiredType);
}
public class DefaultBeanFactory implements BeanFactory, BeanRegistry {

    private List<String> beanNames = new ArrayList<>();

    private Map<String, BeanDefinition> beans = new ConcurrentHashMap<>();
    private Map<Class<?>, BeanDefinition> typeBeans = new ConcurrentHashMap<>();

    private String configLocation;

    public String getConfigLocation() {
        return configLocation;
    }

    protected void setConfigLocation(String configLocation) {
        this.configLocation = configLocation;
    }

    public DefaultBeanFactory() {

    }


    protected void refresh() throws IOException, SAXException, ParserConfigurationException {
        // 加载beanDefinition
        XmlBeanDefinitionReader definitionReader = new XmlBeanDefinitionReader();
        definitionReader.setConfigLocation(configLocation);
        definitionReader.setBeanRegistry(this);

        definitionReader.loadBeanDefinitions();
    }

    @Override
    public Object getBean(String name)  {
        if (!beanNames.contains(name)) {
            return null;
        }

        BeanDefinition beanDefinition = beans.get(name);
        String beanClassName = beanDefinition.getBeanClassName();
        Class<?> beanClass = null;
        try {
            beanClass = Class.forName(beanClassName);
            Object obj = beanClass.newInstance();
            return obj;
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public <T> T getBean(Class<T> requiredType) {
        if (!typeBeans.containsKey(requiredType)) {
            return null;
        }

        BeanDefinition beanDefinition = typeBeans.get(requiredType);
        String beanClassName = beanDefinition.getBeanClassName();
        Class<?> beanClass = null;
        try {
            beanClass = Class.forName(beanClassName);
            Object bean = beanClass.newInstance();
            return (T) bean;
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public void registry(String name, Object beanObject) throws ClassNotFoundException {
        if (null == name && "".equals(name)) {
            return;
        }

        if (beanNames.contains(name)) {
            return;
        }

        beanNames.add(name);
        BeanDefinition beanDefinition = (BeanDefinition) beanObject;
        beans.put(name, beanDefinition);
        Class<?> beanClass = Class.forName(beanDefinition.getBeanClassName());
        typeBeans.put(beanClass, beanDefinition);
    }
}

主要功能点:

  1. 使用Map存储bean定义;
  2. 调用xml配置文件的BeanDefinition的解析;
  3. 实现BeanRegistry接口的注册方法;
  4. 实现BeanFactory的获取bean方法;

5. 容器包装

上面基本完成了一个Ioc容器,最后我们来一个假包装,更像spring一样操作:

public class ClassPathXmlApplicationContext extends DefaultBeanFactory {

    public ClassPathXmlApplicationContext(String configLocation) {
        setConfigLocation(configLocation);

        try {
            this.refresh();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (SAXException e) {
            e.printStackTrace();
        } catch (ParserConfigurationException e) {
            e.printStackTrace();
        }
    }
}

6. 测试

创建一个HelloWorldService与实现类,并在classpath路径下创建xml配置文件,配置helloWorldService bean。

6.1. HelloWorldService

public interface HelloWorldService {

    String sayHello(String name);
}

public class HelloWorldServiceImpl implements HelloWorldService {
    @Override
    public String sayHello(String name) {
        return "hello: " + name;
    }
}

6.2. beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans>
    <bean id="helloWorldService" class="samples.myioc.service.impl.HelloWorldServiceImpl"/>
</beans>

6.3. main启动类

public class App {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");

        HelloWorldService helloWorldService = applicationContext.getBean(HelloWorldServiceImpl.class);
        System.out.println(helloWorldService.sayHello("zhangsan"));

        HelloWorldService helloWorldService1 = (HelloWorldService) applicationContext.getBean("helloWorldService");
        System.out.println(helloWorldService1.sayHello("lisi"));
    }
}

查看运行结果:

hello: zhangsan
hello: lisi

7. 总结

经过四天的学习,从对spring一无所知开始,现在已经可以自己实现一个简单的ioc容器了。收获最大的可能就是,对于看起来复杂的东西,不要有畏难心理,只要你付出行动,并坚持下去,总会有收获。

Copyright © wychuan.com 2017 all right reserved,powered by Gitbook该文件修订时间: 2018-01-09 08:05:07

results matching ""

    No results matching ""