(十一)手写简单的Spring框架


Spring学习目录

上一篇:(十)Spring之回顾反射机制

下一篇:(十二)Spring IoC注解式开发

Spring IoC容器的实现原理:工厂模式 + 解析XML + 反射机制。

第一步:搭建环境

采用Maven方式新建模块:取名MySpring
引入dom4j和jaxen的依赖,因为要使用它解析XML文件,还有junit依赖和log4j2的依赖,方便测试。
log4j2.xml文件放到类路径下。

    <!--dem4j依赖-->
    <dependency>
      <groupId>org.dom4j</groupId>
      <artifactId>dom4j</artifactId>
      <version>2.1.3</version>
    </dependency>
    <!--jaxen依赖-->
    <dependency>
      <groupId>jaxen</groupId>
      <artifactId>jaxen</artifactId>
      <version>1.2.0</version>
    </dependency>

    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.13.2</version>
      <scope>test</scope>
    </dependency>
    <!--log4j2的依赖-->
    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-core</artifactId>
      <version>2.19.0</version>
    </dependency>
    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-slf4j2-impl</artifactId>
      <version>2.19.0</version>
    </dependency>

第二步:准备好要管理的Bean

准备好我们要管理的Bean,这些Bean只是为了方便测试,也就是说这些Bean在将来开发完框架之后是要删除。
创建User类:

public class User {
    private String name;
    private int age;

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

创建UserDao类:

public class UserDao {
    public void insert(){
        System.out.println("正在新增用户信息。。。");
    }
}

创建UserDao类:

public class UserService {
    private UserDao userDao;

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    public void save(){
        userDao.insert();
    }
}

第三步:准备myspring.xml配置文件

注意这个配置文件也是框架使用者提供,将来开发完框架之后是要删除。
使用value给简单属性赋值。使用ref给非简单属性赋值。

<?xml version="1.0" encoding="UTF-8"?>
<beans>
    <!--这个配置文件也是框架使用者提供-->
    <bean id="user" class="com.myspring.bean.User">
        <property name="name" value="张三"/>
        <property name="age" value="20"/>
    </bean>

    <bean id="userDao" class="com.myspring.bean.UserDao">

    </bean>
    <bean id="userService" class="com.myspring.bean.UserService">
        <property name="userDao" ref="userDao"/>
    </bean>
</beans>

第四步:编写MyApplicationContext接口

使用Spring的时候我们第一个要做的是获取Spring容器对象,是一个ApplicationContext接口,所以我们也提供这样一个接口,这个接口将来是要通过getBean方法获取Bean的,所以我们提供这样一个方法。

/**
 * MySpring框架应用上下文
 */
public interface MyApplicationContext {
    /**
     * 根据Bean的名称获取对应的Bean对象
     * @param beanName myspring配置文件中bean标签的id
     * @return 对应的Bean单例对象
     */
    Object getBean(String beanName);
}

第五步:编写MyClassPathXmlApplicationContext

使用Spring的时候获取Spring容器对象是通过new ClassPathXmlApplicationContext得到的,这个类ApplicationContext接口的实现类。要加载类路径下的Spring配置文件。
所以我们创建MyClassPathXmlApplicationContext类继承MyApplicationContext接口。


public class MyClassPathXmlApplicationContext implements MyApplicationContext{
    @Override
    public Object getBean(String beanName) {
        return null;
    }
}

第六步:采用Map集合存储Bean

Spring是提供三级缓存存储不同时期的Bean,我们不用那么麻烦,采用一个Map集合存储Bean实例。Map集合的key存储bean的id,value存储Bean实例。Map<String,Object>
在MyClassPathXmlApplicationContext类中添加Map<String,Object>属性。
并且在MyClassPathXmlApplicationContext类中添加构造方法,该构造方法的参数接收myspring.xml文件。
同时实现getBean方法。

public class ClassPathXmlApplicationContext implements ApplicationContext{
    /**
     * 存储bean的Map集合
     */
    private Map<String,Object> singletonObjects = new HashMap<>();

    /**
     * 在该构造方法中,解析myspring.xml文件,创建所有的Bean实例,并将Bean实例存放到Map集合中。
     * @param resource 配置文件路径(要求在类路径当中)
     */
    public ClassPathXmlApplicationContext(String configLocation) {

    }

    @Override
    public Object getBean(String beanName) {
        return singletonObjects.get(beanName);
    }
}

第七步:解析配置文件实例化所有Bean

在MyClassPathXmlApplicationContext的构造方法中解析配置文件,获取所有bean的类名,通过反射机制调用无参数构造方法创建Bean。并且将Bean对象存放到Map集合中。


public MyClassPathXmlApplicationContext(String configLocation) {
    try {
        //1.使用Dom4j创建reader对象
            SAXReader reader = new SAXReader();
            //2.通过类加载器获取SqlMapper的配置文件输入流
            InputStream myspring_stream = ClassLoader.getSystemClassLoader().getResourceAsStream(configLocation);
            //3.读XML文件,返回一个文档对象document
            Document document = reader.read(myspring_stream);
            //4.对标签进行处理
            //(1)获取所有的bean标签
            List<Node> nodes = document.selectNodes("//bean");
            //(2)对bean标签进行遍历,创建对象曝光到Map集合中
        	nodes.forEach(node -> {
                try {
                    //向下转型,因为Element有更多的方法 Element 继承了 Branch 又继承了 Node ,Node方法比较少。
                    Element beanElt = (Element) node;
                    //获取Bean的id
                    String id = beanElt.attributeValue("id");
                    //获取class属性值
                    String className = beanElt.attributeValue("class");
                    logger.info("beanName:" + id);//使用log4j2记录
                    logger.info("beanClassName:" + className);
                    //通过反射机制创建对象,将其放到Map集合中
                    Class<?> aClass = Class.forName(className);
                    Constructor<?> defaultCon = aClass.getDeclaredConstructor();
                    Object beanObj = defaultCon.newInstance();
                    //将bean曝光,加入Map集合
                    singletonObjects.put(id,beanObj);
                    logger.info(singletonObjects.toString());
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
       } catch (Exception e) {
        e.printStackTrace();
    }
}

第八步:给Bean的属性赋值

通过反射机制调用set方法,给Bean的属性赋值。
继续在MyClassPathXmlApplicationContext构造方法中编写代码。

	//(3)再次对bean标签进行遍历,这次主要对bean进行赋值
            nodes.forEach(node -> {
                try {
                    //向下转型,因为Element有更多的方法 Element 继承了 Branch 又继承了 Node ,Node方法比较少。
                    Element beanElt = (Element) node;
                    //获取Bean的id
                    String id = beanElt.attributeValue("id");
                    //获取class属性值
                    String className = beanElt.attributeValue("class");
                    //获取Class
                    Class<?> aClass = Class.forName(className);
                    //获取该bean标签下所有的属性property标签
                    List<Element> propertys = beanElt.elements("property");
                    //遍历property标签
                    propertys.forEach(property ->{
                        try {
                            //获取属性名
                            String propertyName = property.attributeValue("name");
                            logger.info(propertyName);
                            //获取set方法名
                            String setMethodName = "set" + propertyName.toUpperCase().charAt(0) + propertyName.substring(1);
                            //获取属性类型
                            Field field = aClass.getDeclaredField(propertyName);
                            //获取set方法
                            Method setMethod = aClass.getDeclaredMethod(setMethodName,field.getType());
                            //调用set方法
                            //这里的具体值有可能是value或者ref,需要进行判断
                            String value = property.attributeValue("value");
                            String ref = property.attributeValue("ref");
                            Object actualValue = null;
                            if (value != null) {//简单类型
                                //这里比较麻烦,因为获取到的value的是字符串,需要根据属性类型转换成对应的类型
                                //为了程序简单化,我们定义8种基本类型以及对应的包装类,加上String为简单类型
                                //首先获取属性类型名的简称
                                String propertySimpleName = field.getType().getSimpleName();
                                switch (propertySimpleName){
                                    case "byte":actualValue = Byte.parseByte(value);break;
                                    case "short":actualValue = Short.parseShort(value);break;
                                    case "int":actualValue = Integer.parseInt(value);break;
                                    case "long":actualValue = Long.parseLong(value);break;
                                    case "float":actualValue = Float.parseFloat(value);break;
                                    case "double":actualValue = Double.parseDouble(value);break;
                                    case "boolean":actualValue = Boolean.parseBoolean(value);break;
                                    case "char":actualValue = value.charAt(0);break;
                                    case "Byte":actualValue = Byte.valueOf(value);break;
                                    case "Short":actualValue = Short.valueOf(value);break;
                                    case "Integer":actualValue = Integer.valueOf(value);break;
                                    case "Long":actualValue = Long.valueOf(value);break;
                                    case "Float":actualValue = Float.valueOf(value);break;
                                    case "Double":actualValue = Double.valueOf(value);break;
                                    case "Boolean":actualValue = Boolean.valueOf(value);break;
                                    case "Character":actualValue = Character.valueOf(value.charAt(0));break;
                                    case "String":actualValue = value;break;
                                }
                                setMethod.invoke(singletonObjects.get(id), actualValue);
                            }
                            if (ref != null) {//非简单类型
                                setMethod.invoke(singletonObjects.get(id), singletonObjects.get(ref));
                            }

                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    });
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });

测试MySpring框架

    @Test
    public void testMySpring(){
        MyApplicationContext myApplicationContext = new MyClassPathXmlApplicationContext("myspring.xml");
        Object user = myApplicationContext.getBean("user");
        System.out.println(user);
        UserService userService = ((UserService) myApplicationContext.getBean("userService"));
        userService.save();
    }

测试完毕,运行成功
请添加图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Spring框架一个非常庞大的框架,涵盖了很多的功能和模块。如果要手写一个简单版的Spring框架,我们可以从以下几个方面入手: 1. IOC容器 Spring框架的核心就是IOC容器。我们可以实现一个简单的IOC容器,通过注解或配置文件的方式来管理Bean。首先,我们需要定义一个Bean类,它包含了类名、类路径、是否单例等信息。然后,我们需要解析注解或配置文件,将所有的Bean信息存储到一个Map中。最后,在需要使用Bean的地方,我们可以通过Bean的名称从Map中获取Bean实例。 2. AOP Spring框架的另一个核心是AOP。AOP可以帮助我们实现各种各样的切面功能,例如事务管理、日志记录等。我们可以通过定义切点和切面来实现AOP。切点定义了哪些方法需要被代理,切面定义了具体的代理逻辑。我们可以使用JDK动态代理或者CGLIB动态代理来实现代理逻辑。 3. MVC Spring框架还提供了一个MVC模块来帮助我们实现Web应用程序。我们可以实现一个简单的DispatcherServlet来接收HTTP请求,并且根据请求路径和请求方法来调用相应的Controller方法。Controller方法可以返回一个ModelAndView对象,其中包含了响应页面的路径和数据模型。最后,我们可以使用模板引擎来渲染响应页面。 以上是实现一个简单Spring框架的基本思路。当然,这只是一个简单的示例,实际上Spring框架还包括了很多其他的功能和模块,例如JDBC、ORM等。如果想要更深入地了解Spring框架,可以参考Spring官方文档或者相关书籍。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

忆亦何为

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值