深入剖析:手写Spring源码的精简版解析与实现(附GitHub链接)

569 阅读9分钟

引出问题

  1. 原生 Spring 如何实现依赖注入和 singleton、prototype
  2. 原生 Spring 如何实现 BeanPostProcessor
  3. 原生 Spring 是如何实现 AOP

自己手动实现

一图胜千言

源代码

实现任务阶段 1- 编写自己 Spring 容器,实现扫描包, 得到bean 的class 对象

使用注解方式代替xml配置方式, 使用xml配置只要使用dom4j解析技术就行, 道理一样的

如何创建 普通Maven项目

我的目录结构是

└─com
    └─hspedu
        └─Spring
            │  AppMain.java  测试类
            │
            ├─Annotation 参考原生实现
            │      AfterReturning.java
            │      Aspect.java
            │      Autowired.java
            │      Before.java
            │      Component.java
            │      ComponentScan.java
            │      Scope.java
            │
            ├─component 
            │      Car.java
            │      HspBeanPostProcessor.java
            │      MonsterDAO.java
            │      MonsterService.java
            │      SmartAnimal.java
            │      SmartAnimalAspect.java
            │      SmartDog.java
            │
            ├─ioc
            │      BeanDefinition.java
            │      HspSpringApplicationContext.java
            │      HspSpringConfig.java
            │
            └─processor
                    BeanPostProcessor.java
                    InitializingBean.java

需要的依赖

<dependencies>  
	<dependency>  
		<groupId>junit</groupId>  
		<artifactId>junit</artifactId>  
		<version>4.13.2</version>  
		<scope>compile</scope>  
	</dependency>  
	  
	<dependency>  
		<groupId>commons-lang</groupId>  
		<artifactId>commons-lang</artifactId>  
		<version>2.5</version>  
	</dependency>  
</dependencies>

HspSpringApplicationContext.java

非常重要的类, 先看其中的一个方法

    public void beanDefinitionByScan(Class configClass) {
        // 1. 拿到配置类上拿到的注解
        ComponentScan componentScan = (ComponentScan) configClass.getDeclaredAnnotation(ComponentScan.class);
        // 要扫描包的路径 com.hspedu.Spring.Annotation
        String path = componentScan.value();
//        System.out.println("path = " + path);
        // com/hspedu/Spring/Annotation 路径
        // 1. 找到类的加载器 --> APP类加载器
        ClassLoader classLoader = HspSpringApplicationContext.class.getClassLoader();
        path = path.replaceAll("\\.", "/");
        // 2. 通过类的加载器, 获得扫描包的资源url
        URL resource = classLoader.getResource(path);
        // 3. 将要加载的资源(.class)路径进行遍历 io知识
        File file = new File(resource.getFile());
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            for (File f : files) {
                String absolutePath = f.getAbsolutePath();
                if (!absolutePath.endsWith(".class")) {
                    // 如果不是以.class结尾的就不进行下面的操作
                    return;
                }
                // 反射需要 的是 com.hspedu.Spring.component.MyComponent
                // 获取到类名 类似于 MyComponent
                String className = absolutePath.substring(absolutePath.lastIndexOf("\\") + 1, absolutePath.lastIndexOf(".class"));
                // 反射需要的是 全类名
                String classFullName = path.replaceAll("/", ".") + "." + className;
            }
        }
    }

需要被扫描的配置类 HspSpringConfig.java

import com.hspedu.Spring.Annotation.ComponentScan;  
  
/**  
* ClassName: HspSpringConfig  
* Package: com.hspedu.Spring.Annotation  
*  
* @Author: leikooo  
* @Creat: 2023/5/29 - 12:02  
* @Description: 模拟原生spring容器配置文件(beans.xml)  
*/  
  
@ComponentScan(value = "com.hspedu.Spring.component")  
public class HspSpringConfig {  
  
  
}

实现任务阶段 2- 扫描将 bean 信息封装到 BeanDefinition 对象,并放入到Map

BeanDefinition.java

用于记录Bean的信息 class反射全路径 + 是否是单例

/**
 * ClassName: BeanDefinition
 * Package: com.hspedu.Spring.ioc
 *
 * @Author: leikooo
 * @Creat: 2023/5/29 - 17:05
 * @Description:  用于记录Bean的信息 class反射全路径 + 是否是单例
 */
public class BeanDefinition {
    private String scope;
    public Class clazz;

    public String getScope() {
        return scope;
    }

    public void setScope(String scope) {
        this.scope = scope;
    }

    public Class getClazz() {
        return clazz;
    }

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

HspSpringApplicationContext.java

package com.hspedu.Spring.ioc;

import com.hspedu.Spring.Annotation.Autowired;
import com.hspedu.Spring.Annotation.Component;
import com.hspedu.Spring.Annotation.ComponentScan;
import com.hspedu.Spring.Annotation.Scope;
import com.hspedu.Spring.component.MonsterDAO;
import com.hspedu.Spring.processor.BeanPostProcessor;
import com.hspedu.Spring.processor.InitializingBean;
import org.apache.commons.lang.StringUtils;

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Enumeration;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

/**
 * ClassName: HspSpringApplicationContext
 * Package: com.hspedu.Spring.ioc
 *
 * @Author: leikooo
 * @Creat: 2023/5/29 - 16:39
 * @Description:
 */
public class HspSpringApplicationContext {
    // 使用反射需要Class类
    private Class configClass;

    // 定义一个属性 BeanDefinitionMap  -> 存放BeanDefinition
    private final ConcurrentHashMap<String, BeanDefinition> BeanDefinitionMap = new ConcurrentHashMap<>();

    // 定义一个SingletonObjects  ->  存放单例对象
    private final ConcurrentHashMap<String, Object> SingletonObjects = new ConcurrentHashMap<>();

    public HspSpringApplicationContext(Class configClass) {
        beanDefinitionByScan(configClass);
        Enumeration<String> keys = BeanDefinitionMap.keys();
        while (keys.hasMoreElements()) {
            // 得到beanName
            String beanName = keys.nextElement();
            // 通过beanName 得到对应的beanDefinition对象
            BeanDefinition beanDefinition = BeanDefinitionMap.get(beanName);
            // 判断 是单例还是多例
            String scope = beanDefinition.getScope();
            if ("singleton".equals(scope)) {
                // 单例直接创建
                Object bean = createBean(beanName, beanDefinition);
                if (!SingletonObjects.containsKey(beanName)) {
                    // 如果已经创建了, 就不用再进行创建了
                    SingletonObjects.put(beanName, bean);
                }
            }
        }
    }

    /**
     * 该方法完成对指定包的扫描, 并将Bean信息封装到 BeanDefinition对象, 并放入到Map之中
     *
     * @param configClass 配置对象
     */
    public void beanDefinitionByScan(Class configClass) {
        // 1. 拿到配置类上拿到的注解
        ComponentScan componentScan = (ComponentScan) configClass.getDeclaredAnnotation(ComponentScan.class);
        // 要扫描包的路径 com.hspedu.Spring.Annotation
        String path = componentScan.value();
//        System.out.println("path = " + path);
        // com/hspedu/Spring/Annotation 路径
        // 1. 找到类的加载器 --> APP类加载器
        ClassLoader classLoader = HspSpringApplicationContext.class.getClassLoader();
        path = path.replaceAll("\\.", "/");
        // 2. 通过类的加载器, 获得扫描包的资源url
        URL resource = classLoader.getResource(path);
        // 3. 将要加载的资源(.class)路径进行遍历 io知识
        File file = new File(resource.getFile());
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            for (File f : files) {
                String absolutePath = f.getAbsolutePath();
                if (!absolutePath.endsWith(".class")) {
                    // 如果不是以.class结尾的就不进行下面的操作
                    return;
                }
                // 反射需要 的是 com.hspedu.Spring.component.MyComponent
                // 获取到类名 类似于 MyComponent
                String className = absolutePath.substring(absolutePath.lastIndexOf("\\") + 1, absolutePath.lastIndexOf(".class"));
                // 反射需要的是 全类名
                String classFullName = path.replaceAll("/", ".") + "." + className;

                try {
                    Class<?> clazz = classLoader.loadClass(classFullName);
                    if (clazz.isAnnotationPresent(Component.class)) {
                        // 获得注解上面的value属性, 作为key
                        if (clazz.isAnnotationPresent(Component.class)) {
                            // 如果该类使用了@Component
                            // 1. 得到 Component 注解
                            Component componentAnnotation = clazz.getDeclaredAnnotation(Component.class);
                            // 2. 的配置value值, 疑问 如果程序员没有配置value
                            String beanName = componentAnnotation.value();
                            if ("".equals(beanName)) {
                                // 如果没有指定值, 那么直接首字母小写
                                // 注意这里的 StringUtils 是  org.apache.commons.lang 目录下面的
                                beanName = StringUtils.uncapitalize(className);
                                String name = clazz.getName();
//                                System.out.println("name = " + name);
                            }
                            // 3. 这里把bean信息封装到 BeanDefinitionMap
                            BeanDefinition beanDefinition = new BeanDefinition();
                            beanDefinition.setClazz(clazz);
                            // 4. 获取到Scope的值
                            if (clazz.isAnnotationPresent(Scope.class)) {
                                Scope scopeAnnotation = clazz.getDeclaredAnnotation(Scope.class);
                                String value = scopeAnnotation.value();
                                if ("".equals(value)) {
                                    value = "singleton";
                                }
                                beanDefinition.setScope(value);
                            } else {
                                // 没有配置scope就获取其他配置的值
                                beanDefinition.setScope("singleton");
                            }
                            // 5. 将beanDefinition 放到Map
                            BeanDefinitionMap.put(beanName, beanDefinition);
                        }
                    } else {
                        System.out.println("这不是一个SpringBean bean = " + clazz + " 类名 " + className);
                    }
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }
}


解析:

这两个Map集合非常重要

  1. 定义一个属性 BeanDefinitionMap -> 存放BeanDefinition , 这个是存放类的信息
  2. 定义一个 SingletonObjects -> 存放单例对象, 如果是单例对象那么直接创建放到map之中

实现任务阶段 3- 初始化 bean 单例池,并完成 getBean 方法, createBean 方法

完成 getBean() 方法

有三种情况

  1. 如果这个bean不存在就抛异常
  2. 如果这个bean存在, 但是单例池中没有这个对象 --> 调用creatBean()
  3. 如果这个bean存在, 单例池中有对象 --> 从单例池中直接取出
	
	// creatBean() 方法非常重要很多东西都在其中
	public Object creatBean(String beanName, BeanDefinition beanDefinition) {
        Object bean = null;
        try {
            bean = beanDefinition.getClazz().getDeclaredConstructor().newInstance();
            System.out.println("=======初始化======" + bean);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return bean;
    }

    // 获取bean
    public Object getBean(String beanName) {
        if (!beanDefinitionMap.containsKey(beanName)) {
            throw new RuntimeException("NO SUCH BEAN");
        }
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        if ("singleton".equals(beanDefinition.getScope())) {
            return SingletonMap.get(beanName);
        } else {
	        // 如果单例池里面没有对应的对象, 那么创建
            return creatBean(beanName, beanDefinition);
        }
    }

实现任务阶段 4- 完成依赖注入

creatBean() 方法实现依赖注入就非常合适, 因为不管是 singleton 还是 prototype 都需要使用这个方法, 而且这时候对象不会放到单例池之中 ( 或者返回 )

	public Object creatBean(String beanName, BeanDefinition beanDefinition) {
        Object bean = null;
        try {
            bean = beanDefinition.getClazz().getDeclaredConstructor().newInstance();

            // 这里实现自动装配
            Field[] fields = bean.getClass().getDeclaredFields();
            for (Field field : fields) {
                if (field.isAnnotationPresent(Autowired.class)) {
                    // 如果某一个属性是标识了 @Autowired 注解
                    String fieldName = field.getName();
                    field.setAccessible(true);
                    Object o = getBean(fieldName);
                    // 第一个参数是 对象
                    field.set(bean, o);
                }
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return bean;
    }

实现任务阶段 5- bean 后置处理器实现和初始化方法

容器中有一个常用的方法是, 根据该类是否实现了某个接口, 来判断是否要执行某个逻辑, 这个其实就是Java基础的接口编程实际运用

这个修改的地方就不止 creatBean() 这个方法了, 还有需要在 beanDefinitionByScan() 进行修改

这里为了简化, 专门为了 实现接口的类创建了一个List, 便于以后使用. 但是底层还是在 单例池中 【如果是那样的话太过复杂】

import com.hspedu.Spring.Annotation.Autowired;
import com.hspedu.Spring.Annotation.Component;
import com.hspedu.Spring.Annotation.ComponentScan;
import com.hspedu.Spring.Annotation.Scope;
import com.hspedu.Spring.processor.BeanPostProcessor;
import com.hspedu.Spring.processor.InitializingBean;
import org.apache.commons.lang.StringUtils;

import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

/**
 * ClassName: HspSpringApplicationContext
 * Package: com.hspedu.Spring.ioc
 *
 * @Author: leikooo
 * @Creat: 2023/6/1 - 12:12
 * @Description:
 */
public class HspSpringApplicationContext {
    private Class configure;
    private final ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>();
    // 注意这里是object
    private final ConcurrentHashMap<String, Object> SingletonMap = new ConcurrentHashMap<String, Object>();
    private final List<BeanPostProcessor> beanPostProcessors = new ArrayList<BeanPostProcessor>();

    public HspSpringApplicationContext(Class configure) {
        beanDefinitionByScan(configure);
        // 拿到已经整好的定义bean
        Enumeration<String> keys = beanDefinitionMap.keys();
        while (keys.hasMoreElements()) {
            String beanName = keys.nextElement();
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            if ("singleton".equals(beanDefinition.getScope())) {
                // 如果是单例直接创建, 放到单例池
                SingletonMap.put(beanName, creatBean(beanName, beanDefinition));
            }
        }
    }

    public Object creatBean(String beanName, BeanDefinition beanDefinition) {

        Object bean = null;
        try {
            bean = beanDefinition.getClazz().getDeclaredConstructor().newInstance();

            // 这里实现自动装配
            Field[] fields = bean.getClass().getDeclaredFields();
            for (Field field : fields) {
                if (field.isAnnotationPresent(Autowired.class)) {
                    // 如果某一个属性是标识了 @Autowired 注解
                    String fieldName = field.getName();
                    field.setAccessible(true);
                    Object o = getBean(fieldName);
                    field.set(bean, o);
                }
            }
            System.out.println("=======初始化======" + bean);

			// 实现后置通知  
			for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {  
				Object o = beanPostProcessor.postProcessBeforeInitialization(bean, beanName);  
				bean = o != null ? o : bean;  
			}
            // 实现初始化方法
            if (InitializingBean.class.isAssignableFrom(bean.getClass())) {
                ((InitializingBean) bean).afterPropertiesSet();
            }

			for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {  
				Object o = beanPostProcessor.postProcessAfterInitialization(bean, beanName);  
				bean = o != null ? o : bean;  
			}
            
            System.out.println("---------------------");
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return bean;
    }

    // 获取bean
    public Object getBean(String beanName) {
        if (!beanDefinitionMap.containsKey(beanName)) {
            throw new RuntimeException("NO SUCH BEAN");
        }
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        if ("singleton".equals(beanDefinition.getScope())) {
            return SingletonMap.get(beanName);
        } else {
            return creatBean(beanName, beanDefinition);
        }
    }

    // 扫描装配
    public void beanDefinitionByScan(Class configure) {
        this.configure = configure;
        if ((configure != null) && configure.isAnnotationPresent(ComponentScan.class)) {
            ComponentScan componentScan = (ComponentScan) configure.getDeclaredAnnotation(ComponentScan.class);
            String path = componentScan.value();
//        System.out.println("path = " + path);
            // com/hspedu/Spring/Annotation 路径
            // 1. 找到类的加载器 --> APP类加载器
            ClassLoader classLoader = HspSpringApplicationContext.class.getClassLoader();
            path = path.replaceAll("\\.", "/");
            // 2. 通过类的加载器, 获得扫描包的资源url
            URL resource = classLoader.getResource(path);
            // 3. 将要加载的资源(.class)路径进行遍历 io知识
            File file = new File(resource.getFile());
            if (file.isDirectory()) {
                File[] files = file.listFiles();
                for (File f : files) {
                    String absolutePath = f.getAbsolutePath();
                    if (!absolutePath.endsWith(".class")) {
                        // 如果不是以.class结尾的就不进行下面的操作
                        return;
                    }
                    // 反射需要 的是 com.hspedu.Spring.component.MyComponent
                    // 获取到类名 类似于 MyComponent
                    String className = absolutePath.substring(absolutePath.lastIndexOf("\\") + 1, absolutePath.lastIndexOf(".class"));
                    // 反射需要的是 全类名
                    String classFullName = path.replaceAll("/", ".") + "." + className;
                    // 造对象, 放到单例池
                    try {
                        Class<?> clazz = Class.forName(classFullName);
                        if (clazz.isAnnotationPresent(Component.class)) {
                            String beanName = clazz.getDeclaredAnnotation(Component.class).value();
                            if ("".equals(beanName)) {
                                // 如果没写有指定beanName那么就应类名小写代替
                                beanName = StringUtils.uncapitalize(className);
                            }

                            // 这里判断是不是后置处理器
                            if (BeanPostProcessor.class.isAssignableFrom(clazz)) {
                                // 如果实现了 BeanPostProcessor 接口
                                beanPostProcessors.add((BeanPostProcessor) clazz.getDeclaredConstructor().newInstance());
                                // 就不需要在放到 beanDefinitionMap 里面
                                continue;
                            }
                            BeanDefinition beanDefinition = new BeanDefinition();

                            beanDefinition.setClazz(clazz);
                            if (clazz.isAnnotationPresent(Scope.class)) {
                                String scope = clazz.getDeclaredAnnotation(Scope.class).value();
                                if ("".equals(scope)) {
                                    // 没有指定默认singleton
                                    scope = "singleton";
                                }
                                beanDefinition.setScope(scope);
                            } else {
                                beanDefinition.setScope("singleton");
                            }
                            beanDefinitionMap.put(beanName, beanDefinition);
                        }

                    } catch (ClassNotFoundException | NoSuchMethodException | InstantiationException |
                             IllegalAccessException | InvocationTargetException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        }
    }
}

BeanPostProcessor 接口

只要实现这个接口, 就可以实现调用 后置处理器

public interface BeanPostProcessor {
    /**
     * 该方法会在bean的初始化方法后调用
     *
     * @param bean
     * @param beanName
     * @return
     */
    default Object postProcessAfterInitialization(Object bean, String beanName) {
        return bean;
    }

    /**
     * 该方法在bean的初始化方法之前调用
     *
     * @param bean
     * @param beanName
     * @return
     */
    default Object postProcessBeforeInitialization(Object bean, String beanName) {
        return bean;
    }

}

InitializingBean 接口

只要实现这个接口就可以实现bean的初始化方法

/**  
* ClassName: InitializingBean  
* Package: com.hspedu.Spring.processor  
*  
* @Author: leikooo  
* @Creat: 2023/5/30 - 17:47  
* @Description: 我们自己写的  
* 1. 我们根据原生的Spring, 定义了InitializingBean  
* 2. 该接口中有一个方法 void afterPropertiesSet() throws Exception;  
* 3. 这个方法会在setter方法之后执行, 就相当于原来配置的初始化方法  
* 4. 当一个Bean实现这个接口之后, 就实现了afterPropertiesSet, 那么这个方法方法就实现了初始化方法  
*/  
public interface InitializingBean {  
  
void afterPropertiesSet() throws Exception;  
}
/**
 * ClassName: HspBeanPostProcessor
 * Package: com.hspedu.Spring.component
 *
 * @Author: leikooo
 * @Creat: 2023/6/2 - 17:49
 * @Description:
 */
@Component
public class HspBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        System.out.println(bean + "的后置通知 postProcessBeforeInitialization");
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        System.out.println(bean + "的后置通知 postProcessAfterInitialization");
        return bean;
    }
}

实现任务阶段 6- AOP 机制实现

AOP的实现依赖于 后置处理器

其实就是在后置处理器进行 动态代理 返回代理对象, 就实现了AOP

/**
 * ClassName: HspBeanPostProcessor
 * Package: com.hspedu.Spring.component
 *
 * @Author: leikooo
 * @Creat: 2023/6/2 - 17:49
 * @Description:
 */
@Component
public class HspBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        System.out.println(bean + "的后置通知 postProcessBeforeInitialization");
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        System.out.println(bean + "的后置通知 postProcessAfterInitialization");
        // 这里AOP机制
        if (bean instanceof SmartAnimal) {
            // 如果实现了 接口
            return Proxy.newProxyInstance(
                    bean.getClass().getClassLoader(),
                    bean.getClass().getInterfaces(),
                    new InvocationHandler() {
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            Object obj = null;
                            if ("getSum".equals(method.getName())) {
                                SmartAnimalAspect.beforeLog();
                                obj = method.invoke(bean, args);
                                SmartAnimalAspect.afterLog();
                            } else {
	                            // 如果该方法没有加注解被标识那么还是执行原来的方法
                                obj = method.invoke(bean, args);
                            }
                            return obj;
                        }
                    }
            );
        }
        return bean;
    }
}

实现效果

补充内容

简单分析AOP和BeanPostProcessor的关系

首先 AOP实现Spring可以通过给类加入注解 @EnableAspectJAutoProxy 来实现

AnnotationAwareAspectJAutoProxyCreator.class 的类图

总结:

  1. AOP 底层是基于 BeanPostProcessor 机制的.
  2. 即在 Bean 创建好后,根据是否需要 AOP 处理,决定返回代理对象,还是原生Bean
  3. 在返回代理对象时,就可以根据要代理的类和方法来返回
  4. 其实这个机制并不难,本质就是在 BeanPostProcessor 机制+ 动态代理技术5)

类的加载器

java 的类加载器 3 种

  1. Bootstrap 类加载器--------------对应路径 jre/lib
  2. Ext 类加载器--------------------对应路径 jre/lib/ext
  3. App 类加载器-------------------对应路径 classpath
  • classpath这个是一系列路径