jsoncat: 仿 Spring Boot 但不同于 Spring Boot 的一个轻量级的 HTTP 框架

国庆节的时候,我就曾经把 jsoncat 的 IoC 性能给写了,具体能够看这篇文章《手写“SpringBoot”近况:IoC模块曾经实现》 。

明天这篇文章就来简略分享一下本人写 IoC 的思路与具体的代码实现。

IoC (Inverse of Control:管制反转)AOP(Aspect-Oriented Programming:面向切面编程) 能够说是 Spring 框架提供的最外围的两个性能。但但凡理解过 Spring 的小伙伴,那必定对这个两个概念十分十分理解。不理解的小伙伴,能够查看《面试被问了几百遍的 IoC 和 AOP ,还在傻傻搞不清楚?》这篇通俗易懂的文章。

思考到这篇文章要手写 Spring 框架的 IoC 性能,所以,我这里还是简略介绍一下 IoC 。如果你不太分明 IoC 这个概念,肯定要搞懂之后再看前面具体的代码实现环节。

IoC 介绍

IoC(Inverse of Control:管制反转)是一种设计思维,也就是 将本来在程序中手动创建对象的控制权交由Spring框架来治理。 IoC 在其余语言中也有利用,并非 Spring 特有。

IoC 容器

IoC 容器是用来实现 IoC 的载体,被治理的对象就被寄存在IoC容器中。IoC 容器在 Spring 中实际上就是个Map(key,value),Map 中寄存了各种被治理的对象。

IoC 解决了什么问题

将对象之间的相互依赖关系交给 IoC 容器来治理,并由 IoC 容器实现对象的注入。这样能够很大水平上简化利用的开发,把利用从简单的依赖关系中解放出来。 IoC 容器就像是一个工厂一样,当咱们须要创立一个对象的时候,只须要配置好配置文件/注解即可,齐全不必思考对象是如何被创立进去的。 在理论我的项目中一个 Service 类可能有几百甚至上千个类作为它的底层,如果咱们须要实例化这个 Service,你可能要每次都要搞清这个 Service 所有底层类的构造函数,这可能会把人逼疯。如果利用 IoC 的话,你只须要配置好,而后在须要的中央援用就行了,这大大增加了我的项目的可维护性且升高了开发难度。

IoC 和 DI 别再傻傻分不清楚

IoC(Inverse of Control:管制反转)是一种设计思维 或者说是某种模式。这个设计思维就是 将本来在程序中手动创建对象的控制权,交由 Spring 框架来治理。 IoC 在其余语言中也有利用,并非 Spring 特有。IoC 容器是 Spring 用来实现 IoC 的载体, IoC 容器实际上就是个 Map(key,value),Map 中寄存的是各种被治理的对象。

IoC 最常见以及最正当的实现形式叫做依赖注入(Dependency Injection,简称 DI)。

并且,老马(Martin Fowler)在一篇文章中提到将 IoC 改名为 DI,原文如下,原文地址:https://martinfowler.com/arti... 。

IoC实现思路

????留神 :以下思路未波及解决循环依赖的问题!

开始代码实现之前,咱们先简略聊聊实现 IoC 的思路,搞清楚了思路之后,实现起来就非常简单了。

  1. 扫描指定包下的特定注解比方@Component标记的类,并将这些类保存起来。
  2. 遍历所有被特定注解比方@Component标记的类,而后将这些类通过反射实例化并通过一个 Map 保存起来,Map 的 key 为类名,value为类对象。
  3. 再一次遍历所有被特定注解比方@Component标记的类,并获取类中所有的字段,如果类被 @Autowired 注解标记的话,就进行第 4 步。
  4. 通过字段名 key,从bean容器中获取对应的对象 value。
  5. 判断获取到的对象是否为接口。如果是接口的话,须要获取接口对应的实现类,而后再将指定的实现类的实例化对象通过反射赋值给指定对象。如果不是接口的话,就间接将获取到的对象通过反射赋值给指定对象。

IoC 实现外围代码

外围注解

@Autowired :注解对象

@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface Autowired {}

@Component :申明对象被IoC容器治理

@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface Component {    String name() default "";}

@Qualifier: 指定注入的bean(当接口有多个实现类的时候须要应用)

@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})@Retention(RetentionPolicy.RUNTIME)@Inherited@Documentedpublic @interface Qualifier {    String value() default "";}

工具类

简略封装一个反射工具类。工具类蕴含3个前面会用到的办法:

  1. scanAnnotatedClass() :扫描指定包下的被指定注解标记的类(应用Reflections这个反射框架一行代码即可解决扫描获取指定注解的类)。
  2. newInstance() : 传入 Class 即可返回 Class 对应的对象。
  3. setField() :为对象的指定字段赋值。
@Slf4jpublic class ReflectionUtil {    /**     * scan the classes marked by the specified annotation in the specified package     *     * @param packageName specified package name     * @param annotation  specified annotation     * @return the classes marked by the specified annotation in the specified package     */    public static Set<Class<?>> scanAnnotatedClass(String packageName, Class<? extends Annotation> annotation) {        Reflections reflections = new Reflections(packageName, new TypeAnnotationsScanner());        Set<Class<?>> annotatedClass = reflections.getTypesAnnotatedWith(annotation, true);        log.info("The number of class Annotated with  @RestController :[{}]", annotatedClass.size());        return annotatedClass;    }    /**     * create object instance through class     *     * @param cls target class     * @return object created by the target class     */    public static Object newInstance(Class<?> cls) {        Object instance = null;        try {            instance = cls.newInstance();        } catch (InstantiationException | IllegalAccessException e) {            log.error("new instance failed", e);        }        return instance;    }    /**     * set the value of a field in the object     *     * @param obj   target object     * @param field target field     * @param value the value assigned to the field     */    public static void setField(Object obj, Field field, Object value) {        field.setAccessible(true);        try {            field.set(obj, value);        } catch (IllegalAccessException e) {            log.error("set field failed", e);            e.printStackTrace();        }    }  }

依据实现思路写代码

????留神 :以下代码未波及解决循环依赖的问题!以下是 IoC 实现的外围代码,残缺代码地址:https://github.com/Snailclimb/jsoncat 。

1.扫描指定包下的特定注解比方@Component标记的类,并将这些类保存起来。

扫描指定注解@RestController@Component并保存起来:

public class ClassFactory {    public static final Map<Class<? extends Annotation>, Set<Class<?>>> CLASSES = new ConcurrentHashMap<>();    //1.扫描指定包下的特定注解比方`@Component`标记的类,并将这些类保存起来    public static void loadClass(String packageName) {        Set<Class<?>> restControllerSets = ReflectionUtil.scanAnnotatedClass(packageName, RestController.class);        Set<Class<?>> componentSets = ReflectionUtil.scanAnnotatedClass(packageName, Component.class);        CLASSES.put(RestController.class, restControllerSets);        CLASSES.put(Component.class, componentSets);    }}

2.遍历所有被特定注解比方@Component标记的类,而后将这些类通过反射实例化并通过一个 Map 保存起来,Map 的 key 为类名,value为类对象。

public final class BeanFactory {    public static final Map<String, Object> BEANS = new ConcurrentHashMap<>(128);    public static void loadBeans() {        // 2.遍历所有被特定注解比方 @Component 标记的类,而后将这些类通过反射实例化并通过一个 Map 保存起来,Map 的 key 为类名,value为类对象        ClassFactory.CLASSES.forEach((annotation, classes) -> {            if (annotation == Component.class) {                //将bean实例化, 并放入bean容器中                for (Class<?> aClass : classes) {                    Component component = aClass.getAnnotation(Component.class);                    String beanName = "".equals(component.name()) ? aClass.getName() : component.name();                    Object obj = ReflectionUtil.newInstance(aClass);                    BEANS.put(beanName, obj);                }            }            if (annotation == RestController.class) {                for (Class<?> aClass : classes) {                    Object obj = ReflectionUtil.newInstance(aClass);                    BEANS.put(aClass.getName(), obj);                }            }        });    }}

3.再一次遍历所有被特定注解比方@Component标记的类,并获取类中所有的字段,如果类被 @Autowired 注解标记的话,就进行第 4 步。

public class DependencyInjection {    public static void dependencyInjection(String packageName) {        Map<String, Object> beans = BeanFactory.BEANS;        if (beans.size() == 0) return;        //3.再一次遍历所有被特定注解比方 @Component 标记的类,并获取类中所有的字段,如果类被 `@Autowired` 注解标记的话,就进行第 4 步。        // 3.1.遍历bean容器中的所有对象        beans.values().forEach(bean -> {            // 3.2.获取对象所属的类申明的所有字段/属性            Field[] beanFields = bean.getClass().getDeclaredFields();            if (beanFields.length == 0) return;            //3.3.遍历对象所属的类申明的所有字段/属性            for (Field beanField : beanFields) {              //3.4.判断字段是否被 @Autowired 注解标记                if (beanField.isAnnotationPresent(Autowired.class)) {                    //4.通过字段名 key,从bean容器中获取对应的对象 value。                    //4.1.字段对应的类型                    Class<?> beanFieldClass = beanField.getType();                    //4.2.字段对应的类名                    String beanName = beanFieldClass.getName();                    if (beanFieldClass.isAnnotationPresent(Component.class)) {                        Component component = beanFieldClass.getAnnotation(Component.class);                        beanName = "".equals(component.name()) ? beanFieldClass.getName() : component.name();                    }                    //4.3.从bean容器中获取对应的对象                    Object beanFieldInstance = beans.get(beanName);                    //5.判断获取到的对象是否为接口。如果是接口的话,须要获取接口对应的实现类,而后再将指定的实现类的实例化对象通过反射赋值给指定对象。如果不是接口的话,就间接将获取到的对象通过反射赋值给指定对象。                    if (beanFieldClass.isInterface()) {                        //如果是接口,获取接口对应的实现类                        Set<Class<?>> subClasses = getSubClass(packageName, beanFieldClass);                        //没有实现类的话就抛出异样                        if (subClasses.size() == 0) {                            throw new InterfaceNotHaveImplementedClassException("interface does not have implemented class exception");                        }                        //实现类只有一个话,间接获取                        if (subClasses.size() == 1) {                            Class<?> aClass = subClasses.iterator().next();                            beanFieldInstance = ReflectionUtil.newInstance(aClass);                        }                        //实现类多与一个的话,依据 Qualifier 注解的值获取                        if (subClasses.size() > 1) {                            Class<?> aClass = subClasses.iterator().next();                            Qualifier qualifier = beanField.getDeclaredAnnotation(Qualifier.class);                            beanName = qualifier == null ? aClass.getName() : qualifier.value();                            beanFieldInstance = beans.get(beanName);                        }                    }                    // 如果最初获取到的字段对象为null,就抛出异样                    if (beanFieldInstance == null) {                        throw new CanNotDetermineTargetBeanException("can not determine target bean");                    }                    //通过反射设置指定对象中的指定字段的值                    ReflectionUtil.setField(bean, beanField, beanFieldInstance);                }            }        });    }    /**     * 获取接口对应的实现类     */    @SuppressWarnings("unchecked")    public static Set<Class<?>> getSubClass(String packageName, Class<?> interfaceClass) {        Reflections reflections = new Reflections(packageName);        return reflections.getSubTypesOf((Class<Object>) interfaceClass);    }}

我整顿了一份优质原创PDF资源收费分享给大家,大部分内容都是我的原创,少部分来自敌人。

<img src="https://cdn.jsdelivr.net/gh/javaguide-tech/blog-images/2020-10/image-20201012105544846.png" style="zoom:50%;" />

<img src="https://cdn.jsdelivr.net/gh/javaguide-tech/blog-images/2020-10/image-20201012105608336.png" alt="image-20201012105608336" style="zoom:50%;" />

下载地址:https://cowtransfer.com/s/fbed14f0c22a4d 。
我是 Guide 哥,一 Java 后端开发,会一点前端,自在的少年。咱们下期再见!微信搜“JavaGuide”回复“面试突击”支付我整顿的 4 本原创PDF