Mr. Cappuccino的第59杯咖啡——简单手写SpringIOC框架

环境搭建

基于XML方式

项目结构

在这里插入图片描述

项目代码

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>com</groupId>
    <artifactId>spring</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>5.2.1.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>5.2.1.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.1.RELEASE</version>
        </dependency>
    </dependencies>

</project>

UserBean.java

package com.spring.bean;

/**
 * @author honey
 * @date 2023-08-08 14:58:16
 */
public class UserBean {
}

spring.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="userBean" class="com.spring.bean.UserBean"/>

</beans>

SpringTest01.java

package com.spring.test;

import com.spring.bean.UserBean;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author honey
 * @date 2023-08-08 14:59:56
 */
public class SpringTest01 {

    public static void main(String[] args) {
        // 读取spring.xml
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        // 从IOC容器中读取对象
        UserBean userBean = applicationContext.getBean("userBean", UserBean.class);
        System.out.println(userBean);
    }
}
运行结果

在这里插入图片描述

基于注解方式

项目结构

在这里插入图片描述

项目代码

ScanBean.java

package com.spring.bean.scan;

import org.springframework.stereotype.Component;

/**
 * @author honey
 * @date 2023-08-08 16:37:26
 */
@Component
public class ScanBean {
}

SpringConfig.java

package com.spring.config;

import com.spring.bean.UserBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

/**
 * @author honey
 * @date 2023-08-08 16:30:21
 */
@Configuration
@ComponentScan(value = {"com.spring.bean.scan"})
public class SpringConfig {

    @Bean(name = "user")
    public UserBean userBean() {
        return new UserBean();
    }
}

SpringTest02.java

package com.spring.test;

import com.spring.bean.UserBean;
import com.spring.bean.scan.ScanBean;
import com.spring.config.SpringConfig;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/**
 * @author honey
 * @date 2023-08-08 16:31:25
 */
public class SpringTest02 {

    public static void main(String[] args) {
        // 加载SpringConfig
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
        // 从IOC容器中读取对象
        UserBean userBean = applicationContext.getBean("user", UserBean.class);
        System.out.println(userBean);
        ScanBean scanBean = applicationContext.getBean("scanBean", ScanBean.class);
        System.out.println(scanBean);
    }
}
运行结果

在这里插入图片描述

简单手写SpringIOC框架

核心原理

底层使用map集合管理对象,key=beanId,value=实例对象

private final Map<String, Object> beanMap = new ConcurrentHashMap<>();

基于XML方式

原理

基于反射+工厂模式+DOM技术

  1. 使用DOM技术解析spring.xml文件;
  2. 获取bean的id和class属性;
  3. 根据类的完整路径使用反射技术初始化对象;
  4. 使用工厂模式管理初始化对象;
项目结构

在这里插入图片描述

项目代码

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>com</groupId>
    <artifactId>ext-spring-ioc</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>5.2.1.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>5.2.1.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.1.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>dom4j</groupId>
            <artifactId>dom4j</artifactId>
            <version>1.6.1</version>
        </dependency>
    </dependencies>

</project>

UserBean.java

package com.spring.bean;

/**
 * @author honey
 * @date 2023-08-08 16:56:32
 */
public class UserBean {
}

spring.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="userBean" class="com.spring.bean.UserBean"/>

</beans>

SpringIocXml.java

package com.spring.ext;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.springframework.core.io.ClassPathResource;

import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author honey
 * @date 2023-08-08 16:57:17
 */
public class SpringIocXml {

    private final Map<String, Object> beanMap = new ConcurrentHashMap<>();

    public SpringIocXml() throws IOException, DocumentException {
        init();
    }

    public <T> T getBean(String name) {
        return (T) beanMap.get(name);
    }

    /**
     * 初始化IOC容器
     */
    private void init() throws IOException, DocumentException {
        // 解析spring.xml配置
        ClassPathResource classPathResource = new ClassPathResource("spring.xml");
        File xmlFile = classPathResource.getFile();
        SAXReader saxReader = new SAXReader();
        Document doc = saxReader.read(xmlFile);
        // 获取根节点
        Element rootElement = doc.getRootElement();
        // 获取bean节点信息
        List<Element> beans = rootElement.elements("bean");
        for (Element bean : beans) {
            try {
                String beanId = bean.attribute("id").getValue();
                String classPath = bean.attribute("class").getValue();
                // 使用反射机制初始化对象,并将对象存入Map集合
                Class<?> clazz = Class.forName(classPath);
                Object object = clazz.newInstance();
                beanMap.put(beanId, object);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

}

SpringTest01.java

package com.spring.test;

import com.spring.bean.UserBean;
import com.spring.ext.SpringIocXml;
import org.dom4j.DocumentException;

import java.io.IOException;

/**
 * @author honey
 * @date 2023-08-08 17:04:35
 */
public class SpringTest01 {

    public static void main(String[] args) throws DocumentException, IOException {
        SpringIocXml springIocXml = new SpringIocXml();
        UserBean userBean = springIocXml.getBean("userBean");
        System.out.println(userBean);
    }
}
运行结果

在这里插入图片描述

基于注解方式

原理

基于反射+工厂模式实现

  1. 判断配置类上是否有@Configuration注解;
  2. 获取配置类中的所有方法,判断方法上是否有@Bean注解,如果有则获取方法的返回值作为实例对象;
  3. 判断配置类上是否有@ComponentScan注解,如果有则扫描指定包下的所有类,并判断类上是否有@Component注解,如果有则通过反射技术初始化对象;
  4. 使用工厂模式管理初始化对象/实例对象;
项目结构

在这里插入图片描述

项目代码

pom.xml

<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.7.7</version>
</dependency>

ScanBean.java

package com.spring.bean.scan;

import org.springframework.stereotype.Component;

/**
 * @author honey
 * @date 2023-08-09 14:28:33
 */
@Component
public class ScanBean {
}

SpringConfig.java

package com.spring.config;

import com.spring.bean.UserBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

/**
 * @author honey
 * @date 2023-08-09 14:26:40
 */
@Configuration
@ComponentScan(value = {"com.spring.bean.scan"})
public class SpringConfig {

    @Bean
    public UserBean userBean() {
        return new UserBean();
    }
}

SpringIocAnnotation.java

package com.spring.ext;

import cn.hutool.core.lang.ClassScanner;
import cn.hutool.core.util.StrUtil;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author honey
 * @date 2023-08-09 14:12:21
 */
public class SpringIocAnnotation {

    private final Object config;
    private final Map<String, Object> beanMap = new ConcurrentHashMap<>();

    public SpringIocAnnotation(Object config) {
        this.config = config;
        init();
    }

    /**
     * 初始化IOC容器
     */
    public void init() {
        // 判断配置类上是否有@Configuration注解
        Configuration configuration = this.config.getClass().getDeclaredAnnotation(Configuration.class);
        if (configuration == null) {
            return;
        }

        // 处理@Bean注解
        Class<?> clazz = config.getClass();
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for (Method method : declaredMethods) {
            // 判断方法上是否有@Bean注解
            Bean bean = method.getDeclaredAnnotation(Bean.class);
            if (bean != null) {
                try {
                    // 获取beanId
                    String[] value = bean.value();
                    String beanId = value.length > 0 ? value[0] : method.getName();
                    // 获取方法的返回值
                    Object object = method.invoke(config);
                    beanMap.put(beanId, object);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }

        // 处理@Component注解
        ComponentScan componentScan = clazz.getDeclaredAnnotation(ComponentScan.class);
        if (componentScan != null) {
            for (String packageName : componentScan.value()) {
                try {
                    // 扫描指定包下的所有类
                    Set<Class<?>> classes = ClassScanner.scanPackage(packageName);
                    for (Class<?> c : classes) {
                        // 判断类上是否有@Component注解
                        Annotation component = c.getDeclaredAnnotation(Component.class);
                        if (component != null) {
                            try {
                                // 获取beanId
                                String beanId = StrUtil.lowerFirst(c.getSimpleName());
                                // 通过反射技术初始化对象
                                Object beanObject = c.newInstance();
                                beanMap.put(beanId, beanObject);
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public <T> T getBean(String name) {
        return (T) beanMap.get(name);
    }
}

SpringTest02.java

package com.spring.test;

import com.spring.bean.UserBean;
import com.spring.bean.scan.ScanBean;
import com.spring.config.SpringConfig;
import com.spring.ext.SpringIocAnnotation;

/**
 * @author honey
 * @date 2023-08-09 14:24:36
 */
public class SpringTest02 {

    public static void main(String[] args) {
        SpringIocAnnotation springIocAnnotation = new SpringIocAnnotation(new SpringConfig());
        UserBean userBean = springIocAnnotation.getBean("userBean");
        System.out.println(userBean);
        ScanBean scanBean = springIocAnnotation.getBean("scanBean");
        System.out.println(scanBean);
    }
}
运行结果

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值