手写spring(旧版)

去年实现的IOC逻辑,对其进行了优化

手写spring(一)IOC实现

 

下面是我写的旧版,现在看来过度仿照源码,保持类名与方法名一致等等操作,致使一个简单实现spring逻辑的代码有点冗余。

以下代码留做纪念:

 

因为手写tomcat已经完成了,所以现在来实现spring

当然,spring本身就是使用java写的,手写spring主要是为了熟悉源码,所以参考源码实现了一个简单的spring。

源码:spring-projects/spring-framework: Spring Framework (github.com)

1.springIOC

1.自定义注解

官方源码:

spring-framework/spring-beans/src/main/java/org/springframework/beans/factory/annotation at main · spring-projects/spring-framework (github.com)

新建org.springframework.annotation包,在其下新建四个注解

@Controller,@Service,@Repository都有带@Component父注解

源码有个@Documented注解,生成帮助文档的,没什么用,就不加。

首先实现Component

package org.springframework.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE) //TYPE:用于描述类、接口(包括注解类型) 或enum声明
@Retention(RetentionPolicy.RUNTIME) //注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在;
public @interface Component {
    String value() default ""; //注解参数
}

 

如下代码,
@AliasFor(annotation = Component.class) 该注解是为了属性起别名,但是我暂时只是想实现一个简单的spring,就不写它了

实现@Controller

package org.springframework.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE) //TYPE:用于描述类、接口(包括注解类型) 或enum声明
@Retention(RetentionPolicy.RUNTIME) //注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在;
@Component
public @interface Controller {
    //@AliasFor(annotation = Component.class) 
    String value() default ""; //注解参数
}

实现@Service

package org.springframework.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE) //TYPE:用于描述类、接口(包括注解类型) 或enum声明
@Retention(RetentionPolicy.RUNTIME) //注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在;
@Component
public @interface Service {

    //@AliasFor(annotation = Component.class)
    String value() default ""; //注解参数
}

实现@Repository

package org.springframework.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE) //TYPE:用于描述类、接口(包括注解类型) 或enum声明
@Retention(RetentionPolicy.RUNTIME) //注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在;
@Component
public @interface Repository {

    //@AliasFor(annotation = Component.class)
    String value() default ""; //注解参数
}

实现@Autowired

package org.springframework.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
*   ElementType:
*     CONSTRUCTOR:用于描述构造器
*     METHOD:用于描述方法
*     FIELD:用于描述域
*     LOCAL_VARIABLE:用于描述局部变量
*     PACKAGE:用于描述包
*     PARAMETER:用于描述参数
*     TYPE:用于描述类、接口(包括注解类型) 或enum声明
*/
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {
    //参数默认值为true
    boolean required() default true;
}

 

2.获取扫描包路径

常见的,我们需要读取xml文件

比如component-scan属性

参考官方源码:

spring-framework/spring-context/src/main/java/org/springframework/context/support/ClassPathXmlApplicationContext.java at main · spring-projects/spring-framework (github.com)

就是对xml文件进行解析, 通过XmlBeanDefinitionReader来读取xml配置文件,loadBeanDefinitions函数读取XML配置文件,而在源码中还有考虑到许多,比如获取到spring XML配置文件的地址,配置它的环境,设置成员属性等,这里我们只是实现一个简单的spring,不考虑这么多,所以我直接解析xml文件。

初始化spring容器中的refresh()方法中,会调用obtainFreshBeanFactory()方法,它是读取并解析spring xml配置文件的入口

类似,我们,仿照一个。

新建org/springframework/context/support/ClassPathXmlApplicationContext.java

package org.springframework.context.support;

import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;

/**
* @author PoemsAndDreams
* @date 2023-09-25 10:42
*/
public class ClassPathXmlApplicationContext {

    private String configResources;

    private XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader();

    public ClassPathXmlApplicationContext(String configResources) {
        this.configResources = configResources;
        //初始化spring容器
        refresh();
    }

    private void refresh() {

        // obtainFreshBeanFactory 加载spring入口
        obtainFreshBeanFactory();

    }

    private void obtainFreshBeanFactory() {
        //加载xml配置文件
        loadBeanDefinitions();
    }

    private void loadBeanDefinitions() {
        xmlBeanDefinitionReader.loadBeanDefinitions(configResources);
    }

}

新建org/springframework/beans/factory/xml/XmlBeanDefinitionReader.java

package org.springframework.beans.factory.xml;

import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.IOException;
import java.io.InputStream;

/**
* @author PoemsAndDreams
* @date 2023-09-25 10:15
*/
public class XmlBeanDefinitionReader {
    //加载xml配置文件
    public void loadBeanDefinitions(String configResources){

        //获取扫描包路径
        getComponentScanPackage(configResources);
        System.out.println(getComponentScanPackage(configResources));

    }

    private String getComponentScanPackage(String configResources) {
        //DOM4J 解析 XML
        InputStream resourceAsStream = null;

        try {
            //创建 SAXReader 对象
            SAXReader saxReader = new SAXReader();
            //获取class对象加载文件返回流
            resourceAsStream = XmlBeanDefinitionReader.class.getClassLoader().getResourceAsStream(configResources);
            //获取document对象
            Document document = saxReader.read(resourceAsStream);
            //获取根节点
            Element rootElement = document.getRootElement();
            //获取扫描包
            Element element = rootElement.element("component-scan");
            Attribute attribute = element.attribute("base-package");
            return attribute.getValue();
        } catch (DocumentException e) {
            throw new RuntimeException(e);
        }finally {
            if (resourceAsStream != null){
                try {
                    resourceAsStream.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }

    }
}

新建一个模块UseSpring来测试功能,spring模块是上述我们写的代码的模块

引入我们写的spring项目依赖,这样就可以使用我们自己写的代码了

<dependency>
    <groupId>com.dreams</groupId>
    <artifactId>spring</artifactId>
    <version>1.0-SNAPSHOT</version>
    <scope>compile</scope>
</dependency>

在resources加入新建applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>

<!--以下为spring格式校验链接,引入可以对xml格式校验-->

<!--<beans xmlns="http://www.springframework.org/schema/beans"-->
<!-- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"-->
<!-- xmlns:context="http://www.springframework.org/schema/context"-->
<!-- xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">-->

<!--spring的xml格式校验链接结束-->

<!-- 获取扫描包路径,spring的xml需要加入context:,我们自己实现的spring就不需要加-->
<!-- <context:component-scan base-package="com.yutian"></context:component-scan>-->

    <component-scan base-package="com.yutian"></component-scan>
<!--</beans>-->

测试一下

package com.yutian;

import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;


public class TestController {
    @Test
    public void test(){
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        //......
        System.out.println(context);
    }
}

 

3.ioc逻辑实现

参考官方源码

spring-framework/spring-context/src/main/java/org/springframework/context/annotation/ClassPathBeanDefinitionScanner.java at main · spring-projects/spring-framework (github.com)

BeanDefinition 主要是用来描述 Bean,其存储了 Bean 的相关信息,Spring 实例化 Bean 时需读取该 Bean 对应的 BeanDefinition。BeanDefinition 整体可以分为两类,一类是描述通用的 Bean,还有一类是描述注解形式的 Bean。

所以我们新建一个BeanDefinition 类

package org.springframework.beans.factory.config;


/**
* @author PoemsAndDreams
* @date 2023-09-26 11:14
* Bean的定义类
*/
public class BeanDefinition {

    private String BeanName;
    private Class clazz;
    //其他信息,如单例等,这里实现一个简单的spring,就不实现了


    //Getter and Setter
    public Class getClazz() {
        return clazz;
    }

    public void setClazz(Class clazz) {
        this.clazz = clazz;
    }

    public String getBeanName() {
        return BeanName;
    }

    public void setBeanName(String beanName) {
        BeanName = beanName;
    }
}

在XmlBeanDefinitionReader类中加入

//存储bean定义信息,所以扫描到的
private ConcurrentHashMap<String,BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();

//Getter方法
public ConcurrentHashMap<String, BeanDefinition> getBeanDefinitionMap() {
    return beanDefinitionMap;
}

完善一下XmlBeanDefinitionReader的loadBeanDefinitions方法

public void loadBeanDefinitions(String configResources){

    //获取扫描包
    String scanPackage = getComponentScanPackage(configResources);
    //获取扫描包路径
    findScanPackagePath(scanPackage);

}

getComponentScanPackage函数

//获取扫描包
private String getComponentScanPackage(String configResources) {
    //DOM4J 解析 XML
    InputStream resourceAsStream = null;

    try {
        //创建 SAXReader 对象
        SAXReader saxReader = new SAXReader();
        //获取class对象加载文件返回流
        resourceAsStream = XmlBeanDefinitionReader.class.getClassLoader().getResourceAsStream(configResources);
        //获取document对象
        Document document = saxReader.read(resourceAsStream);
        //获取根节点
        Element rootElement = document.getRootElement();
        //获取扫描包
        Element element = rootElement.element("component-scan");
        Attribute attribute = element.attribute("base-package");
        return attribute.getValue();
    } catch (DocumentException e) {
        throw new RuntimeException(e);
    }finally {
        if (resourceAsStream != null){
            try {
                resourceAsStream.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

}

findScanPackagePath函数,获取扫描包路径

private void findScanPackagePath(String scanPackage) {
    ClassLoader classLoader = XmlBeanDefinitionReader.class.getClassLoader();
    scanPackage = scanPackage.replace(".","/");
    URL url = classLoader.getResource(scanPackage);
    //因为获取的目录可能含有空格,且会使用%20替代空格,所以我们需要替换回去
    String urlFile = url.getFile();
    if (urlFile.contains("%20")){
        urlFile = urlFile.replace("%20"," ");
    }
    File file = new File(urlFile);
    //加载并实例化
    loadAllClass(file);

}

loadAllClass(file)加载并实例化,将其放到Bean定义类Map的BeanDefinitionMap中,其中,一个接口存放其实现类的class.

//加载
private void loadAllClass(File path) {
    File[] files = path.listFiles();
    for (File file : files) {
        //如果是个目录
        if (!file.isDirectory()){
            //获取文件路劲
            String fileName = file.getAbsolutePath();
            if (fileName.endsWith(".class")){
                String className = fileName.substring(fileName.indexOf("com"), fileName.indexOf(".class"));
                className = className.replace("\\",".");
                //获取类加载器
                ClassLoader classLoader = XmlBeanDefinitionReader.class.getClassLoader();
                try {
                    //加载类
                    Class<?> clazz = classLoader.loadClass(className);

                    //类是否有Component,Service,Controller,Repository注解
                    if (clazz.isAnnotationPresent(Component.class) || clazz.isAnnotationPresent(Controller.class) || clazz.isAnnotationPresent(Service.class) || clazz.isAnnotationPresent(Repository.class)){

                        Component componentAnnotation = clazz.getDeclaredAnnotation(Component.class);
                        Controller controllerAnnotation = clazz.getDeclaredAnnotation(Controller.class);
                        Service serviceAnnotation = clazz.getDeclaredAnnotation(Service.class);
                        Repository repositoryAnnotation = clazz.getDeclaredAnnotation(Repository.class);

                        String value = "";
                        //Bean定义类
                        BeanDefinition beanDefinition = new BeanDefinition();
                        //判断注解是否有value值
                        if (componentAnnotation != null || controllerAnnotation != null || serviceAnnotation != null || repositoryAnnotation != null){
                            if (componentAnnotation != null && !componentAnnotation.value() .equals("")){
                                value = componentAnnotation.value();
                            }else if (controllerAnnotation != null && !controllerAnnotation.value().equals("")){
                                value = controllerAnnotation.value();
                            }else if (serviceAnnotation != null && !serviceAnnotation.value().equals("")){
                                value = serviceAnnotation.value();
                            } else if (repositoryAnnotation != null && !repositoryAnnotation.value().equals("")) {
                                value = repositoryAnnotation.value();
                            } else {
                                String name = clazz.getSimpleName();
                                //默认以开头小写的类名作为实例名
                                value = name.valueOf(name.charAt(0)).toLowerCase() + name.substring(1);

                            }
                            //不能重名
                            if (beanDefinitionMap.get(value) != null) {
                                throw new RuntimeException("spring IOC Container is already exists " + beanDefinitionMap.get(value));
                            }
                            beanDefinition.setClazz(clazz);
                            beanDefinition.setBeanName(value);
                            //保存到bean定义
                            beanDefinitionMap.put(value,beanDefinition);

                            //获取到该类实现的所有接口
                            Class[] interfaces = clazz.getInterfaces();
                            //在beanDefinitionMap中存储为一个接口对应一个实现类
                            for (Class anInterface : interfaces) {
                                String interfaceSimpleName = anInterface.getSimpleName();
                                interfaceSimpleName = interfaceSimpleName.valueOf(interfaceSimpleName.charAt(0)).toLowerCase() + interfaceSimpleName.substring(1);
                                beanDefinitionMap.put(interfaceSimpleName,beanDefinition);
                            }
                        }

                    }

                } catch (ClassNotFoundException e) {
                    throw new RuntimeException(e);
                }

            }
        }
        else {
            loadAllClass(file);
        }
    }
}

回到ClassPathXmlApplicationContext类,加入属性

//@Autowired注入逻辑
private AutowiredAnnotationBeanPostProcessor autowiredAnnotationBeanPostProcessor = new AutowiredAnnotationBeanPostProcessor();

更改refresh()方法

private void refresh() {

    // obtainFreshBeanFactory 加载spring入口
    obtainFreshBeanFactory();

    //实例化Bean
    createBean();
}

 

createBean()方法如下,@Autowired逻辑在createBeanInstance()方法

private void createBean() {

    autowiredAnnotationBeanPostProcessor.setXmlBeanDefinitionReader(xmlBeanDefinitionReader);
    //具体逻辑
    autowiredAnnotationBeanPostProcessor.createBeanInstance();
}

可以看到具体逻辑在AutowiredAnnotationBeanPostProcessor类

新建org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java

其中InstanceMap保存实例化,方便我们使用

package org.springframework.beans.factory.annotation;

import org.springframework.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.concurrent.ConcurrentHashMap;


/**
* @author PoemsAndDreams
* @date 2023-09-25 12:34
*/
public class AutowiredAnnotationBeanPostProcessor {

    private XmlBeanDefinitionReader xmlBeanDefinitionReader;

    //实例化存储
    private ConcurrentHashMap<String, Object> InstanceMap = new ConcurrentHashMap<>();

    public ConcurrentHashMap<String, Object> getInstanceMap() {
        return InstanceMap;
    }

    //Setter方法
    public void setXmlBeanDefinitionReader(XmlBeanDefinitionReader xmlBeanDefinitionReader) {
        this.xmlBeanDefinitionReader = xmlBeanDefinitionReader;
    }

}

createBeanInstance()方法也在AutowiredAnnotationBeanPostProcessor类

public void createBeanInstance() {
    ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = xmlBeanDefinitionReader.getBeanDefinitionMap();
    for (String beanName : beanDefinitionMap.keySet()) {
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        Class clazz = beanDefinition.getClazz();
        try {
            //实例化
            Object instance = clazz.getDeclaredConstructor().newInstance();
            //保存至InstanceMap,方便使用
            InstanceMap.put(beanName,instance);
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        }
    }
    //自动注入逻辑
    postProcessProperties();
}

@Autowired注入逻辑在postProcessProperties方法

//@Autowired注入逻辑
public void postProcessProperties() {
    ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = xmlBeanDefinitionReader.getBeanDefinitionMap();

    for (String beanName : beanDefinitionMap.keySet()) {
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        //获取到实际的class
        Class clazz = beanDefinition.getClazz();
        //获取到所有字段
        Field[] declaredFields = clazz.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            //是否有Autowired注解
            if (declaredField.isAnnotationPresent(Autowired.class)) {
                Class<?> aClass = declaredField.getType();
                Object instance = null;
                try {
                    String name = declaredField.getName();
                    //获取实现该接口的类
                    instance = InstanceMap.get(name);
                    Object o = InstanceMap.get(beanName);
                    declaredField.setAccessible(true);
                    declaredField.set(o, instance);
                } catch (IllegalAccessException e) {
                    throw new RuntimeException(e);
                }
            }

        }

    }

}

最后在ClassPathXmlApplicationContext.java加入获取bean的方法

public Object getBean(String beanName){
    //获取
    ConcurrentHashMap<String, Object> instanceMap = autowiredAnnotationBeanPostProcessor.getInstanceMap();
    Object o = instanceMap.get(beanName);
    //获取不到即抛出异常
    if (o == null){
        throw new RuntimeException("No bean named" + beanName +" available");
    }
    return o;
}


public Object getBean(Class<?> clazz) {
    ConcurrentHashMap<String, Object> instanceMap = autowiredAnnotationBeanPostProcessor.getInstanceMap();
    ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = xmlBeanDefinitionReader.getBeanDefinitionMap();
    Object bean = new Object();
    for (BeanDefinition value : beanDefinitionMap.values()) {
        Class aClass = value.getClazz();
        if (aClass == clazz){
            String beanName = value.getBeanName();
            bean = instanceMap.get(beanName);
            return bean;
        }
    }
    //获取不到即抛出异常
    throw new RuntimeException("No qualifying bean of type" + clazz + " available");
}

测试一下,

如图的目录结构

在UserController

package com.yutian.controller;

import com.yutian.service.impl.UserServiceImpl;
import org.springframework.annotation.Autowired;
import org.springframework.annotation.Controller;

/**
* @author PoemsAndDreams
* @date 2023-09-27 07:49
*/
@Controller(value = "uc")
public class UserController {
    @Autowired
    UserServiceImpl userService;

    public void test(){
        userService.test();
    }
}

在UserService

package com.yutian.service;

/**
* @author PoemsAndDreams
* @date 2023-09-27 07:49
*/
public interface UserService {
    void test();
}

在UserServiceImpl

package com.yutian.service.impl;

import com.yutian.service.UserService;
import org.springframework.annotation.Service;

/**
* @author PoemsAndDreams
* @date 2023-09-27 07:50
*/
@Service
public class UserServiceImpl implements UserService {
    @Autowired
    UserDao userDao;

    @Override
    public void test() {
        userDao.test();
    }
}

UserDao

package com.yutian.Dao;

/**
* @author PoemsAndDreams
* @date 2023-09-27 15:00
*/
public interface UserDao {
    void test();
}

UserDaoImpl

package com.yutian.Dao.Impl;

import com.yutian.Dao.UserDao;
import org.springframework.annotation.Repository;

/**
* @author PoemsAndDreams
* @date 2023-09-27 15:00
*/
@Repository
public class UserDaoImpl implements UserDao {
    @Override
    public void test() {
        System.out.println("Hello spring");
    }
}

在test

import com.yutian.controller.UserController;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
* @author PoemsAndDreams
* @date 2023-09-27 07:54
*/
public class Test {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        //......
        UserController userController = (UserController) context.getBean("uc");

        userController.test();

        UserController userController1 = (UserController) context.getBean(UserController.class);
        userController1.test();

        System.out.println(userController1 == userController);

    }
}

运行如图

 

暂无评论

发送评论 编辑评论

|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇