当前位置: 首页 > 图灵资讯 > 技术篇> 手写spring框架

手写spring框架

来源:图灵教育
时间:2023-06-08 09:22:15

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

我们把自己的框架命名为:myspring(春天)

1.第一步:创建模块myspring62

Modulen采用Maven新建:myspring

手写spring框架_手写spring框架

jar用于包装,dom4j和jaxen的依赖被引入,因为它用于分析XML文件和junit依赖。

<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0"         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">    <modelVersion>4.0.0</modelVersion>    <groupId>org.myspringframework</groupId>    <artifactId>myspring</artifactId>    <version>1.0.0</version>    <packaging>jar</packaging>    <dependencies>        <dependency>            <groupId>org.dom4j</groupId>            <artifactId>dom4j</artifactId>            <version>2.1.3</version>        </dependency>        <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>    </dependencies>    <properties>        <maven.compiler.source>17</maven.compiler.source>        <maven.compiler.target>17</maven.compiler.target>    </properties></project>

2.第二步:准备我们要管理的Bean62

准备好我们要管理的Bean(这些Bean将在未来开发框架后删除)

注意包名,不要使用org.myspringframework包,因为这些bean不是内置框架。它是由未来使用我们框架的程序员提供的。

package com.powernode.myspring.bean;///手写spring框架  62//普通beanpublic 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 +                '}';    }}

package com.powernode.myspring.bean;///手写spring框架  62//daopublic class UserDao {    public void insert(){        System.out.println(mysql数据库正在保存用户信息);    }}

package com.powernode.myspring.bean;///手写spring框架  62//servicepublic class UserService {    private UserDao userDao;    public void setUserDao(UserDao userDao) {        this.userDao = userDao;    }    public void save(){        userDao.insert();    }}

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

未来框架开发完成后,该文件也将被删除。因为该配置文件的提供者应该是使用该框架的程序员。

随意命名文件,我们在这里称之为:myspring.xml

文件可以放在类路径中,我们可以把文件放在类的根路径下。

<?xml version="1.0" encoding="UTF-8"?><!--该配置文件由使用myspring框架的开发人员提供 62--><beans>    <bean id="user" class="com.powernode.myspring.bean.User">        <property name="name" vlaue="张三"></property>        <property name="age" vlaue="30"></property>    </bean>    <bean id="userService" class="com.powernode.myspring.bean.UserService">        <property name="userDao" ref="userDaoBean"/>    </bean>    <bean id="userDaoBean" class="com.powernode.myspring.bean.UserDao"></bean></beans>

使用value赋值简单属性。使用ref赋值非简单属性。

4.第四步:编写Applicationcontext接口633

在Applicationcontext接口中提供一种getbean()方法,通过这种方法可以获得bean对象。

注意包名:这个接口是myspring框架的一员。

package org.myspringframework.core;/** * 手写spring框架  63 * 上下文接口应用于MySpring框架。  63 **/public interface ApplicationContext {    /**     * 根据bean的名字获取相应的bean对象。     * @param beanName bean标签id在myspring配置文件中。     * @return 单例bean对象对应。     */    Object getBean(String beanName);}

5.第五步:编写ClaspathXmlaplicationcontext633

classpathXmlaplicationcontext是aplicationconcontext接口的实现类。这种从类路径中加载myspring.xml配置文件。

package org.myspringframework.core;public class ClassPathXmlApplicationContext implements ApplicationContext{    @Override    public Object getBean(String beanId) {        return null;    }}

6.第六步:确定使用Map集存储Bean633

确定使用Map集合存储Bean实例。Map集合的key存储beanID,value存储bean实例。Map

Map属性添加到ClasspathXmlaplicationcontext类中。

并在ClaspathXmlaplicationcontext类中添加结构方法,该结构方法的参数接收myspring.xml文件。

同时实现getbean方法。

package org.myspringframework.core;import java.util.HashMap;import java.util.Map;public class ClassPathXmlApplicationContext implements ApplicationContext{    /**     * 存储bean的Map集合     */    private Map beanMap = new HashMap<>();    /**     * 在这种结构方法中,分析myspring.创建所有Bean实例的xml文件,并将Bean实例存储在Map集合中。     * @param resource 配置文件路径(类别路径中需要)     */    public ClassPathXmlApplicationContext(String resource) {    }    @Override    public Object getBean(String beanId) {        return beanMap.get(beanId);    }}

7.第七步:分析所有Bean644的配置文件实例化

在ClaspathXmlaplicationcontext的结构方法中分析配置文件,获取所有bean的类名,并通过反射机制调用无参数结构方法创建bean。并将bean对象存储在Map集合中

/*** 在这种结构方法中,分析myspring.创建所有Bean实例的xml文件,并将Bean实例存储在Map集合中。* @param resource 配置文件路径*/public ClassPathXmlApplicationContext(String resource) {    try {        SAXReader reader = new SAXReader();        Document document = reader.read(ClassLoader.getSystemClassLoader().getResourceAsStream(resource));        // 获取所有bean标签        List<Node> beanNodes = document.selectNodes("//bean");        // 遍历集合        beanNodes.forEach(beanNode -> {            Element beanElt = (Element) beanNode;            // 获取id            String id = beanElt.attributeValue("id");            // clasname获得clasname            String className = beanElt.attributeValue("class");            try {                // 通过反射机制创建对象                Class<?> clazz = Class.forName(className);                Constructor<?> defaultConstructor = clazz.getDeclaredConstructor();                Object bean = defaultConstructor.newInstance();                // 存储到Map集合中                beanMap.put(id, bean);            } catch (Exception e) {                e.printStackTrace();            }        });    } catch (Exception e) {        e.printStackTrace();    }}

8.第八步:测试能否获得Bean64

编制测试程序。

//测试publicc class MySpringTest {    @Test    public void testMySpring(){        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("myspring.xml");    }}

手写spring框架_构造方法_02

9.第九步:给Bean属性赋值65-67

通过反射机制调用set法赋值Bean属性。

继续在ClaspathXmlaplicationcontext结构方法中编写代码。

// 所有的bean标签再次遍历,这次主要是赋值对象的属性。  65            nodes.forEach(node -> {                try {                    Element beanElt = (Element) node;                    // 获取id                    String id = beanElt.attributeValue("id");                    // clasname获得clasname                    String className = beanElt.attributeValue("class");                    // 获取Class                    Class<?> aClass = Class.forName(className);                    // 在bean标签下获得所有属性property标签                    List<Element> propertys = beanElt.elements("property");                    // 遍历所有属性标签                    propertys.forEach(property -> {                        try {                            // 获取属性名                            String propertyName = property.attributeValue("name");                            // 获取属性类型                            Field field = aClass.getDeclaredField(propertyName);                            logger.info(属性名:" + propertyName);                            // 获取set方法名                            String setMethodName = "set" + propertyName.toUpperCase().charAt(0) + propertyName.substring(1);                            // 获取set方法                            Method setMethod = aClass.getDeclaredMethod(setMethodName, field.getType());                            // 获取具体值   67                            String value = property.attributeValue("value"); // "30"                            Object actualValue = null; // 真值                            String ref = property.attributeValue("ref");                            if (value != null) {                                // 说明这个值是简单的类型                                // 调用set方法(set方法没有返回值)                                // 让我们声明一下myspring框架:我们只支持这些类型作为简单类型                                // byte short int long float double boolean char                                // Byte Short Integer Long Float Double Boolean Character                                // String                                // 获取属性类型名称                                String propertyTypeSimpleName = field.getType().getSimpleName();                                switch (propertyTypeSimpleName) {                                    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;                                }                                setMethod.invoke(singletonObjects.get(id), actualValue);                            }                            if (ref != null) {                                // 说明这个值是非简单的类型  66                                // 调用set方法(set方法没有返回值)                                setMethod.invoke(singletonObjects.get(id), singletonObjects.get(ref));                            }                        } catch (Exception e) {                            e.printStackTrace();                        }                    });                } catch (Exception e) {                    e.printStackTrace();                }            });

10.第十步:测试68

//测试publicc class MySpringTest {    @Test    public void testMySpring(){        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("myspring.xml");        Object user = applicationContext.getBean("user");        System.out.println(user);        UserService userService = (UserService) applicationContext.getBean("userService");        userService.save();    }}

手写spring框架_maven_03

11.第十一步:688包装发布

删除多余的类别和配置文件,用maven打包发布。

手写spring框架_构造方法_04

手写spring框架_手写spring框架_05

12.代码汇总org.myspringframework.coreApplicationContext

package org.myspringframework.core;/** * 手写spring框架  63 * 上下文接口应用于MySpring框架。  63 **/public interface ApplicationContext {    /**     * 根据bean的名字获取相应的bean对象。     * @param beanName bean标签id在myspring配置文件中。     * @return 单例bean对象对应。     */    Object getBean(String beanName);}

ClassPathXmlApplicationContext

package org.myspringframework.core;import java.io.InputStream;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.Method;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;import org.slf4j.Logger;import org.slf4j.LoggerFactory;/** * 手写spring框架  63 * ClaspathXmlaplicationcontext是aplicationcontext接口实现的类别  63 **/public class ClassPathXmlApplicationContext implements ApplicationContext{    private static final Logger logger = LoggerFactory.getLogger(ClassPathXmlApplicationContext.class);    private Map<String, Object> singletonObjects = new HashMap<>();    /**     * 对myspring的配置文件进行分析,然后对所有bean对象进行初始化。  63     * @param configLocation spring配置文件的路径。  63     * @param configLocation spring配置文件的路径。注:使用claspathXmlaplicationcontext,配置文件应放在类别路径下。     */    public ClassPathXmlApplicationContext(String configLocation) {        try {            //  64            // 分析myspring.xml文件,然后实例化Bean,将Bean存储在singletonobjects集合中。            // 这是dom4jXML文件分析的核心对象。            SAXReader reader = new SAXReader();            // 获取输入流,指向配置文件            ///Classloader获取类加载器,getsystemclasloder获取系统类加载器            ///getresourceasstream以流的形式获取系统资源            InputStream in = ClassLoader.getSystemClassLoader().getResourceAsStream(configLocation);            // 读文件            Document document = reader.read(in);            // 获取所有bean标签            List<Node> nodes = document.selectNodes("//bean");            // bean标签遍历            nodes.forEach(node -> {                try {                    // 向下转型的目的是在Element界面中使用更丰富的方法。                    Element beanElt = (Element) node;                    // 获取id属性                    String id = beanElt.attributeValue("id");                    // 获取class属性                    String className = beanElt.attributeValue("class");                    logger.info("beanName=" + id);                    logger.info("beanClassName="+className);                    // 通过反射机制创建对象,将其放入Map集中,提前曝光。                    // 获取Class                    Class<?> aClass = Class.forName(className);                    // 获取无参数结构的方法                    Constructor<?> defaultCon = aClass.getDeclaredConstructor();                    // 实例Bean调用无参数结构法                    Object bean = defaultCon.newInstance();                    // 曝光Bean,加入Map集合                    singletonObjects.put(id, bean);                    // 记录日志                    logger.info(singletonObjects.toString());                } catch (Exception e) {                    e.printStackTrace();                }            });            // 再次遍历所有的bean标签,这一次主要是赋值对象的属性。  65            nodes.forEach(node -> {                try {                    Element beanElt = (Element) node;                    // 获取id                    String id = beanElt.attributeValue("id");                    // clasname获得clasname                    String className = beanElt.attributeValue("class");                    // 获取Class                    Class<?> aClass = Class.forName(className);                    // 在bean标签下获得所有属性property标签                    List<Element> propertys = beanElt.elements("property");                    // 遍历所有属性标签                    propertys.forEach(property -> {                        try {                            // 获取属性名                            String propertyName = property.attributeValue("name");                            // 获取属性类型                            Field field = aClass.getDeclaredField(propertyName);                            logger.info(属性名:" + propertyName);                            // 获取set方法名                            String setMethodName = "set" + propertyName.toUpperCase().charAt(0) + propertyName.substring(1);                            // 获取set方法                            Method setMethod = aClass.getDeclaredMethod(setMethodName, field.getType());                            // 获取具体值   67                            String value = property.attributeValue("value"); // "30"                            Object actualValue = null; // 真值                            String ref = property.attributeValue("ref");                            if (value != null) {                                // 说明这个值是简单的类型                                // 调用set方法(set方法没有返回值)                                // 让我们声明一下myspring框架:我们只支持这些类型作为简单类型                                // byte short int long float double boolean char                                // Byte Short Integer Long Float Double Boolean Character                                // String                                // 获取属性类型名称                                String propertyTypeSimpleName = field.getType().getSimpleName();                                switch (propertyTypeSimpleName) {                                    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;                                }                                setMethod.invoke(singletonObjects.get(id), actualValue);                            }                            if (ref != null) {                                // 说明这个值是非简单的类型  66                                // 调用set方法(set方法没有返回值)                                setMethod.invoke(singletonObjects.get(id), singletonObjects.get(ref));                            }                        } catch (Exception e) {                            e.printStackTrace();                        }                    });                } catch (Exception e) {                    e.printStackTrace();                }            });        } catch (Exception e) {            e.printStackTrace();        }    }    @Override    public Object getBean(String beanName) {        return singletonObjects.get(beanName);    }}

pom.xml

<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0"         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">    <modelVersion>4.0.0</modelVersion>    <groupId>org.myspringframework</groupId>    <artifactId>course15</artifactId>    <version>1.0.0</version><!--    打包方式  62-->    <packaging>jar</packaging><!--    打包方式  62-->    <packaging>jar</packaging><!--    依赖-->    <dependencies><!--        dom4j是一个java组件,可以分析xml文件-->        <dependency>            <groupId>org.dom4j</groupId>            <artifactId>dom4j</artifactId>            <version>2.1.3</version>        </dependency>        <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>    </dependencies>    <properties>        <maven.compiler.source>17</maven.compiler.source>        <maven.compiler.target>17</maven.compiler.target>    </properties></project>