首页 > 代码库 > 自己实现简单Spring Ioc

自己实现简单Spring Ioc

IoC则是一种 软件设计模式,简单来说Spring通过工厂+反射来实现IoC。

 

原理简单说明:

其实就是通过解析xml文件,通过反射创建出我们所需要的bean,再将这些bean挨个放到集合中,然后对外提供一个getBean()方法,以便我们获得这bean。
通俗来讲就如同婚姻介绍所,只需要告诉它找个什么样的女朋友,然后婚介就会按照我们的要求,提供一个mm,如果婚介给我们的人选不符合要求,我们就会抛出异常。

 

简单实现:


1.需要引用maven依赖:

<dependency>
   <groupId>org.jdom</groupId>
   <artifactId>jdom</artifactId>
   <version>1.1.3</version>
</dependency>

2.beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans>
    <bean id="user" class="com.example.domain.User" />
    <bean id="userDAO" class="com.example.dao.impl.UserDAOImpl" />
    <bean id="userService" class="com.example.service.UserService">
        <property name="userDAO" bean="userDAO" />
    </bean>
</beans>

3.BeanFactory

package com.example.ioc;

public interface BeanFactory {
    Object getBean(String name);
}

4.ClassPathXmlApplicationContext:读取xml文件内容,并创建对象及对象关系(使用setter方式)

package com.example.ioc;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.input.SAXBuilder;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


public class ClassPathXmlApplicationContext implements BeanFactory {
    private Map<String, Object> beans = new HashMap<String, Object>();

    public ClassPathXmlApplicationContext() throws Exception {
        SAXBuilder sb = new SAXBuilder();
        // 构造文档对象
        Document doc = sb.build(ClassPathXmlApplicationContext.class
                .getClassLoader().getResourceAsStream("beans.xml"));
        // 获取根元素
        Element root = doc.getRootElement();
        // 取到根元素所有元素
        List list = root.getChildren();

        setBeans(list);
    }

    //设置Bean
    private void setBeans(List list) throws Exception {
        for (int i = 0; i < list.size(); i++) {
            Element element = (Element) list.get(i);
            String id = element.getAttributeValue("id");
            //取得class子元素
            String clzss = element.getAttributeValue("class");
            //通过反射进行实例化
            Object o = Class.forName(clzss).newInstance();
            beans.put(id, o);

            setProperty(element, o);
        }
    }

    //获取property进行依赖注入
    private void setProperty(Element element, Object o) throws Exception {
        for (Element property : (List<Element>) element.getChildren("property")) {
            String name = property.getAttributeValue("name");
            String bean = property.getAttributeValue("bean");
            //从beans.xml中根据id取到类的对象
            Object beanObj = this.getBean(bean);
            System.out.println(beanObj);//com.example.dao.impl.UserDAOImpl@2f4d3709
            //组成setXXX方法名
            String methodName = "set" + name.substring(0, 1).toUpperCase() + name.substring(1);
            // 反射机制对方法进行调用,将对象在加载bean时就注入到环境上下文中
            Method m = o.getClass().getMethod(methodName, beanObj.getClass().getInterfaces()[0]);
            m.invoke(o, beanObj);
        }
    }


    @Override
    public Object getBean(String name) {
        return beans.get(name);
    }
}

以上为核心代码,当然在实际情况中,这一块要复杂的多, 例如:可以一个bean引用另一个bean,还可以有多个配置文件、通过多种方式载入配置文件等等,不过原理还是采用Java的反射机制。

实现的效果为:
Service service=(Service)beans.get("userService");
Dao dao = (Dao)beans.get("userDAO");
//依赖注入,Service实现依赖dao的实现
service.setDao(dao);

5.User:实体类

package com.example.domain;

public class User {
    private String userName;
    private String password;

    /**
     * @return the userName
     */
    public String getUserName() {
        return userName;
    }

    /**
     * @param userName the userName to set
     */
    public void setUserName(String userName) {
        this.userName = userName;
    }

    /**
     * @return the password
     */
    public String getPassword() {
        return password;
    }

    /**
     * @param password the password to set
     */
    public void setPassword(String password) {
        this.password = password;
    }

    public String toString() {
        StringBuffer sb = new StringBuffer();
        sb.append(this.userName);
        sb.append(this.password);
        return sb.toString();
    }
}

6.UserDAO

package com.example.dao;

import com.example.domain.User;

public interface UserDAO {
    void save(User u);

    void delete();
}

 

7.UserDAOImpl

package com.example.dao.impl;

import com.example.dao.UserDAO;
import com.example.domain.User;


public class UserDAOImpl implements UserDAO {
    @Override
    public void save(User u) {
        System.out.println("User:" + u.toString());
    }

    @Override
    public void delete() {
        System.out.println("delete User");

    }
}

8.UserService

package com.example.service;

import com.example.dao.UserDAO;
import com.example.domain.User;

public class UserService {

    private UserDAO userDAO;

    public void addUser(User u) {
        this.userDAO.save(u);
    }

    /**
     * @return the userDAO
     */
    public UserDAO getUserDAO() {
        return userDAO;
    }

    /**
     * @param userDAO the userDAO to set
     */
    public void setUserDAO(UserDAO userDAO) {
        this.userDAO = userDAO;
    }
}

9.测试:

package com.example.ioc;

import com.example.domain.User;
import com.example.service.UserService;


public class RunIoc {
    public static void main(String[] args) throws Exception {
        BeanFactory factory = new ClassPathXmlApplicationContext();
        //通过工厂直接获取
        UserService userService = (UserService) factory.getBean("userService");
        //其实User也可以从工厂中获得
        User u = (User) factory.getBean("user");
        //User u = new User();
        u.setUserName("tom");
        u.setPassword("123456");
        userService.addUser(u);//打印结果tom123456
    }
}

小结

上文仅仅是简单地模拟了spring的IOC的实现,虽然只是完成了spring中依赖注入的一小部分,但还是很好地展现了Java反射机制在spring中的应用

 

自己实现简单Spring Ioc