Spring源码分析文档 - 图1
请像对待每一行代码一样对待每篇文章 —- 鹰嘴豆

学习目标


  1. SpringMVC的执行过程

    1. 在xml中注册serlvet,配置匹配的映射路径,这个selvet是DispatcherServlet类

    2. DispatcherServlet继承HttpServletBean祖父类,里面的init方法进行数据的初始化,例如springmvc容器的初始化

    3. 初始化好容器以后,回调OnRefresh方法进行初始化springMVC的默认策略类(配置在DispatcherServlet.properties)

    4. 根据request请求,调用Servlet提供的接口service来分发request请求方法到具体的doXXX()方法中

    5. 在各自的doXXX方法中调用processRequest处理请求,这个方法处理请求,并发出一个事件,具体委托doService处理

开始学习


  1. 加载Properties的代码片段

    1. 这个例子是加载spring提供的默认的策略类的properties文件

    2. spring提供了对properties处理的封装,基于spring的框架可以复用这些工具类

      1. static {
      2. try {
      3. ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
      4. defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
      5. }
      6. catch (IOException ex) {
      7. throw new IllegalStateException("Could not load 'DispatcherServlet.properties': " + ex.getMessage());
      8. }
      9. }
  1. spring中对枚举的处理

    1. 枚举和类差不多,可以有static{}静态处理,可以有静态方法处理

    2. 下面代码可以看到spring对enum干净的处理方式 ```java public enum HttpMethod {

      GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE;

private static final Map<String, HttpMethod> mappings = new HashMap<String, HttpMethod>(8);

static {
    for (HttpMethod httpMethod : values()) {
        mappings.put(httpMethod.name(), httpMethod);
    }
}


/**
 * Resolve the given method value to an {@code HttpMethod}.
 * @param method the method value as a String
 * @return the corresponding {@code HttpMethod}, or {@code null} if not found
 * @since 4.2.4
 */
public static HttpMethod resolve(String method) {
    return (method != null ? mappings.get(method) : null);
}


/**
 * Determine whether this {@code HttpMethod} matches the given
 * method value.
 * @param method the method value as a String
 * @return {@code true} if it matches, {@code false} otherwise
 * @since 4.2.4
 */
public boolean matches(String method) {
    return (this == resolve(method));
}

}



3. 获取spring的上下文容器

   1. 使用WebApplicationContextUtils工具类从servlet上下文中获取根上下文,获取子上下文,
```java
WebApplicationContext rootContext =
                WebApplicationContextUtils.getWebApplicationContext(getServletContext());

// 使用下面的key存放在servlet上下文中
String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";
  1. spring初始化handlerMapping

    1. 获取父容器、子容器中指定类型的bean

      1. 容器有一个api为getBeansOfType,通过这个api获取指定类型的bean,这个api的原理是什么呢?
  1. spring的循环依赖发现 - depend-on

    1. 每个bean C都有一个depend-on的集合A存储依赖的beans,还有一个集合B是存储depend-on=”target” 这个target bean 作为key, target所在的bean的集合作为value。循环遍历集合A,在集合B中以bean C为key查找出集合,在集合中找到这个遍历的集合A的值是否存在,如果存在就表示循环依赖,dependenciesForBeanMap这个map的key存放的是depend-on中的bean名字,value是一个集合,存放的是depend-on所在的bean。
  2. 递归查找方法重写的个数,从接口,超类,当前类中去查找

    public static int getMethodCountForName(Class<?> clazz, String methodName) {
         Assert.notNull(clazz, "Class must not be null");
         Assert.notNull(methodName, "Method name must not be null");
         int count = 0;
         Method[] declaredMethods = clazz.getDeclaredMethods();
         for (Method method : declaredMethods) {
             if (methodName.equals(method.getName())) {
                 count++;
             }
         }
         Class<?>[] ifcs = clazz.getInterfaces();
         for (Class<?> ifc : ifcs) {
             count += getMethodCountForName(ifc, methodName);
         }
         if (clazz.getSuperclass() != null) {
             count += getMethodCountForName(clazz.getSuperclass(), methodName);
         }
         return count;
     }
    
  1. spring在实例化bean的时候会先执行BeanPostProcessors,使用它去得到一个bean对象代替直接实例化目标bean的class,作为创建的bean实例

  2. 先是用工厂方法初始化实例,如果没有工厂方法则使用默认的构造函数初始化实例

  3. 判断对象是否是cglib代理,只要获取对象的className,判断name是否包含$$符号就可以

  4. cglib会自己生产一个带$$名字的对象,它继承了指定代理的class,只需要通过getSuperClass就可以获取到这个被代理的class

  5. 两个class比较直接用==即可

    public static void main(String[] args) {
       HelloOne helloOne = new HelloOne();
       Class<?> helloOneClass = helloOne.getClass();
       Method[] methods = helloOneClass.getMethods();
       for(Method method : methods) {
           // 判断方法所在的class是否是HelloOne
           if (method.getDeclaringClass() == HelloOne.class) {
               System.out.println("你很牛逼");
           }
       }
        System.out.println();
    }
    
  1. 获取基本类型的class

    1. java提供getPrimitiveClass获取基本类型的Class,例如Double.TYPE获取到Double的Class类型
      TYPE = (Class<Double>) Class.getPrimitiveClass("double")
      
  1. 判断方法的返回值是否是Void
    method.getReturnType() != Void.TYPE
    
  1. 返回基本类型的包装类型

    public static Class<?> getBoxedClass(Class<?> c) {
        if (c == Integer.TYPE) {
            c = Integer.class;
        } else if (c == Boolean.TYPE) {
            c = Boolean.class;
        } else if (c == Long.TYPE) {
            c = Long.class;
        } else if (c == Float.TYPE) {
            c = Float.class;
        } else if (c == Double.TYPE) {
            c = Double.class;
        } else if (c == Character.TYPE) {
            c = Character.class;
        } else if (c == Byte.TYPE) {
            c = Byte.class;
        } else if (c == Short.TYPE) {
            c = Short.class;
        }
    
        return c;
    }
    

    Integer.type和Integer.class什么区别?

  1. 判断方法修饰符是不是public和static
    Modifier.isPublic(method.getModifiers()) && !Modifier.isStatic(method.getModifiers())
    
  1. 根据属性拼接setter方法并执行
    String setter = "set" + property.substring(0, 1).toUpperCase() + property.substring(1);
    Object value = method.invoke(annotation);
    
  1. spring通过工厂方法、构造函数,普通bean反射初始化来完成对象的创建,在spring中分别存在下面的方法进行初始化

    1. AbstractAutowireCapableBeanFactory.autowireConstructor
      AbstractAutowireCapableBeanFactory.instantiateBean
      AbstractAutowireCapableBeanFactory.autowireConstructor
      
  1. getBeanPostProcessors,在对象初始化后,会使用beanPostProcessor来进行属性注解的注入,比如
    AutowiredAnnotationBeanPostProcessor.postProcessMergedBeanDefinition
    
  1. Application Context实现的规范
    Spring源码分析文档 - 图2

  2. 传值的方式

    1. 通过构造函数传参,这需要构造一个对象

      1. 这个构造对象中提供了一些功能函数
    2. 通过方法调用传参

    3. 全局变量,比如静态变量、内部类公用外部类属性,当前线程的对象复用

  3. isAssignableFrom 和 instanceof

    1. A.class.isAssignableFrom(B). 表示A是父类或者接口, B是子类或者实现接口,B必须是Class<?>

    2. A instanceof B A是B的子类或者A是B的实现, B是类名,A是具体实例

  4. Assert 抛出的是IllegalArgumentException异常,可以用来校验参数

    public static void notNull(@Nullable Object object, String message) {
        if (object == null) {
            throw new IllegalArgumentException(message);
        }
    }
    
  1. 将对象转换成指定的类型
    BeanDefinitionDocumentReader.class.cast(BeanUtils.instantiateClass(this.documentReaderClass))
    
  1. 集合转数组,数组转集合
    public static String[] toStringArray(Collection<String> collection) {
        if (collection == null) {
            return null;
        }
        // 集合转数组
        return collection.toArray(new String[collection.size()]);
    }
    // 数组转集合
    Arrays.asList(nameArr)
    
  1. StringTokenizer的使用,用String.split代替它
    public static void main(String[] args) {
        StringTokenizer stringTokenizer = new StringTokenizer("12,3;45,6;7", ",;");
        while (stringTokenizer.hasMoreElements()) {
            System.out.println(stringTokenizer.nextToken());
        }
    }
    //输出: 12  3  45  6  7
    
  1. 子类转成父类再转成子类

    1. 子类转成父类不需要强转,父类转成子类需要强转

    2. 子类转成父类以后,再将父类转成子类,不会丢失原先子类的其他方法,可以看成是同一个内存空间,父子在方法区域做了不可见的操作

      public static void main(String[] args) {
      HelloOne helloOne = new HelloOne();
      // 子类HelloOne转父类NiuBi
      NiuBi niuBi = helloOne;
      // 子类HelloOne转接口Hello类型
      Hello hello = helloOne;
      // good是NiuBi才有的方法,这里需要强转回到HelloOne
      ((HelloOne) hello).good();
      // 自由Hello才有testHello,这里需要强转成HelloOne这个实现类
      String show = ((HelloOne) niuBi).testHello();
      System.out.println(show);
      }
      
  1. 对象间的值传递

    1. 构造函数传递,需要构造一个对象,这个对象提供所需要的功能,在构造时传递外部参数,这些参数可能也是一些功能,在构造函数执行时可以初始化一些数据

    2. 方法形参传递,可以不用构造一个对象,也就是说可以是一个静态的方法,传递给形参的参数可以是外部赋予的一个功能,通过这个外部的功能对象就能完成需要的功能

    3. 很多的功能类就像是机器,只要上油就能功能,在面相对象中,传参就是上油,参数可以包装(用另外的类包装,包装的目的是构建一个新的更细的功能类,继续上油运行)

    4. 类成员属性可以在定义的时候就初始化成默认的功能类

    5. 在执行某个功能方法时对组合的功能(成员属性)赋值,所附的值可以在方法中创建

  2. 接口/继承/组合

    1. 接口,作用是制定一种规范,这种规范在实现类中实现,从接口中可以了解到这个类的功能

    2. 继承,复用功能,层次结构深,一些设计模式实现的必要前提(模板方法)

    3. 组合,setter/构造函数等方式传递, 也可以通过具体的方法的形参依赖来完成功能的服用

  3. 获取系统环境以及属性

    public static void main(String[] args) {
        System.out.println(System.getenv());
        System.out.println(System.getProperties());
    }
    
  1. 接口与类继承与实现

    1. 接口可以extends接口,extends多个接口用逗号隔开

    2. 类implements 接口,可以实现多个接口,用逗号隔开

    3. 类A实现了接口A, 接口B继承了接口A, 那么类B继承A再实现接口B,这时候类B不需要再实现接口A的方法,因为已经继承了类A,只需要实现接口B即可 ```java /**

    • 类功能描述
    • HelloOne实现了Hello接口,所以NiuBi就不需要在实现Hello接口中的方法 *
    • @author 鹰嘴豆
    • @date 2019/2/14
    • 时间 作者 版本 描述
    • ==================================================== */ public class NiuBi extends HelloOne implements HelloExt, Hello{
@Override
public void showTimer() {

}

@Override
public void printShow() {

}

}


   4. 如果所实现的多个接口中包含有重复的接口方法,那么只需要实现一个接口即可

31. 深层次的接口与类关系

   1. 如下类图,接口继承接口,每个接口都是某个功能的规范,不同层次结构上的类实现了不同层次的接口,只要实现了这些特定功能的规范,就说明这个层次结构上的类具有这个功能特性,被子类继承以后就会继承这个功能特性<br />![](https://cdn.nlark.com/yuque/0/2019/png/188448/1550212628401-e945d41a-db34-4da5-bb22-a571f080795d.png#width=771)


   2. 重复接口方法只要实现一遍即可,上面的类图很难避免同一个接口规范被重复继承,不过没关系,无论你是继承父类还是自己实现,只要接口规范实现过一次即可


32. ApplicationContext和BeanFactory的区别

   1. 看下类图的区别,ApplicationContext继承ListableBeanFactory和HierarchicalBeanFactory,也就是它有这两个接口的功能特性(容器功能, 父子容器?)<br />![](https://cdn.nlark.com/yuque/0/2019/png/188448/1550213339774-432de382-059d-4f4e-8df1-932bc7360375.png#width=771)


   2. BeanFactory继承,有注册功能,自动写入功能,但也有容器,父子容器的功能<br />![](https://cdn.nlark.com/yuque/0/2019/png/188448/1550213566220-46dd0d1b-68cb-4724-97c9-6ec94bf2ccf4.png#width=771)


   3. ApplicationContext组合了BeanFactory功能(ApplicationContext有个属性是BeanFactory类型),也就是说ApplicationContext是基于BeanFactory进一步实现的功能类,复用BeanFactory功能,适当实现个性化的功能,或者优化BeanFactory中的功能,青春于蓝胜于蓝,推动BeanFactory的创建


33. 将继承层次深的对象分解功能

   1. 继承层次深的对象,实现了多个接口,继承了多个类,这时候可以将这个对象分解成不同功能的类,装配到不同粒度的对象里面,完成功能的实现

   2. 对象间的通信,某个类需要完成任务时,需要借助另外一个类的功能,这时候就可以考虑,如何进行通信,有几种方式,继承、接口实现、组合、方法间传递, 指定默认功能类

34. spring的注册接口中包含了大量的集合缓存<br />![](https://cdn.nlark.com/yuque/0/2019/png/188448/1550287559342-98c89784-6862-4285-8f93-ff48fc102830.png#width=799)

35. Spring的refresh的执行过程

   1. prepareRefresh,在开始refresh的时候记录刷新的时间、刷新的活动标志及初始化数据,如servlet的上下文,参数等

   2. obtainFreshBeanFactory,创建BeanFactory并将xml中的元数据信息缓存注册,beanFactory用用listableBeanFactory,Register等接口功能,实现了容器,注册等功能特性

   3. postProcessBeanFactory, 准备post-processor 后置处理器,处理serlvet的servletContext和servletConfig的参数,并注册一些作用域功能

   4. invokeBeanFactoryPostProcessors,执行所有ApplicationContext上下文中的BeanFactoryPostProcessor的后置处理类。BeanFactoryPostProcessor的子类BeanDefinitionRegistryPostProcessor注册ApplicationContext上下文中的带注解@Configuration,或者其他Lite如@Import @ComponetScan,@Bean等的配置类里面定义的元数据

   5. registerBeanPostProcessors,注册所有的BeanPostProcessors对象,缓存在BeanFactory的集合中

   6. initMessageSource,注册DelegatingMessageSource到BeanFactory的单例对象缓存集合中

   7. initApplicationEventMulticaster,注册SimpleApplicationEventMulticaster到BeanFactory的单例缓存集合(singletonObjects)中

   8. onRefresh,给上下文初始化其他特殊的bean,先自动查找ApplicationContext是否存在themeSource的bean,如果找不到默认初始化ResourceBundleThemeSource

   9. registerListeners,注册ApplicationListener, 先注册静态的指定的监听器,然后在ApplicationContext上下文中查找所有ApplicationListener类型的监听器,并注册,使用这些监听器广播一些早先存储的事件

   10. 

36. 集合在遍历时别的地方做修改

   1. java集合会在Iterator迭代的时候实现遍历时是否修改的检查,所以在forEach迭代时做集合的更改就会爆异常,下面采用副本拷贝就可以解决

      1. ```java
public static void main(String[] args) {
        // 构建集合
        List<String> names = new ArrayList<>();
        names.add("yingzuidou");
        names.add("designer");
        // 在遍历是增加元素,使用forEach打印改变集合之前的所有元素,所以forEach比for(,,)安全
        for (String name : names) {
            System.out.println(name);
            List<String> updates = new ArrayList<>(names.size() + 1);
            updates.addAll(names);
            updates.add("good");
            names = updates;
        }
        System.out.println(names);
    }
  1. BeanFactoryPostProcessor和BeanPostProcessor两个接口的区别

    1. BeanPostProcessor,对新的bean实例做自定义修改,ApplicationContext自动发现BeanPostProcessor的bean,并在随后的任何bean创建时应用这个BeanPostProcessor功能,做实例自定义修改。例如在某种类型对象创建时给对象组合其他的功能对象,spring定义了一些BeanPostProcessor处理器对指定类型对象做进一步的处理

    2. BeanFactoryPostProcessor自定义修改Bean元数据的属性定义,适配Bean元数据属性,ApplicationContext会自动发现所有的BeanFactoryPostProcessor,并在bean创建之前去更改bean的属性

  2. org.springframework.asm.ClassReader读取class的字节码类

  3. spring的元数据基于java配置

    1. 有lite配置和full配置两种,full配置表示类使用@Configuration标记,lite配置是使用以下的注解,或者在类中使用@Bean
      static {
       candidateIndicators.add(Component.class.getName());
       candidateIndicators.add(ComponentScan.class.getName());
       candidateIndicators.add(Import.class.getName());
       candidateIndicators.add(ImportResource.class.getName());
      }
      

学习总结


  1. 线程安全的处理方法

    1. 将内容放到Request属性中

    2. 将内容方法ThreadLocal中

  2. 技巧总结

    1. 缓存,一般使用集合进行缓存

    2. 反射进行一些操作

  3. 对spring封装的一些理解

    1. 类理解成功能类,更具体可以理解成加工厂

    2. 将原生未处理的猪切成不同粒度的部分,将这些原生的部分包装到指定的功能类中,这些功能类提供不同的加工方法对原生的猪肉加工

    3. 包装的方法可以通过构造函数传递猪肉,也可以通过setter进行传递

贡献者列表


编辑人 编辑时间 编辑内容
鹰嘴豆 2019/1/31 初稿