Spring 核心源码

1.Servlet

1.1

导入Servlet依赖,将Maven的JDK版本改为8,且将Maven打包方式改为war

  1. <dependency>
  2. <groupId>javax.servlet</groupId>
  3. <artifactId>javax.servlet-api</artifactId>
  4. <version>4.0.1</version>
  5. </dependency>
  1. <build>
  2. <!-- MAVEN默认JDK5,手动改为JDK8-->
  3. <plugins>
  4. <plugin>
  5. <groupId>org.apache.maven.plugins</groupId>
  6. <artifactId>maven-compiler-plugin</artifactId>
  7. <version>3.8.1</version>
  8. <configuration>
  9. <source>1.8</source>
  10. <target>1.8</target>
  11. <encoding>UTF-8</encoding>
  12. </configuration>
  13. </plugin>
  14. </plugins>
  15. </build>
  1. <packaging>war</packaging>

1.2 创建一个Servlet对象

  1. package com.william.servlet;
  2. import javax.servlet.ServletException;
  3. import javax.servlet.annotation.WebServlet;
  4. import javax.servlet.http.HttpServlet;
  5. import javax.servlet.http.HttpServletRequest;
  6. import javax.servlet.http.HttpServletResponse;
  7. import java.io.IOException;
  8. @WebServlet("/hello")
  9. public class HellowServlet extends HttpServlet {
  10. @Override
  11. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  12. resp.getWriter().print("Spring");
  13. }
  14. }

1.3 部署到Tomcat

SpringIoC源码 - 图1

1.4 创建三层架构,并逐层注入

1.5 创建静态工厂类

为了实现解耦合,创建工厂,将创建对象的事情交给工厂,便于维护

  1. package com.william.factory;
  2. import com.william.dao.HellowDao;
  3. import com.william.dao.impl.HellowDaoImpl;
  4. public class BeanFactory {
  5. public static HellowDao getDao() {
  6. return new HellowDaoImpl();
  7. }
  8. }

1.6 实例从工厂获取

  1. // private HellowDao hellowDao = new HellowDaoImpl();
  2. private HellowDao hellowDao = BeanFactory.getDao();

1.7 配置文件

工厂中的代码在业务变更的时候还是需要修改,进一步优化为实现类写到配置文件中

  1. 定义一个外部的配置properties文件
  1. helloDao=com.william.dao.impl.HellowDaoImpl
  1. 用java读取配置文件
  1. package com.william.factory;
  2. import com.william.dao.impl.HellowDaoImpl;
  3. import java.io.IOException;
  4. import java.lang.reflect.InvocationTargetException;
  5. import java.util.Properties;
  6. public class BeanFactory {
  7. private static Properties properties;
  8. static {
  9. properties = new Properties();
  10. try {
  11. //通过反射,读取配置文件
  12. properties.load(BeanFactory.class.getClassLoader().getResourceAsStream("factory.properties"));
  13. } catch (IOException e) {
  14. e.printStackTrace();
  15. }
  16. }
  17. public static Object getDao() {
  18. //读取到配置文件中的属性
  19. String value = properties.getProperty("helloDao");
  20. //反射机制创建对象
  21. try {
  22. Class clazz = Class.forName(value);
  23. Object object = clazz.getConstructor(null).newInstance(null);
  24. return object;
  25. } catch (ClassNotFoundException e) {
  26. e.printStackTrace();
  27. } catch (InstantiationException e) {
  28. e.printStackTrace();
  29. } catch (IllegalAccessException e) {
  30. e.printStackTrace();
  31. } catch (InvocationTargetException e) {
  32. e.printStackTrace();
  33. } catch (NoSuchMethodException e) {
  34. e.printStackTrace();
  35. }
  36. return null;
  37. }
  38. }

如果实体类变更,只需修改配置文件factory.properties中的路径

1.8 缓存实现单例

  1. private static Map<String, Object> cache = new HashMap<>();
  1. //判断缓存中是否存在bean
  2. if (!cache.containsKey(beanName))

1.9 保证多线程下单例

  1. public static Object getDao(String beanName) {
  2. //判断缓存中是否存在bean
  3. if (!cache.containsKey(beanName)) {
  4. //加锁
  5. synchronized (BeanFactory.class) {
  6. if (!cache.containsKey(beanName)) {
  7. // 将bean存入缓存
  8. //反射机制创建对象
  9. try {
  10. //读取到配置文件中的属性
  11. String value = properties.getProperty(beanName);
  12. Class clazz = Class.forName(value);
  13. Object object = clazz.getConstructor(null).newInstance(null);
  14. cache.put(beanName, object);
  15. } catch (ClassNotFoundException e) {
  16. e.printStackTrace();
  17. } catch (InstantiationException e) {
  18. e.printStackTrace();
  19. } catch (IllegalAccessException e) {
  20. e.printStackTrace();
  21. } catch (InvocationTargetException e) {
  22. e.printStackTrace();
  23. } catch (NoSuchMethodException e) {
  24. e.printStackTrace();
  25. }
  26. }
  27. }
  28. }
  29. return cache.get(beanName);
  30. }

2.Spring IoC 的使用

XML方式已经被淘汰,现主要考虑注解的方式

2.1 导入SpringFramework依赖

  1. <dependency>
  2. <groupId>org.springframework</groupId>
  3. <artifactId>spring-context</artifactId>
  4. <version>5.3.3</version>
  5. </dependency>
  1. package com.william.spring.entity;
  2. import lombok.Data;
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.beans.factory.annotation.Value;
  5. import org.springframework.stereotype.Component;
  6. @Data
  7. @Component
  8. public class Account {
  9. @Value("20193818")
  10. private Integer id;
  11. @Value("william")
  12. private String name;
  13. @Value("20")
  14. private Integer age;
  15. @Autowired
  16. private Order order;
  17. }
  1. package com.william.spring.entity;
  2. import lombok.Data;
  3. import org.springframework.beans.factory.annotation.Value;
  4. import org.springframework.stereotype.Component;
  5. @Component
  6. @Data
  7. public class Order {
  8. @Value("123")
  9. private Integer orderId;
  10. @Value("1000.0")
  11. private Float price;
  12. }
  1. package com.william.spring.test;
  2. import org.springframework.context.ApplicationContext;
  3. import org.springframework.context.annotation.AnnotationConfigApplicationContext;
  4. public class Test {
  5. public static void main(String[] args) {
  6. //加载IoC容器
  7. ApplicationContext applicationContext = new AnnotationConfigApplicationContext("com.william.spring.entity");
  8. System.out.println(applicationContext.getBean("account"));
  9. }
  10. }

3. IoC基于注解的执行原理

IoC组件

  • 扫描包的组件
    • 扫描所有添加了注解的目标类
  • 获取原材料的组件
    • 通过Class反射获取目标类
    • 通过beanName注入IoC
  • 根据原材料创建bean并以key-value的方式放入IoC

4. 手写IoC容器

4.1 思路

  1. 自定义一个MyAnnotationConfigApplicationContext,构造器中传入要扫描的包名
  2. 获取包下的所有类
  3. 遍历这些类,找出添加了@Component注解的类,获取其Class和对应的beanName,封装成BeanDefinition对象,存入集合Set(保证单例),这个集合即为IoC自动装载的原材料
  4. 遍历Set集合,根据原材料创建bean,还需检测属性有无@Value注解,如果有还需给属性赋值,后以k-v的方式存入缓存区
  5. 提供getBean方法,通过beanName得到bean

4.2 代码实现

  1. 先创建一个BeanDefinition对象
  1. package com.william.mySpring;
  2. import lombok.AllArgsConstructor;
  3. import lombok.Data;
  4. @Data
  5. @AllArgsConstructor
  6. public class BeanDefinition {
  7. private String beanName;
  8. private Class beanClass;
  9. }
  1. 创建注解
  1. package com.william.mySpring;
  2. import java.lang.annotation.ElementType;
  3. import java.lang.annotation.Retention;
  4. import java.lang.annotation.RetentionPolicy;
  5. import java.lang.annotation.Target;
  6. @Target(ElementType.TYPE)
  7. @Retention(RetentionPolicy.RUNTIME)
  8. public @interface Component {
  9. String value() default "";
  10. }
  1. package com.william.mySpring;
  2. import java.lang.annotation.ElementType;
  3. import java.lang.annotation.Retention;
  4. import java.lang.annotation.RetentionPolicy;
  5. import java.lang.annotation.Target;
  6. @Target(ElementType.FIELD)
  7. @Retention(RetentionPolicy.RUNTIME)
  8. public @interface Value {
  9. String value();
  10. }
  1. 设计一个工具类,能够遍历包下的所有类,返回一个集合
    ```java package com.william.mySpring;

import java.io.File; import java.io.FileFilter; import java.io.IOException; import java.net.JarURLConnection; import java.net.URL; import java.net.URLDecoder; import java.util.Enumeration; import java.util.LinkedHashSet; import java.util.Set; import java.util.jar.JarEntry; import java.util.jar.JarFile;

public class MyTools {

  1. public static Set<Class<?>> getClasses(String pack) {
  2. // 第一个class类的集合
  3. Set<Class<?>> classes = new LinkedHashSet<Class<?>>();
  4. // 是否循环迭代
  5. boolean recursive = true;
  6. // 获取包的名字 并进行替换
  7. String packageName = pack;
  8. String packageDirName = packageName.replace('.', '/');
  9. // 定义一个枚举的集合 并进行循环来处理这个目录下的things
  10. Enumeration<URL> dirs;
  11. try {
  12. dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);
  13. // 循环迭代下去
  14. while (dirs.hasMoreElements()) {
  15. // 获取下一个元素
  16. URL url = dirs.nextElement();
  17. // 得到协议的名称
  18. String protocol = url.getProtocol();
  19. // 如果是以文件的形式保存在服务器上
  20. if ("file".equals(protocol)) {
  21. // 获取包的物理路径
  22. String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
  23. // 以文件的方式扫描整个包下的文件 并添加到集合中
  24. findClassesInPackageByFile(packageName, filePath, recursive, classes);
  25. } else if ("jar".equals(protocol)) {
  26. // 如果是jar包文件
  27. // 定义一个JarFile
  28. System.out.println("jar类型的扫描");
  29. JarFile jar;
  30. try {
  31. // 获取jar
  32. jar = ((JarURLConnection) url.openConnection()).getJarFile();
  33. // 从此jar包 得到一个枚举类
  34. Enumeration<JarEntry> entries = jar.entries();
  35. findClassesInPackageByJar(packageName, entries, packageDirName, recursive, classes);
  36. } catch (IOException e) {
  37. // log.error("在扫描用户定义视图时从jar包获取文件出错");
  38. e.printStackTrace();
  39. }
  40. }
  41. }
  42. } catch (IOException e) {
  43. e.printStackTrace();
  44. }
  45. return classes;
  46. }
  47. private static void findClassesInPackageByJar(String packageName, Enumeration<JarEntry> entries, String packageDirName, final boolean recursive, Set<Class<?>> classes) {
  48. // 同样的进行循环迭代
  49. while (entries.hasMoreElements()) {
  50. // 获取jar里的一个实体 可以是目录 和一些jar包里的其他文件 如META-INF等文件
  51. JarEntry entry = entries.nextElement();
  52. String name = entry.getName();
  53. // 如果是以/开头的
  54. if (name.charAt(0) == '/') {
  55. // 获取后面的字符串
  56. name = name.substring(1);
  57. }
  58. // 如果前半部分和定义的包名相同
  59. if (name.startsWith(packageDirName)) {
  60. int idx = name.lastIndexOf('/');
  61. // 如果以"/"结尾 是一个包
  62. if (idx != -1) {
  63. // 获取包名 把"/"替换成"."
  64. packageName = name.substring(0, idx).replace('/', '.');
  65. }
  66. // 如果可以迭代下去 并且是一个包
  67. if ((idx != -1) || recursive) {
  68. // 如果是一个.class文件 而且不是目录
  69. if (name.endsWith(".class") && !entry.isDirectory()) {
  70. // 去掉后面的".class" 获取真正的类名
  71. String className = name.substring(packageName.length() + 1, name.length() - 6);
  72. try {
  73. // 添加到classes
  74. classes.add(Class.forName(packageName + '.' + className));
  75. } catch (ClassNotFoundException e) {
  76. // .error("添加用户自定义视图类错误 找不到此类的.class文件");
  77. e.printStackTrace();
  78. }
  79. }
  80. }
  81. }
  82. }
  83. }
  84. private static void findClassesInPackageByFile(String packageName, String packagePath, final boolean recursive, Set<Class<?>> classes) {
  85. // 获取此包的目录 建立一个File
  86. File dir = new File(packagePath);
  87. // 如果不存在或者 也不是目录就直接返回
  88. if (!dir.exists() || !dir.isDirectory()) {
  89. // log.warn("用户定义包名 " + packageName + " 下没有任何文件");
  90. return;
  91. }
  92. // 如果存在 就获取包下的所有文件 包括目录
  93. File[] dirfiles = dir.listFiles(new FileFilter() {
  94. // 自定义过滤规则 如果可以循环(包含子目录) 或则是以.class结尾的文件(编译好的java类文件)
  95. @Override
  96. public boolean accept(File file) {
  97. return (recursive && file.isDirectory()) || (file.getName().endsWith(".class"));
  98. }
  99. });
  100. // 循环所有文件
  101. for (File file : dirfiles) {
  102. // 如果是目录 则继续扫描
  103. if (file.isDirectory()) {
  104. findClassesInPackageByFile(packageName + "." + file.getName(), file.getAbsolutePath(), recursive, classes);
  105. } else {
  106. // 如果是java类文件 去掉后面的.class 只留下类名
  107. String className = file.getName().substring(0, file.getName().length() - 6);
  108. try {
  109. // 添加到集合中去
  110. // classes.add(Class.forName(packageName + '.' +
  111. // className));
  112. // 经过回复同学的提醒,这里用forName有一些不好,会触发static方法,没有使用classLoader的load干净
  113. classes.add(Thread.currentThread().getContextClassLoader().loadClass(packageName + '.' + className));
  114. } catch (ClassNotFoundException e) {
  115. // log.error("添加用户自定义视图类错误 找不到此类的.class文件");
  116. e.printStackTrace();
  117. }
  118. }
  119. }
  120. }

}

  1. 4. 创建MyAnnotationConfigApplicationContext类,实现将所有目标类封装成beanDefinition的集合并返回的方法,后创建bean,且根据@Value注解给属性赋值,根据@Autowired@Qualifier实现给对象类型的属性从容器中获取值,最后注入容器
  2. ```java
  3. package com.william.mySpring;
  4. import java.lang.reflect.Field;
  5. import java.lang.reflect.InvocationTargetException;
  6. import java.lang.reflect.Method;
  7. import java.util.*;
  8. public class MyAnnotationConfigApplicationContext {
  9. private Map<String, Object> ioc = new HashMap<>();
  10. public Object getBean(String beanName) {
  11. return ioc.get(beanName);
  12. }
  13. public MyAnnotationConfigApplicationContext(String pack) {
  14. // 遍历包,找到所有目标类,并封装成beanDefinition
  15. Set<BeanDefinition> beanDefinitions = findBeanDefinitions(pack);
  16. // 根据原材料创建bean
  17. creatObject(beanDefinitions);
  18. // 自动装载
  19. autowiredObject(beanDefinitions);
  20. }
  21. private void autowiredObject(Set<BeanDefinition> beanDefinitions) {
  22. Iterator<BeanDefinition> iterator = beanDefinitions.iterator();
  23. while (iterator.hasNext()) {
  24. BeanDefinition beanDefinition = iterator.next();
  25. Class beanClass = beanDefinition.getBeanClass();
  26. Field[] declaredFields = beanClass.getDeclaredFields();
  27. for (Field declaredField : declaredFields) {
  28. Autowired annotation = declaredField.getAnnotation(Autowired.class);
  29. if (annotation != null) {
  30. Qualifier qualifier = declaredField.getAnnotation(Qualifier.class);
  31. if (qualifier != null) {
  32. // byName
  33. try {
  34. String beanName = qualifier.value();
  35. Object bean = getBean(beanName);
  36. String fieldName = declaredField.getName();
  37. String setter = "set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
  38. Method method = beanClass.getMethod(setter, declaredField.getType());
  39. Object object = getBean(beanDefinition.getBeanName());
  40. method.invoke(object, bean);
  41. } catch (NoSuchMethodException e) {
  42. e.printStackTrace();
  43. } catch (IllegalAccessException e) {
  44. e.printStackTrace();
  45. } catch (InvocationTargetException e) {
  46. e.printStackTrace();
  47. }
  48. } else {
  49. // byType
  50. try {
  51. String typeName = declaredField.getType().getName();
  52. String beanName = typeName.replaceAll(declaredField.getType().getPackage().getName() + ".", "");
  53. beanName = beanName.substring(0, 1).toLowerCase() + beanName.substring(1);
  54. Object bean = getBean(beanName);
  55. String fieldName = declaredField.getName();
  56. String setter = "set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
  57. Method method = beanClass.getMethod(setter, declaredField.getType());
  58. Object object = getBean(beanDefinition.getBeanName());
  59. method.invoke(object, bean);
  60. } catch (NoSuchMethodException e) {
  61. e.printStackTrace();
  62. } catch (IllegalAccessException e) {
  63. e.printStackTrace();
  64. } catch (InvocationTargetException e) {
  65. e.printStackTrace();
  66. }
  67. }
  68. }
  69. }
  70. }
  71. }
  72. /**
  73. * 创建bean并且注入到IoC
  74. *
  75. * @param beanDefinitions
  76. */
  77. public void creatObject(Set<BeanDefinition> beanDefinitions) {
  78. Iterator<BeanDefinition> iterator = beanDefinitions.iterator();
  79. while (iterator.hasNext()) {
  80. BeanDefinition beanDefinition = iterator.next();
  81. Class beanClass = beanDefinition.getBeanClass();
  82. String beanName = beanDefinition.getBeanName();
  83. try {
  84. Object instance = beanClass.getConstructor().newInstance();
  85. //根据@Value给成员变量赋值
  86. Field[] declaredFields = beanClass.getDeclaredFields();
  87. for (Field declaredField : declaredFields) {
  88. Value annotationVar = declaredField.getAnnotation(Value.class);
  89. if (annotationVar != null) {
  90. //获取注解的值并完成类型转换
  91. String value = annotationVar.value();
  92. Object val = null;
  93. switch (declaredField.getType().getName()) {
  94. case "java.lang.Integer":
  95. val = Integer.parseInt(value);
  96. break;
  97. case "java.lang.String":
  98. val = value;
  99. break;
  100. case "java.lang.Float":
  101. val = Float.parseFloat(value);
  102. break;
  103. }
  104. String fieldName = declaredField.getName();
  105. String setterName = "set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
  106. Method method = beanClass.getMethod(setterName, declaredField.getType());
  107. method.invoke(instance, val);
  108. }
  109. }
  110. ioc.put(beanName, instance);
  111. } catch (InstantiationException e) {
  112. e.printStackTrace();
  113. } catch (InvocationTargetException e) {
  114. e.printStackTrace();
  115. } catch (NoSuchMethodException e) {
  116. e.printStackTrace();
  117. } catch (IllegalAccessException e) {
  118. e.printStackTrace();
  119. }
  120. }
  121. }
  122. /**
  123. * 将包下的所有类扫描,将带有@Component注解的类封装成BeanDefinition对象放入集合返回
  124. *
  125. * @param pack
  126. * @return
  127. */
  128. public Set<BeanDefinition> findBeanDefinitions(String pack) {
  129. // 1. 获取包下所有类
  130. Set<Class<?>> classes = MyTools.getClasses(pack);
  131. Iterator<Class<?>> iterator = classes.iterator();
  132. Set<BeanDefinition> beanDefinitions = new HashSet<>();
  133. while (iterator.hasNext()) {
  134. // 2. 遍历这些类找到添加了@Component的类
  135. Class<?> clazz = iterator.next();
  136. Component componentAnnotation = clazz.getAnnotation(Component.class);
  137. if (componentAnnotation != null) {
  138. // 如果添加了@Component,则获取component的value作为beanName
  139. String beanName = componentAnnotation.value();
  140. // 如果component的value为空,则将类名的首字母小写形式作为beanName
  141. if ("".equals(beanName)) {
  142. String className = clazz.getName().replaceAll(clazz.getPackage().getName() + ".", "");
  143. beanName = className.substring(0, 1).toLowerCase() + className.substring(1);
  144. }
  145. // 3. 将这些类封装成BeanDefinition,装载到Set中
  146. beanDefinitions.add(new BeanDefinition(beanName, clazz));
  147. }
  148. }
  149. return beanDefinitions;
  150. }
  151. }