【Spring】手写 Spring 框架 IoC

根据 【动力节点】最新Spring框架教程,全网首套Spring6教程,跟老杜从零学spring入门到高级 以及老杜的原版笔记 https://www.yuque.com/docs/share/866abad4-7106-45e7-afcd-245a733b073f?# 《Spring6》 进行整理, 文档密码:mg9b


Spring 相关文章整理汇总归纳于:https://www.yuque.com/u27599042/zuisie


回顾反射机制

调用方法的四要素

  • 调用方法的四要素
    • 第一要素:使用哪个对象进行方法的调用
    • 第二要素:调用对象的哪个方法
    • 第三要素:调用方法的时候,需要传递什么参数
    • 第四要素:方法执行结束之后的返回结果
  • 总结:调用哪个对象的哪个方法,需要传递什么参数,该方法返回什么值
  • 即使是使用反射机制进行方法的调用,也同样需要具备着四要素

反射机制调用方法

/**
 * ClassName: SomeService
 * Package: PACKAGE_NAME
 * Description:
 *
 * @Author tcw
 * @Create 2023-05-27 10:46
 * @Version 1.0
 */
public class SomeService {
    
    
    public void doSome() {
    
    
        System.out.println(" public void doSome() 执行");
    }
    
    public String doSome(String s) {
    
    
        System.out.println(" public void doSome(String s) 执行");
        return s;
    }
    
    public String doSome(String s, Integer i) {
    
    
        System.out.println(" public void doSome(String s, Integer i) 执行");
        return s + " " + i;
    }
}
import java.lang.reflect.Method;

/**
 * ClassName: Main
 * Package: PACKAGE_NAME
 * Description:
 *
 * @Author tcw
 * @Create 2023-05-27 10:45
 * @Version 1.0
 */
public class Main {
    
    
    public static void main(String[] args) throws Exception {
    
    
        // 加载类,获取类对应的Class对象
        Class<?> someServiceClass = Class.forName("SomeService");
        
        // 创建对象
        // 调用类的无参构造方法创建对象
        SomeService someService = (SomeService) someServiceClass.getDeclaredConstructor().newInstance();
    
        // 获取方法 public String doSome(String s, Integer i)
        // 参数一:获取方法的方法名
        // 参数二:可变参数,获取方法的形参列表类型
        Method doSomeMethod = someServiceClass.getDeclaredMethod("doSome", String.class, Integer.class);
        
        // 调用方法(四要素:调用哪个对象的哪个方法,需要传递什么参数,该方法返回什么值)
        // 参数一:调用哪个对象的该方法  someService
        // 参数二:可变参数,调用方法需要传递的形参  "Hello World", 111
        // invoke 的返回值就是调用方法的返回值  Object res
        Object res = doSomeMethod.invoke(someService, "Hello World", 111);
        System.out.println(res);
    }
}

Spring DI 核心实现

  • 需求:假设你现在已知以下信息:
    • 1.有这样一个类,类名叫做:包名.User
    • 2.这个类符合javabean规范。属性私有化,对外提供公开的setter和getter方法。
    • 3.你还知道这个类当中有一个属性,属性的名字叫做 age
    • 4.并且你还知道age属性的类型是int类型。
  • 请使用反射机制调用set方法,给User对象的age属性赋值。
package cw.study.spring.pojo;

/**
 * ClassName: User
 * Package: cw.study.spring.pojo
 * Description:
 *
 * @Author tcw
 * @Create 2023-05-28 9:59
 * @Version 1.0
 */
public class User {
    
    
    private String name;
    private int age;
    
    public User() {
    
    
    }
    
    public User(String name, int age) {
    
    
        this.name = name;
        this.age = age;
    }
    
    @Override
    public String toString() {
    
    
        return "User{" + "name='" + name + '\'' + ", age=" + age + '}';
    }
    
    public String getName() {
    
    
        return name;
    }
    
    public void setName(String name) {
    
    
        this.name = name;
    }
    
    public int getAge() {
    
    
        return age;
    }
    
    public void setAge(int age) {
    
    
        this.age = age;
    }
}
import cw.study.spring.pojo.User;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
 * ClassName: Main
 * Package: PACKAGE_NAME
 * Description:
 *
 * @Author tcw
 * @Create 2023-05-27 10:45
 * @Version 1.0
 */
public class Main {
    
    
    public static void main(String[] args) throws Exception {
    
    
        // 1.有这样一个类,类名叫做:包名.User
        String className = "cw.study.spring.pojo.User";
        // 2.这个类符合javabean规范。属性私有化,对外提供公开的setter和getter方法。
        // 3.你还知道这个类当中有一个属性,属性的名字叫做 age
        String fieldName = "age";
        // 4.并且你还知道age属性的类型是int类型。
        // 请使用反射机制调用set方法,给User对象的age属性赋值。
        
        // 加载类
        Class<?> userClass = Class.forName(className);
        
        // 创建对象
        User user = (User) userClass.getDeclaredConstructor().newInstance();
        
        // 获取属性
        Field field = userClass.getDeclaredField(fieldName);
        // 获取属性的类型
        Class<?> fieldType = field.getType();
    
        // 获取方法名
        String methodName = "set" + fieldName.toUpperCase().charAt(0) + fieldName.substring(1);
        // 获取方法
        Method method = userClass.getDeclaredMethod(methodName, fieldType);
        
        // 调用方法
        method.invoke(user, 22);
    
        System.out.println(user);
    }
}

image.png

手写 Spring 框架 IoC

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

环境准备

创建模块

image.png

打包方式与依赖引入

<?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>cw.study.spring</groupId>
	<artifactId>spring-study-011-myspring</artifactId>
	<version>1.0-SNAPSHOT</version>
	<packaging>jar</packaging>
	
	<dependencies>
		<!-- dom4j 解析XML文件 -->
		<dependency>
			<groupId>org.dom4j</groupId>
			<artifactId>dom4j</artifactId>
			<version>2.1.3</version>
		</dependency>
    <!-- jaxen 需要使用xpath -->
		<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>

JavaBean

  • 这些JavaBean是使用框架的用户写的
  • image.png
package cw.study.spring.bean;

/**
 * ClassName: User
 * Package: cw.study.spring.bean
 * Description:
 *
 * @Author tcw
 * @Create 2023-05-31 12:06
 * @Version 1.0
 */
public class User {
    
    
    private String name;
    private Integer age;
    
    public User() {
    
    
    }
    
    public void setName(String name) {
    
    
        this.name = name;
    }
    
    public void setAge(Integer age) {
    
    
        this.age = age;
    }
    
    @Override
    public String toString() {
    
    
        return "User{" + "name='" + name + '\'' + ", age=" + age + '}';
    }
}
package cw.study.spring.bean;

/**
 * ClassName: UserDao
 * Package: cw.study.spring.bean
 * Description:
 *
 * @Author tcw
 * @Create 2023-05-31 12:07
 * @Version 1.0
 */
public class UserDao {
    
    
    public void insert() {
    
    
        System.out.println("数据库正在保存用户信息...");
    }
}
package cw.study.spring.bean;

/**
 * ClassName: UserService
 * Package: cw.study.spring.bean
 * Description:
 *
 * @Author tcw
 * @Create 2023-05-31 14:08
 * @Version 1.0
 */
public class UserService {
    
    
    private UserDao userDao;
    
    public void save() {
    
    
        userDao.insert();
    }
    
    public void setUserDao(UserDao userDao) {
    
    
        this.userDao = userDao;
    }
}

配置文件

  • 这个配置文件是使用框架的用户写的
  • image.png
<?xml version="1.0" encoding="UTF-8" ?>

<beans>
	
	<bean id="user" class="cw.study.spring.bean.User">
		<property name="name" value="张三"/>
		<property name="age" value="21"/>
	</bean>
	
	<bean id="userDao" class="cw.study.spring.bean.UserDao"/>
	
	<bean id="userService" class="cw.study.spring.bean.UserService">
		<property name="userDao" ref="userDao"/>
	</bean>
	
</beans>

Spring 框架核心接口实现 ApplicationContext

package org.myspringframework.core;

/**
 * ClassName: ApplicationContext
 * Package: org.myspringframework.core
 * Description:
 * MySpring框架应用上下文接口
 *
 * @Author tcw
 * @Create 2023-05-31 14:22
 * @Version 1.0
 */
public interface ApplicationContext {
    
    
    
    /**
     * 通过Bean的id从容器中获取相应的Bean对象
     *      Bean的名称就是配置文件中Bean的id
     *
     * @param beanName Bean的id
     * @return Bean对象
     */
    Object getBean(String beanName);
    
}

Spring 框架核心接口实现类 ClassPathXmlApplicationContext

package org.myspringframework.core;

import org.dom4j.*;
import org.dom4j.io.SAXReader;

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


/**
 * ClassName: ClassPathXmlApplicationContext
 * Package: org.myspringframework.core
 * Description:
 *
 * @Author tcw
 * @Create 2023-05-31 14:25
 * @Version 1.0
 */
public class ClassPathXmlApplicationContext implements ApplicationContext{
    
    
    
    // 用于存放Bean对象的map集合,使用Bean的名称可以获取相应的Bean对象
    private Map<String, Object> singletonObjects = new HashMap<>();
    
    /**
     * 根据Spring配置文件创建Spring容器的构造方法
     * 该方法一执行就会对配置文件进行解析创建Bean对象
     * 使用ClassPathXmlApplicationContext,配置文件应该放在类路径下
     *
     * @param configLocation Spring配置文件的路径
     */
    public ClassPathXmlApplicationContext(String configLocation) {
    
    
        try {
    
    
            // 解析XML核心配置文件,实例化Bean,将Bean存放到map集合中
            // 获取dom4j解析xml文件的核心对象,读取xml文件中的内容
            SAXReader reader = new SAXReader();
            // 获取xml的输入流,通过类加载器以流的方式获取资源
            InputStream inputStream = ClassLoader.getSystemClassLoader().getResourceAsStream(configLocation);
            // 读取xml文件中的内容,获取xml对应的文档对象
            Document document = reader.read(inputStream);
            // 获取xml文件中所有的bean标签
            List<Node> beansNode = document.selectNodes("//bean");
            // 遍历所有的bean标签,创建相应的对象放到容器中
            beansNode.forEach(bean->{
    
    
                try {
    
    
                    // System.out.println(bean);
                    // org.dom4j.tree.DefaultElement@74ad1f1f
                    // bean实际上是Element类型,向下转型,因为Element中方法更丰富
                    Element beanElement = (Element) bean;
                    // 获取Bean id
                    String id = beanElement.attributeValue("id");
                    // 获取bean的类型
                    String className = beanElement.attributeValue("class");
                    // System.out.println(id + ": " + className);
                    // 利用反射机制进行对象的创建,并放到map集合中,进行提前曝光
                    Object beanInstance = createBeanObjectByReflect(className);
                    // 将创建的对象放入map集合中进行曝光
                    singletonObjects.put(id, beanInstance);
                    // System.out.println(singletonObjects);
                } catch (Exception e) {
    
    
                    e.printStackTrace();
                }
            });
            // 再次遍历所有的bean标签,为创建的所有bean对象进行属性的赋值
            beansNode.forEach(bean->{
    
    
                try {
    
    
                    // 进行强转
                    Element beanElement= (Element) bean;
                    // 获取bean的id
                    String id = beanElement.attributeValue("id");
                    // // 判断bean是否已经被创建了
                    // if (!singletonObjects.containsKey(id)) {
    
    
                    //     // 如果没有创建bean对象,则进行bean对象的创建
                    //     Object beanInstance = createBeanObjectByReflect(className);
                    // }
                    // 获取bean的类名
                    String className = beanElement.attributeValue("class");
                    // 加载类
                    Class<?> clazz = Class.forName(className);
                    // 获取bean标签中的所有子标签属性标签
                    List<Element> properties = beanElement.elements("property");
                    // 遍历bean标签中的所有子标签属性标签,为bean的属性进行赋值
                    properties.forEach(property->{
    
    
                        try {
    
    
                            // 获取property标签的name属性值,获取要为哪个属性赋值
                            String fieldName = property.attributeValue("name");
                            // 获取property标签的value属性值,获取要为属性赋的值(标签中如果无value,返回null)
                            String fieldValue = property.attributeValue("value");
                            // 获取property标签的ref属性值,获取要为属性赋的值(标签中如果无ref,返回null)
                            String ref = property.attributeValue("ref");
                            // System.out.println(fieldName + " = " + fieldValue);
                            // 获取set方法的方法名
                            String setMethodName = "set" + fieldName.toUpperCase().charAt(0) + fieldName.substring(1);
                            // 获取set方法对应的属性的类型
                            Class<?> fieldType = clazz.getDeclaredField(fieldName).getType();
                            // 获取set方法
                            Method setMethod = clazz.getDeclaredMethod(setMethodName, fieldType);
                            // 如果属性的值是通过value进行赋值
                            if (fieldValue != null) {
    
    
                                // 值为简单类型
                                // 调用id对应的bean对象的set方法(set方法无返回值)
                                // 从标签的属性中获取的值都为字符串类型,但是set方法需要的参数有具体的数据类型,需要进行处理
                                // 获取set方法参数的简单类型名
                                String fieldTypeSimpleName = fieldType.getSimpleName();
                                String propertyValue = fieldValue; // 简单类型的属性值
                                // 进行数据类型匹配,对参数进行类型转换
                                Object propertyVal = null;
                                switch (fieldTypeSimpleName) {
    
    
                                    case "byte": case "Byte":
                                        propertyVal = Byte.valueOf(propertyValue);
                                        break;
                                    case "short": case "Short":
                                        propertyVal = Short.valueOf(propertyValue);
                                        break;
                                    case "int": case "Integer":
                                        propertyVal = Integer.valueOf(propertyValue);
                                        break;
                                    case "long": case "Long":
                                        propertyVal = Long.valueOf(propertyValue);
                                        break;
                                    case "float": case "Float":
                                        propertyVal = Float.valueOf(propertyValue);
                                        break;
                                    case "double": case "Double":
                                        propertyVal = Double.valueOf(propertyValue);
                                        break;
                                    case "boolean": case "Boolean":
                                        propertyVal = Boolean.valueOf(propertyValue);
                                        break;
                                    case "char": case "Character":
                                        propertyVal = propertyValue.charAt(0);
                                        break;
                                    case "String":
                                        propertyVal = propertyValue;
                                        break;
                                }
                                // 调用id对应的bean对象的set方法(set方法无返回值)
                                setMethod.invoke(singletonObjects.get(id), propertyVal);
                            }
                            // 如果属性的值是通过ref进行赋值
                            if (ref != null) {
    
    
                                // 值为非简单类型
                                // 调用id对应的bean对象的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();
        }
    }
    
    /**
     * 通过反射机制进行相应类的对象的创建
     *
     * @param className 类的全限定类名
     * @return 类的对象
     * @throws Exception 异常
     */
    private Object createBeanObjectByReflect(String className) throws Exception {
    
    
        // 加载类
        Class<?> clazz = Class.forName(className);
        // 获取类的无参构造器创建对象
        return clazz.getDeclaredConstructor().newInstance();
    }
    
    /**
     * 根据bean的id获取bean对象
     *
     * @param beanName Bean的id
     * @return bean对象
     */
    @Override
    public Object getBean(String beanName) {
    
    
        return singletonObjects.get(beanName);
    }
}

MySpring 框架测试

@org.junit.Test
public void test01() {
    
    
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("myspring.xml");
    UserService userService = (UserService) applicationContext.getBean("userService");
    userService.save();
    User user = (User) applicationContext.getBean("user");
    System.out.println(user);
}

image.png

猜你喜欢

转载自blog.csdn.net/m0_53022813/article/details/132058717