【Spring】手写 Spring 框架 IoC

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 【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);
    }
}

手写 Spring 框架 IoC

  • Spring IoC 的实现原理:工厂模式 + 解析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>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是使用框架的用户写的
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;
    }
}

配置文件

  • 这个配置文件是使用框架的用户写的
<?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);
}

相关文章
|
23天前
|
数据采集 监控 前端开发
二级公立医院绩效考核系统源码,B/S架构,前后端分别基于Spring Boot和Avue框架
医院绩效管理系统通过与HIS系统的无缝对接,实现数据网络化采集、评价结果透明化管理及奖金分配自动化生成。系统涵盖科室和个人绩效考核、医疗质量考核、数据采集、绩效工资核算、收支核算、工作量统计、单项奖惩等功能,提升绩效评估的全面性、准确性和公正性。技术栈采用B/S架构,前后端分别基于Spring Boot和Avue框架。
|
1月前
|
Java API 数据库
构建RESTful API已经成为现代Web开发的标准做法之一。Spring Boot框架因其简洁的配置、快速的启动特性及丰富的功能集而备受开发者青睐。
【10月更文挑战第11天】本文介绍如何使用Spring Boot构建在线图书管理系统的RESTful API。通过创建Spring Boot项目,定义`Book`实体类、`BookRepository`接口和`BookService`服务类,最后实现`BookController`控制器来处理HTTP请求,展示了从基础环境搭建到API测试的完整过程。
42 4
|
1月前
|
Java API 数据库
Spring Boot框架因其简洁的配置、快速的启动特性及丰富的功能集而备受开发者青睐
本文通过在线图书管理系统案例,详细介绍如何使用Spring Boot构建RESTful API。从项目基础环境搭建、实体类与数据访问层定义,到业务逻辑实现和控制器编写,逐步展示了Spring Boot的简洁配置和强大功能。最后,通过Postman测试API,并介绍了如何添加安全性和异常处理,确保API的稳定性和安全性。
38 0
|
27天前
|
前端开发 Java 数据库连接
Spring 框架:Java 开发者的春天
Spring 框架是一个功能强大的开源框架,主要用于简化 Java 企业级应用的开发,由被称为“Spring 之父”的 Rod Johnson 于 2002 年提出并创立,并由Pivotal团队维护。
43 1
Spring 框架:Java 开发者的春天
|
20天前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个前后端分离的应用框架,实现动态路由和菜单功能
本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个前后端分离的应用框架,实现动态路由和菜单功能。首先,确保开发环境已安装必要的工具,然后创建并配置 Spring Boot 项目,包括添加依赖和配置 Spring Security。接着,创建后端 API 和前端项目,配置动态路由和菜单。最后,运行项目并分享实践心得,帮助开发者提高开发效率和应用的可维护性。
37 2
|
19天前
|
消息中间件 NoSQL Java
springboot整合常用中间件框架案例
该项目是Spring Boot集成整合案例,涵盖多种中间件的使用示例,每个案例项目使用最小依赖,便于直接应用到自己的项目中。包括MyBatis、Redis、MongoDB、MQ、ES等的整合示例。
77 1
|
27天前
|
Java 数据库连接 开发者
Spring 框架:Java 开发者的春天
【10月更文挑战第27天】Spring 框架由 Rod Johnson 在 2002 年创建,旨在解决 Java 企业级开发中的复杂性问题。它通过控制反转(IOC)和面向切面的编程(AOP)等核心机制,提供了轻量级的容器和丰富的功能,支持 Web 开发、数据访问等领域,显著提高了开发效率和应用的可维护性。Spring 拥有强大的社区支持和丰富的生态系统,是 Java 开发不可或缺的工具。
|
22天前
|
XML 缓存 Java
搞透 IOC、Spring IOC ,看这篇就够了!
本文详细解析了Spring框架的核心内容——IOC(控制反转)及其依赖注入(DI)的实现原理,帮助读者理解如何通过IOC实现组件解耦,提高程序的灵活性和可维护性。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
|
1月前
|
人工智能 Java API
阿里云开源 AI 应用开发框架:Spring AI Alibaba
近期,阿里云重磅发布了首款面向 Java 开发者的开源 AI 应用开发框架:Spring AI Alibaba(项目 Github 仓库地址:alibaba/spring-ai-alibaba),Spring AI Alibaba 项目基于 Spring AI 构建,是阿里云通义系列模型及服务在 Java AI 应用开发领域的最佳实践,提供高层次的 AI API 抽象与云原生基础设施集成方案,帮助开发者快速构建 AI 应用。本文将详细介绍 Spring AI Alibaba 的核心特性,并通过「智能机票助手」的示例直观的展示 Spring AI Alibaba 开发 AI 应用的便利性。示例源
|
1月前
|
人工智能 开发框架 Java
总计 30 万奖金,Spring AI Alibaba 应用框架挑战赛开赛
Spring AI Alibaba 应用框架挑战赛邀请广大开发者参与开源项目的共建,助力项目快速发展,掌握 AI 应用开发模式。大赛分为《支持 Spring AI Alibaba 应用可视化调试与追踪本地工具》和《基于 Flow 的 AI 编排机制设计与实现》两个赛道,总计 30 万奖金。
下一篇
无影云桌面