自定义IOC

本文介绍了如何使用Java反射和XML解析实现一个简单的Spring框架。通过自定义BeanFactory接口,解析beans.xml配置文件,创建并管理Bean实例,实现对象间的依赖注入。在测试中展示了如何从容器获取Bean并执行业务操作,强调了控制反转(IOC)的思想,降低了对象间的耦合,提高了程序的灵活性和可维护性。
摘要由CSDN通过智能技术生成

自定义IOC:

概述:
Spring通过一个配置文件描述Bean与Bean之间的依赖关系,利用java语言的反射功能实例化Bean,并建立Bean之间的依赖关系。Spring的IOC容器在完成这些底层工作的基础上,还提供了Bean实例缓存生命周期管理、Bean实例代理、事件发布、资源加载等高级服务

接下来我们通过这一段话,去自己实现一个简单的Spring框架,通过反射和XML解析来建立对象及对象之间的关系

1.导入所需依赖

<dependencies>
    <!--DOM4J 的支持:解析XML文件-->
    <dependency>
        <groupId>org.dom4j</groupId>
        <artifactId>dom4j</artifactId>
        <version>2.1.1</version>
    </dependency>
    <!--junit-->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.22</version>
        <scope>provided</scope>
    </dependency>
</dependencies>

2.BeanFactory是提供了最基本的IOC容器功能的一个接口

自定义BeanFactory

//Bean工厂,作用就是从容器中返回特定名称的 Bean
public interface BeanFactory {
    /**
     * 从容器中获取bean
     * @param beanName
     * @return
     */
    public Object getBean(String beanName);
}

3.编写程序运行所需资源

实体类

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Person {
    private String name;
    private int age;
}

数据访问层接口

public interface PersonDao {
    /**
     * 添加一个person
     * @param person
     */
    public void addPerson(Person person);
}

数据访问层实现类

public class PersonDaoImpl implements PersonDao {
    @Override
    public void addPerson(Person person) {
        System.out.println("获取person信息,姓名"+person.getName()+",年龄:"+person.getAge());
        System.out.println("用户保存成功");
    }
}

业务层接口

public interface PersonService {
    public void addPerson(Person person);
}

业务层实现

public class PersonServiceImpl implements PersonService {
    //注入dao层
    private PersonDao personDao;
    //添加setter方法,模拟spring依赖注入中的set注入方式
    public void setPersonDao(PersonDao personDao) {
        this.personDao = personDao;
    }

    @Override
    public void addPerson(Person person) {
        personDao.addPerson(person);
    }
}

4.创建beans.xml文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE PUBLIC>
<beans>
    <!--在容器中注入person对象-->
    <bean id="person" class="com.lqz.pojo.Person"/>
    <!--在容器中注入personDao对象:实现类,接口不能创建对象-->
    <bean id="personDao" class="com.lqz.dao.impl.PersonDaoImpl"/>

    <bean id="personService" class="com.lqz.service.impl.PersonServiceImpl">
        <!--引入注入的dao对象:ref即引用那个类-->
        <property name="personDao" ref="personDao"/>
    </bean>
</beans>

5.解析xml文件

public class ClassPathXmlApplicationContext implements BeanFactory{
    //存放bean容器的Map
    Map<String,Object> beans = new HashMap<>();

    public ClassPathXmlApplicationContext() throws Exception{
        //使用dom4j解析自己写的beans.xml文件
        SAXReader reader = new SAXReader();
        //指定解析的配置文件
        String path = "beans.xml";
        //获取DOM树
        Document document = reader.read(this.getClass().getClassLoader().getResourceAsStream(path));
        //利用树获取beans.xml的beans根节点
        Element rootElement = document.getRootElement();
        //测试获取的根节点是否为beans
        // System.out.println(rootElement.getName());//beans ok
        //获取根节点下的所有元素
        List<Element> elements = rootElement.elements();
        for (Element element : elements) {
            //测试获取元素名称是否为bean
            //System.out.println(element.getName().equals("bean"));//true ok

            //通过每个bean元素获取到id这个属性,即beanName
            String id = element.attributeValue("id");
            //拿到全限定类名,以便反射调用方法
            String aClass = element.attributeValue("class");

            //测试
            //System.out.println("id=" + id + "  ,aClass=" + aClass);//ok

            //获取Class对象
            Class<?> clazz = Class.forName(aClass);
            //通过全限定类名实例化对象:控制反转
            Object object = clazz.newInstance();
            //测试
            //System.out.println("object = " + object);//ok

            //将实例化的对象存入map中
            beans.put(id,object);

            //遍历bean元素下的子元素
            List<Element> sonEl = element.elements();
            for (Element e2 : sonEl) {
                String name = e2.attributeValue("name");
                //引用的那个bean对象
                String refBean = e2.attributeValue("ref");
                //测试
                //System.out.println(name+"---------"+refBean);//ok

                //要调用service中注入的setPersonDao方法:拼接set方法
                String invokeMethod = "set" + name.substring(0,1).toUpperCase()+name.substring(1);

                //测试
               //System.out.println(invokeMethod);//setPersonDao ok
                
                //通过key获取value值,对象
                Object bean = getBean(refBean);
                
                //反射,获取方法对象,反向调用
                Method method = object.getClass().getMethod(invokeMethod, bean.getClass().getInterfaces()[0]);
                //测试
                //System.out.println(method);//ok
                
                //反向调用
                method.invoke(object,bean);//依赖注入
            }
        }
    }
    @Override
    public Object getBean(String beanName) {
        return this.beans.get(beanName);
    }
}

6.测试

public class MyTest {
    @Test
    public void test() throws Exception {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext();
		//从容器中拿到id为person的beanName
        Person person = (Person)context.getBean("person");
        person.setName("张三");
        person.setAge(12);
        System.out.println(person);

        PersonServiceImpl personService = (PersonServiceImpl) context.getBean("personService");
        personService.addPerson(person);
        //Person(name=张三, age=12)
        //获取person信息,姓名张三,年龄:12
        //用户保存成功 ok
    }
}

总结

ioc是一种思想,也叫控制反转
传统的开发,创建对象会使用到了new关键字
在Spring中,将创建对象和对象之间的依赖交给了IOC容器管理,由IOC容器进行注入、组合对象,处理对象之间的依赖关系

这样对象与对象之间是松耦合、便于测试、功能可复用(减少对象的创建和内存消耗),程序更好维护、灵活度高、扩展性变高

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值